Share

LinkedIn

Profile Specific Style Sheets

When creating content within Sitecore, or any CMS for that matter, you would like to view the content exactly how it will appear on the live site. Sitecore allows you to specify a CSS file to load into the RadEditor.

This lets you view the styles in Design mode as they would appear on the published site and also populates the Apply CSS Class dropdown list with available CSS classes.  Unfortunately, the standard behavior only allows a single style sheet and any class defined within it will be made available in the class dropdown. Often times this is less than ideal. Frequently, certain pages or elements of a page have special style rules that are not shared across the whole site. In this post we will show how to extend Sitecore to allow you to specify a style sheet and the classes available in the Class dropdown as part of the editor profile.

Configuring Sitecore

Before we can write code to load style sheets from the editor profile, we need to create items in the profile that will store that information. We start by creating a couple of simple templates in the core database. The Style Sheet template has just one field, Style Sheet Path. The Class template has two fields, Label and Selector.

20110620ben1 20110620ben2 20110620ben3

Next, we duplicate an existing profile and add a couple of new folders and items based on the above templates. Note that the selectors specified in the Class items must appear in one of the style sheets that will be loaded or you will not see any effect after applying the class.

20110620ben4 20110620ben5 20110620ben6

Finally, we switch back to the master database and add the path to our editor profile in the source field of a rich text field on one of our templates.

20110620ben7

Sitecore 6.3 and Earlier

There has been an article on SDN for several years about loading dynamic style sheets. This serves as the basis of our code for Sitecore 6.3 and earlier. We extend the RADEditor page class and hide the Page_Load method with one of our own.

01.using System;
02.using System.Linq;
03.using Sitecore.Data.Items;
04.using Sitecore.Security.Accounts;
05.using Sitecore.SecurityModel;
06.using Sitecore.Web;
07.   
08.namespace Aws.BlogPosts.ProfileSpecificStylesheets.SixTwo
09.{
10.    public class StylishRadEditor : Sitecore.Shell.Controls.RADEditor.RADEditor
11.    {
12.        protected new void Page_Load(object sender, EventArgs args)
13.        {
14.            base.Page_Load(sender, args);
15.   
16.            string queryString = WebUtil.GetQueryString("mo", "Editor");
17.            User user = Sitecore.Context.User;
18.            if (!user.IsAuthenticated)
19.            {
20.                user = Sitecore.Security.Accounts.User.FromName(WebUtil.GetQueryString("us"), true);
21.            }
22.            using (new UserSwitcher(user))
23.            {
24.                using (new SecurityEnabler())
25.                {
26.   
27.                    string source = WebUtil.GetQueryString("so",
28.                        (queryString == "IDE")
29.                            ? "/sitecore/system/Settings/Html Editor Profiles/Rich Text IDE"
30.                            : "/sitecore/system/Settings/Html Editor Profiles/Rich Text Default");
31.                    Item profileItem = Sitecore.Context.Database.Items[source];
32.   
33.                    AddStyleSheets(profileItem);
34.                }
35.            }
36.        }
37.   
38.        protected virtual void AddStyleSheets(Item profileItem)
39.        {
40.            Item styleSheetsFolder = profileItem.Children["Style Sheets"];
41.            if (null != styleSheetsFolder)
42.            {
43.                string strNoCache = "?noCache=" + DateTime.Now.Ticks.ToString();
44.                RichText.CssFiles =
45.                    styleSheetsFolder.Children.Select(x => x["Style Sheet Path"] + strNoCache).ToArray();
46.            }
47.   
48.            Item classesFolder = profileItem.Children["Classes"];
49.            if (null != classesFolder)
50.            {
51.                foreach (Item child in classesFolder.Children)
52.                {
53.                    RichText.CssClasses.Add(child["Label"], child["Selector"]);
54.                }
55.            }
56.        }
57.    }
58.}

Most of the code in the Page_Load method here is lifted straight from the original code via Reflector. It is just locating the proper editor profile while taking security into account.  Just now, we are more concerned with the AddStyleSheets method. This method looks under the profile item for a folder named “Style Sheets” and then loads the style sheet specified by each of its children. Then, it looks for a folder named “Classes” and adds an item to the CssClasses collection for each of its children. Adding items to this collection prevents the editor from automatically parsing the loaded style sheets to populate the Class dropdown. The result is that the dropdown contains only the classes that we explicitly configure for it, but the design mode can still correctly display additional classes.

Sitecore 6.4 and Later
In version 6.4, the EditorConfiguration class was introduced. A default EditorConfiguration type can be specified in the Web.config and that default can be overridden by each editor profile. These classes, which must extend Sitecore.Shell.Controls.RichTextEditor.EditorConfiguration, have a series of methods that allow you add custom configuration logic to the RadEditor. We will take advantage of this to greatly simplify our code for more recent versions.

When it was first introduced, the EditorConfiguration class did not have a method specifically intended for customizing the style sheets that were loaded. Thanks to a suggestion from yours truly, the SetupStylesheets method was added in 6.4.1 rev. 110324. You can still use this method in 6.4.0, but you will need to override one of the other methods and use it for something that wasn’t strictly intended.

01.using System;
02.using System.Collections.Generic;
03.using System.Linq;
04.using System.Web;
05.using Sitecore.Data.Items;
06.using Telerik.Web.UI;
07.   
08.namespace Aws.BlogPosts.ProfileSpecificStylesheets.SixFourOne
09.{
10.    public class StylishEditorConfiguration : Sitecore.Shell.Controls.RichTextEditor.EditorConfiguration
11.    {
12.        public Item ProfileItem { get; private set; }
13.   
14.        public StylishEditorConfiguration(Item profile) : base(profile)
15.        {
16.            ProfileItem = profile;
17.        }
18.   
19.        protected override void SetupStylesheets()
20.        {
21.   
22.            Item stylesheetsFolder = ProfileItem.Children["Stylesheets"];
23.            if (null != stylesheetsFolder)
24.            {
25.                string strNoCache = "?noCache=" + DateTime.Now.Ticks.ToString();
26.                foreach (Item child in stylesheetsFolder.Children)
27.                {
28.                    Editor.CssFiles.Add(child["Stylesheet Path"] + strNoCache);
29.                }
30.            }
31.   
32.            Item classesFolder = ProfileItem.Children["Classes"];
33.            if (null != classesFolder)
34.            {
35.                foreach (Item child in classesFolder.Children)
36.                {
37.                    Editor.CssClasses.Add(child["Label"], child["Selector"]);
38.                }
39.            }
40.        }
41.    }
42.}

In the constructor here, we save a reference to the profile item. This saves us from having to repeat the couple of dozen lines of code to find the profile item that we had to have for earlier versions. The SetupStylesheets method here contains the same code that was in the AddStyleSheets method seen previously.

Preview
The last piece to our puzzle is the editor preview. This is the view of the Rich Text field you have before you click the Show Editor button. The implementation for the editor preview has not changed significantly since Sitecore 5, so a single version of this customization will suffice for all versions of Sitecore you are likely to come across.

01.using System;
02.using System.Web.UI;
03.using Sitecore.Data.Items;
04.using Sitecore.Security.Accounts;
05.using Sitecore.SecurityModel;
06.using Sitecore.Web;
07.   
08.namespace Aws.BlogPosts.ProfileSpecificStylesheets.SixTwo
09.{
10.    public class StylishPreview : Sitecore.Shell.Controls.RADEditor.Preview
11.    {
12.        protected override void OnLoad(System.EventArgs e)
13.        {
14.            base.OnLoad(e);
15.   
16.            string source = null;
17.            Sitecore.Data.ID fieldId;
18.            if (Sitecore.Data.ID.TryParse(WebUtil.GetQueryString("fld"), out fieldId))
19.            {
20.                TemplateFieldItem fieldDefinition = Sitecore.Context.ContentDatabase.GetItem(fieldId);
21.                if (null != fieldDefinition) source = fieldDefinition.Source;
22.            }
23.   
24.            if (string.IsNullOrEmpty(source))
25.            {
26.                string mode = WebUtil.GetQueryString("mo", "Editor");
27.                source = (mode == "IDE")
28.                    ? "/sitecore/system/Settings/Html Editor Profiles/Rich Text IDE"
29.                    : "/sitecore/system/Settings/Html Editor Profiles/Rich Text Default";
30.            }
31.   
32.            User user = Sitecore.Context.User;
33.            if (!user.IsAuthenticated)
34.            {
35.                user = Sitecore.Security.Accounts.User.FromName(WebUtil.GetQueryString("us"), true);
36.            }
37.            using (new UserSwitcher(user))
38.            {
39.                using (new SecurityEnabler())
40.                {
41.                    Item profileItem = Sitecore.Context.Database.Items[source];
42.                    AddStylesheets(profileItem);
43.                }
44.            }
45.        }
46.   
47.        protected virtual void AddStylesheets(Item profileItem)
48.        {
49.            Item stylesheetsFolder = profileItem.Children["Stylesheets"];
50.            if (null != stylesheetsFolder)
51.            {
52.                string strNoCache = "?noCache=" + DateTime.Now.Ticks.ToString();
53.   
54.                foreach (Item child in stylesheetsFolder.Children)
55.                {
56.                    Stylesheets.Controls.Add(
57.                        new LiteralControl(string.Format("",
58.                                                         child["Stylesheet Path"])));
59.                }
60.            }
61.        }
62.    }
63.}

Here we extend the Sitecore.Shell.Controls.RADEditor.Preview class and override the OnLoad method. The ID of the current field is extracted from the “fld” query string parameter. This is then used to lookup the source specified for the field. The source of a Rich Text field should be the path to an editor profile. Once the profile item is located, the link tags for the style sheets specified in in its “Style Sheets” folder are added to the Stylesheets placeholder.

Sitecore development, Sitecore custom code

Comments

Add a Comment

*
*

Please confirm you are human by typing the text you see in this image:

Ben said: 9/28/2011 at 3:40 PM

Thanks for asking that question. I ran into that same problem the first time I tried to customize the editor in 6.4, but I forgot to mention it in the post.

The Rich Text Default profile has a Configuration Type item that has a field named Type. By default, that field is set, overriding any value set in the web.config file. You need to clear that field in order for the setting in the web.config file to be used. I brought this to the attention of Sitecore Support back in January, but they told me it was by design.

Jason said: 9/29/2011 at 1:23 PM

Thank you very much! That's awesome.

Jason said: 9/30/2011 at 10:35 AM

This is what I ended up doing. We also have different "Paragraphs" (a part of Telerik's RTE with its own drop down box) depending on the site, so I overrode the SetupParagraphs as well.

I tried to make two different profiles, but found it difficult to get a reference to the other profile, accessing the core db and all that... probably can be done but it's ok.

So I duplicated the Paragraphs item under the Profile, check to see which site we're on, then populate the paragraphs accordingly, like so:

string startItem = ""; // figure out the start item
string folder = "Paragraphs";
if (startItem == "other site") folder = "Other Site Paragraphs";

Item paragraphsFolder = this.Profile.Children[folder];

foreach (Item child in paragraphsFolder.Children)
{
string header = child.Fields["Header"].Value;
string value = child.Fields["Value"].Value;

this.Editor.Paragraphs.Add(new EditorParagraph(value, header));
}


Thanks for this article and your help in the comments, one less sitecore problem to worry about!

william said: 4/25/2013 at 9:23 PM

hi, i m trying to use this for my sitecore 6.5 multiple sites..

it was working till my content editor informed me that when u click "table" and "table wizard", then select "CSS class". After clicking "OK", go to html view. instead of "<table class="ClassName"...", it is showing "<table className="ClassName"...."

any workaround for that?