0

On my dev server I am usually updating CSS files and deploying the solution from VS and the changes are reflected right way. When I move the solution to a staging server and when the CSS is changed after the solution is deployed on the staging server.. I do the following

  1. Deactivate feature
  2. Retract solution
  3. remove solution
  4. add solution
  5. deploy solution
  6. activate feature

After step 6 the updated CSS file is not copied to the "Style Library". I have to manually delete the CSS file and then complete the 6 steps above.

Is there a way to automatically copy the updated CSS file without having to delete it first? Do I need to manually loop through all the files in my module and do a check out -> check in -> publish?

Is this process improved in 2013? It looks like we have a new attribute in the elements.xml file called "ReplaceContent". What does that do? If I have it like this?

<Module Name="Style Library" Url="Style Library" RootWebOnly="TRUE">
    <File Path="Style Library\BlueWebSite\CSS\customv6.css" Url="BlueWebSite/CSS/customv6.css" Type="GhostableInLibrary" />
  </Module>

2 Answers 2

1

Have you modified the CSS file since first deploying the feature? It's possible that the file has become unghosted and as such it won't change based on whats in the file system anymore. This is to protect customizations you've made in SharePoint from getting overwritten by the file system every time an update is made. This answer provides a script that will let you see the ghosting status of the file.

This page talks about the terms Ghosted, Unghosted, and Reghosted. The jist of it is as follows:

  • Ghosted means that the Site Definition pages have Not been Customized (Ghosted == Un-Customized) and the pages in your Site Definitions run directly from the file system (cached of course).
  • Unghosted means that the site has been Customized (Unghosted == Customized). When you customize a v2 site in Frontpage or a v3 site in SharePoint Designer, or you add custom fields to a Doc Library, or you customize a site in the UI and then save it as a Template - upload it into a Template Gallery - and then create sites using that template; then the changes that you made are stored in the database as a diff and that is referred to as Unghosted
  • When we talk about Reghosting, such as using the "Reset to Site Definition" option in Site Settings, or the Reghost switch in STSADM when running a Gradual Upgrade, or using C# code for Reghosting (Blog 11 in the Upgrade series); then the Diff in the database is nulled out (the customizations are deleted) and the Response once again is just the Site Definition page that runs out of the file system (Reghosting == Changing from Customized to Un-Customized).

The last item I'd like to point out is that Ghosted files are cached. Make sure that you don't have any caching going on that is preventing you from seeing your changes. Since this is stored in a document library you can easily verify the contents of the file as I'm sure you have already done but its an important step to check and is worth mentioning.

0

In Sharepoint 2013 with cumulative updates of march and april there is a new boolean attribute called ReplaceContent that you can use in the Elements.xml. In my project I use this setting and it seems to work.

The module won't replace files that have already been provisioned.

You could delete them in Feature deactivating if you want to re-provision files in a feature.

I use this code in my project in the feature deactivating

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
        if (properties != null)
        {
            using (SPSite currentSite = (SPSite)properties.Feature.Parent)
            {
                using (var web = currentSite.OpenWeb())
                {
                    var ElementDefinitions = properties.Definition.GetElementDefinitions(CultureInfo.CurrentCulture);

                    foreach (SPElementDefinition ElementDefinition in ElementDefinitions)
                    {
                        if (ElementDefinition.ElementType == "Module")
                        {
                            Helper.UpdateFilesInModule(ElementDefinition, web);
                        }
                    }
                }

            }
        }
    }


internal static class Helper
{
    internal static void UpdateFilesInModule(SPElementDefinition elementDefinition, SPWeb web)
    {
        XElement xml = elementDefinition.XmlDefinition.ToXElement();
        XNamespace xmlns = "http://schemas.microsoft.com/sharepoint/";
        string featureDir = elementDefinition.FeatureDefinition.RootDirectory;
        Module module = (from m in xml.DescendantsAndSelf()
                         select new Module
                         {
                             ProvisioningUrl = m.Attribute("Url").Value,
                             PhysicalPath = featureDir,
                             Files = (from f in m.Elements(xmlns.GetName("File"))
                                      select new Module.File
                                      {

                                          FilePath = (m.Attribute("Path") == null) ? string.Empty : Path.Combine(featureDir, m.Attribute("Path").Value),
                                          Name = f.Attribute("Url").Value,
                                          Properties = (from p in f.Elements(xmlns.GetName("Property"))
                                                        select p).ToDictionary(
                                                          n => n.Attribute("Name").Value,
                                                          v => v.Attribute("Value").Value)
                                      }).ToArray()
                         }).First();

        if (module == null)
        {
            return;
        }

        foreach (Module.File file in module.Files)
        {
            string virtualPath = string.Concat(web.Url, "/", module.ProvisioningUrl, "/", file.Name);

            SPFile fileDelete = web.GetFile(virtualPath);
            if (fileDelete != null)
            {
                fileDelete.Delete();
            }
        }

    }

    public static XElement ToXElement(this XmlNode node)
    {
        XDocument xDoc = new XDocument();

        using (XmlWriter xmlWriter = xDoc.CreateWriter())

            node.WriteTo(xmlWriter);

        return xDoc.Root;

    }
}

public class Module
{
    public string ProvisioningUrl { get; set; }
    public string PhysicalPath { get; set; }
    public Module.File[] Files { get; set; }

    public class File
    {
        public string FilePath { get; set; }
        public string Name { get; set; }
        public Dictionary<string, string> Properties { get; set; }
    }
}

Have a nice day

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.