Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

On our farm, I'm deploying some JavaScript and CSS files under the _layouts folder.

I'm then referencing them, where it's required using a <SharePoint:ScriptLink> or a <SharePoint:CssRegistration> control :

<SharePoint:CssRegistration 
    runat="server" 
    Name="/_layouts/styles/company/custom.css" />
<SharePoint:ScriptLink 
    runat="server" 
    Name="/_layouts/scripts/company/custom.js" 
    Localizable="false" />

This is working fine.

However, these assets are cached in the client browser. When I deploy minor updates, I have to ask users to press Ctrl+F5 to reload without the cache, but it's not an acceptable workaround.

Is there a way to ensure the latest version is loaded, without disabling the cache?

By now, I can see two possible solutions:

  1. version the assets in the file name (custom.1.0.css, custom.1.1.css, ...): I don't like this solution because it requires to update every code that reference the asset
  2. Add a "revision number" to the url. I can trick using this code :

    <SharePoint:CssRegistration 
        runat="server" 
        Name="/_layouts/styles/company/custom.css"
        id="customCss" />
    <SharePoint:ScriptLink 
        runat="server" 
        Name="/_layouts/scripts/company/custom.js" 
        Localizable="false"
        id="customJs" />
    

    And in the codebehind :

        private static readonly long g_AssemblyTimeStamp = File.GetCreationTime(System.Reflection.Assembly.GetExecutingAssembly().Location).Ticks;
        protected override void OnInit(EventArgs e)
        {
            customJs.Name += "?rev=" + g_AssemblyTimeStamp;
            customCss.Name += "?rev=" + g_AssemblyTimeStamp;
        }
    

    This adds to the script url something like ?rev=634946193703232026. The browser then sees a new url and reload it, and still cache this file.

    This is working, but still requires some plumbing code to make it works.

Is there any clean solution to meet this requirement?

share|improve this question

4 Answers 4

up vote 10 down vote accepted

SPUtility provide us a method called - MakeBrowserCacheSafeLayoutsUrl(string, boolean) and other overload methods.

Based on last modification this itself generate a MD5 hash code and append it to css or js urls. We don't have to worry about finding version numbers or generating id based on creation date, this is already handled.

I have used this in my project for handling css caching for custom css. SharePoint internally does same for OOB css which are rendered by CSSLink.

protected override void OnInit(EventArgs e)
{
        CssRegistration cssRegistration1 = new CssRegistration();        
        cssRegistration1.After = "corev4.css";
        cssRegistration1.ConditionalExpression = "IE 7";
        cssRegistration1.Name = SPUtility.MakeBrowserCacheSafeLayoutsUrl("FolderBelowLayouts/Styles/CustomCss.css", false); //will be /_layouts/15/FolderBelowLayouts/Styles/CustomCss.css?rev=...
        Controls.Add(cssRegistration1);
        base.OnInit(e);
}

and for link tags you can directly do:

<link rel="stylesheet" type="text/css" href="<%=SPUtility.MakeBrowserCacheSafeLayoutsUrl("FolderBelowLayouts/Styles/CustomCss.css", false)%>" />

Refer to: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.utilities.sputility.makebrowsercachesafelayoutsurl.aspx

share|improve this answer
1  
I don't think SPUtility.MakeBrowserCacheSafeLayoutsUrl is a good solution. Not a solution at all, really. It does generate ?rev= string after your file, but it does so only once. if you change your file it will not regenerate ?rev= string because ?rev part is stored in a private field in SPUtility.s_layoutsFileHashCache. This cache does not get purged during iisreset and after new wsp is installed. Please, correct me if i'm wrong. –  Denis Molodtsov Dec 29 '13 at 22:11
    
Just to update on Denis' comment (and his question here: sharepoint.stackexchange.com/questions/85984/…) - MakeBrowserCacheSafeLayoutsUrl works as advertised. The Cache is cleared with each IISRESET. –  Dennis G Nov 18 at 16:43

Steve,

there is no clean solution for your problem. Browsers cache files by url, you can avoid caching appending in query string unique value per deployment. For example

_layouts/my_js_file.js?v=<current date> - will be refreshed from cache when day changes

_layouts/my_js_file.js?v=<GUID> - if guid is generated on every request, this file should never cache

_layouts/my_js_file.js?v=<product version> - more preferable solution, browser will update cache on every new version that was deployed

Actually, ScriptLink should take care about this, internally it has a method that appends unique id into query string based on js file content, if content changed, hash is also changed and new unique id is generated.

I recommend you to read this article about your issue.

share|improve this answer
1  
I don't understand. You stated that the scriptlink control will add the hash to the url automatically. This is not the case in my case. Is it possible that this does not occurs in subfolders or _layouts? Actually, my deployment is under _layouts/CustomerName/file... –  Steve B Jan 29 '13 at 14:09
    
@SteveB, if this is not expected behaviour for you, you should go away from ScriptLink and implement your own script control (ScriptLink is sealed, you can't extend it) with your logic. –  Kai Jan 29 '13 at 14:47
    
Actually, I have a solution, which is described in my answer. Having to rewrite the script link completely won't be an improvement. Anyways, thanks for the suggestion. –  Steve B Jan 29 '13 at 14:53

Not sure if this is a cleaner solution, but a couple of years back I did something similar for an Internet facing publishing site.

First, we were using SharePoint Server with the publishing infrastructure hence having a "/Style Library/" with major versioning enabled to store assets like images, CSS & JS files.

Then we created a "PlaceholderAdditionalPageHead" SharePoint delegate control with OnInit code to retrieve the last published major version number of the file in question and append it to the URL like you did.

This saved us from deploying files to "/_layouts/" and also no GAC deployment as we used an ASCX WebControl. Both were requirements set by higher-ups we couldn't get around.

Unfortunately, I can't get my hands on the code currently because I don't have a VM handy with the sourcecode repository holding that old project code. Hope it still helps a little bit...

UPDATE: I forgot to mention that the ASCX WebControl had to be deployed to the server. However, since our solution was part of a larger package we were allowed to deploy the ASCX to the server (otherwise we would have used a DocLib with inline code enabled per web.config). Sorry for missing that part first-hand.

share|improve this answer
    
thanks for your post. But I don't any problem to deploy under the layouts directory. In fact, I don't see how changing the deployment location may help to solve my issue (as you said, you had to add some plumbing code) –  Steve B Jan 25 '13 at 12:58

The solution is, to get rid of the starting "/" in your Name values.

JS

SharePoint will automatically check, if the js file changes and will append a custom query string with a revision number, just like it does with the internal js files, if you check the source of your website. You just have to change the Name in your Scriptlink control like:

<SharePoint:ScriptLink 
    runat="server" 
    Name="scripts/company/custom.js" 
    Localizable="false"
    id="customJs" />

This will change the path to your custom.js on the website to s.th. like:

<script src="/_Layouts/scripts/company/custom.js?rev=HVORLc5FI20n7W90mjha3A%3D%3D"></script>

(If you have your JS files under _layouts/1033, you should set localizable to "true" and keep the Name path the same).

CSS

For CSS, the relative path starts under "/_layouts/1033/styles", so to get to "/_layouts" and make SharePoint take care of Cache Busting, you should change your CSSRegistration Name to

This will change the path to your custom.css on the website to s.th. like:

<link id="CssRegistration2" rel="stylesheet" type="text/css" href="/_Layouts/1033/styles/../../scripts/company/custom.css?rev=p63%2BuzTeSJc22nVGNZ5zwg%3D%3D"/>

(For CSS, I am not sure, if you can use Localizable="False")

Interesting article: http://community.rightpoint.com/blogs/viewpoint/archive/2012/02/24/cache-busting-with-the-sharepoint-cssregistration-control.aspx

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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