Keeping track of images in dynamic content plugins


Using images in dynamic content plugins is easy and something I use a lot to keep control over images that editors insert to the their content. Although the following code shows how easy this is done the EPiServer dynamic content plugin framework does not seem to take into account that images and files might be moved to another folder in the file manager. This post will explain how to solve this little caveat.

[EPiServer.DynamicContent.DynamicContentPlugIn(DisplayName = "Image")]
public partial class Image : DynamicContentBase
{
   // Property for holding the image state.
   private PropertyImageUrl _image;

   public Image()
   {
      //Prepare the property
      _image = new PropertyImageUrl();
      _image.Name = "Image";
      this.Properties.Add(_image);
   }

   public string File
   {
      get
      {
         //If available: return the image file path
         if (_image != null && !_image.IsNull)
         {
            return _image.Value.ToString();
         }
         return string.Empty;
      }
   }

   public override Control GetControl(PageBase hostPage)
   {
      //Rendering will be done using the Render method
      return null;
   }

   public override string Render(PageBase hostPage)
   {
      //Return an image tag with the selected image
      return string.Format("<img src=\"{0}\" />", this.Image);
   }

   public override bool RendersWithControl
   {
      //Tell the EPiServer to use the render method
      get { return false; }
   }
}

Although this is a very easy way to create a Dynamic Content Plugin and uses the build-in EPiServer PropertyImage is seems to come with one major problem: EPiServers filemanager does not keep track of the stored image data when used in a dynamic content plugins. When you add an image to a pagetype this does happen and when you move an image to another folder you even get the pages where the image is used. If you then proceed with the relocation all references to the image on pages are automatically updated.

Recently I ran into this problem when an editor of a website decided to reorganize their files and images. As a result all pages with moved files showed images with red crosses (IE). Clearly I had to find another way of storing the image since I couldn’t depend on EPiServer here. The solution lay in what was actually stored in the state bag op de plugin.

The solution

The quick way to build the plugin is letting EPiServer handle the read and writes of the plugin data but apparently EPiServer stores the virtual path of the jmage, including the folder in which it resides. Since the virtual path changes when the file is moved this will break the image when that happens.

To overcome this we need to tell EPiServer to store a more persistant reference to the file and after some searching found the PermanentFileLink property on the UnifiedFile object. This property contains a url to the file that stays the same regardless of its location in the filemanager. Ideal for this situation and easily implemented since the UnifiedFile constructor takes both virtual paths and permanent links so switching from one to the other is easy.

All I had to do now was to override the state read and write procedure of the plugin and tell EpiServer to store the permanent link and to convert it to a virtual link before parsing it into the image property. This shows how:

public override string State
{
   get
   {
      //If there is an image file path: build the state datat
      if (!string.IsNullOrWhiteSpace(this.File))
      {
         //Decode the file path 
         var img = HttpUtility.UrlDecode(this.File);

         //Get the permanent link to the image because the 
         //permanent link does not change when the image is moved
         //to another folder in the filemanager.
         var imgLink = string.Empty;
         if (!string.IsNullOrEmpty(img))
         {
            //Get the unified file object from EPiServer using the
            //virtual filepath of the selected image
            var ufImg = (UnifiedFile)System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetFile(img);
            //Retrieve the permanent link to the image
            imgLink = ufImg.PermanentLinkVirtualPath.ToString();
         }
         //The state we return is the permanent link
         return imgLink;
      }
      //If no image is selected, return an empty state
      return string.Empty;
   }
   set
   {
      //Process the state if there is a value 
      if (!string.IsNullOrEmpty(value))
      {
         //Create an EPiServer UnifiedFile object from the state
         //data which should contain the permanent link to the 
         //image
         var ufImg = (UnifiedFile)System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetFile(imgLink);
         //Use the virtual filepath of the object to parse into
         //the PropertyImage object
         _image.ParseToSelf(ufImg.VirtualPath);
      }
   }
}

Obviously this will work for documents as well. If the plugin consists of more then one property then remind to add those to the state read and write procedures. Since EPiServer won’t take care of this anymore you have to implement your own method. A quick and easy way is to concatenate the values of each property with e.g. a pipe (|) when the state get is called and split it up again when it is set.

About Sandor Voordes

I was born on the 17th of november 1974 in Groningen, a city in the far north of the Netherlands. I grew up for the larger part in the province of Groningen but lived for several seperate periods in Zambia and Mozambique. I am currently 38 years old, living together with my Israeli wife in the beautiful city of Utrecht, dead center Netherlands. From there I commute to work and do what I do best: build high quality websites with my colleages at Tam Tam. Check my LinkedIn for more information.
This entry was posted in ASP.Net, EPiServer and tagged , , . Bookmark the permalink.

Leave a comment