Enabling/attaching and disabling/detaching content providers from Episerver edit UI

How and why you should register content providers from the UI.

icon of user profile

Published 15th of december 2018
Episerver > 8

Content Providers in Episerver is a way of publishing external data as pages/blocks etc… but also a way of publishing content in several places on your web/sites. Due to the lack of publishing in several containers/parents, this is a way of cloning data and keeping friendly urls. For example list news or job posts in a multisite environment.

More about coding a provider: https://devblog.gosso.se/2016/09/contentprovider-example-for-episerver-former-pageprovider/

Registering a provider in webconfig / episerver looks like this

<episerver> 
<contentProvider> 
<providers> 
<add entryPoint="66" name="ClonedNews" sourceid="20" categoryid="16" type="Gosso.EPiServer.ContentProviders.ClonedByCategoryContentProvider, GOSSO.EPiServer" /> 
</providers> 
</contentProvider> 
</episerver>

However, this is kind of static, and the downside is that that entrypoint nod/page is disabled for edit.

And sometimes you need to change settings on that page so a better approach would be NOT to register the content provider thru the web.config.

Best practice registering a content provider

First of all, i’ve made a base class for all my startpages (multi site or single site scenario)

using EPiServer.Core;
using System.ComponentModel.DataAnnotations;

namespace Gosso.Mvc.Models.Pages
{
    public abstract class JobProviderBasePage : BaseStartPage
    {
        [Display(
            Name = "Activate cloning of job posts on this site",
            Description = "",
            GroupName = "Job",
            Order = 1
        )]
        public virtual bool EnableProvider { get; set; }

        [Display(
            Name = "Reload the provider",
            Description = "If you want to reload the provider, click here.",
            GroupName = "Job",
            Order = 2
        )]
        public virtual bool ReloadProvider { get; set; }

        [Display(
            Name = "Get from this page (source)",
            Description = "",
            GroupName = "Job",
            Order = 20
        )]
        public virtual PageReference JobSource { get; set; }

        [Display(
            Name = "Clone to this page (target)",
            Description = "",
            GroupName = "Job",
            Order = 30
        )]
        public virtual PageReference JobEntryPoint { get; set; }

        [Display(
            Name = "Some other input to provider",
            Description = "anything you need to your provider",
            GroupName = "Job",
            Order = 40
        )]
        public virtual string JobCategories { get; set; }

    }
}

Use a onPublishing event to handle changes “ReloadProvider”

       
//in an [InitializableModule]

IContentEvents events = ServiceLocator.Current.GetInstance<IContentEvents>();
events.PublishingContent += PublishingContent;


 private void PublishingContent(object sender, ContentEventArgs e)
{

            if (!(e.Content is PageData))
                    return;

            if (ContentReference.IsNullOrEmpty(e.ContentLink))
                return; //new page

            if (e.Content is JobProviderBasePage)
            {
                JobProviderBasePage oldPage = ProviderHelper.GetLastVersion(e.ContentLink.ToPageReference(), (e.Content as ILocalizable).MasterLanguage.TwoLetterISOLanguageName) as JobProviderBasePage;

                if (oldPage != null && oldPage.ReloadProvider != (e.Content as JobProviderBasePage).ReloadProvider)
                {
                    (e.Content as JobProviderBasePage).ReloadProvider = false;
                    ProviderHelper.ReloadProvider((e.Content as JobProviderBasePage));
                }
            }
}

Then i have a helper class to manage the load/reload/remove providers

using EPiServer;
using EPiServer.Configuration;
using EPiServer.Construction;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.ServiceLocation;
using EPiServer.Web;
using Kriminalvarden.Mvc.Models.Pages;
using log4net;
using System;
using System.Collections.Specialized;
using System.Linq;

namespace Gosso.Mvc.Business.ContentProviders
{
    /// <summary>
    ///     Helpers for the content providers
    /// </summary>
    public static class ProviderHelper
    {
        #region Static Fields

        /// <summary>
        ///     Initializes the <see cref="LogManager">LogManager</see>
        ///     class.
        /// </summary>
        private static readonly ILog Logger = LogManager.GetLogger(typeof(ProviderHelper));

        #endregion

        #region Public Methods and Operators        

        public static bool AddJobProvider(string name, JobProviderBasePage jobsettings)
        {
            if (jobsettings != null && jobsettings.JobCategories != null
                                    && jobsettings.JobEntryPoint != null
                                    && jobsettings.JobSource != null)
            {

                var provider = new JobPageClonedContentProvider(
                    ServiceLocator.Current.GetInstance<IdentityMappingService>(),
                    ServiceLocator.Current.GetInstance<IContentTypeRepository>(),
                    ServiceLocator.Current.GetInstance<IContentFactory>(),
                    ServiceLocator.Current.GetInstance<IUrlSegmentGenerator>(),
                    ServiceLocator.Current.GetInstance<IContentCacheKeyCreator>(),
                    ServiceLocator.Current.GetInstance<IContentRepository>());
                // add configuration settings for entry point and capabilites

                var providerValues = new NameValueCollection();
                providerValues.Add(ContentProviderElement.EntryPointString,
                    jobsettings.JobEntryPoint.ToString());
                providerValues.Add("sourceid", jobsettings.JobSource.ToString());
                providerValues.Add("categoryids", jobsettings.JobCategories);
                

                try
                {
                    provider.Initialize(name, providerValues);
                    var providerManager = ServiceLocator.Current.GetInstance<IContentProviderManager>();
                    providerManager.ProviderMap.AddProvider(provider);
                    return true;
                }
                catch
                {
                    return false;
                }
            }

            return false;
        }

        /// <summary>
            /// Deletes the provider.
            /// </summary>
            /// <param name="providerName">Name of the provider.</param>
            /// <returns> <c>true</c> if [the provider was removed]; otherwise, <c>false</c>.</returns>
            /// <exception cref="System.InvalidOperationException">
            /// Provider setting not found.
            /// or
            /// </exception>
            public static bool DeleteProvider(string providerName)
        {
            IContentProviderManager providerManager = ServiceLocator.Current.GetInstance<IContentProviderManager>();
            ContentProvider pageProvider = providerManager.ProviderMap.GetProvider(providerName);

            try
            {
                if (pageProvider != null)
                {
                    providerManager.ProviderMap.RemoveProvider(providerName);
                }

                CacheManager.Clear();
            }
            catch (Exception exception)
            {
                Logger.Error(exception.Message, exception);
                throw new InvalidOperationException(exception.Message);
            }

            return true;
        }

        #endregion

        public static bool ReloadProvider(JobProviderBasePage jobProviderBasePage)
        {
            if (jobProviderBasePage.EnableProvider)
            {
                if (DeleteProvider("jobprovider-" + SiteDefinition.Current.Name))
                {
                    return AddJobProvider("jobprovider-" + SiteDefinition.Current.Name, jobProviderBasePage);
                }
                else
                    return false;
            }
            else
            {
                return DeleteProvider("jobprovider-" + SiteDefinition.Current.Name);
            }

        }

        public static IContent GetLastVersion(PageReference reference, string lang)
        {
            var versionRepository = ServiceLocator.Current.GetInstance<IContentVersionRepository>();
            var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();

            var versions = versionRepository.List(reference);
            var lastVersion = versions
                .OrderBy(v => v.Saved)
                .Take(versions.Count() - 1)
                .OrderByDescending(v => v.Saved)
                .FirstOrDefault(version => version.LanguageBranch == lang);

            if (lastVersion == null)
            {
                //var msg = string.Format("Unable to find last version for ContentReference '{0}'.", reference.ID);
                //throw new Exception(msg);
                return null;
            }

            return contentRepository.Get<IContent>(lastVersion.ContentLink, LanguageSelector.AutoDetect(true));
        }

        public static bool AddJobProviderSite1(string name)
        {
            if (ServiceLocator.Current.GetInstance<IContentLoader>().TryGet(ContentReference.StartPage, out StartPage startpage))
            {
                if (ServiceLocator.Current.GetInstance<IContentLoader>()
                    .TryGet(startpage.Site1Start, out JobProviderBasePage jobsettings))
                {
                    if (jobsettings.EnableProvider)
                        return AddJobProvider(name, jobsettings);
                }
            }

            return false;
        }

        public static bool AddJobProviderSite2(string name)
        {
            if (ServiceLocator.Current.GetInstance<IContentLoader>().TryGet(ContentReference.StartPage, out StartPage startpage))
            {
                if (ServiceLocator.Current.GetInstance<IContentLoader>()
                    .TryGet(startpage.Site2Start, out JobProviderBasePage jobsettings))
                {
                    if (jobsettings.EnableProvider)
                        return AddJobProvider(name, jobsettings);
                }
            }

            return false;
        }
    }
}

Last, Register from code OnInit

//in an [InitializableModule]

//programmatically registering a content provider
ProviderHelper.AddJobProviderSite2("jobprovider-sitename");

SEO terms

  • Attaching ContentProviders programmatically
  • Reset content providers runtime
  • programmatically add content provider

About the author

Luc Gosso
– Independent Senior Web Developer
working with Azure and Episerver

Twitter: @LucGosso
LinkedIn: linkedin.com/in/luc-gosso/
Github: github.com/lucgosso