ﻁ DevExpertise » Blog Archive » Installing a Theme as a SharePoint Feature

DevExpertise

Practical tips and tricks for all things .NET, SharePoint, Silverlight, InfoPath, and general application development.

cialis fabriqué en inde compra cialis sildenafil moins cher
viagra cialis kaufen venta de levitra cialis beter dan viagra
http://wrkc.kings.edu/index.php?Name=290... compra de viagra por internet achat viagra generique moins cher
cialis trop cher pago de pastillas cialis a través de paypal
viagra vente libre achat sildenafi
viagra professionnel viagra kaufen in österreich viagra kaufen
http://www.cerphi.net/?size=246700&price... cialis pris apotek http://www.cerphi.net/?size=125948&price... levitra generico españa viagra priser vendita cialis siti buoni

Installing a Theme as a SharePoint Feature

Posted by DevExpert on February 11th, 2009

I briefly went over my approach to creating a custom SharePoint theme in my previous post, and I even included a downloadable solution package that you can install on your farm (provided you have the .NET 3.5 Framework installed).  How did I accomplish this?  Pretty easily actually.  Unfortunately there isn’t much documentation or examples of this out there, so allow me to put an end to that!

SharePoint features and solutions are absolutely essential if you want to provide an easy and maintainable method of deploying custom artifacts to your SharePoint servers.  A theme is a perfect candidate for this, as everything is file-system based and located with the 12 directory.  The only odd thing that throws a monkey wrench in this seemingly simple process is the SPTHEMES.XML file, which must be updated to include an entry for your custom theme.  Here’s a portion of that file, with the custom theme I developed in my previous post highlighted:

image

Now, you could by all means include the SPTHEMES.XML file in your solution and have it overwrite the existing file, but what if you have other themes defined in your file?  That approach will overwrite it, and you’ll have to reenter everything.  The recommended approach to this is to create a feature receiver that fires when the feature is installed and modifies the SPTHEMES.XML.  When the feature is installed, a custom <Templates> section is added for the custom theme, and when it’s uninstalled it’s removed.

NOTE: A feature is “installed” when you run the STSADM –o installfeature command, or when a feature is contained in a solution package and that solution package is deployed.

One important thing to mention is that this feature is scoped for the farm, not an individual site-collection or site, as the files deployed to the file system are used by the entire farm.  Let’s first take a look at the feature.xml file:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
         Id="2D965A22-73A9-4e00-A530-06F2AF6EC89F"
         Title="DevExpertise Vista Theme"
         Description="Adds the DevExpertise Vista Theme"
         Scope="Farm"
         Version="1.0.0.0"
         ImageUrl="DevExpertise\devexpertiseLogo.png"
         ReceiverAssembly="DevExpertise.SharePoint.Themes, Version=1.0.0.0, 
                           Culture=neutral, PublicKeyToken=d39eedb6cff9b1c8"
         ReceiverClass ="DevExpertise.SharePoint.Themes.FeatureReceiver">    
</Feature>

As you can see, it is executing a custom FeatureReceiver class.  At a high-level, the receiver looks like this:

[UPDATED 5/27/2009]

I received a couple comments that let me know that if this feature is activated on a farm with multiple front-ends, then the FeatureActivated event only gets fired on a single web front-end.  This is absolutely TRUE, and an oversight on my part (thanks guys!).  The solution is extremely simple – put your code in the FeatureInstalled and FeatureUninstalling events instead, as these get fired on EVERY web front-end in your farm!!  I’ve modified the code to reflect this:

public class FeatureReceiver: SPFeatureReceiver {

    private enum ModificationType { Add, Remove }

    public override void FeatureInstalled(SPFeatureReceiverProperties properties) {
        ModifySPTheme(ModificationType.Add);

        // if necessary, loop through all sites and set theme
    }

    public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {
        ModifySPTheme(ModificationType.Remove);

        // if necessary, loop through all sites and reset theme
    }

    public override void FeatureActivated(SPFeatureReceiverProperties properties) {
        // do nothing
    }

    public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
        // do nothing
    }
}

For the sake of simplicity, I’m not including code to set the theme on any sites.  Since this is a farm feature, it doesn’t make sense to set the theme for a particular site here, but by all means if you wanted to you could.  Basically this feature receiver is only for modifying the SPTHEMES.XML file.

Let’s take a look at the ModifySPTheme() method.  It uses LINQ to XML to open and parse the file, add the necessary elements when the feature is installed, and remove it when the feature is uninstalling:

private void ModifySPTheme(ModificationType type) {
    XDocument doc = null;
    XNamespace ns = "http://tempuri.org/SPThemes.xsd";

    // path to the SPTHEMES.XML file
    string spthemePath = Path.Combine(SPUtility.GetGenericSetupPath(@"TEMPLATE\LAYOUTS\1033"), "SPTHEMES.XML");
    string contents = string.Empty;

    // read the contents of the SPTHEMES.XML file
    using (StreamReader streamReader = new StreamReader(spthemePath)) {
        contents = streamReader.ReadToEnd();
        streamReader.Close();
    }

    using (StringReader stringReader = new StringReader(contents.Trim())) {
        // create a new XDocument from the contents of the file
        doc = XDocument.Load(stringReader);

        // retrieve all elements with a TemplateID of 'VISTA'.  At most, there should only be one
        var element = from b in doc.Element(ns + "SPThemes").Elements(ns + "Templates")
                      where b.Element(ns + "TemplateID").Value == "VISTA"
                      select b;

        // determine if the VISTA theme element already exists
        bool exists = (element != null && element.Count() > 0);

        if (type == ModificationType.Add) {
            if (!exists) {
                // create an XElement that defines our custom VISTA theme
                XElement xml =
                    new XElement(ns + "Templates",
                        new XElement(ns + "TemplateID", "VISTA"),
                        new XElement(ns + "DisplayName", "DevExpertise Vista Theme"),
                        new XElement(ns + "Description", "A Vista-like Theme"),
                        new XElement(ns + "Thumbnail", "images/DevExpertise.SharePoint.Themes/VISTA/thVISTA.gif"),
                        new XElement(ns + "Preview", "images/DevExpertise.SharePoint.Themes/VISTA/thVISTA.gif"));

                // add the element to the file and save
                doc.Element(ns + "SPThemes").Add(xml);
                doc.Save(spthemePath);
            }
        }
        else {
            if (exists) {
                // if the element exists, remove it and save
                element.Remove();
                doc.Save(spthemePath);
            }
        }

        stringReader.Close();
    }
}

Pretty slick, huh? Now, keep in mind that this only modifies the SPTHEMES.XML file – it does NOT set the theme for any site.  It will still be up to the user or site admins to set the theme for a given site.  Also, if this theme is already applied to a site and the feature is deactivated, it won’t reset the theme – it will only remove the entry from the theme settings page in Site Settings.  Finally, if a site has this theme installed and the solution package is uninstalled and/or removed, your site’s theme will be messed up because the source files are gone.  It is probably beneficial to at least loop through all the sites and reset the theme to something else if and when the feature is deactivated, but I’ll leave that to you. Enjoy!

8 Responses to “Installing a Theme as a SharePoint Feature”

  1. Michael Says:

    As far as I observed, FeatureReceivers only run on one server in a farm, the one where they are activated from. As you directly manipulate the spthemes.xml, this will only happen on one server and won’t be replicated through the farm. When you try to change the theme from another server, you won’t see the theme in the list.

  2. Keith Dahlby Says:

    It’s worth mentioning that FeatureActivated will only fire on the server from which the feature is activated (which happens on install for Farm-scoped features). If you have multiple WFEs, you can use a timer job to run this code on each every server.

    Nice use of LINQ to XML!

    Cheers ~
    Keith

  3. DevExpert Says:

    You are absolutely right, and that was an oversight on my part. The correct approach is to put the code in the FeatureInstalled/FeatureUninstalling events, as these get fired on each and every web front-end. I’ve updated the post to reflect this.

    Thanks for pointing this out!!

  4. DevExpert Says:

    Michael,

    You are correct (others have pointed this out to me as well)! Take a look at the updated post to see my corrections. Thanks for pointing this out!

  5. kant Says:

    The only complete material to deploy custom theme as a feature is here !!Good one

  6. Ron Says:

    It would be awesone if you could share the whole sample project source code. This doesn’t give the complete picture of how to create the project/solution. Thanks!

  7. DevExpert Says:

    Ron, I definitely understand wanting complete solutions, but there isn’t anything else that is needed other than what’s in this post. All you need is an assembly that has the FeatureReceiver class and a feature.xml file — that’s it.

  8. Jennifer Says:

    Very cool. Thanks for sharing!

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>