A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

Viet Anh
Oct 1, 2018
  3325
(3 votes)

Customizing catalog export contents to Episerver Campaign

My first blog post on EPiServer World :)

At the end of Sept 2018, we introduced new package named EPiServer.Campaign.Commerce, that supports exporting catalog content to EPiServer Campaign.

The developer guide document for this new feature can be found here. This blog focuses on how to customize the data to be exported.

  • IProductLoader
    • GetEntries: Gets the entries need to export to campaign. By default, this collects all variants and packages under selected catalog and market language.
      Note: Since the catalog data is exported to Campaign system for marketing purpose. Then the core service only use items with price. The ones without price won't be processed.

      For example, you want to export variant under catalog, but not packages. Then your customization can be something like this:

          [ServiceConfiguration(ServiceType = typeof(IProductLoader), Lifecycle = ServiceInstanceScope.Singleton)]
          public class CustomProductLoader : IProductLoader
          {
              private readonly IContentLoader _contentLoader;
              private readonly ReferenceConverter _referenceConverter;
      
              public CustomProductLoader(IContentLoader contentLoader, ReferenceConverter referenceConverter)
              {
                  _contentLoader = contentLoader;
                  _referenceConverter = referenceConverter;
              }
      
              public IEnumerable<EntryContentBase> GetEntries(IEnumerable<string> catalogNames, IMarket market)
              {
                  var catalogs = catalogNames.Any()
                      ? _contentLoader.GetChildren<CatalogContent>(_referenceConverter.GetRootLink()).Where(x => catalogNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase))
                      : _contentLoader.GetChildren<CatalogContent>(_referenceConverter.GetRootLink());
      
                  foreach (var catalog in catalogs)
                  {
                      foreach (var variant in _contentLoader.GetChildren<VariationContent>(catalog.ContentLink, market.DefaultLanguage))
                      {
                          yield return variant;
                      }
                  }
              }
          }


  • IProductFieldsHandler
    • PopulateCategoryFields: Gets the category hierarchy of a product. The category levels are separated by "#". Note that Campaign system can process up to 10 levels of category.
    • PopulateTextFields: Populates the fields Text1-Text10
      For example:

              public void PopulateTextFields(ProductModel model, EntryContentBase entry, IPriceValue price, IEnumerable<DiscountPrice> discountedPrices)
              {
                  var fashion = entry as FashionVariant;
                  model.Text1 = fashion.Size;
                  model.Text2 = fashion.Color;
                  model.Text3 = fashion.MinQuantity.ToString();
                  model.Text4 = fashion.MaxQuantity.ToString();
                  model.Text5 = fashion.Weight.ToString();
                  model.Text6 = discountedPrices?.FirstOrDefault()?.Price.ToString() ?? string.Empty;
                  model.Text7 = price.UnitPrice.ToString();
              }


    • PopulateImageFields: Populates the fields Image1-Image6
      For example:

              public void PopulateImageFields(ProductModel model, EntryContentBase entry)
              {
                  model.Image1 = new ProductImageModel
                  {
                      ImageUrl = "http://LinkToImage1.com",
                      ImageLink = "http://Image1HrefLink.com"
                  };
      
                  model.Image2 = new ProductImageModel
                  {
                      ImageUrl = "http://LinkToImage2.com",
                      ImageLink = "http://Image2HrefLink.com"
                  };
      
                  model.Image3 = new ProductImageModel
                  {
                      ImageUrl = "http://LinkToImage3.com",
                      ImageLink = "http://Image3HrefLink.com"
                  };
              }



    • PopulateLinkFields: Populates the fields Link1-Link3
      For example:

              public void PopulateLinkFields(ProductModel model, EntryContentBase entry)
              {
                  model.Link1 = new ProductLinkModel()
                  {
                      Text = "Link 1",
                      Url = "http://LinkToProduct1.com"
                  };
      
                  model.Link2 = new ProductLinkModel()
                  {
                      Text = "Link 2",
                      Url = "http://LinkToProduct2.com"
                  };
      
                  model.Link3 = new ProductLinkModel()
                  {
                      Text = "Link 3",
                      Url = "http://LinkToProduct3.com"
                  };
              }


    • PopulateAdditionalDataFields: Populates the fields AdditionalData1-AdditionalData20

In default implementation, EPiServer set default value for texts, images, links, additional data and category in corresponding methods. Then if you only override some of those methods, be sure to override correct data in correct method. For example, overriding for PopulateTextFields should only set model.Text[x]. Otherwise, your custom value may be reset somewhere else by the default implementations.

Hope this helps.

Oct 01, 2018

Comments

Please login to comment.
Latest blogs
Looking back at Optimizely in 2025

Explore Optimizely's architectural shift in 2025, which removed coordination cost through a unified execution loop. Learn how agentic Opal AI and...

Andy Blyth | Dec 17, 2025 |

Cleaning Up Content Graph Webhooks in PaaS CMS: Scheduled Job

The Problem Bit of a niche issue, but we are building a headless solution where the presentation layer is hosted on Netlify, when in a regular...

Minesh Shah (Netcel) | Dec 17, 2025

A day in the life of an Optimizely OMVP - OptiGraphExtensions v2.0: Enhanced Search Control with Language Support and Synonym Slots

Supercharge your Optimizely Graph search experience with powerful new features for multilingual sites and fine-grained search tuning. As search...

Graham Carr | Dec 16, 2025

A day in the life of an Optimizely OMVP - Optimizely Opal: Specialized Agents, Workflows, and Tools Explained

The AI landscape in digital experience platforms has shifted dramatically. At Opticon 2025, Optimizely unveiled the next evolution of Optimizely Op...

Graham Carr | Dec 16, 2025

Optimizely CMS - Learning by Doing: EP09 - Create Hero, Breadcrumb's and Integrate SEO : Demo

  Episode 9  is Live!! The latest installment of my  Learning by Doing: Build Series  on  Optimizely Episode 9 CMS 12  is now available on YouTube!...

Ratish | Dec 15, 2025 |

Building simple Opal tools for product search and content creation

Optimizely Opal tools make it easy for AI agents to call your APIs – in this post we’ll build a small ASP.NET host that exposes two of them: one fo...

Pär Wissmark | Dec 13, 2025 |