Todd Bleeker's 12 Hive

All MindsharpBlogs

Are you pondering what I'm pondering?

My Links

Post Categories

Archives

Blog Stats

Guidelines to Centralize SharePoint Style/Script Customizations

It's no secret that I think that the Content Editor Web Part (CEWP) rocks:

  • It is easy to export and reuse elsewhere.
  • It is easy to send to your friends for them to use.
  • It is not tied to the site definition ID or the LCID.
  • It always overrides any previous style/script definition (because of it's position on the page).
  • It is easy to remove when you no longer need it. 
  • It can be hidden from view so that only it's effects are seen.
  • It is already on your server: No "Assembly" Required

In fact, most of my CEWP solutions start on a given page trying to solve a given problem. If you have followed some of my articles, you know that my solutions don't usually stop there.

This post is intended to get you past a one-off solution (which I will continue to showcase as they are the easiest to understand) to a wider deployment that doesn't result in replicative copies of the same code being propagated across your farm. In other words, a more maintainable solution; a solution that will easily move from v2 to v3.

Let's take the simple example of hiding the Quick Launch bar (always a great place to start). As I describe here: a CEWP with the code embedded in it can be placed into the Page Web Part Gallery, the Site Collection Web Part Gallery, and/or the Virtual Server Web Part Gallery so that end users can drag and drop it into situations where they will benefit from it most. Of course, each time it is placed onto a page, a copy of the code is placed onto the page embedded within the Web Part.

The trouble comes when you want to alter every instance of the original code within each of the Web Parts already in use. Or, you want the effects to be realized 1) on every instance of a Web Part Page, 2) on every Web Part Page in every Web, or 3) across your entire farm.

Problem: Maintaining deployed code

Continuing with the Quick Launch bar example, let's say that hiding the entire Quick Launch was the solution that the demanders were clamoring for. So, I created a CEWP that embedded that functionality and gave it to my users to put in whatever context they saw fit. After a few months there may be dozens, even hundreds of instances of that code on various Web Part Pages throughout my farm. The demanders are happy until one day Bob (God love him) discovers a bug in my solution (God forbid). Or perhaps on a creative day I (God love me) added a new feature to the existing code and want roll that out to everyone. I could send out an email that says something like:

Attention everyone, Bob discovered an anomaly in the Hide Quick Launch Web Part so if you used that Web Part on any of your pages, please remove it and add in the updated version from your Virtual Server Web Part Gallery.

OR

Attention everyone, you will love the new feature I added to the Hide Quick Launch Web Part so if you used that Web Part on any of your pages, please remove it and add the updated version from your Virtual Server Web Part Gallery. You'll love it.

Of course, everyone will immediately remember everywhere that they used that Web Part and rush out to replace it, right?

What to do?

Fortunately, you have several options that vary in scope but each solution requires pre-use Web Part dependencies. Pre-use means: certain steps that must be taken before the Web Part can be dragged into a Web Part Zone. I detest pre-use Web Part dependencies. I much prefer my Web Parts to contain everything that they require as embedded, URL accessible class resources. But, alas, that option isn't available with the CEWP. So, lets explore the potential options:

Solution 1: Link to the code rather than embedding it (scope remains the instance page but the source is centralized)

  • Start with a working CEWP embedded with code that does something that you want to do.
  • Copy the style/script code from the CEWP's Source Editor into a text file. The file extension that you use will not matter and the code does not require any change.
  • Upload the text file into a document library accessible to everyone that could potentially see the CEWP. If the file was called QuickLaunch.css and the document library was called CodeLibrary and it was located in the root of the wss1 virtual server. The fully qualified URL to the document would be:
       http://wss1/CodeLibrary/QuickLaunch.css
    In some circumstances, a root relative URL will suffice (I tend to use these whenever possible) but most of the time, to get portability, you will need the full URL.
  • Remove the style/script code from the existing CEWP or add a new CEWP to a test page and set the Content Link to the URL of the text document that contains the code. So, clicking on the Source Editor... button would present an empty dialog box and the Content Link would take us to a page that contained the code that used to be in the Source Editor. Click on the Test Link link. If SharePoint asks if you want to open or save the file you know you have a valid link, you can choose cancel and continue with these steps.
  • Click OK or Apply and you should see that the Web Part is still functioning as it was when the code was embedded.
  • Export the Web Part as a Dashboard Web Part (DWP).
  • Place the DWP into a Gallery if you like.

Now users can add this Web Part to any page they like. If you need to fix Bob's bug or deploy a new feature you simply upload the text file with the new code overwriting the existing text file. Note that if the text file, the document library, the Web, or the Virtual Server are not available to the consuming page, the CEWP will cease to work properly. The crux of a pre-use Web Part dependency is it's dependency on something other than itself and the Web Part Framework.

       
 

Solution 2: Embed the code in a direct-mode page in your custom site definition (scope becomes every instance of the direct-mode page)

If the solution you are looking to implement is only applicable to one page in a given site definition and will always need to be implemented on that page, you may want to go straight to the source and modify the direct-mode page (this is not supported by Microsoft but may be invaluable anyway). Of course, you will need write access to the ASPX pages in your custom site definition on each Web server to make this solution work.

Also, given you have access to this page, you may want to consider other ways of accomplishing your goal. With the CEWP you only have client-side tools like JavaScript and CSS. But within this environment, you can modify everything outside of the LinkBar and WebPartZone containers and all ghosted pages will adopt your changes. I'm sure that you don't have any unghosted pages, but if you did, they won't see these changes unless you reghosted them using the Ghost Hunter Web Part in Maurice's Web Part Toolkit (a must have tool). FWIW, I run a ghosted-only shop.

So, in the direct-mode page, I wouldn't hide the Quick Launch using JavaScript, I would just delete the table that generates it so that it isn't sent to the browser in the first place. However, there are many times when the style/script you want to run won't change at all.

The important thing is where you place the code on this page. Remember, cascading code cascades. So, if you put your style/script overrides in the wrong place within this file; it is your code that will be overridden, not SharePoint's.

I typically place my code just before the tag. That way I can be relatively certain that my style is overriding any built-in SharePoint defined styles but any CEWP can still override these styles again on any instance page.

SharePoint places most of its script wherever the <form>tag is placed. So, I typically place my <script>... code immediately following the <form>tag. Keep in mind that frequently client-side JavaScript will need the DOM in the browser to be completely loaded before it can run (think of it like EnsureChildControls in a Web Part). So, running your function on window.onload will likely change the sequence in which the JavaScript executes. Similar to a left outer join in SQL requiring left outer joins on subsequent tables, when you start using window.onload to run functions you will likely need to run all subsequent functions on window.onload so that the order is preserved.

Check out this cross-browser window.onload code I "kyped" from somewhere (?credit original source here?) and then modified to meet my needs:

function AddToWindowOnload(eventToAdd)
{
  var SILENCE_ERRORS = true;
  try
  {
    if(typeof window.attachEvent != "undefined")
    {
      //Try using IE's attachEvent
      window.attachEvent("onload", eventToAdd);
    }
    else if(typeof window.addEventListener != "undefined")
    {
      //Then, try using Mozilla's addEventListener
      window.addEventListener("load", eventToAdd, false);
    }
    else if(window.onload != null)
    {
      //Otherwise, append to existing window.onload events,
      //if any, by creating a function
      var origOnload = window.onload;
      window.onload = function(e)
      {
        origOnload(e);
        eventToAdd();
      };
    }
    else
    {
      //Otherwise, assign directly to window.onload
      window.onload = eventToAdd;
    }
  }
  catch(e)
  {
    if(SILENCE_ERRORS)
    {
      window.onerror = function(){return true;}}else{alert(e.message)};
    }
  }
}

Solution 3: Embed the code in an AlternateCSS or CustomJsUrl file (scope grows to every page in an entire site definition)

If the solution you are looking to implement is applicable to every page in a given site definition and will always need to be implemented, you may want add your code to the override files I explain my Custom Site Definition Recommendations article on Mindsharp's premium content site (similar details are also in my Advisor article). Of course, you will need write access to the these override files for your custom site definition in the 60 hive on each Web server to make this solution work. Unfortunately, the direct-mode pages in your custom site definition only refer to the default, farm-level ows.css file using a static HTML link. So, to support AlternateCSS, all the direct-mode pages in your entire custom site definition will need to be modified slightly. I advise changing this link to use SharePoint's CssLink server control. Fortunately, you can change this globally and simultaneously from VS.NET for all 100+ ASPX pages in your custom site definition using the following steps:

  1. Open VS.NET and choose Edit > Find and Replace > Replace in Files (Ctrl+Shift+H) from the VS.NET menu
  2. Enter this text in the Find what text box:
  3. Enter this text in the Replace with text box:
  4. Enter this text in the Look in text box:
    C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\1033\[sitedefinition]
    where [sitedefinition] is the subdirectory where your custom site definition exists
  5. Ensure that Look in subfolders is checked and that the File types is changed from *.* to *.ASPX
  6. Click on the Replace All button.
  7. Confirm the warning that this can't be undone and VS.NET should replace the link in all files in the custom site definition.

Using CustomJSUrl requires no direct-mode page changes.

Again, given you have access to these custom site definition files, you may again want to consider other ways of accomplishing your goal. But usually the client-side JavaScript and CSS that you developed using the CEWP will not require much, if any change. Just realize that this code will now be used on every page of every Web that is based on your custom site definition.

This is a relatively easy change to make once your site definition is ready. You simply copy all of the code inside of the identifying tags into the appropriate file. For instance, if the CEWP had the following style code:

<style>
  .ms-nav{ display: none; }
</style>

You would copy only the following CSS code into the file identified by the AlternateCSS attribute in the Project element of your custom site definition's ONET.XML file:

.ms-nav{ display: none; }

Note that the style tags are not needed because this code is now included on the page within a file that is referenced by the href attribute of a stylesheet link in the underlying HTML:

<link rel="stylesheet" type="text/css" href="/_layouts/1033/Custom/SITEDEF/ows.css">

Similarly, JavaScript will not need the surrounding script tags. For instance, my Disable Right Click CEWP has the following script code to prevent right clicking on the page:

<script type="Text/JavaScript" language="JavaScript">
  if(typeof browseris != 'undefined' && browseris.ie)
  {
    document.oncontextmenu = function(){return false;};
  }
  else
  {
    document.oncontextmenu = "return false";
  }
</script>

You would copy only the following JavaScript code into the file identified by the CustomJSUrl attribute of the Project element of your custom site definition's ONET.XML file:

if(typeof browseris != 'undefined' && browseris.ie)
{
  document.oncontextmenu = function(){return false;};
}
else
{
  document.oncontextmenu = "return false";
}

Note that the script tags are not needed because this code is now included on the page within a file that is referenced by the src attribute of a script tag in the underlying HTML:

<script src="/_layouts/1033/Custom/SITEDEF/ows.js"></script>

Solution 4: Embed the code in one of SharePoint's built-in files (scope grow to every page in the entire farm)

If the solution you are looking to implement is applicable to every page in EVERY site definition and will always need to be implemented, you may want add your code to the files that are built-in to SharePoint. I don't necessarily recommend this option but for some it will be the best option, especially if you started using SharePoint with the out-of-the-box site definitions. Of course, you will need write access to both of these files and, since they are owned by Microsoft, any changes that you make to them could be overwritten by an upgrade or service pack.

My approach to this has been to create my own files to keep my farm-level style/script code separate from Microsoft's and then I only have one reference in SharePoint's files to my custom code. So, I add a reference to my farm-wide custom CSS file from the built-in CSS file that is included on every Web Part page in the farm. I do this by adding the following code to the bottom of the built-in .\60\TEMPLATE\LAYOUTS\1033\STYLES\OWS.CSS file:

@import url("/_layouts/1033/Custom/ows.css");

This will make the styles defined in the referenced file applicable to every page in the entire farm. Like Solution 3, the contents of the custom ows.css file should not include any style tags but, of course, you will need to create a file (in this example called ows.css in the Custom folder, even if it is initially just an empty file). Also note that on Site Settings page for a SharePoint Portal Server there is a special way of referencing farm-wide CSS file to styles unique to the farm. The URL can be set in Site Settings > Change portal site properties and SharePoint site creation settings > Custom Cascading Style Sheet. The textbox is blank by default and I haven't tested where these styles would be inserted into the cascade (I don't do much with SPS).

Similarly, I add the following code to the bottom of the built-in .\60\TEMPLATE\LAYOUTS\1033\ows.js file (but above all the SIG lines):

//Add custom script to head of every page in the farm
try
{
  var head = document.getElementsByTagName("head");
  if(head)
  {
    var script = document.createElement("script");
    script.language = "javascript";
    script.type = "text/javascript";
    script.src = "/_layouts/1033/Custom/ows.js";
    script.defer = true;
    head[0].appendChild(script);
  }
}
catch(e){ /* ignore */}

This will append the script defined in the referenced file to the head of every page in the entire farm. This is obviously after the built-in farm JavaScript but before script inserted at the <form> tag is defined. Again, like Solution 3, the contents of the custom ows.js file should not include any script tags.

I trust these tips will help everyone implement a more maintainable solution that will smoothly migrate, if needed, to the next version of SharePoint.

<Todd />

posted on Friday, March 10, 2006 3:25 PM

Feedback

# re: Guidelines to Centralize SharePoint Style/Script Customizations 3/11/2006 6:18 AM Bob Fox

WOW! I think that sums it up for this article.
Thanks Todd.

Bob Fox

# Anleitung zur Zentralisierung von SharePoint Style- und Script-Anpassungen 3/16/2006 11:36 AM SharePoint, SharePoint and stuff

Todd Bleeker (the K&ouml;nig des Content Editor Webparts) hat sein Erfahrungen bei der SharePoint-Anpassung...

# Anleitung zur Zentralisierung von SharePoint Style- und Script-Anpassungen 3/16/2006 11:38 AM SharePoint, SharePoint and stuff

Todd Bleeker (the König des Content Editor Webparts) hat seine Erfahrungen bei der SharePoint-Anpassung...

# Anpassa din Teamsajt med CEWP 4/7/2006 8:00 AM Jan-Olov Eriksson

När jag ändå pratar om att anpassa SharePoint, har du nånsin funderat på hur man kan dölja Quick Launch...

# re: Todd's Google Search Web Part 10/31/2006 3:37 PM Todd Bleeker's 60 Hive

# re: Guidelines to Centralize SharePoint Style/Script Customizations 4/1/2007 11:38 PM diko

http://www.allegato-h.vecchialimonaia.it
http://www.bocchini-gay.vecchialimonaia.it
http://www.culi-e-fighe.vecchialimonaia.it

# re: Guidelines to Centralize SharePoint Style/Script Customizations 6/2/2008 5:21 AM Motor Oyunları

Again, like Solution 3, the contents of the custom ows.js file should not include any script tags

# re: Guidelines to Centralize SharePoint Style/Script Customizations 6/2/2008 5:22 AM Araba Resim

WOW! I think that sums it up for this article.

# re: Guidelines to Centralize SharePoint Style/Script Customizations 7/3/2008 7:09 PM youtube

Great, Todd. Mine has however required substantial modification ...

# re: Guidelines to Centralize SharePoint Style/Script Customizations 7/16/2008 8:43 AM Chat

Thank you my friend..

# re: Guidelines to Centralize SharePoint Style/Script Customizations 7/20/2008 6:22 PM youtube

thanks.

# re: Chat 7/30/2008 10:50 AM Chat

thank you...
http://www.aleminyeri.net

# çizgi film 8/11/2008 5:00 AM Çizgi Film

very good

# film izle 8/11/2008 5:00 AM film izle

very good

# gelinlikler 8/11/2008 5:01 AM Gelinlikler

very good

# masaüstü resimleri 8/11/2008 5:01 AM masaüstü resimleri

very good

# mercedes yedek parçaları 8/11/2008 5:02 AM Mercedes Yedek Parçaları

very good

# autocad kursu 8/11/2008 5:05 AM autocad kursu

very good

# Bay 8/11/2008 5:05 AM Havuz

very good

# yemek tarifleri 8/11/2008 5:07 AM yemek tarifleri

very good

# Bay 8/11/2008 5:10 AM havuz

very good

# müzik dinle 8/11/2008 5:10 AM müzik dinle

very good

# re: Guidelines to Centralize SharePoint Style/Script Customizations 8/14/2008 10:38 AM gaziosmanpaşa

very good

# karadeniz resimleri 8/24/2008 4:02 PM karadeniz

Hi…internet is very good world. Because we are learning the information. And than one day fall down internet, we are tobe orphanhood. Thank you very much…

# karadeniz resimleri 8/24/2008 4:02 PM karadeniz

Hi…internet is very good world. Because we are learning the information. And than one day fall down internet, we are tobe orphanhood. Thank you very much…

# re: Guidelines to Centralize SharePoint Style/Script Customizations 9/7/2008 8:41 PM Video Converter for Mac

To enjoy your favorite movie, video clips on your Mac OS, you need equiped with powerful video software on Mac, here you can find them.
http://wwww.downloadyoutubeformac.com/
http://wwww.flvconvertermac.com/

# re: Guidelines to Centralize SharePoint Style/Script Customizations 9/7/2008 8:42 PM Video Converter for Mac

http://wwww.divxtodvdconvertermac.com/
http://wwww.youtubeconvertermac.com/

Title  
Name  
Url
CAPTCHA
Protected by Clearscreen.SharpHIPEnter the code you see:
Comments