Sitecore Page Editor Tips and tricks – component datasource versions

Out of the box, Sitecore does nothing if a components datasouce doesn’t exist or if there aren’t any versions in the current language. This means that you will have to handle this your self.
Well I thought that it would be nice to let the rules engine in Sitecore handle this.

My plan was to create a global conditional rendering rule that would check the following criterias:

  • Is the Rendering Item of the current rendering configured with a Datasource Template?
  • If not, ignore the following statements and display the rendering!
  • Does the rendering have any datasource set?
  • Does the datasource exist?
  • If the datasource item is a media item, the next statement should be ignored and the rendering displayed.
  • Does it have any versions in the current language?

If it would fail on any of these statement, the rendering should be hidden. Since there weren’t any existing condition which did this, I had to build it my self. Things said and done and the result was this:

public class HasItemVersionForDatasource : OperatorCondition
      where T : ConditionalRenderingsRuleContext
    {
        protected override bool Execute(T ruleContext)
        {
            var renderingItem = ruleContext.Item.Database.GetItem(ruleContext.Reference.RenderingID);
            var dsLocation = renderingItem.Fields["Datasource Location"].Value;
            var dsTemplate = renderingItem.Fields["Datasource Template"].Value;
            if ((dsLocation.Length > 0 || dsTemplate.Length > 0) && string.IsNullOrEmpty(ruleContext.Reference.Settings.DataSource)) return false;
            else if (string.IsNullOrEmpty(ruleContext.Reference.Settings.DataSource)) return true;

            var datasourceitem = ruleContext.Item.Database.GetItem(ruleContext.Reference.Settings.DataSource);
            return datasourceitem.Paths.IsMediaItem || datasourceitem.Versions.Count > 0;
        }
    }

This condition was added to Sitecore and enabled for the Conditional Rendering Rules. Since the condition doesn’t take any parameters you can write anything you like, I went with “where the renderings datasource has item version”.
A new rule at /sitecore/system/Settings/Rules/Conditional Renderings/Global Rules was created using the new condition looking like this:

 

With this now set up, all renderings with a datasource which doesn’t have any version in the current language will be hidden. And in the Page Editor they will be visible as grey blocks like this:

 

That’s it, pretty usefull right? 😀 Now you’ll be relieved from performing null-checks and so on in each component. And also you will get a clear indication in the Page Editor that there are components that aren’t translated.

Note that this only handles guid and path based datasources and not query based datasources. However it could surely be extended.

I will follow up with some additional post regarding Page Editor tips and tricks 😀

 

Page editor and structuring related items

Since Sitecore 6.4.1 I’ve been working alot with the Page Editor in Sitecore and how to make the editor experience as good as possible. I’ve found that it’s hard to keep the editor mainly editing in the Page Editor due to various limitations. One of those are structure.

My latest Sitecore solution, using Sitecore 7.1, utilizes the designer in the Page Editor to it’s full potential where I allow the editor to completely reorganize the content the way they see fit using rows, columns and blocks. As you can imagine this puts a higher demand on structure as one page can consist of several sub layouts which each have it’s own item and then add up that you have loads of pages constructed this way.

Idealy the editor could manage the structure at the same time as he/she is adding the content to the page in the page editor. However this is not possible out of the box in Sitecore.

Many of you might at this time say Item Buckets. However this requires the editor to search for the content and my experience with our customers is that many of them aren’t ready for this because they like to manage their own structure.

Well the solution to my problem ended up being quit simple. All I wanted was to have the context menu you have in the content editor. I also remebered that this context menu was available in the media browser when selecting media, so I started out looking there.

I found this attribute on the TreeviewEx in the media browser xml file (\sitecore\shell\Applications\Media\MediaBrowser\MediaBrowser.xml):

ContextMenu='Treeview.GetContextMenu("contextmenu")'

I made a copy of the SelectRenderingDatasource.xml (\sitecore\shell\Applications\Dialogs\SelectRenderingDatasource\SelectRenderingDatasource.xml) file to put in the override folder and added that attribute to the TreeviewEx in that file and the Context Menu was there. However it ended up opening a little off as you can see in this video http://screencast.com/t/FkwhxHcM. So I had to make some adjustments to the code opening the context menu.

I ended up altering the attribute to this:

ContextMenu='Treeview.GetContextMenu("dialoguemenu")'

And make the following changes to the \sitecore\shell\Controls\Browser.js, I replaced this part:

if (dimensions.width > 0) {
  switch (data.where) {
    case "contextmenu":
      x = evt.pageX || evt.x;
      y = evt.pageY || evt.y;
    break;

With this:

if (dimensions.width > 0) {
  switch (data.where) {
    case "contextmenu":
    case "dialoguemenu":
      x = evt.pageX || evt.x;
      y = evt.pageY || evt.y;
    break;

And wrapped this part:

var vp = ctl.viewportOffset();
x += vp.left;
y += vp.top;

With this if case:

if (data.where != "dialoguemenu") {
  var vp = ctl.viewportOffset();
  x += vp.left;
  y += vp.top;
}

This made the context menu pop up where the mouse was when right clicking.

I know this might not be the optimal solution. I would prefer that you could configure the rendering with a folder template like you do with a datasource template which you could have a dedicated button for like the “Create new content” button and not relying on a context menu. This would increase the experience with the Page Editor it think. Unfortunatly I didn’t have more time to look into this, maybe next time 🙂

Playing with Sitecore Include files

Sitecores include files which are stored in the /App_Config/Include/ folder are a nifty way of alter configurations without editing the web.config file. However sometimes you find yourself questioning how you would write the configurations in the include file.

Unfortunately there’s a lack of good documentations so most of the time you will have to go with the trial and error approach. The only useful documentation I’ve found is this and some forum posts.
As your help you have the tool for showing the merged web.config file at your side. Which can be found at /sitecore/admin/showconfig.aspx

I’ve been poking around in the Sitecore.Xml.Patch.XmlPatchUtils class and found out that the following attributes are available when writing your configuration file.

patch:before
patch:after
patch:instead
patch:delete
patch:attribute

Most often you don’t need to write that complicated configurations like when you want to add a processor into a pipeline before or after a specific processor. The most basic is just adding at the last position like this example where we add a command at the last position.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <commands>
            <command name="test:mycommand" type="MyClasses.MyCommand, MyAssembly"/>            
        </commands>
    </sitecore>
</configuration>

Sometimes you need do add it at a specific position, like before or after a processor. The following example will add the “MyClasses.DoMyStuffProcessor, MyAssembly” processor after the “Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel” processor in the httpRequestBegin pipeline.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <httpRequestBegin>
                <processor patch:after="*[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" type="MyClasses.DoMyStuffProcessor, MyAssembly" method="SomeProcess"/>
            </httpRequestBegin>
        </pipelines>
    </sitecore>
</configuration>

Often you want to change the value of an element like a Sitecore setting, like changing the MailServer value. This is done like this.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <settings>
            <setting name="MailServer">
                <patch:attribute name="value">localhost</patch:attribute>
            </setting>
        </settings>
    </sitecore>
</configuration>

Or maybe remove one which is done like this

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <settings>
            <setting name="SomeSetting">
                <patch:delete/>
            </setting>
        </settings>
    </sitecore>
</configuration>

However sometimes you want to alter some more complex structure like when changing the interval of the task agent for the master database. Since this configuration looks allot like the one for the core database, where the only difference is the value of the sub elementlike you can see below.

<agent type="Sitecore.Tasks.DatabaseAgent" method="Run" interval="00:10:00">
  <param desc="database">core</param>
  <param desc="schedule root">/sitecore/system/tasks/schedules</param>
  <LogActivity>true</LogActivity>
</agent>
<!-- Agent to process schedules embedded as items in a database -->
<agent type="Sitecore.Tasks.DatabaseAgent" method="Run" interval="00:10:00">
  <param desc="database">master</param>
  <param desc="schedule root">/sitecore/system/tasks/schedules</param>
  <LogActivity>true</LogActivity>
</agent>

Here we only want to change the agent for the master database. What I did was using the patch:instead attribute and replaced the entire configuration like this.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <scheduling>
            <agent patch:instead="*[@type='Sitecore.Tasks.DatabaseAgent' and contains(.,'master')]" method="Run" type="Sitecore.Tasks.DatabaseAgent" interval="00:30:00">
                <param desc="database">master</param>
                <param desc="schedule root">/sitecore/system/tasks/schedules</param>
                <LogActivity>true</LogActivity>
            </agent>
        </scheduling>
    </sitecore>
</configuration>

As you can see I used a little xpath where i look for an agent of the type ‘Sitecore.Tasks.DatabaseAgent’ and which contains the string ‘master’ somewhere inside.

You can probably alter any Sitecore configuration using include files, but some alterations will probably have you use more complex xpath queries.

I hope this little playtime with Sitecores include file was helpful.