Using EPiServer Virtual Path Provider with file properties

by: Fredrik Karlsson

In EPiServer CMS 5 you can both build your own Virtual Path Providers (from now on simply VPP) for accessing files as well as change the files properties. In EPiServer CMS 5 R2 it is even possible to add these properties to each Path Provider, so now it's starting to be useful.

In this post, I will create a custom VPP called VirtualPathStartPublishProvider and add a property to all files in that VPP called StartPublishDate and the visitors will not be able to access the file until the start publish date has happend. You will simply get a 404 not found.

Changes in the web.config

This is how we add it in web.config:

   1:  <virtualPath customFileSummary="~/FileSummary.config">
   2:      <providers>
   3:          <clear />
   4:          <add showInFileManager="true"
   5:               virtualName="StartPublish Files"
   6:               virtualPath="~/StartPublishFiles/"
   7:               bypassAccessCheck="false"
   8:               indexingServiceCatalog="Web"
   9:               physicalPath="C:\Inetpub\EPiServerR2\VPP\StartPublishFiles"
  10:               name="StartPublishFiles"
  11:               type="EPiServer.StartPublishProvider.VirtualPathStartPublishProvider,
  12:  EPiServer.Templates.Public"
  13:               customFileSummary="~/StartPublishProvider.config" />

Let's go over this line by line.
Line 4: showInFileManager="true" is necessery in order to show it at all.
Line 5: Is what it's supposed to be called in the file manager
Line 6: Is what the file path will be
Line 11: The class as well as the assembly (line 12)
Line 13: Where we get our own File summary

File summary

The file summary is a config file with a lot of html in. You can always open the existing FileSummary.config to see some examples. This specialised File summary is a lot smaller, just the StartPublish property.

<root xmlns:xforms="http://www.w3.org/2002/xforms"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <model>
        <instance>
            <StartPublish />
        </instance>
    </model>
    <table id="id_matrix"
           border="0">
        <tr>
            <td class="EP-tableCaptionCell">
                Start publish date [YYYY-MM-DD]
            </td>
            <td valign="top"
                width="100"
                height="10">
                <div id="id_field1">
                    <xforms:input ref="StartPublish"
                                  value=""
                                  id="id_field2"
                                  size="40"
                                  class="commonInput" />
                </div>
            </td>
        </tr>
    </table>
</root>

Just remeber to add the same name under <model><instance><PROPERTY_NAME /></instance></model> as well in your ref on the xform part.

The Custom Page Provider

We simply load the file and check if it is a unified file. If it is, try and load the property "StartPublish" and make sure the date has passed.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using EPiServer.Web.Hosting;
using System.Collections.Specialized;
 
namespace EPiServer.StartPublishProvider
{
    public class VirtualPathStartPublishProvider : 
        EPiServer.Web.Hosting.VirtualPathVersioningProvider
    {
        public VirtualPathStartPublishProvider(string name,
            NameValueCollection configParameters)
            : base(name, configParameters)
        { }
 
        public override VirtualFile GetFile(string virtualPath)
        {
            // Get the file as always, to get access and versioning and all of that
            VirtualFile file = base.GetFile(virtualPath);
 
            // We want to always show it in edit mode and in File manager...
            if (IsInFileManager || IsEditOrPreviewMode)
            {
                return file;
            }
 
            // Cast it as a UnifiedFile
            UnifiedFile uf = file as UnifiedFile;
 
            // If it is null, then its something else, lets return it as it is...
            if (uf != null)
            {
                // Make sure we have some attributes
                if (uf.Summary.Dictionary.Count > 0)
                {
                    // Get our StartPublish attribute
                    string date = uf.Summary.Dictionary["StartPublish"] as string;
                    if (!string.IsNullOrEmpty(date))
                    {
                        DateTime dt = DateTime.MinValue;
                        if (DateTime.TryParse(date.Trim(), out dt))
                        {
                            // Make sure it is a value in the past
                            if (dt != DateTime.MinValue && dt < DateTime.Now)
                            {
                                return file;
                            }
                        }
                    }
                }
                return null;
            }
            return file;
        }
 
        /// <summary>
        /// Are we in the file manager
        /// </summary>
        public bool IsInFileManager
        {
            get
            {
                HttpRequest request = HttpContext.Current.Request;
                Uri referrer = request.UrlReferrer;
                return referrer != null 
                    && referrer.Host == request.Url.Host 
                    && referrer.Segments[referrer.Segments.Length - 1].Equals("ActionWindow.aspx", StringComparison.InvariantCultureIgnoreCase);
            }
        }
 
        /// <summary>
        /// Are we in edit or preview mode
        /// </summary>
        public static bool IsEditOrPreviewMode
        {
            get
            {
                HttpRequest request = HttpContext.Current.Request;
                Uri referrer = request.UrlReferrer;
                return referrer != null 
                    && referrer.Host == request.Url.Host 
                    && referrer.Segments[referrer.Segments.Length - 1].Equals("EditPanel.aspx", StringComparison.InvariantCultureIgnoreCase);
            }
        }
    }
}

In reality

We simply add a file, VPP.txt, and set a Start Publish date in the future:
 CustomVppAddFile

It is then added in our custom VPP:
 CustomVppFileAdded

We then add a link on a page to that file:
 CustomVppAddLink

And if you then go to view mode and try to click on the link:
 CustomVppClickLink

You will get a 404 Not found:
CustomVpp404

But if we change the date to a date in the past:
CustomVppPassedDate

We will be able to open the file:
CustomVppWorks

Conclusion

This can be very useful, especially after R2 when you can create a summary file per VPP. It feels a little backward to add the properties in a config-file, but I guess it works for now. What I really miss is someway to get EPiServers standard property types in here, so we can get a nice "Select your date" as well as get a "Must enter value" and some validation. But according to EPiServer, no one seems to use this yet, so hopefully this post will get people to start requesting these things.

Credits

A big thank you to Tomas Unestad for actually implementing this in the first place. We had no idea how it worked so it took a few gray hairs...

18 November 2008


Comments

  1. " as well as get a "Must enter value" and some validation." This should be possible by modifying the xform, right? Think I hade a support case with a question like that for EPiServer 4 a long time ago.
  2. Nothing stoping me from addin extra properties to the XML is it? I am mapping some XMP-data from files over to EpiServer Metadata, and I'm thinking of making the mappingin this file to keep the number of files low.
Post a comment    
User verification Image for user verification  
EPiTrace logger