Codemania: Geeky PageProviders

blog header image

At EPiServer Day 2009 I had the pleasure of entertaining a crowded room filled with the best developer brains in the community of developers working with EPiServer CMS. As promissed, I will use a couple of blog posts on providing the code we showed at the event.

First, I showed how to use the DCPlugin to make easy Dynamic Content out of User Controls. I already blogged about that, so I’ll let that be for now.

Later, it was time to turn geeky with some crazy page providers.

I based all my page providers on the MappedPageProvider.

Here’s the File System Provider:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using EPiServer.Core;
using EPiServer.Research.PageProviders;
 
 
namespace EPiServer.Templates.PageProvider
{
    public class FileProvider : MappedPageProvider
    {
 
        private string rootFolder;
 
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);
            rootFolder = config["rootFolder"];
        }
 
        protected override List<string> GetRootChildren()
        {
            return Directory.GetFileSystemEntries(rootFolder).ToList();
        }
 
        protected override List<string> GetChildren(string Key)
        {
            if (Directory.Exists(Key))
            {
                return Directory.GetFileSystemEntries(Key).ToList();
            }
            else return new List<string>();
        }
 
        protected override EPiServer.Core.PageData GetPage(string Key)
        {
            PageData pd = new PageData();
            string parentK = Path.GetDirectoryName(Key);
            if (parentK == rootFolder) parentK = null;
            FileSystemInfo fsi;
            if (Directory.Exists(Key)) fsi = new DirectoryInfo(Key);
            else fsi = new FileInfo(Key);
            base.InitializePageData(pd, Key, Path.GetFileName(Key), parentK);
            SetPageStatus(pd, VersionStatus.Published);
            if (File.Exists(Key) && (Path.GetExtension(Key).ToLower().EndsWith("txt")))
            {
                StreamReader sr = File.OpenText(Key);
                string s = sr.ReadToEnd();
                pd.SetValue("MainBody","<pre>"+ s+"</pre>");
                sr.Close();
            } else  pd.SetValue("MainBody", "Creation time: " + fsi.CreationTime.ToString() + "<br/>Last Access Time: " + fsi.LastAccessTime.ToString());
            return pd;
        }
 
        public override string SaveNew(PageData page, string ParentKey, EPiServer.DataAccess.SaveAction action)
        {
            if (action == EPiServer.DataAccess.SaveAction.Publish)
            {
                string ParentDir = ParentKey;
                if (ParentDir == null) ParentDir = rootFolder;
                if (!Directory.Exists(ParentDir)) throw (new EPiServerException("Can't create this file"));
 
                string filePath = ParentDir.TrimEnd('\\') + "\\" + page.PageName;
                StreamWriter sw = File.CreateText(filePath);
                sw.Write(page["MainBody"].ToString());
                sw.Close();
                return filePath;
            }
            return null;
        }
 
        protected override string GetPageType(string Key)
        {
            return "[Public] Standard page";
        }
    }
}

It’s set up in web.config like this:

<pageProvider>
      <providers>
        <add name="File" type="EPiServer.Templates.PageProvider.FileProvider,EPiServer.Templates.Public" entryPoint="28" rootFolder="d:\temp\" capabilities="Edit,Create" />

 

It works by using the path to the files as the unique key for the mappings. Make sure that the ASPNET / Network Service has read/write access to your root folder. Also, it uses the Standard page type from the default site – change that if needed.

After that part, I turned really geeky with the Page Provider To Admin mode…A page provider that exposes PageTypes, Categories and Tabs from Admin mode as page in edit-mode. Somewhat funny to work with a PageType that describes a PageType.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using EPiServer.Research.PageProviders;
using EPiServer.DataAbstraction;
using EPiServer.Core;
 
namespace EPiServer.Templates.PageProvider
{
    public class CoreProvider : MappedPageProvider
    {
 
        protected override List<string> GetRootChildren()
        {
            return new List<string>() {"[PageTypes]","[Categories]","[Tabs]"};
        }
 
        protected override List<string> GetChildren(string Key)
        {
            List<string> rt = new List<string>();
            switch (Key)
            {
                case "[PageTypes]":
                    rt.AddRange(PageType.List().Select(pt => "pt_" + pt.ID).ToArray());
                    break;
                case "[Categories]":
                    rt.Add("ca_"+Category.GetRoot().ID.ToString());
                    break;
                case "[Tabs]" :
                    rt.AddRange(TabDefinition.List().Select(td => "ta_" + td.ID).ToArray());
                    break;
 
            }
            if (Key.StartsWith("pt_"))
            {
                //List properties for a page
                rt.AddRange(PageDefinition.List(int.Parse(Key.Substring(3))).Select(pd => "pd_" + pd.ID).ToArray());
            }
            else if (Key.StartsWith("ca_"))
            {
                Category c = Category.Find(int.Parse(Key.Substring(3)));
                foreach (Category subC in c.Categories)
                {
                    rt.Add("ca_" + subC.ID.ToString());
                }
            }
            return rt;
        }
 
        protected override EPiServer.Core.PageData GetPage(string Key)
        {
            PageData pd = new PageData();
            
            if (Key.StartsWith("["))
            {
                base.InitializePageData(pd, Key, Key, null);
            }
            else if (Key.StartsWith("pt_"))
            {
                //We have a page type
                PageType pt=PageType.Load(int.Parse(Key.Substring(3)));
                base.InitializePageData(pd, Key, pt.Name, "[PageTypes]");
                pd["PageTypeDescription"] = pt.Description;
                pd["Filename"] = pt.FileName;
                
            }
            else if (Key.StartsWith("pd_"))
            {
                //We have a page definition (property)
                PageDefinition pdef = PageDefinition.Load(int.Parse(Key.Substring(3)));               
                base.InitializePageData(pd, Key,pdef.Name, "pt_"+pdef.PageTypeID.ToString());
                pd["EditHeading"] = pdef.EditCaption;
                pd["HelpText"] = pdef.HelpText;
                pd["Tab"] = pdef.Tab.Name;
                pd["Type"] = pdef.Type.Name;
            }
            else if (Key.StartsWith("ca_"))
            {
                Category c = Category.Find(int.Parse(Key.Substring(3)));
                base.InitializePageData(pd, Key, c.Name, (c.Parent ==null)? "[Categories]":"ca_" + c.Parent.ID.ToString());
                pd["Description"] = c.Description;
            }
            else if (Key.StartsWith("ta_"))
            {
                TabDefinition td = TabDefinition.Load(int.Parse(Key.Substring(3)));
                base.InitializePageData(pd, Key, td.Name, "[Tabs]");
                pd["MainBody"] = td.RequiredAccess.ToString();
            }
            pd.StartPublish = DateTime.Now;
            base.SetPageStatus(pd, VersionStatus.Published);
            //pd.ACL.Add(new EPiServer.Security.AccessControlEntry("Allan", EPiServer.Security.AccessLevel.FullAccess));
            return pd;
        }
 
        protected override void SetCacheSettings(PageData page, CacheSettings cacheSettings)
        {
            base.SetCacheSettings(page, cacheSettings);
            cacheSettings.CancelCaching = true;
        }
 
        protected override void SetCacheSettings(PageReference pageLink, PageReferenceCollection childrenReferences, CacheSettings cacheSettings)
        {
            base.SetCacheSettings(pageLink, childrenReferences, cacheSettings);
            cacheSettings.CancelCaching = true;
        }
 
        public override void Save(PageData page, string Key, EPiServer.DataAccess.SaveAction action)
        {
            if (action == EPiServer.DataAccess.SaveAction.Publish)
            {
                PageType pt = PageType.Load(int.Parse(Key.Substring(3)));
                pt.Description = page["PageTypeDescription"].ToString();
                pt.Save();
                
            }
        }
 
        protected override string GetPageType(string Key)
        {
            if (Key.StartsWith("pt_"))
            {
                return "[Core] PageType";
            }
            else if (Key.StartsWith("pd_"))
            {
                return "[Core] Property";
            }
            else if (Key.StartsWith("ca_"))
            {
                return "[Core] Category";
            }
            return "[Public] Standard page";
        }
    }
}

This needs to have a couple of new PageTypes defined.

Also, register it in web.config like this:

<add name="Core" type="EPiServer.Templates.PageProvider.CoreProvider,EPiServer.Templates.Public" entryPoint="30" capabilities="Edit,Create" />

Since it doesn’t really make sense to see “preview” of any pages of this type, you might want to disable preview entirely for them and instead show the edit-tab. This is done by this plugin:

using System;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Linq;
 
using EPiServer.Core;           // addref: EPiServer.dll
using EPiServer.PlugIn;         // addref: EPiServer.dll
using EPiServer.DataAbstraction;// addref: EPiServer.dll
using EPiServer.UI.Edit;        // addref: EPiServer.UI.dll
using EPiServer.UI.WebControls; // addref: EPiServer.UI.dll
 
namespace EPiServer.CoreProvider
{
    [GuiPlugIn(Area = PlugInArea.EditPanel)]
    public class RemoveViewTab : ICustomPlugInLoader
    {
        public PlugInDescriptor[] List()
        {
            // hook LoadComplete-event on EditPanel page
            EditPanel editPanel = HttpContext.Current.Handler as EditPanel;
 
            if (null != editPanel)
            {
                //Only modify tabs when the page selected is of a specific type - in this case the types name starts with "[Core]".
                if ((editPanel as PageBase).CurrentPage.PageTypeName.StartsWith("[Core]"))
                {
                    editPanel.LoadComplete += new EventHandler(editPanel_LoadComplete);
                }
            }
 
            //Never return a plugin - we don't want to add tabs.
            return new PlugInDescriptor[0] { };
        }
 
        protected void editPanel_LoadComplete(object sender, EventArgs e)
        {
            // find the TabStrip with id = "actionTab"
            TabStrip actionTabStrip = this.FindControl<TabStrip>(sender as Control, "actionTab");
 
            //THIS IS WHERE YOU CAN DO WHATEVER YOU WANT TO THE TABS!
 
            // remove View-tab and set active tab to Edit.
            if(actionTabStrip.SelectedTab==0)   actionTabStrip.SetSelectedTab(1);
            ((Tab)actionTabStrip.Controls[0]).Visible = false;
 
        }
 
        // try to locate control of type T with ID==id
        // recurses into each childcontrol until first match is found
        protected T FindControl<T>(Control control, string id) where T : Control
        {
            T controlTest = control as T;
 
            if (null != controlTest && (null == id || controlTest.ID.Equals(id)))
                return controlTest;
 
            foreach (Control c in control.Controls)
            {
                controlTest = FindControl<T>(c, id);
                if (null != controlTest)
                    return controlTest;
            }
 
            return null;
        }
 
        protected T FindControl<T>(Control control) where T : Control
        {
            return FindControl<T>(control, null);
        }
    }
}

 

Enjoy!

Recent posts