What do you do when you need extra resources (JavaScript, CSS) brought into your Content Editor and Page Editor experience? Maybe you have developed a custom field type that requires Google Maps API. Maybe many of your renderings use an external library or a client side helper of your own when in IsPageEditorEditing()
. Whatever the case might be you may want to inject resources into your Content and Page Editor environments and do it once globally. And in a declarative way. This blog post will walk you through doing exactly that.
Declarative Configuration
As you probably have guessed we will exploit two different pipelines – one for Content Editor and one for Page Editor. The way I do things in there will be a little different due to the nature of how two editors render themselves but the way I configured the resources to inject is universal:
<processor ...> <styles hint="list:addStyleResource"> <resource>/sitecore modules/...</resource> </styles> <scripts hint="list:addScriptResource"> <resource>//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js</resource> <resource>/sitecore modules/...</resource> </scripts> </processor>If you are not familiar with dependency injection features of the Sitecore’s Configuration Factory I recommend this blog post by John West:
To accept the configuration the processors will declare two collection fields and expose two Add*()
methods:
private readonly IList<string> _scripts = new List<string>(); private readonly IList<string> _styles = new List<string>(); public void AddStyleResource(string resource) { _styles.Add(resource); } public void AddScriptResource(string resource) { _scripts.Add(resource); }Content Editor
Content Editor is rendered via
renderContentEditor
:<renderContentEditor> <processor patch:before="*[1]" type="{Namespace}.InjectContentEditorResources, {Assembly}"> <styles hint="list:addStyleResource"> ... </styles> <scripts hint="list:addScriptResource"> ... </scripts> </processor> </renderContentEditor>And then in the body of the
Process(PipelineArgs args)
(safety checks removed for brevity):var page = HttpContext.Current.Handler as Page; foreach (string script in _scripts) { page.Header.Controls.Add( new LiteralControl( "<script type='text/javascript' language='javascript' src='{0}'></script>".FormatWith(script))); } foreach (string css in _styles) { page.Header.Controls.Add( new LiteralControl( "<link rel="stylesheet" type="text/css" href="{0}"/>".FormatWith(css))); }Page Editor
Page Editor is different. It is designed to render your web site extended with additional elements to support inline and WYSIWYG editing.
[su_note note_color=”#fafafa”]Here at BrainJocks we fully embraced MVC once Sitecore officially supported it in 6.6 and so I won’t cover the pre-MVC pipelines[/su_note]
Take a close look at this configuration patch:
<mvc.requestEnd patch:source="Sitecore.Mvc.config"> <processor type="Sitecore.Mvc.ExperienceEditor.Pipelines.Request.RequestEnd.AddPageExtenders ..." patch:source="Sitecore.MvcExperienceEditor.config"/> </mvc.requestEnd>The
AddPageExtenders
creates a wrapping stream filter that is set onHttpContext.Response.Filter
. When it runs it injects page extenders (scripts and styles) right after the<body>
tag. The extenders are collected viamvc.renderPageExtenders
.And here we go:
&amp;lt;mvc.renderPageExtenders&amp;gt; &amp;lt;processor patch:after="*[last()]" type="{Namespace}.InjectPageEditorResources, {Assembly}"&amp;gt; &amp;lt;styles hint="list:addStyleResource"&amp;gt; ... &amp;lt;/styles&amp;gt; &amp;lt;scripts hint="list:addScriptResource"&amp;gt; ... &amp;lt;/scripts&amp;gt; &amp;lt;/processor&amp;gt; &amp;lt;/mvc.renderPageExtenders&amp;gt;And in the processor itself:
private const string CssLinkPattern = @"&amp;lt;link href=""{0}"" rel=""stylesheet"" /&amp;gt;"; public override void Process(RenderPageExtendersArgs args) { // ... Render(args.Writer); } protected override bool Render(TextWriter output) { // ... foreach (string style in _styles) { output.Write(CssLinkPattern, style); } foreach (string script in _scripts) { output.Write(Sitecore.Web.HtmlUtil.GetClientScriptIncludeHtml(script)); } return true; }That’s it. Your extra resources are now present in both Content and Page Editor. Enjoy!
Hello Pavel. Really nice and informative post.
Thanks.
Hey Paul, very useful post, thanks.
I’ve implemented this on 8.1 and you don’t need to call Render in the Process method. Otherwise, it works perfectly.
Thanks,
Nick
Pingback: Sitecore Taglist Field | Georgi Bilyukov Blog
Hello, thanks for the post, it helped a lot… but not completely.
When I implement the code, it adds the CSS and JS files to the ribbon which of course is in an iframe. I need those files to render outside the ribbon’s iframe.
Would you happen to have any idea as to why it only affects the ribbon?
FYI, I am using Sitecore 8.1 update 3
Thanks
Pingback: A Different Approach: Injecting Resources into the Experience Editor – Eric Stafford
Pingback: Take Two – A Different Approach: Injecting Resources into the Experience Editor – Eric Stafford
Pingback: Injecting Resources into Experience Editor in Powerful Ways | jammykam
excellent points altogether, you just gained a brand new reader. What would you suggest in regards to your post that you made a few days ago? Any positive?