Ad Extensions Code Example

This example shows how to add, get, and delete extensions for an account's ad extension library, set, get, and delete the extension associations with a campaign, and determine why an extension failed editorial reviews.

Tip

To get access and refresh tokens for your Bing Ads user and make your first service call using the Bing Ads API, see the Quick Start sample. You'll want to review the Get Started guide and walkthroughs for your preferred language e.g., C#, Java, Php, and Python.

Supporting files for C#, Java, Php, and Python examples are available at GitHub. You can clone each repository or repurpose snippets as needed.

using System;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Threading.Tasks;
using Microsoft.BingAds.V12.CampaignManagement;
using Microsoft.BingAds;

namespace BingAdsExamplesLibrary.V12
{
    /// <summary>
    /// This example demonstrates how to add, get, and delete extensions for an account's ad extension library, 
    /// set, get, and delete the extension associations with a campaign, and determine why an extension failed 
    /// editorial review.
    /// </summary>
    public class AdExtensions : ExampleBase
    {
        public override string Description
        {
            get { return "Ad Extensions | Campaign Management V12"; }
        }

        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment;

                CampaignManagementExampleHelper CampaignManagementExampleHelper = 
                    new CampaignManagementExampleHelper(this.OutputStatusMessage);
                CampaignManagementExampleHelper.CampaignManagementService = 
                    new ServiceClient<ICampaignManagementService>(authorizationData, environment);

                // Add a campaign that will later be associated with ad extensions. 

                var campaigns = new[] {
                    new Campaign
                    {
                        Name = "Women's Shoes " + DateTime.UtcNow,
                        Description = "Red shoes line.",

                        // You must choose to set either the shared  budget ID or daily amount.
                        // You can set one or the other, but you may not set both.
                        BudgetId = null,
                        DailyBudget = 50,
                        BudgetType = BudgetLimitType.DailyBudgetStandard,
                        BiddingScheme = new EnhancedCpcBiddingScheme(),

                        TimeZone = "PacificTimeUSCanadaTijuana",

                        // Used with FinalUrls shown in the sitelinks that we will add below.
                        TrackingUrlTemplate =
                            "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}"
                    }
                };

                AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync(
                    authorizationData.AccountId, 
                    campaigns,
                    false);
                long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray();
                CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds);
                CampaignManagementExampleHelper.OutputArrayOfBatchError(addCampaignsResponse?.PartialErrors);

                // Specify the extensions.

                var adExtensions = new AdExtension[] {
                    //new AppAdExtension
                    //{
                    //    AppPlatform = "Windows",
                    //    AppStoreId = "AppStoreIdGoesHere",
                    //    DestinationUrl = "DestinationUrlGoesHere",
                    //    DisplayText = "Contoso",
                    //},
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly = false,
                        Scheduling = new Schedule {

                            // For this example assume the call center is open Monday - Friday from 9am - 9pm
                            // in the account's time zone.

                            UseSearcherTimeZone = false,
                            DayTimeRanges = new[]
                            {
                                new DayTime
                                {
                                    Day = Day.Monday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Tuesday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Wednesday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Thursday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Friday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date {
                                Month = 12,
                                Day = 31,
                                Year = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new CalloutAdExtension
                    {
                        Text = "Callout Text"
                    },
                    //new ImageAdExtension
                    //{
                    //    AlternativeText = "Image Extension Alt Text",
                    //    ImageMediaIds = new long[] {
                    //        (await CampaignManagementExampleHelper.AddMediaAsync(
                    //            authorizationData.AccountId, 
                    //            GetImageMedia())).MediaIds[0]
                    //    }
                    //},
                    new LocationAdExtension {
                        PhoneNumber = "206-555-0100",
                        CompanyName = "Contoso Shoes",
                        Address = new Microsoft.BingAds.V12.CampaignManagement.Address {
                            StreetAddress = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName = "Woodinville",
                            ProvinceName = "WA",
                            CountryCode = "US",
                            PostalCode = "98608"
                        },
                        Scheduling = new Schedule {

                            // For this example assume you want to drive traffic every Saturday morning
                            // in the search user's time zone.

                            UseSearcherTimeZone = true,
                            DayTimeRanges = new[]
                            {
                                new DayTime
                                {
                                    Day = Day.Saturday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 12,
                                    EndMinute = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date {
                                Month = 12,
                                Day = 31,
                                Year = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new PriceAdExtension
                    {
                        Language = "English",
                        TableRows = new PriceTableRow[]
                        {
                            new PriceTableRow
                            {
                                CurrencyCode = "USD",
                                Description = "Come to the event",
                                FinalUrls = new string[]
                                {
                                    "https://contoso.com"
                                },
                                Header = "New Event",
                                Price = 9.99,
                                PriceQualifier = PriceQualifier.From,
                                PriceUnit = PriceUnit.PerDay,
                            },
                            new PriceTableRow
                            {
                                CurrencyCode = "USD",
                                Description = "Come to the next event",
                                FinalUrls = new string[]
                                {
                                    "https://contoso.com"
                                },
                                Header = "Next Event",
                                Price = 9.99,
                                PriceQualifier = PriceQualifier.From,
                                PriceUnit = PriceUnit.PerDay,
                            },
                            new PriceTableRow
                            {
                                CurrencyCode = "USD",
                                Description = "Come to the final event",
                                FinalUrls = new string[]
                                {
                                    "https://contoso.com"
                                },
                                Header = "Final Event",
                                Price = 9.99,
                                PriceQualifier = PriceQualifier.From,
                                PriceUnit = PriceUnit.PerDay,
                            },
                        },
                        PriceExtensionType = PriceExtensionType.Events,
                        TrackingUrlTemplate = "http://tracker.com?url={lpurl}&matchtype={matchtype}",
                        UrlCustomParameters = new CustomParameters
                        {
                            // Each custom parameter is delimited by a semicolon (;) in the Bulk file
                            Parameters = new[] {
                                new CustomParameter(){
                                    Key = "promoCode",
                                    Value = "PROMO1"
                                },
                                new CustomParameter(){
                                    Key = "season",
                                    Value = "summer"
                                },
                            }
                        },
                    },
                    new ReviewAdExtension
                    {
                        IsExact = true,
                        Source = "Review Source Name",
                        Text = "Review Text",
                        Url = "http://review.contoso.com" // The Url of the third-party review. This is not your business Url.
                    },
                    new StructuredSnippetAdExtension
                    {
                        Header = "Brands",
                        Values = new [] { "Windows", "Xbox", "Skype"}
                    }
                };

                // Get sitelink ad extensions
                adExtensions = adExtensions.Concat(GetSampleSitelinkAdExtensions()).ToArray();


                // Add all extensions to the account's ad extension library
                var addAdExtensionsResponse = (await CampaignManagementExampleHelper.AddAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensions
                ));
                var adExtensionIdentities = addAdExtensionsResponse?.AdExtensionIdentities;
                CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(addAdExtensionsResponse?.NestedPartialErrors);

                OutputStatusMessage("Added ad extensions.\n");

                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new List<AdExtensionIdToEntityIdAssociation>();

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new List<long>();

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations. 

                foreach (var adExtensionIdentity in adExtensionIdentities)
                {
                    if (adExtensionIdentity != null)
                    {
                        adExtensionIdToEntityIdAssociations.Add(new AdExtensionIdToEntityIdAssociation
                        {
                            AdExtensionId = adExtensionIdentity.Id,
                            EntityId = (long)campaignIds[0]
                        });

                        adExtensionIds.Add(adExtensionIdentity.Id);
                    }
                }

                // Associate the specified ad extensions with the respective campaigns or ad groups. 
                await CampaignManagementExampleHelper.SetAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                );

                OutputStatusMessage("Set ad extension associations.\n");

                // Get editorial rejection reasons for the respective ad extension and entity associations.
                var getAdExtensionsEditorialReasonsResponse =
                    (await CampaignManagementExampleHelper.GetAdExtensionsEditorialReasonsAsync(
                        authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations,
                        AssociationType.Campaign
                    ));
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[])getAdExtensionsEditorialReasonsResponse?.EditorialReasons;
                CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsEditorialReasonsResponse?.PartialErrors);

                AdExtensionsTypeFilter adExtensionsTypeFilter =
                    AdExtensionsTypeFilter.AppAdExtension |
                    AdExtensionsTypeFilter.CallAdExtension |
                    AdExtensionsTypeFilter.CalloutAdExtension |
                    AdExtensionsTypeFilter.ImageAdExtension |
                    AdExtensionsTypeFilter.LocationAdExtension |
                    AdExtensionsTypeFilter.SitelinkAdExtension |
                    AdExtensionsTypeFilter.PriceAdExtension |
                    AdExtensionsTypeFilter.ReviewAdExtension |
                    AdExtensionsTypeFilter.StructuredSnippetAdExtension;

                // Get all ad extensions added above.
                var getAdExtensionsByIdsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter
                ));
                adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray();
                CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsByIdsResponse?.PartialErrors);

                OutputStatusMessage("List of ad extensions that were added above:\n");
                CampaignManagementExampleHelper.OutputArrayOfAdExtension(adExtensions);

                // Get only the location extensions and remove scheduling.

                adExtensionsTypeFilter = AdExtensionsTypeFilter.LocationAdExtension;

                getAdExtensionsByIdsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter
                ));
                adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray();

                // In this example partial errors will be returned for indices where the ad extensions 
                // are not location ad extensions because we only requested AdExtensionsTypeFilter.LocationAdExtension.
                // This is an example, and ideally you would only send the required ad extension IDs.

                CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsByIdsResponse?.PartialErrors);

                var updateExtensions = new List<AdExtension>();
                var updateExtensionIds = new List<long>();

                foreach (var extension in adExtensions)
                {
                    // GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met.
                    if (extension != null && extension.Id != null)
                    {
                        // Remove read-only elements that would otherwise cause the update operation to fail.
                        var updateExtension = SetReadOnlyAdExtensionElementsToNull(extension);

                        // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
                        // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
                        // for the ad extension. In this example, we will remove any existing scheduling by setting this element  
                        // to an empty Schedule object.
                        updateExtension.Scheduling = new Schedule { };

                        updateExtensions.Add(updateExtension);
                        updateExtensionIds.Add((long)updateExtension.Id);
                    }
                }

                OutputStatusMessage("Removing scheduling from the location ad extensions..\n");
                await CampaignManagementExampleHelper.UpdateAdExtensionsAsync(authorizationData.AccountId, updateExtensions);

                // Get only the location extensions to output the result.

                getAdExtensionsByIdsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    updateExtensionIds,
                    adExtensionsTypeFilter
                ));
                adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray();
                CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsByIdsResponse?.PartialErrors);

                OutputStatusMessage("List of ad extensions that were updated above:\n");
                CampaignManagementExampleHelper.OutputArrayOfAdExtension(adExtensions);

                // Delete the ad extension associations, ad extensions, and campaign, that were previously added.  
                // At this point the ad extensions are still available in the account's ad extensions library. 

                await CampaignManagementExampleHelper.DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                );
                OutputStatusMessage("Deleted ad extension associations.\n");

                // Delete the ad extensions from the account�s ad extension library.

                await CampaignManagementExampleHelper.DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds
                );
                OutputStatusMessage("Deleted ad extensions.\n");

                await CampaignManagementExampleHelper.DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] });
                OutputStatusMessage(string.Format("Deleted Campaign Id {0}\n", (long)campaignIds[0]));
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException<Microsoft.BingAds.V12.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V12.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V12.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            // Catch Customer Management service exceptions
            catch (FaultException<Microsoft.BingAds.V12.CustomerManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V12.CustomerManagement.ApiFault> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }

        private IList<Media> GetImageMedia()
        {
            var media = new List<Media>();
            var image = new Microsoft.BingAds.V12.CampaignManagement.Image();

            // This example uses an image with 1.5:1 aspect ratio.
            // For more information about available aspect ratios and min / max dimensions,
            // see the Image data object reference documentation.

            image.Data = GetImage15x10Data();
            image.Type = "Image15x10";
            image.MediaType = "Image";
            media.Add(image);

            var request = new AddMediaRequest
            {
                Media = media
            };

            return media;
        }

        public string GetImage15x10Data()
        {
            var png = new System.Drawing.Bitmap("blankimageadextension.png");
            using (MemoryStream ms = new MemoryStream())
            {
                png.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                byte[] imageBytes = ms.ToArray();
                string base64String = Convert.ToBase64String(imageBytes);
                return base64String;
            }
        }

        // Gets an example SitelinkAdExtension list. 
        private SitelinkAdExtension[] GetSampleSitelinkAdExtensions()
        {
            return new[] {
                new SitelinkAdExtension {
                    Description1 = "Simple & Transparent.",
                    Description2 = "No Upfront Cost.",
                    DisplayText = "Women's Shoe Sale 1",

                    // With FinalUrls you can separate the tracking template, custom parameters, and 
                    // landing page URLs. 
                    FinalUrls = new[] {
                        "http://www.contoso.com/womenshoesale"
                    },
                    // Final Mobile URLs can also be used if you want to direct the user to a different page 
                    // for mobile devices.
                    FinalMobileUrls = new[] {
                        "http://mobile.contoso.com/womenshoesale"
                    },
                    // You could use a tracking template which would override the campaign level
                    // tracking template. Tracking templates defined for lower level entities 
                    // override those set for higher level entities.
                    // In this example we are using the campaign level tracking template.
                    TrackingUrlTemplate = null,

                    // Set custom parameters that are specific to this ad extension, 
                    // and can be used by the ad extension, ad group, campaign, or account level tracking template. 
                    // In this example we are using the campaign level tracking template.
                    UrlCustomParameters = new CustomParameters
                    {
                        Parameters = new[] {
                            new CustomParameter(){
                                Key = "promoCode",
                                Value = "PROMO1"
                            },
                            new CustomParameter(){
                                Key = "season",
                                Value = "summer"
                            },
                        }
                    },
                },
                new SitelinkAdExtension
                {
                    Description1 = "Do Amazing Things With Contoso.",
                    Description2 = "Read Our Case Studies.",
                    DisplayText = "Women's Shoe Sale 2",

                    // With FinalUrls you can separate the tracking template, custom parameters, and 
                    // landing page URLs. 
                    FinalUrls = new[] {
                        "http://www.contoso.com/womenshoesale"
                    },
                    // Final Mobile URLs can also be used if you want to direct the user to a different page 
                    // for mobile devices.
                    FinalMobileUrls = new[] {
                        "http://mobile.contoso.com/womenshoesale"
                    },

                    Scheduling = new Schedule {

                        // For this example assume you want to drive traffic every Saturday morning
                        // in the search user's time zone.

                        UseSearcherTimeZone = true,
                        DayTimeRanges = new[]
                        {
                            new DayTime
                            {
                                Day = Day.Saturday,
                                StartHour = 9,
                                StartMinute = Minute.Zero,
                                EndHour = 12,
                                EndMinute = Minute.Zero,
                            },
                        },
                        StartDate = null,
                        EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date {
                            Month = 12,
                            Day = 31,
                            Year = DateTime.UtcNow.Year + 1
                        },
                    },

                    // You could use a tracking template which would override the campaign level
                    // tracking template. Tracking templates defined for lower level entities 
                    // override those set for higher level entities.
                    // In this example we are using the campaign level tracking template.
                    TrackingUrlTemplate = null,

                    // Set custom parameters that are specific to this ad extension, 
                    // and can be used by the ad extension, ad group, campaign, or account level tracking template. 
                    // In this example we are using the campaign level tracking template.
                    UrlCustomParameters = new CustomParameters
                    {
                        Parameters = new[] {
                            new CustomParameter(){
                                Key = "promoCode",
                                Value = "PROMO2"
                            },
                            new CustomParameter(){
                                Key = "season",
                                Value = "summer"
                            },
                        }
                    },
                }
            };
        }
    }
}
package com.microsoft.bingads.examples.v12;

import java.util.ArrayList;
import java.util.Calendar;

import com.microsoft.bingads.*;
import com.microsoft.bingads.v12.campaignmanagement.*;

public class AdExtensions extends ExampleBase {

    public static void main(java.lang.String[] args) {
     
        try
        {
            //authenticateWithOAuth();
            authorizationData = getAuthorizationData(null,null);

            CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient<ICampaignManagementService>(
                    authorizationData,
                    API_ENVIRONMENT,
                    ICampaignManagementService.class);
                        
            Calendar calendar = Calendar.getInstance();
                                      
            // Specify one or more campaigns.

            ArrayOfCampaign campaigns = new ArrayOfCampaign();
            Campaign campaign = new Campaign();
            campaign.setName("Summer Shoes " + System.currentTimeMillis());
            campaign.setDescription("Summer shoes line.");
            campaign.setBudgetType(BudgetLimitType.DAILY_BUDGET_STANDARD);
            campaign.setDailyBudget(50.00);
            campaign.setTimeZone("PacificTimeUSCanadaTijuana");

            // Used with FinalUrls shown in the sitelinks that we will add below.
            campaign.setTrackingUrlTemplate(
                        "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}");

            campaigns.getCampaigns().add(campaign);

            AddCampaignsResponse addCampaignsResponse = CampaignManagementExampleHelper.addCampaigns(
                    authorizationData.getAccountId(), 
                    campaigns,
                    false);
            ArrayOfNullableOflong campaignIds = addCampaignsResponse.getCampaignIds();
            ArrayOfBatchError campaignErrors = addCampaignsResponse.getPartialErrors();
            CampaignManagementExampleHelper.outputArrayOfNullableOflong(campaignIds);
            CampaignManagementExampleHelper.outputArrayOfBatchError(campaignErrors);

            // Specify the extensions.

            ArrayOfAdExtension adExtensions = new ArrayOfAdExtension();

            AppAdExtension appAdExtension = new AppAdExtension();
            appAdExtension.setAppPlatform("Windows");
            appAdExtension.setAppStoreId("AppStoreIdGoesHere");
            appAdExtension.setDestinationUrl("DestinationUrlGoesHere");
            appAdExtension.setDisplayText("Contoso");
            // If you supply the AppAdExtension properties above, then you can add this line.
            //adExtensions.getAdExtensions().add(appAdExtension);

            CallAdExtension callAdExtension = new CallAdExtension();
            callAdExtension.setCountryCode("US");
            callAdExtension.setPhoneNumber("2065550100");
            callAdExtension.setIsCallOnly(false);
            // For this example assume the call center is open Monday - Friday from 9am - 9pm
            // in the account's time zone.
            Schedule callScheduling = new Schedule();
            ArrayOfDayTime callDayTimeRanges = new ArrayOfDayTime();
            DayTime callMonday = new DayTime();
            callMonday.setDay(Day.MONDAY);
            callMonday.setStartHour(9);
            callMonday.setStartMinute(Minute.ZERO);
            callMonday.setEndHour(21);
            callMonday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callMonday);
            DayTime callTuesday = new DayTime();
            callTuesday.setDay(Day.TUESDAY);
            callTuesday.setStartHour(9);
            callTuesday.setStartMinute(Minute.ZERO);
            callTuesday.setEndHour(21);
            callTuesday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callTuesday);
            DayTime callWednesday = new DayTime();
            callWednesday.setDay(Day.WEDNESDAY);
            callWednesday.setStartHour(9);
            callWednesday.setStartMinute(Minute.ZERO);
            callWednesday.setEndHour(21);
            callWednesday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callWednesday);
            DayTime callThursday = new DayTime();
            callThursday.setDay(Day.THURSDAY);
            callThursday.setStartHour(9);
            callThursday.setStartMinute(Minute.ZERO);
            callThursday.setEndHour(21);
            callThursday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callThursday);
            DayTime callFriday = new DayTime();
            callFriday.setDay(Day.FRIDAY);
            callFriday.setStartHour(9);
            callFriday.setStartMinute(Minute.ZERO);
            callFriday.setEndHour(21);
            callFriday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callFriday);
            callScheduling.setDayTimeRanges(callDayTimeRanges);
            callScheduling.setEndDate(new com.microsoft.bingads.v12.campaignmanagement.Date());
            callScheduling.getEndDate().setDay(31);
            callScheduling.getEndDate().setMonth(12);
            callScheduling.getEndDate().setYear(calendar.get(Calendar.YEAR) + 1);
            callScheduling.setStartDate(null);
            callAdExtension.setScheduling(callScheduling);
            adExtensions.getAdExtensions().add(callAdExtension);

            CalloutAdExtension calloutAdExtension = new CalloutAdExtension();
            calloutAdExtension.setText("Callout text");
            adExtensions.getAdExtensions().add(calloutAdExtension);

            LocationAdExtension locationAdExtension = new LocationAdExtension();
            locationAdExtension.setPhoneNumber("206-555-0100");
            locationAdExtension.setCompanyName("Contoso Shoes");
            com.microsoft.bingads.v12.campaignmanagement.Address address = 
                    new com.microsoft.bingads.v12.campaignmanagement.Address();
            address.setStreetAddress("1234 Washington Place");
            address.setStreetAddress2("Suite 1210");
            address.setCityName("Woodinville");
            address.setProvinceName("WA"); 
            address.setCountryCode("US");
            address.setPostalCode("98608");
            locationAdExtension.setAddress(address);
            // For this example assume you want to drive traffic every Saturday morning
            // in the search user's time zone.
            Schedule locationScheduling = new Schedule();
            ArrayOfDayTime locationDayTimeRanges = new ArrayOfDayTime();
            DayTime locationDayTime = new DayTime();
            locationDayTime.setDay(Day.SATURDAY);
            locationDayTime.setStartHour(9);
            locationDayTime.setStartMinute(Minute.ZERO);
            locationDayTime.setEndHour(12);
            locationDayTime.setEndMinute(Minute.ZERO);
            locationDayTimeRanges.getDayTimes().add(locationDayTime);
            locationScheduling.setDayTimeRanges(locationDayTimeRanges);
            locationScheduling.setEndDate(new com.microsoft.bingads.v12.campaignmanagement.Date());
            locationScheduling.getEndDate().setDay(31);
            locationScheduling.getEndDate().setMonth(12);
            locationScheduling.getEndDate().setYear(calendar.get(Calendar.YEAR) + 1);
            locationScheduling.setStartDate(null);
            locationAdExtension.setScheduling(locationScheduling);
            adExtensions.getAdExtensions().add(locationAdExtension);

            ReviewAdExtension reviewAdExtension = new ReviewAdExtension();
            reviewAdExtension.setIsExact(true);
            reviewAdExtension.setSource("Review Source Name");
            reviewAdExtension.setText("Review Text");
            // The Url of the third-party review. This is not your business Url.
            reviewAdExtension.setUrl("http://review.contoso.com"); 
            adExtensions.getAdExtensions().add(reviewAdExtension);
                        
            StructuredSnippetAdExtension structuredSnippetAdExtension = new StructuredSnippetAdExtension();
            structuredSnippetAdExtension.setHeader("Brands");
            com.microsoft.bingads.v12.campaignmanagement.ArrayOfstring values = new com.microsoft.bingads.v12.campaignmanagement.ArrayOfstring();
            values.getStrings().add("Windows");
            values.getStrings().add("Xbox");
            values.getStrings().add("Skype");
            structuredSnippetAdExtension.setValues(values);
            adExtensions.getAdExtensions().add(structuredSnippetAdExtension);
            
            // We are using a helper function to get sample sitelink ad extensions.
            adExtensions.getAdExtensions().addAll(getSampleSitelinkAdExtensions());
            
            // Add all extensions to the account's ad extension library
            AddAdExtensionsResponse addAdExtensionsResponse = CampaignManagementExampleHelper.addAdExtensions(
                authorizationData.getAccountId(),
                adExtensions
                );

            CampaignManagementExampleHelper.outputArrayOfBatchErrorCollection(addAdExtensionsResponse.getNestedPartialErrors());
            ArrayOfAdExtensionIdentity adExtensionIdentities = addAdExtensionsResponse.getAdExtensionIdentities();
                    
            outputStatusMessage("Added ad extensions.\n");

            // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
            // operations each require a list of type AdExtensionIdToEntityIdAssociation.
            ArrayOfAdExtensionIdToEntityIdAssociation adExtensionIdToEntityIdAssociations = new ArrayOfAdExtensionIdToEntityIdAssociation();

            // GetAdExtensionsByIds requires a list of type long.
            ArrayOflong adExtensionIds = new ArrayOflong();

            // Loop through the list of extension IDs and build any required data structures
            // for subsequent operations. 

            for (AdExtensionIdentity adExtensionIdentity : adExtensionIdentities.getAdExtensionIdentities()) {
                AdExtensionIdToEntityIdAssociation adExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation();
                adExtensionIdToEntityIdAssociation.setAdExtensionId(adExtensionIdentity.getId());
                adExtensionIdToEntityIdAssociation.setEntityId(campaignIds.getLongs().get(0));
                adExtensionIdToEntityIdAssociations.getAdExtensionIdToEntityIdAssociations().add(adExtensionIdToEntityIdAssociation);

                adExtensionIds.getLongs().add(adExtensionIdentity.getId());
            }

            // Associate the specified ad extensions with the respective campaigns or ad groups. 
            CampaignManagementExampleHelper.setAdExtensionsAssociations(
                authorizationData.getAccountId(), 
                adExtensionIdToEntityIdAssociations, 
                AssociationType.CAMPAIGN
                );

            outputStatusMessage("Set ad extension associations.\n");

            // Get editorial rejection reasons for the respective ad extension and entity associations.
            GetAdExtensionsEditorialReasonsResponse getAdExtensionsEditorialReasonsResponse = CampaignManagementExampleHelper.getAdExtensionsEditorialReasons(
                authorizationData.getAccountId(), 
                adExtensionIdToEntityIdAssociations, 
                AssociationType.CAMPAIGN
                );
            
            ArrayOfAdExtensionEditorialReasonCollection adExtensionEditorialReasonCollection = getAdExtensionsEditorialReasonsResponse.getEditorialReasons();

            ArrayList<AdExtensionsTypeFilter> adExtensionsTypeFilter = new ArrayList<AdExtensionsTypeFilter>();
            
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.APP_AD_EXTENSION);
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.CALL_AD_EXTENSION);
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.CALLOUT_AD_EXTENSION);
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.LOCATION_AD_EXTENSION);
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.REVIEW_AD_EXTENSION);
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.SITELINK_AD_EXTENSION);
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.STRUCTURED_SNIPPET_AD_EXTENSION);
                        
            // Get the specified ad extensions from the account's ad extension library.
            GetAdExtensionsByIdsResponse getAdExtensionsByIdsResponse = CampaignManagementExampleHelper.getAdExtensionsByIds(
                authorizationData.getAccountId(),
                adExtensionIds, 
                adExtensionsTypeFilter
            );
            adExtensions = getAdExtensionsByIdsResponse.getAdExtensions();
            
            outputStatusMessage("List of ad extensions that were added above:\n");
            CampaignManagementExampleHelper.outputArrayOfAdExtension(adExtensions);
            CampaignManagementExampleHelper.outputArrayOfAdExtensionEditorialReasonCollection(adExtensionEditorialReasonCollection);
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAdExtensionsByIdsResponse.getPartialErrors());            
            
            // Get only the location extensions and remove scheduling.

            adExtensionsTypeFilter = new ArrayList<AdExtensionsTypeFilter>();
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.LOCATION_AD_EXTENSION);

            getAdExtensionsByIdsResponse = CampaignManagementExampleHelper.getAdExtensionsByIds(
                authorizationData.getAccountId(),
                adExtensionIds,
                adExtensionsTypeFilter
            );
            adExtensions = getAdExtensionsByIdsResponse.getAdExtensions();
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAdExtensionsByIdsResponse.getPartialErrors());

            ArrayOfAdExtension updateExtensions = new ArrayOfAdExtension();
            ArrayOflong updateExtensionIds = new ArrayOflong();

            for (AdExtension extension : adExtensions.getAdExtensions())
            {
                // GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met.
                if(extension != null && extension.getId() != null)
                {
                    // Remove read-only elements that would otherwise cause the update operation to fail.
                    AdExtension updateExtension = setReadOnlyAdExtensionElementsToNull(extension);

                    // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
                    // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
                    // for the ad extension. In this example, we will remove any existing scheduling by setting this element  
                    // to an empty Schedule object.
                    updateExtension.setScheduling(new Schedule());

                    updateExtensions.getAdExtensions().add(updateExtension);
                    updateExtensionIds.getLongs().add((long)updateExtension.getId());
                }
            }

            outputStatusMessage("Removing scheduling from the location ad extensions..\n");
            CampaignManagementExampleHelper.updateAdExtensions(authorizationData.getAccountId(), updateExtensions);

            // Get only the location extension to output the result.
            getAdExtensionsByIdsResponse = CampaignManagementExampleHelper.getAdExtensionsByIds(
                authorizationData.getAccountId(),
                updateExtensionIds,
                adExtensionsTypeFilter
            );
            adExtensions = getAdExtensionsByIdsResponse.getAdExtensions();
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAdExtensionsByIdsResponse.getPartialErrors());

            outputStatusMessage("List of ad extensions that were updated above:\n");
            CampaignManagementExampleHelper.outputArrayOfAdExtension(adExtensions);
            CampaignManagementExampleHelper.outputArrayOfAdExtensionEditorialReasonCollection(adExtensionEditorialReasonCollection);
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAdExtensionsByIdsResponse.getPartialErrors());   


            // Remove the specified associations from the respective campaigns or ad groups. 
            // The extensions are still available in the account's extensions library. 
            CampaignManagementExampleHelper.deleteAdExtensionsAssociations(
                authorizationData.getAccountId(),
                adExtensionIdToEntityIdAssociations,
                AssociationType.CAMPAIGN
                );

            outputStatusMessage("Deleted ad extension associations.\n");

            // Deletes the ad extensions from the account's ad extension library.
            CampaignManagementExampleHelper.deleteAdExtensions(
                authorizationData.getAccountId(),
                adExtensionIds
                );

            outputStatusMessage("Deleted ad extensions.\n");

            // Delete the campaign from the account.

            ArrayOflong deleteCampaignIds = new ArrayOflong();
            deleteCampaignIds.getLongs().add(campaignIds.getLongs().get(0));
            CampaignManagementExampleHelper.deleteCampaigns(authorizationData.getAccountId(), deleteCampaignIds);
            outputStatusMessage(String.format("Deleted CampaignId %d\n", campaignIds.getLongs().get(0)));
        
        } 
        catch (Exception ex) {
            String faultXml = BingAdsExceptionHelper.getBingAdsExceptionFaultXml(ex, System.out);
            String message = BingAdsExceptionHelper.handleBingAdsSDKException(ex, System.out);
            ex.printStackTrace();
        }
    }
        
    private static ArrayList<SitelinkAdExtension> getSampleSitelinkAdExtensions(){
        ArrayList<SitelinkAdExtension> sitelinkAdExtensions = new ArrayList<SitelinkAdExtension>();
        
        // Define the first SitelinkAdExtension
        
        SitelinkAdExtension sitelinkAdExtensionA = new SitelinkAdExtension();
        sitelinkAdExtensionA.setDescription1("Simple & Transparent.");
        sitelinkAdExtensionA.setDescription2("No Upfront Cost.");
        sitelinkAdExtensionA.setDisplayText("Women's Shoe Sale 1");

        // With FinalUrls you can separate the tracking template, custom parameters, and 
        // landing page URLs. 
        com.microsoft.bingads.v12.campaignmanagement.ArrayOfstring finalUrls = new com.microsoft.bingads.v12.campaignmanagement.ArrayOfstring();
        finalUrls.getStrings().add("http://www.contoso.com/womenshoesale");
        sitelinkAdExtensionA.setFinalUrls(finalUrls);

        // Final Mobile URLs can also be used if you want to direct the user to a different page 
        // for mobile devices.
        com.microsoft.bingads.v12.campaignmanagement.ArrayOfstring finalMobileUrls = new com.microsoft.bingads.v12.campaignmanagement.ArrayOfstring();
        finalMobileUrls.getStrings().add("http://mobile.contoso.com/womenshoesale");
        sitelinkAdExtensionA.setFinalMobileUrls(finalMobileUrls);

        // You could use a tracking template which would override the campaign level
        // tracking template. Tracking templates defined for lower level entities 
        // override those set for higher level entities.
        // In this example we are using the campaign level tracking template.
        sitelinkAdExtensionA.setTrackingUrlTemplate(null);

        // Set custom parameters that are specific to this ad extension, 
        // and can be used by the ad extension, ad group, campaign, or account level tracking template. 
        // In this example we are using the campaign level tracking template.
        CustomParameters urlCustomParameters = new CustomParameters();
        CustomParameter customParameter1 = new CustomParameter();
        customParameter1.setKey("promoCode");
        customParameter1.setValue("PROMO1");
        ArrayOfCustomParameter customParameters = new ArrayOfCustomParameter();
        customParameters.getCustomParameters().add(customParameter1);
        CustomParameter customParameter2 = new CustomParameter();
        customParameter2.setKey("season");
        customParameter2.setValue("summer");
        customParameters.getCustomParameters().add(customParameter2);
        urlCustomParameters.setParameters(customParameters);
        sitelinkAdExtensionA.setUrlCustomParameters(urlCustomParameters);

        sitelinkAdExtensions.add(sitelinkAdExtensionA);
        
        // Define the second SitelinkAdExtension
        
        SitelinkAdExtension sitelinkAdExtensionB = new SitelinkAdExtension();
        sitelinkAdExtensionB.setDescription1("Do Amazing Things With Contoso.");
        sitelinkAdExtensionB.setDescription2("Read Our Case Studies.");
        sitelinkAdExtensionB.setDisplayText("Women's Shoe Sale 2");
        sitelinkAdExtensionB.setFinalUrls(finalUrls);
        sitelinkAdExtensionB.setFinalMobileUrls(finalMobileUrls);
        CustomParameters urlCustomParameters2 = new CustomParameters();
        CustomParameter customParameter3 = new CustomParameter();
        customParameter3.setKey("promoCode");
        customParameter3.setValue("PROMO2");
        ArrayOfCustomParameter customParameters2 = new ArrayOfCustomParameter();
        customParameters2.getCustomParameters().add(customParameter3);
        CustomParameter customParameter4 = new CustomParameter();
        customParameter4.setKey("season");
        customParameter4.setValue("summer");
        customParameters2.getCustomParameters().add(customParameter4);
        urlCustomParameters2.setParameters(customParameters2);
        sitelinkAdExtensionB.setUrlCustomParameters(urlCustomParameters2);

        sitelinkAdExtensions.add(sitelinkAdExtensionB);
        
        return sitelinkAdExtensions;
    }
}
<?php

namespace Microsoft\BingAds\Samples\V12;

// For more information about installing and using the Bing Ads PHP SDK, 
// see https://go.microsoft.com/fwlink/?linkid=838593.

require_once __DIR__ . "/../vendor/autoload.php";

include __DIR__ . "/AuthHelper.php";
include __DIR__ . "/OutputHelper.php";
include __DIR__ . "/CampaignManagementExampleHelper.php";

use SoapVar;
use SoapFault;
use Exception;

// Specify the Microsoft\BingAds\V12\CampaignManagement classes that will be used.
use Microsoft\BingAds\V12\CampaignManagement\Campaign;
use Microsoft\BingAds\V12\CampaignManagement\BudgetLimitType;
use Microsoft\BingAds\V12\CampaignManagement\AdExtension;
use Microsoft\BingAds\V12\CampaignManagement\AppAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\CallAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\CalloutAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\ImageAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\LocationAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\ReviewAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\SiteLinksAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\SitelinkAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\StructuredSnippetAdExtension;
use Microsoft\BingAds\V12\CampaignManagement\KeyValuePairOfstringstring;
use Microsoft\BingAds\V12\CampaignManagement\Schedule;
use Microsoft\BingAds\V12\CampaignManagement\DayTime;
use Microsoft\BingAds\V12\CampaignManagement\Day;
use Microsoft\BingAds\V12\CampaignManagement\Minute;
use Microsoft\BingAds\V12\CampaignManagement\Date;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionAssociation;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionAssociationCollection;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionEditorialReason;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionEditorialReasonCollection;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionEditorialStatus;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionIdentity;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionIdToEntityIdAssociation;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionStatus;
use Microsoft\BingAds\V12\CampaignManagement\AdExtensionsTypeFilter;
use Microsoft\BingAds\V12\CampaignManagement\Address;
use Microsoft\BingAds\V12\CampaignManagement\SiteLink;
use Microsoft\BingAds\V12\CampaignManagement\AssociationType;
use Microsoft\BingAds\V12\CampaignManagement\CustomParameters;
use Microsoft\BingAds\V12\CampaignManagement\CustomParameter;

// Specify the Microsoft\BingAds\Auth classes that will be used.
use Microsoft\BingAds\Auth\ServiceClient;
use Microsoft\BingAds\Auth\ServiceClientType;

// Specify the Microsoft\BingAds\Samples classes that will be used.
use Microsoft\BingAds\Samples\V12\AuthHelper;
use Microsoft\BingAds\Samples\V12\CampaignManagementExampleHelper;

$GLOBALS['AuthorizationData'] = null;
$GLOBALS['Proxy'] = null;
$GLOBALS['CampaignManagementProxy'] = null; 

// Disable WSDL caching.

ini_set("soap.wsdl_cache_enabled", "0");
ini_set("soap.wsdl_cache_ttl", "0");

try
{
    // Authenticate for Bing Ads services with a Microsoft Account.
    
    AuthHelper::Authenticate();

    $GLOBALS['CampaignManagementProxy'] = new ServiceClient(
        ServiceClientType::CampaignManagementVersion12, 
        $GLOBALS['AuthorizationData'], 
        AuthHelper::GetApiEnvironment());

    date_default_timezone_set('UTC');
       
    // Specify one or more campaigns.
    
    $campaigns = array();
   
    $campaign = new Campaign();
    $campaign->Name = "Winter Clothing " . $_SERVER['REQUEST_TIME'];
    $campaign->Description = "Winter clothing line.";
    $campaign->BudgetType = BudgetLimitType::DailyBudgetStandard;
    $campaign->DailyBudget = 50.00;
    $campaign->TimeZone = "PacificTimeUSCanadaTijuana";

    // Used with FinalUrls shown in the sitelinks that we will add below.
    $campaign->TrackingUrlTemplate =
        "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}";

    $campaigns[] = $campaign;
    
    print "AddCampaigns\n";
    $addCampaignsResponse = CampaignManagementExampleHelper::AddCampaigns(
        $GLOBALS['AuthorizationData']->AccountId, 
        $campaigns,
        false);
    $nillableCampaignIds = $addCampaignsResponse->CampaignIds;
    CampaignManagementExampleHelper::OutputArrayOfLong($nillableCampaignIds);
    if(isset($addCampaignsResponse->PartialErrors->BatchError)){
        CampaignManagementExampleHelper::OutputArrayOfBatchError($addCampaignsResponse->PartialErrors);
    }
    
    // Specify the extensions.
    
    $adExtensions = array();

    // Specify an app extension.
    
    $extension = new AppAdExtension();
    $extension->AppPlatform = "Windows";
    $extension->AppStoreId="AppStoreIdGoesHere";
    $extension->DisplayText= "Contoso";
    $extension->DestinationUrl="DestinationUrlGoesHere";
    $encodedExtension = new SoapVar(
        $extension, SOAP_ENC_OBJECT, 
        'AppAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace());
    // If you supply the AppAdExtension properties above, then you can add this line.
    //$adExtensions[] = $encodedExtension;
    
    // Specify a call extension.
    
    $extension = new CallAdExtension();
    $extension->CountryCode = "US";
    $extension->PhoneNumber = "2065550100";
    $extension->IsCallOnly = false;
    // For this example assume the call center is open Monday - Friday from 9am - 9pm
    // in the account's time zone.
    $callScheduling = new Schedule(); 
    $callDayTimeRanges = array();
    for($index = 0; $index < 5; $index++)
    {
        $dayTime = new DayTime();
        $dayTime->StartHour = 9;
        $dayTime->StartMinute = Minute::Zero;
        $dayTime->EndHour = 21;
        $dayTime->EndMinute = Minute::Zero;
        $callDayTimeRanges[] = $dayTime;
    }
    $callDayTimeRanges[0]->Day = Day::Monday;
    $callDayTimeRanges[1]->Day = Day::Tuesday;
    $callDayTimeRanges[2]->Day = Day::Wednesday;
    $callDayTimeRanges[3]->Day = Day::Thursday;
    $callDayTimeRanges[4]->Day = Day::Friday;
    $callScheduling->DayTimeRanges = $callDayTimeRanges;    
    $callScheduling->UseSearcherTimeZone = false;
    $callScheduling->StartDate = null;
    $callSchedulingEndDate = new Date();
    $callSchedulingEndDate->Day = 31;
    $callSchedulingEndDate->Month = 12;
    $callSchedulingEndDate->Year = date("Y");
    $callScheduling->EndDate = $callSchedulingEndDate;
    $extension->Scheduling = $callScheduling;
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'CallAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace());
    $adExtensions[] = $encodedExtension;

    // Specify a callout extension.
    
    $extension = new CalloutAdExtension();
    $extension->Text = "Callout Text";
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'CalloutAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace());
    $adExtensions[] = $encodedExtension;
    
    // Specify a location extension.
    
    $extension = new LocationAdExtension();
    $extension->PhoneNumber = "206-555-0100";
    $extension->CompanyName = "Alpine Ski House";
    $extension->Address = new Address;
    $extension->Address->StreetAddress = "1234 Washington Place";
    $extension->Address->StreetAddress2 = "Suite 1210";
    $extension->Address->CityName = "Woodinville";
    $extension->Address->ProvinceName = "WA"; 
    $extension->Address->CountryCode = "US";
    $extension->Address->PostalCode = "98608";
    $locationScheduling = new Schedule();
    $locationDayTimeRanges = array();
    $locationDayTime = new DayTime();
    $locationDayTime->Day = Day::Saturday;
    $locationDayTime->StartHour = 9;
    $locationDayTime->StartMinute = Minute::Zero;
    $locationDayTime->EndHour = 12;
    $locationDayTime->EndMinute = Minute::Zero;
    $locationDayTimeRanges[] = $locationDayTime;
    $locationScheduling->DayTimeRanges = $locationDayTimeRanges;    
    $locationScheduling->UseSearcherTimeZone = false;
    $locationScheduling->StartDate = null;
    $locationSchedulingEndDate = new Date();
    $locationSchedulingEndDate->Day = 31;
    $locationSchedulingEndDate->Month = 12;
    $locationSchedulingEndDate->Year = date("Y");
    $locationScheduling->EndDate = $locationSchedulingEndDate;
    $extension->Scheduling = $locationScheduling;
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'LocationAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace());
    $adExtensions[] = $encodedExtension;  

    // Specify a review extension.
    
    $extension = new ReviewAdExtension();
    $extension->IsExact = true;
    $extension->Source = "Review Source Name";
    $extension->Text = "Review Text";
    // The Url of the third-party review. This is not your business Url.
    $extension->Url = "http://review.contoso.com"; 
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'ReviewAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace());
    $adExtensions[] = $encodedExtension;
    
    // Specify a structured snippet extension.
    
    $extension = new StructuredSnippetAdExtension();
    $extension->Header = "Brands";
    $extension->Values = array("Windows", "Xbox", "Skype");
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'StructuredSnippetAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace());
    $adExtensions[] = $encodedExtension;
    
    foreach(GetSampleSitelinkAdExtensions() as $encodedExtension)
    {
        $adExtensions[] = $encodedExtension;
    }
        
    // Add all extensions to the account's ad extension library
    
    print("Add ad extensions.\n\n");
    $addAdExtensionsResponse = CampaignManagementExampleHelper::AddAdExtensions(
        $GLOBALS['AuthorizationData']->AccountId, 
        $adExtensions
        );    
    CampaignManagementExampleHelper::OutputArrayOfAdExtensionIdentity($addAdExtensionsResponse->AdExtensionIdentities);
    CampaignManagementExampleHelper::OutputArrayOfBatchErrorCollection($addAdExtensionsResponse->NestedPartialErrors);
        
    $adExtensionIdentities = $addAdExtensionsResponse->AdExtensionIdentities;

    // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
    // operations each require a list of type AdExtensionIdToEntityIdAssociation.
    
    $adExtensionIdToEntityIdAssociations = array ();

    // GetAdExtensionsByIds requires a list of type long.
    
    $adExtensionIds = array ();
                
    // Loop through the list of extension IDs and build any required data structures
    // for subsequent operations. 
    
    $associations = array();
    
    for ($index = 0; $index < count($adExtensionIdentities->AdExtensionIdentity); $index++)
    {
        if(!empty($adExtensionIdentities->AdExtensionIdentity[$index]) && isset($adExtensionIdentities->AdExtensionIdentity[$index]->Id))
        {
            $adExtensionIdToEntityIdAssociations[$index] = new AdExtensionIdToEntityIdAssociation();
            $adExtensionIdToEntityIdAssociations[$index]->AdExtensionId = $adExtensionIdentities->AdExtensionIdentity[$index]->Id;;
            $adExtensionIdToEntityIdAssociations[$index]->EntityId = $nillableCampaignIds->long[0];
                    
            $adExtensionIds[$index] = $adExtensionIdentities->AdExtensionIdentity[$index]->Id;
        }
    };

    // Associate the specified ad extensions with the respective campaigns or ad groups. 
    
    $setAdExtensionsAssociationsResponse = CampaignManagementExampleHelper::SetAdExtensionsAssociations(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIdToEntityIdAssociations,
        AssociationType::Campaign);
    
    CampaignManagementExampleHelper::OutputArrayOfBatchError($setAdExtensionsAssociationsResponse->PartialErrors);

    // Get editorial rejection reasons for the respective ad extension and entity associations.
    
    $adExtensionEditorialReasonCollection = CampaignManagementExampleHelper::GetAdExtensionsEditorialReasons(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIdToEntityIdAssociations,
        AssociationType::Campaign
        )->EditorialReasons;
          
    $adExtensionsTypeFilter = array(
        AdExtensionsTypeFilter::AppAdExtension,
        AdExtensionsTypeFilter::CallAdExtension,
        AdExtensionsTypeFilter::CalloutAdExtension,
        AdExtensionsTypeFilter::ImageAdExtension,
        AdExtensionsTypeFilter::LocationAdExtension,
        AdExtensionsTypeFilter::ReviewAdExtension,
        AdExtensionsTypeFilter::SitelinkAdExtension,
        AdExtensionsTypeFilter::StructuredSnippetAdExtension,
    );
    
    // Get the specified ad extensions from the account'ss ad extension library.
            
    $adExtensions = CampaignManagementExampleHelper::GetAdExtensionsByIds(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIds,
        $adExtensionsTypeFilter
        )->AdExtensions;
            
    print("List of ad extensions that were added above:\n\n");
    CampaignManagementExampleHelper::OutputArrayOfAdExtension($adExtensions);
    
    // Get only the location extensions and remove scheduling.

    $adExtensionsTypeFilter = array(AdExtensionsTypeFilter::LocationAdExtension);

    $adExtensions = CampaignManagementExampleHelper::GetAdExtensionsByIds(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIds,
        $adExtensionsTypeFilter
        )->AdExtensions;

    $updateExtensions = array();
    $updateExtensionIds = array();

    foreach ($adExtensions->AdExtension as $extension)
    {
        // GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met.
        if(!empty($extension) && isset($extension->Id))
        {
            // Remove read-only elements that would otherwise cause the update operation to fail.
            $updateExtension = OutputHelper::SetReadOnlyAdExtensionElementsToNull($extension);

            // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
            // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
            // for the ad extension. In this example, we will remove any existing scheduling by setting this element  
            // to an empty Schedule object.
            $updateExtension->Scheduling = new Schedule();

            $updateExtensions[] = new SoapVar(
                $updateExtension, 
                SOAP_ENC_OBJECT, 
                'LocationAdExtension', 
                $GLOBALS['CampaignManagementProxy']->GetNamespace());
            
            $updateExtensionIds[] = $updateExtension->Id;
        }
    }

    print("Removing scheduling from the location ad extensions..\n\n");
    CampaignManagementExampleHelper::UpdateAdExtensions($GLOBALS['AuthorizationData']->AccountId, $updateExtensions);
    
    $adExtensions = CampaignManagementExampleHelper::GetAdExtensionsByIds(
        $GLOBALS['AuthorizationData']->AccountId,
        $updateExtensionIds,
        $adExtensionsTypeFilter)->AdExtensions;

    print("List of ad extensions that were updated above:\n\n");
    CampaignManagementExampleHelper::OutputArrayOfAdExtension($adExtensions);
    
    // You should omit these delete operations if you want to view the added entities in the 
    // Bing Ads web application or another tool.

    // Remove the specified associations from the respective campaigns or ad groups.
    // The extensions are still available in the account's extensions library.
    CampaignManagementExampleHelper::DeleteAdExtensionsAssociations(
        $GLOBALS['AuthorizationData']->AccountId, 
        $adExtensionIdToEntityIdAssociations, 
        AssociationType::Campaign
    );
    
    // Deletes the ad extensions from the account's ad extension library.
    CampaignManagementExampleHelper::DeleteAdExtensions(
        $GLOBALS['AuthorizationData']->AccountId, 
        $adExtensionIds
    );
    
    foreach ($adExtensionIds as $id)
    {
        printf("Deleted Ad Extension Id %s\n\n", $id);
    }

    // Delete the campaign. 
    CampaignManagementExampleHelper::DeleteCampaigns($GLOBALS['AuthorizationData']->AccountId, array($nillableCampaignIds->long[0]));
    printf("Deleted CampaignId %d\n\n", $nillableCampaignIds->long[0]);
    
}
catch (SoapFault $e)
{
    print "\nLast SOAP request/response:\n";
    printf("Fault Code: %s\nFault String: %s\n", $e->faultcode, $e->faultstring);
    print $GLOBALS['Proxy']->GetWsdl() . "\n";
    print $GLOBALS['Proxy']->GetService()->__getLastRequest()."\n";
    print $GLOBALS['Proxy']->GetService()->__getLastResponse()."\n";
    
    if (isset($e->detail->AdApiFaultDetail))
    {
        CampaignManagementExampleHelper::OutputAdApiFaultDetail($e->detail->AdApiFaultDetail);
        
    }
    elseif (isset($e->detail->ApiFaultDetail))
    {
        CampaignManagementExampleHelper::OutputApiFaultDetail($e->detail->ApiFaultDetail);
    }
    elseif (isset($e->detail->EditorialApiFaultDetail))
    {
        CampaignManagementExampleHelper::OutputEditorialApiFaultDetail($e->detail->EditorialApiFaultDetail);
    }
}
catch (Exception $e)
{
    // Ignore fault exceptions that we already caught.
    if ($e->getPrevious())
    { ; }
    else
    {
        print $e->getCode()." ".$e->getMessage()."\n\n";
        print $e->getTraceAsString()."\n\n";
    }
}

function GetSampleSitelinkAdExtensions()
{
    $adExtensions = array();
    
    for ($index = 0; $index < 2; $index++)
    {
        $extension = new SitelinkAdExtension();
        $extension->Description1 = "Simple & Transparent.";
        $extension->Description2 = "No Upfront Cost.";
        $extension->DisplayText = "Women's Shoe Sale " . ($index+1);

        // With FinalUrls you can separate the tracking template, custom parameters, and 
        // landing page URLs. 

        $extension->FinalUrls = array();
        $extension->FinalUrls[] = "http://www.contoso.com/womenshoesale";

        // Final Mobile URLs can also be used if you want to direct the user to a different page 
        // for mobile devices.
        $extension->FinalMobileUrls = array();
        $extension->FinalMobileUrls[] = "http://mobile.contoso.com/womenshoesale";

        // Set custom parameters that are specific to this sitelink.
        $extension->UrlCustomParameters = new CustomParameters();
        $extension->UrlCustomParameters->Parameters = array();
        $customParameter1 = new CustomParameter();
        $customParameter1->Key = "promoCode";
        $customParameter1->Value = "PROMO" . ($index+1);
        $extension->UrlCustomParameters->Parameters[] = $customParameter1;
        $customParameter2 = new CustomParameter();
        $customParameter2->Key = "season";
        $customParameter2->Value = "summer";
        $extension->UrlCustomParameters->Parameters[] = $customParameter2;   
        
        $encodedExtension = new SoapVar(
            $extension, 
            SOAP_ENC_OBJECT, 
            'SitelinkAdExtension', 
            $GLOBALS['CampaignManagementProxy']->GetNamespace());
        $adExtensions[] = $encodedExtension;
    }
    
    return $adExtensions;
}


from auth_helper import *
from campaignmanagement_example_helper import *

# You must provide credentials in auth_helper.py.

def main(authorization_data):
    
    try:
        
        # Add a campaign that will later be associated with ad extensions. 

        campaigns=campaign_service.factory.create('ArrayOfCampaign')
        campaign=set_elements_to_none(campaign_service.factory.create('Campaign'))
        campaign.Name="Summer Shoes " + strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
        campaign.Description="Summer shoes line."
        campaign.BudgetType='DailyBudgetStandard'
        campaign.DailyBudget=10
        campaign.TimeZone='PacificTimeUSCanadaTijuana'
        campaign.Status='Paused'

        # Used with FinalUrls shown in the sitelinks that we will add below.
        campaign.TrackingUrlTemplate="http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}"

        campaigns.Campaign.append(campaign)

        add_campaigns_response=campaign_service.AddCampaigns(
            AccountId=authorization_data.account_id,
            Campaigns=campaigns
        )
        campaign_ids={
            'long': add_campaigns_response.CampaignIds['long'] if add_campaigns_response.CampaignIds['long'] else None
        }
        if hasattr(add_campaigns_response.PartialErrors, 'BatchError'):
            output_array_of_batcherror(add_campaigns_response.PartialErrors)
        output_status_message("Added campaign(s) with Id(s):\n")
        output_array_of_long(campaign_ids)
        
        # Specify the extensions.

        ad_extensions=campaign_service.factory.create('ArrayOfAdExtension')
        
        app_ad_extension=set_elements_to_none(campaign_service.factory.create('AppAdExtension'))
        app_ad_extension.AppPlatform='Windows'
        app_ad_extension.AppStoreId='AppStoreIdGoesHere'
        app_ad_extension.DisplayText='Contoso'
        app_ad_extension.DestinationUrl='DestinationUrlGoesHere'
        # If you supply the AppAdExtension properties above, then you can add this line.
        #ad_extensions.AdExtension.append(app_ad_extension)

        call_ad_extension=set_elements_to_none(campaign_service.factory.create('CallAdExtension'))
        call_ad_extension.CountryCode="US"
        call_ad_extension.PhoneNumber="2065550100"
        call_ad_extension.IsCallOnly=False
        call_ad_extension.Status=None
        # For this example assume the call center is open Monday - Friday from 9am - 9pm
        # in the account's time zone.
        call_scheduling=set_elements_to_none(campaign_service.factory.create('Schedule'))
        call_day_time_ranges=campaign_service.factory.create('ArrayOfDayTime')
        call_monday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_monday.Day='Monday'
        call_monday.StartHour=9
        call_monday.StartMinute='Zero'
        call_monday.EndHour=21
        call_monday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_monday)
        call_tuesday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_tuesday.Day='Tuesday'
        call_tuesday.StartHour=9
        call_tuesday.StartMinute='Zero'
        call_tuesday.EndHour=21
        call_tuesday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_tuesday)
        call_wednesday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_wednesday.Day='Wednesday'
        call_wednesday.StartHour=9
        call_wednesday.StartMinute='Zero'
        call_wednesday.EndHour=21
        call_wednesday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_wednesday)
        call_thursday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_thursday.Day='Thursday'
        call_thursday.StartHour=9
        call_thursday.StartMinute='Zero'
        call_thursday.EndHour=21
        call_thursday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_thursday)
        call_friday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_friday.Day='Friday'
        call_friday.StartHour=9
        call_friday.StartMinute='Zero'
        call_friday.EndHour=21
        call_friday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_friday)
        call_scheduling.DayTimeRanges=call_day_time_ranges
        call_scheduling_end_date=campaign_service.factory.create('Date')
        call_scheduling_end_date.Day=31
        call_scheduling_end_date.Month=12
        call_scheduling_end_date.Year=strftime("%Y", gmtime())
        call_scheduling.EndDate=call_scheduling_end_date
        call_scheduling.StartDate=None
        call_ad_extension.Scheduling=call_scheduling
        ad_extensions.AdExtension.append(call_ad_extension)

        callout_ad_extension=set_elements_to_none(campaign_service.factory.create('CalloutAdExtension'))
        callout_ad_extension.Text="Callout Text"
        ad_extensions.AdExtension.append(callout_ad_extension)

        location_ad_extension=set_elements_to_none(campaign_service.factory.create('LocationAdExtension'))
        location_ad_extension.PhoneNumber="206-555-0100"
        location_ad_extension.CompanyName="Contoso Shoes"
        address=campaign_service.factory.create('Address')
        address.StreetAddress="1234 Washington Place"
        address.StreetAddress2="Suite 1210"
        address.CityName="Woodinville"
        address.ProvinceName="WA"
        address.CountryCode="US"
        address.PostalCode="98608"
        location_ad_extension.Address=address
        location_scheduling=set_elements_to_none(campaign_service.factory.create('Schedule'))
        location_day_time_ranges=campaign_service.factory.create('ArrayOfDayTime')
        location_day_time=set_elements_to_none(campaign_service.factory.create('DayTime'))
        location_day_time.Day='Saturday'
        location_day_time.StartHour=9
        location_day_time.StartMinute='Zero'
        location_day_time.EndHour=12
        location_day_time.EndMinute='Zero'
        location_day_time_ranges.DayTime.append(location_day_time)
        location_scheduling.DayTimeRanges=location_day_time_ranges
        location_scheduling_end_date=campaign_service.factory.create('Date')
        location_scheduling_end_date.Day=31
        location_scheduling_end_date.Month=12
        location_scheduling_end_date.Year=strftime("%Y", gmtime())
        location_scheduling.EndDate=location_scheduling_end_date
        location_scheduling.StartDate=None
        location_ad_extension.Scheduling=location_scheduling
        ad_extensions.AdExtension.append(location_ad_extension)

        review_ad_extension=set_elements_to_none(campaign_service.factory.create('ReviewAdExtension'))
        review_ad_extension.IsExact=True
        review_ad_extension.Source="Review Source Name"
        review_ad_extension.Text="Review Text"
        # The Url of the third-party review. This is not your business Url.
        review_ad_extension.Url="http://review.contoso.com" 
        ad_extensions.AdExtension.append(review_ad_extension)

        structured_snippet_ad_extension=set_elements_to_none(campaign_service.factory.create('StructuredSnippetAdExtension'))
        structured_snippet_ad_extension.Header = "Brands"
        values=campaign_service.factory.create('ns3:ArrayOfstring')
        values.string.append('Windows')
        values.string.append('Xbox')
        values.string.append('Skype')
        structured_snippet_ad_extension.Values=values
        ad_extensions.AdExtension.append(structured_snippet_ad_extension)
        
        ad_extensions.AdExtension.append(get_sample_sitelink_ad_extensions()['AdExtension'])

        # Add all extensions to the account's ad extension library
        add_ad_extensions_response=campaign_service.AddAdExtensions(
            AccountId=authorization_data.account_id,
            AdExtensions=ad_extensions
        )
        output_status_message("Added ad extensions.\n")
        ad_extension_identities={
            'AdExtensionIdentity': add_ad_extensions_response.AdExtensionIdentities['AdExtensionIdentity'] 
                if add_ad_extensions_response.AdExtensionIdentities['AdExtensionIdentity']
                else None
        }
        if hasattr(add_ad_extensions_response.NestedPartialErrors, 'BatchErrorCollection'):
            output_array_of_batcherrorcollection(add_ad_extensions_response.NestedPartialErrors)       

        # DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
        # operations each require a list of type AdExtensionIdToEntityIdAssociation.
        ad_extension_id_to_entity_id_associations=campaign_service.factory.create('ArrayOfAdExtensionIdToEntityIdAssociation')

        # GetAdExtensionsByIds requires a list of type long.
        ad_extension_ids=[]

        # Loop through the list of extension IDs and build any required data structures
        # for subsequent operations. 

        for ad_extension_identity in ad_extension_identities['AdExtensionIdentity']:
            ad_extension_id_to_entity_id_association=campaign_service.factory.create('AdExtensionIdToEntityIdAssociation')
            ad_extension_id_to_entity_id_association.AdExtensionId=ad_extension_identity.Id
            ad_extension_id_to_entity_id_association.EntityId=campaign_ids['long'][0]
            ad_extension_id_to_entity_id_associations.AdExtensionIdToEntityIdAssociation.append(ad_extension_id_to_entity_id_association)

            ad_extension_ids.append(ad_extension_identity.Id)

        # Associate the specified ad extensions with the respective campaigns or ad groups. 
        campaign_service.SetAdExtensionsAssociations(
            AccountId=authorization_data.account_id,
            AdExtensionIdToEntityIdAssociations=ad_extension_id_to_entity_id_associations,
            AssociationType='Campaign'
        )

        output_status_message("Set ad extension associations.\n")

        # Get editorial rejection reasons for the respective ad extension and entity associations.
        get_ad_extensions_editorial_reasons_response=campaign_service.GetAdExtensionsEditorialReasons(
            AccountId=authorization_data.account_id,
            AdExtensionIdToEntityIdAssociations=ad_extension_id_to_entity_id_associations,
            AssociationType='Campaign'
        )
        ad_extension_editorial_reason_collection={
            'AdExtensionEditorialReasonCollection': get_ad_extensions_editorial_reasons_response.EditorialReasons['AdExtensionEditorialReasonCollection'] 
                if get_ad_extensions_editorial_reasons_response.EditorialReasons['AdExtensionEditorialReasonCollection']
                else None
        }
        if hasattr(get_ad_extensions_editorial_reasons_response.PartialErrors, 'BatchError'):
            output_array_of_batcherror(get_ad_extensions_editorial_reasons_response.PartialErrors)

        ad_extensions_type_filter='AppAdExtension ' \
                                  'CallAdExtension ' \
                                  'CalloutAdExtension ' \
                                  'ImageAdExtension ' \
                                  'LocationAdExtension ' \
                                  'ReviewAdExtension ' \
                                  'SitelinkAdExtension ' \
                                  'StructuredSnippetAdExtension'
                
        # Get the specified ad extensions from the account's ad extension library.
        get_ad_extensions_by_ids_response=campaign_service.GetAdExtensionsByIds(
            AccountId=authorization_data.account_id,
            AdExtensionIds={'long': ad_extension_ids},
            AdExtensionType=ad_extensions_type_filter
        )
        ad_extensions={
            'AdExtension': get_ad_extensions_by_ids_response.AdExtensions['AdExtension'] 
                if get_ad_extensions_by_ids_response.AdExtensions['AdExtension']
                else None
        }
        if hasattr(get_ad_extensions_by_ids_response.PartialErrors, 'BatchError'):
            output_array_of_batcherror(get_ad_extensions_by_ids_response.PartialErrors)

        output_status_message("List of ad extensions that were added above:\n")
        output_array_of_adextension(ad_extensions)
        output_array_of_adextensioneditorialreasoncollection(get_ad_extensions_editorial_reasons_response.EditorialReasons)

        # Get only the location extensions and remove scheduling.

        adExtensionsTypeFilter = 'LocationAdExtension'

        get_ad_extensions_by_ids_response=campaign_service.GetAdExtensionsByIds(
            AccountId=authorization_data.account_id,
            AdExtensionIds={'long': ad_extension_ids},
            AdExtensionType=ad_extensions_type_filter
        )
        ad_extensions={
            'AdExtension': get_ad_extensions_by_ids_response.AdExtensions['AdExtension'] 
                if get_ad_extensions_by_ids_response.AdExtensions['AdExtension']
                else None
        }
        if hasattr(get_ad_extensions_by_ids_response.PartialErrors, 'BatchError'):
            output_array_of_batcherror(get_ad_extensions_by_ids_response.PartialErrors)

        update_extensions=campaign_service.factory.create('ArrayOfAdExtension')
        update_extension_ids = []

        for extension in ad_extensions['AdExtension']:

            # GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met.
            if extension is not None and extension.Id is not None:
            
                # Remove read-only elements that would otherwise cause the update operation to fail.
                update_extension = set_read_only_ad_extension_elements_to_none(extension)

                # If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
                # If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
                # for the ad extension. In this example, we will remove any existing scheduling by setting this element  
                # to an empty Schedule object.
                update_extension.Scheduling=campaign_service.factory.create('Schedule')

                update_extensions.AdExtension.append(update_extension)
                update_extension_ids.append(update_extension.Id)
        
        output_status_message("Removing scheduling from the location ad extensions..\n")
        campaign_service.UpdateAdExtensions(
            AccountId=authorization_data.account_id,
            AdExtensions=update_extensions
        )
        
        get_ad_extensions_by_ids_response=campaign_service.GetAdExtensionsByIds(
            AccountId=authorization_data.account_id,
            AdExtensionIds={'long': update_extension_ids},
            AdExtensionType=ad_extensions_type_filter
        )
        ad_extensions={
            'AdExtension': get_ad_extensions_by_ids_response.AdExtensions['AdExtension'] 
                if get_ad_extensions_by_ids_response.AdExtensions['AdExtension']
                else None
        }
        if hasattr(get_ad_extensions_by_ids_response.PartialErrors, 'BatchError'):
            output_array_of_batcherror(get_ad_extensions_by_ids_response.PartialErrors)

        output_status_message("List of ad extensions that were updated above:\n")
        output_array_of_adextension(ad_extensions)

        # Remove the specified associations from the respective campaigns or ad groups. 
        # The extesions are still available in the account's extensions library. 
        campaign_service.DeleteAdExtensionsAssociations(
            AccountId=authorization_data.account_id,
            AdExtensionIdToEntityIdAssociations=ad_extension_id_to_entity_id_associations,
            AssociationType='Campaign'
        )

        output_status_message("Deleted ad extension associations.\n")

        # Deletes the ad extensions from the account's ad extension library.
        campaign_service.DeleteAdExtensions(
            AccountId=authorization_data.account_id,
            AdExtensionIds={'long': ad_extension_ids},
        )

        output_status_message("Deleted ad extensions.\n")

        campaign_service.DeleteCampaigns(
            AccountId=authorization_data.account_id,
            CampaignIds=campaign_ids
        )

        for campaign_id in campaign_ids['long']:
            output_status_message("Deleted CampaignId {0}\n".format(campaign_id))

        output_status_message("Program execution completed")

    except WebFault as ex:
        output_webfault_errors(ex)
    except Exception as ex:
        output_status_message(ex)

def get_sample_sitelink_ad_extensions():
    sitelink_ad_extensions=campaign_service.factory.create('ArrayOfAdExtension')
    
    for index in range(2):
        sitelink_ad_extension=set_elements_to_none(campaign_service.factory.create('SitelinkAdExtension'))
        sitelink_ad_extension.Description1="Simple & Transparent."
        sitelink_ad_extension.Description2="No Upfront Cost."
        sitelink_ad_extension.DisplayText = "Women's Shoe Sale " + str(index)
            
        # With FinalUrls you can separate the tracking template, custom parameters, and 
        # landing page URLs.
        final_urls=campaign_service.factory.create('ns3:ArrayOfstring')
        final_urls.string.append('http://www.contoso.com/womenshoesale')
        sitelink_ad_extension.FinalUrls=final_urls

        # Final Mobile URLs can also be used if you want to direct the user to a different page 
        # for mobile devices.
        final_mobile_urls=campaign_service.factory.create('ns3:ArrayOfstring')
        final_mobile_urls.string.append('http://mobile.contoso.com/womenshoesale')
        sitelink_ad_extension.FinalMobileUrls=final_mobile_urls

        # You could use a tracking template which would override the campaign level
        # tracking template. Tracking templates defined for lower level entities 
        # override those set for higher level entities.
        # In this example we are using the campaign level tracking template.
        sitelink_ad_extension.TrackingUrlTemplate=None

        # Set custom parameters that are specific to this ad extension, 
        # and can be used by the ad extension, ad group, campaign, or account level tracking template. 
        # In this example we are using the campaign level tracking template.
        url_custom_parameters=campaign_service.factory.create('CustomParameters')
        parameters=campaign_service.factory.create('ArrayOfCustomParameter')
        custom_parameter1=campaign_service.factory.create('CustomParameter')
        custom_parameter1.Key='promoCode'
        custom_parameter1.Value='PROMO' + str(index)
        parameters.CustomParameter.append(custom_parameter1)
        custom_parameter2=campaign_service.factory.create('CustomParameter')
        custom_parameter2.Key='season'
        custom_parameter2.Value='summer'
        parameters.CustomParameter.append(custom_parameter2)
        url_custom_parameters.Parameters=parameters
        sitelink_ad_extension.UrlCustomParameters=url_custom_parameters
        sitelink_ad_extensions.AdExtension.append(sitelink_ad_extension)

    return sitelink_ad_extensions

# Main execution
if __name__ == '__main__':

    print("Python loads the web service proxies at runtime, so you will observe " \
          "a performance delay between program launch and main execution...\n")
    
    authorization_data=AuthorizationData(
        account_id=None,
        customer_id=None,
        developer_token=DEVELOPER_TOKEN,
        authentication=None,
    )

    campaign_service=ServiceClient(
        service='CampaignManagementService', 
        version=12,
        authorization_data=authorization_data, 
        environment=ENVIRONMENT,
    )

    customer_service=ServiceClient(
        'CustomerManagementService', 
        version=12,
        authorization_data=authorization_data, 
        environment=ENVIRONMENT,
    )

    # You should authenticate for Bing Ads production services with a Microsoft Account.
        
    authenticate(authorization_data)
        
    main(authorization_data)

See Also

Get Started with the Bing Ads API