How To Get Real-time Info Of Sharepoint Module Files On Feature Activation Event Listener

Posted by Ahmed Tarek Hasan on 12/28/2012 03:21:00 PM with No comments
Sometimes you need to run some code at the moment of certain feature activation. This is for sure doable by creating an event receiver on the desired feature and then overriding the code of the "FeatureActivated" method. This topic is not the main topic here so I will assume that you already know how to do this.

Ok, but what I will be talking about in this post is how to use the module "Elements.xml" file to get live or real-time data to be used inside your "FeatureActivated" code. Let me explain first what I really mean here.

Sometimes in your "FeatureActivated" code, you need to use some info you already provided in the module "Elements.xml" file. This info could be some files names, some properties or some URLs. For sure the code you write in the "FeatureActivated" code is somehow case specific and you may have no problem to duplicate this type of info between the "Elements.xml" and the "FeatureActivated" code, I know that. But, this introduces an new problem when you apply some changes on the "Elements.xml" and you forget to apply the same changes on the "FeatureActivated" code. This discrepancy in info will for sure cause you problems.

So, the best approach is to centralize your info in one place for you to be more easy and stable to apply further changes and I believe that the best place to use is the "Elements.xml" file cause the main concept behind this file is to make configurations more easy and manageable, so let's keep it that way.

Actually, it is more than that. By centralizing your info in the "Elements.xml" file, you provide system admins with the ability to apply some changes on these deployed "Elements.xml" files to reflect some changes on the live system and this will not need applying any changes on code cause your code uses the "Elements.xml" file as its source of info.

I think now we know what we need to achieve and this is the point where we go deep into some code.

Now, we need some code to access the "Elements.xml" file of certain feature inside the "FeatureActivated" code of this feature to get some info about the modules and their files to be able to use this info in the rest of the "FeatureActivated" code.

Before jumping into the code for this part, let's refresh our memory and have a look onto a sample "Elements.xml" file.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Module Name="pagelayouts" Url="_catalogs/masterpage">
  <File Path="pagelayouts\layout1.aspx" Url="MyLayouts/layout1.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 1" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout1.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
  <File Path="pagelayouts\layout2" Url="MyLayouts/layout2.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 2" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout2.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
 </Module>
</Elements>

This is an "Elements.xml" file for a module having two files. These files are page layouts and each of them has some info like "title", "content type" and "publishing preview image". The files themselves have some info like "path" and "url". Also, the module has info like "name" and "url".

So, now let's see the code for opening this xml file and extracting these info to be used.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using System.Linq;
using System.Text;
using System.Globalization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace DevelopmentSimplyPut.Helpers
{
    public static class FeaturesHelper
    {
        public static List GetModuleFilesInfo(SPFeatureReceiverProperties instance)
        {
            List modules =
                    (from SPElementDefinition element in instance.Feature.Definition.GetElementDefinitions(CultureInfo.CurrentCulture)
                     where element.ElementType == "Module"
                     let xmlns = XNamespace.Get(element.XmlDefinition.NamespaceURI)
                     let module = XElement.Parse(element.XmlDefinition.OuterXml)
                     select new ModuleInfo
                     {
                         Url = module.Attribute("Url").Value,
                         Path = Path.Combine(element.FeatureDefinition.RootDirectory, module.Attribute("Url").Value),
                         Files = (from file in module.Elements(xmlns.GetName("File"))
                                  select new ModuleFile
                                  {
                                      Url = file.Attribute("Url").Value,
                                      Properties = (from property in file.Elements(xmlns.GetName("Property"))
                                                    select property).ToDictionary(
                                                          n => n.Attribute("Name").Value,
                                                          v => v.Attribute("Value").Value)
                                  }).ToList()
                     }).ToList();

            return modules;
        }
    }
    public class ModuleFile
    {
        public string Url { set; get; }
        public Dictionary Properties { set; get; }
    }
    public class ModuleInfo
    {
        public string Url { set; get; }
        public string Path { set; get; }
        public List Files { set; get; }
    }
}

So, now in the "FaeatureActivated" code;
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
 List moduleInfoCollection = FeaturesHelper.GetModuleFilesInfo(properties);
 
 /*
 foreach(ModuleInfo moduleInfo in moduleInfoCollection)
 {
  //moduleInfo.Url
  //moduleInfo.Path
  foreach(ModuleFile fileInfo in moduleInfo.Files)
  {
   //fileInfo.Url
   foreach(KeyValuePair property in fileInfo.Properties)
   {
    //property.Key
    //property.Value
   }
  }
 }
 */
}

You can use the info you get about the modules and files to do what you really want. For sure you may need to use some hard-coded files properties keys but at least these keys should be fixed in the first place and they are not subject to frequent changes (or any changes maybe). But, you have the advantage of dynamic files properties values and this is the most important thing. Now, when you apply changes on these values in the "Elements.xml" file you don't have to track these values in the code to apply matching changes.

Some may think that this is too much code to run for the sake of this purpose and that this approach will affect the overall performance. May be they are right but let's face it, this code will run only once every time the feature is activated and this doesn't happen frequently. Actually, if the system admin needs to activate and deactivate your feature frequently, you may need to reconsider the design.


That's it, hope you find this post helpful.
Good Luck.


 

Categories: ,