Resolve Media Library Items Linked in Sitecore Aliases
Tonight I was doing research on extending the aliases feature in Sitecore, and discovered media library items linked in them are not served correctly “out of the box”:
As an enhancement, I wrote the following HttpRequestProcessor subclass to be used in the httpRequestBegin pipeline:
using Sitecore.Configuration; using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.Pipelines.HttpRequest; using Sitecore.Resources.Media; using Sitecore.Web; namespace Sitecore.Sandbox.Pipelines.HttpRequest { public class MediaAliasResolver : HttpRequestProcessor { public override void Process(HttpRequestArgs args) { Assert.ArgumentNotNull(args, "args"); if (!CanProcessAliases()) { return; } string mediaUrl = GetMediaAliasTargetUrl(args); if (string.IsNullOrWhiteSpace(mediaUrl)) { return; } Context.Page.FilePath = mediaUrl; } private static bool CanProcessAliases() { return Settings.AliasesActive && Context.Database != null; } private static string GetMediaAliasTargetUrl(HttpRequestArgs args) { Assert.ArgumentNotNull(args, "args"); ID targetID = Context.Database.Aliases.GetTargetID(args.LocalPath); if (targetID.IsNull) { return string.Empty; } Item targetItem = args.GetItem(targetID); if (targetItem == null || !IsMediaItem(targetItem)) { return string.Empty; } return GetAbsoluteMediaUrl(targetItem); } private static bool IsMediaItem(Item item) { Assert.ArgumentNotNull(item, "item"); return item.Paths.IsMediaItem && item.TemplateID != TemplateIDs.MediaFolder; } private static string GetAbsoluteMediaUrl(MediaItem mediaItem) { string relativeUrl = MediaManager.GetMediaUrl(mediaItem); return WebUtil.GetFullUrl(relativeUrl); } } }
The HttpRequestProcessor subclass above — after ascertaining the aliases feature is turned on, and the item linked in the requested alias is a media library item — gets the absolute URL for the media library item, and sets it on the FilePath property of the Sitecore.Context.Page instance — this is exactly how the “out of the box” Sitecore.Pipelines.HttpRequest.AliasResolver handles external URLs — and passes along the HttpRequestArgs instance.
I then wedged the HttpRequestProcessor subclass above into the httpRequestBegin pipeline directly before the Sitecore.Pipelines.HttpRequest.AliasResolver:
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <httpRequestBegin> <processor patch:before="processor[@type='Sitecore.Pipelines.HttpRequest.AliasResolver, Sitecore.Kernel']" type="Sitecore.Sandbox.Pipelines.HttpRequest.MediaAliasResolver, Sitecore.Sandbox" /> </httpRequestBegin> </pipelines> </sitecore> </configuration>
Let’s take this for a spin.
I had already defined the following alias in Sitecore beforehand — the error page at the top of this post is evidence of that:
After navigating to http://sandbox/pizza — the URL to the pizza alias in my local Sitecore sandbox instance (don’t click on this link because it won’t go anywhere unless you have a website named sandbox running on your local machine) — I was brought to the media library image on the front-end:
If you have any recommendations on improving this, or further thoughts on using aliases in Sitecore, please share in a comment below.
Until next time, have a pizzalicious day!
Specify the Maximum Width of Images Uploaded to the Sitecore Media Library
Last week someone started a thread in one of the SDN forums asking how one could go about making Sitecore resize images larger than a specific width down to that width.
Yesterday an astute SDN visitor recommended using a custom getMediaStream pipeline processor to set the maximum width for images — a property for this maximum width is exposed via the GetMediaStreamPipelineArgs parameters object passed through the getMediaStream pipeline.
I thought I would try out the suggestion, and came up with the following getMediaStream pipeline processor:
using Sitecore.Diagnostics; using Sitecore.Resources.Media; namespace Sitecore.Sandbox.Resources.Media { public class MaxWidthProcessor { public int MaxWidth { get; set; } public void Process(GetMediaStreamPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); if (!ShouldSetMaxWidth(args)) { return; } args.Options.MaxWidth = MaxWidth; } private bool ShouldSetMaxWidth(GetMediaStreamPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); return MaxWidth > 0 && args.Options.MaxWidth < 1; } } }
I then interlaced it into the getMediaStream pipeline before the ResizeProcessor processor — this is the processor where the magical resizing happens — using the following patch configuration file:
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <getMediaStream> <processor patch:before="processor[@type='Sitecore.Resources.Media.ResizeProcessor, Sitecore.Kernel']" type="Sitecore.Sandbox.Resources.Media.MaxWidthProcessor, Sitecore.Sandbox"> <MaxWidth>1024</MaxWidth> </processor> </getMediaStream> </pipelines> </sitecore> </configuration>
The maximum width for images is set to 1024 pixels — the width I am using in my test below.
Let’s see how we did.
I decided to use one of my favorite images that ships with Sitecore for testing:
As you can see its width is much larger than 1024 pixels:
After uploading the lighthouse image into the media library, its width was set to the maximum specified, and its height was scaled down proportionally:
If you have any thoughts on this, or other ideas on resizing images uploaded to the media library, please drop a comment.
Specify Which Sitecore Web Forms for Marketers Form To Render Via the Query String
Today I saw a post in one of the SDN forums asking how one could go about building a page in Sitecore that can render a Web Forms for Marketers (WFFM) form based on an ID passed via the query string.
I built the following WFFM FormRenderer as a “proof of concept” to accomplish this — this solution assumes the ID we are passing is the ID of the form, and not some other ID (or guid):
using System; using System.Web; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.Form.Core.Configuration; using Sitecore.Form.Core.Renderings; namespace Sitecore.Sandbox.Form.Core.Renderings { public class DetectIDFormRenderer : FormRender { protected override void OnInit(System.EventArgs e) { string detectedFormId = GetDetectedFormId(); if (IsValidFormId(detectedFormId)) { FormID = detectedFormId; } base.OnInit(e); } private static string GetDetectedFormId() { return HttpContext.Current.Request["formId"]; } private static bool IsValidFormId(string id) { return !string.IsNullOrWhiteSpace(id) && IsID(id) && IsFormId(id); } private static bool IsID(string id) { Sitecore.Data.ID sitecoreID; return Sitecore.Data.ID.TryParse(id, out sitecoreID); } private static bool IsFormId(string id) { Item item = StaticSettings.ContextDatabase.GetItem(id); return item != null && item.TemplateID == IDs.FormTemplateID; } } }
The FormRenderer above grabs the specified form’s ID via a query string parameter, ascertains whether it’s truly an ID, and determines whether it is an ID of a WFFM Form in Sitecore — these are done via the IsID and IsFormId methods.
If the supplied form ID is valid, we save it to the FormID property defined in the base FormerRender class. Otherwise, we flow through to the “out of the box” logic.
Now it’s time to register the above class in Sitecore.
I duplicated the “out of the box” Form Webcontrol under /sitecore/layout/Renderings/Modules/Web Forms for Marketers, renamed the item to something appropriate, and updated the code-defining fields to point to our new FormRender above:
I decided to reuse an existing page item with a WFFM form — I didn’t want to step through ‘Insert Form’ wizard so that I could save time — and swapped out the “out of the box” Form Webcontrol with the new one we created above:
I ensured we had a default form set just in case of query string manipulation, or in the event the form cannot be found by the given ID:
I published everything, and navigated to my form page:
I then specified the empty guid:
I manipulated the query string again, but this time passing a valid form ID:
I then changed the form ID again but with another valid form ID:
If you have any suggestions around making this better, or ideas for a different solution, please drop a comment.
Restrict Certain Types of Files From Being Uploaded in Sitecore
Tonight I was struck by a thought while conducting research for a future blog post: should we prevent users from uploading certain types of files in Sitecore for security purposes?
You might thinking “Prevent users from uploading files? Mike, what on Earth are you talking about?”
What I’m suggesting is we might want to consider restricting certain types of files — specifically executables (these have an extension of .exe) and DOS command files (these have an extension of .com) — from being uploaded into Sitecore, especially when files can be easily downloaded from the media library.
Why should we do this?
Doing this will curtail the probability of viruses being spread among our Sitecore users — such could happen if one user uploads an executable that harbors a virus, and other users of our Sitecore system download that tainted executable, and run it on their machines.
As a “proof of concept”, I built the following uiUpload pipeline processor — the uiUpload pipeline lives in /configuration/sitecore/processors/uiUpload in your Sitecore instance’s Web.config — to restrict certain types of files from being uploaded into Sitecore:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; using System.Xml; using Sitecore.Diagnostics; using Sitecore.Globalization; using Sitecore.Pipelines.Upload; namespace Sitecore.Sandbox.Pipelines.Upload { public class CheckForRestrictedFiles : UploadProcessor { private List<string> _RestrictedExtensions; private List<string> RestrictedExtensions { get { if (_RestrictedExtensions == null) { _RestrictedExtensions = new List<string>(); } return _RestrictedExtensions; } } public void Process(UploadArgs args) { foreach(string fileKey in args.Files) { string fileName = GetFileName(args.Files, fileKey); string extension = Path.GetExtension(fileName); if (IsRestrictedExtension(extension)) { args.ErrorText = Translate.Text(string.Format("The file \"{0}\" cannot be uploaded. Files with an extension of {1} are not allowed.", fileName, extension)); Log.Warn(args.ErrorText, this); args.AbortPipeline(); } } } private static string GetFileName(HttpFileCollection files, string fileKey) { Assert.ArgumentNotNull(files, "files"); Assert.ArgumentNotNullOrEmpty(fileKey, "fileKey"); return files[fileKey].FileName; } private bool IsRestrictedExtension(string extension) { return RestrictedExtensions.Exists(restrictedExtension => string.Equals(restrictedExtension, extension, StringComparison.CurrentCultureIgnoreCase)); } protected virtual void AddRestrictedExtension(XmlNode configNode) { if (configNode == null || string.IsNullOrWhiteSpace(configNode.InnerText)) { return; } RestrictedExtensions.Add(configNode.InnerText); } } }
The class above ascertains whether each uploaded file has an extension that is restricted — restricted extensions are defined in the configuration file below, and are added to a list of strings via the AddRestrictedExtensions method — and logs an error message when a file possessing a restricted extension is encountered.
I then tied everything together using the following patch configuration file including specifying some file extensions to restrict:
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <processors> <uiUpload> <processor mode="on" type="Sitecore.Sandbox.Pipelines.Upload.CheckForRestrictedFiles, Sitecore.Sandbox" patch:before="processor[@type='Sitecore.Pipelines.Upload.CheckSize, Sitecore.Kernel']"> <restrictedExtensions hint="raw:AddRestrictedExtension"> <!-- Be sure to prefix with a dot --> <extension>.exe</extension> <extension>.com</extension> </restrictedExtensions> </processor> </uiUpload> </processors> </sitecore> </configuration>
Let’s try this out.
I went to the media library, and attempted to upload an executable:
After clicking the “Open” button, I was presented with the following:
An error in my Sitecore instance’s latest log file conveys why I could not upload the chosen file:
If you have thoughts on this, or have ideas for other processors that should be added to uiUpload pipeline, please share in a comment.