Sitecore XAML application Syntax

I’ve been trying to write my own XAML application recently for a client that needed a specific context-menu click function. Sitecore documented this a while back, and although it doesn’t state how to incorporate a dialog, I was able to look into how the existing controls work in the sitecore/shell directory, and figure out how to show the dialog. What I was then missing was how to write the XML layout for the application.

I was able to get together what I needed for this specific application, but then I was curious as to what else I could do. I went through the XAML documentation, but I eventually found the Sitecore documentation that is more relevant. So, for reference, this is the link that explains all the different controls available and the functionality available:

http://sdn.sitecore.net/Articles/XML%20Sheer%20UI/My%20first%20XML%20application.aspx

So – if you want to begin from scratch, this is the place to begin. The important thing to note here is the almost all controls from the Sitecore.Web.UI.HtmlControls namespace is available, and the examples show few of the important ones.

Some other links that helped:

An edited version of this post also appears on The Runtime Report, Mickey Rahman’s Other Persona.

Adding custom messages to the publish dialog in Sitecore

Sitecore has pretty sophisticated event handling and pipeline hooks functionality built in. What this means is that almost any area of the user experience can be changed to provide a more customized user experience, tailored to the business needs of the users of the Sitecore implementation. This can be done very easily by writing your own code/class, and then wiring it up using the configuration directives. Event handlers are the easiest to implement – what you need to do is make your own class, define your own method in that class, then just add your include config file with the method/class for that event:


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <events>
      <event name="publish:end">
        <handler patch:after="handler[@type='Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel']" type="CustomClassName, CustomAssemblyName" method="CustomMethodName" />
      </event>
    </events>
  </sitecore>
</configuration>

In your method, you get what item is being published and what language is being published from the publisher options. Note that the event triggers once for each language and target combination.


Sitecore.Publishing.Publisher p = (Sitecore.Publishing.Publisher)((Sitecore.Events.SitecoreEventArgs)args).Parameters[0];
Item itemBeingPublished = p.Options.RootItem;
Language languageBeingPublished = p.Options.Language;
Database targetDatabase = p.Options.TargetDatabase.Name

There a myriad of events available (http://sdn.sitecore.net/Articles/API/Using%20Events.aspx)- in this post, I’m concentrating on customizing the publish:end event to add messages to the dialog at the end of the publish job. The publish dialog shows informational content about what was published in the job:

The publish dialog at the end of the publish job

The publish dialog at the end of the publish job

Depending on how complex your publish:end code is, you may want to add some informational text here, to show that whatever you tried to do was successful, or not, or just output some information about what it did. Adding to this message stack is done like so:


var publishJobs = Sitecore.Jobs.JobManager
                    .GetJobs().Where(x => x.Category.Equals("publish"))
                    .ToList();

                foreach (Job j in publishJobs)
                {
                    if (j.Handle.IsLocal)
                    {
                        j.Status.Messages.Add(System.Environment.NewLine + "Your custom message goes here." + System.Environment.NewLine);
                    }

                }

You can add this at any point in your code for the event handler, and it will show in the final screen. If you are throwing exceptions in your code, however, it will show in the screen before this, where it will show the message of the exception. I usually found it cleaner to show it on the final screen, and I also don’t want to interrupt the actual publishing of the items. This is for a publish:end event, so it happens after the publish event is complete, so the actual items are already published. Depending on your scenario, you may want to kill the publishing and interrupt it, in which case an exception may work better. Once added, your message will show like this:

The publish end dialog final screen with the messages inserted as part of the publish:end event code

The publish end dialog final screen with the messages inserted as part of the publish:end event code

An edited version of this post also appears on The Runtime Report.

Sitecore Clones explained in more detail: Part 1

Sitecore clones were introduced first in version 6.3, as a way to centralize content items. It is a very useful way to place your items throughout the content tree, and then have the content controlled from one central location in the content tree. This is, at least the basic usage. There are other granular way to fine-tune this usage, where each of the cloned items have more control over themselves.

I’m not going to go very far into how to use clones, what they are and how they function – it is pretty well documented in the Sitecore documentation, and various Sitecore expert blogs, such as John West’s Blog. What I will detail here are some of the complications that arise when using clones, and how I tackled them.

1. Basic Usage of clones

As stated before, the basic usage of clones is to ‘clone‘ or create a copy of an item in the content tree to another different location within the same content tree. One parent item can have multiple cloned items. What this allows you to do is control the content once, in the parent item, and all the child cloned items will also get the changes (there is more to this, in the following points).

To simply create a clone, first choose the item you want to clone (News Item for Cloning), and then go to configure tab in the ribbon, and click ‘clone

Add a clone of an item by going to Configure->Clone

Add a clone of an item by going to Configure->Clone

When you click ‘clone‘, and a popup will show with the content tree – you then have to choose where the cloned item will be created. Once you hit ‘clone‘ button in the popup, the cloned item will be created. (I chose /home/news):

Cloned item created (shown in grey)

Cloned item created (shown in grey)

Notice that the cloned item is shown in grey. This is one way to tell whether an item is a clone.

Another way to tell if an item is a clone is to see the ‘Quick Info‘ tab – A cloned item will have a value for ‘Created From‘, which points to the original parent item.

From within code, you can check the values of item.IsClone and/or item.SourceUri

Item 'Quick Info' section

Item ‘Quick Info’ section

2. Changes to the cloned item, the parent item, and new items created under the parent.

Once a clone is created, you can still make changes to the clone. Since now there is a link between the parent item and the cloned item, it is somewhat assumed that if I change any fields in the parent item, the cloned item must change also. This is partially correct, but there are a few things that also happens:

    a. If the original value of the field and the value of the field in the cloned item is the same, it applies the changes immediately. When you click on the cloned item, you can see the change immediately.

    You can always click on a cloned item and see which values are the original value (from the parent) and which values have been changed in the cloned item itself – it will state [original value] to designate all fields that are exactly the same as the parent item.

    Cloned item field values

    Cloned item field values

    b. If the value in the field has been changed in the cloned item, and it does not match the parent item, and the value is changed in the parent item, it is not automatically applied (as of version 6.6). In this scenario when anything is changed in the parent item, and you want to see that change in the cloned item, there is an approval process. Sitecore creates a notification, which the user must accept in order to apply the change to the cloned item. For example, changing the title in the News Item For Cloning item creates a notification in the cloned item, which shows up once you click on the cloned item:

    Cloned item field change notification

    Cloned item field change notification

    At this point, you can review the original item to see the changes, accept the changes (which will overwrite the values that has been changed in the cloned item) or reject the change, which effectively keep the values that the cloned item has. If the changes are rejected, the field will not say [original value] anymore for all the fields that do not match the value in the field of the parent item. Subsequent changes to the same fields in the parent item will recreate these notifications.

    This also applies to new sub-items created under the parent item – in this case a different notification appears:

    Cloned item new item created nofication

    Cloned item new item created nofication

    As same with the other notification, the user has the ability to accept or reject the change. In this case if the change is rejected, the sub-item never appears under the cloned item. The entire tree must be re-cloned in order for the new sub-item to get cloned.

For an effective complete control of all cloned items, we have to write code to handle item:saved events for any items that have been cloned, and automatically accept the changes to all cloned items. I’m hoping in the future versions of Sitecore will give us a way to configure (when cloning) to designate whether a change must be forced or not, so there can much more granular content over each piece of item.

For examples of how to write event handlers for this, see here: http://adeneys.wordpress.com/2010/11/02/sharing-content-with-item-clones/

Note: I’ve also written code to do this: if you need some code samples, I will explain this in more detail in Part 2 very shortly. If you need the samples right away, leave a comment and I will get back to you..

3. Managing cloned items from workflow and publishing perspective

Cloned items are nothing but regular Sitecore items, with a link between the cloned item and the parent item. Once published, though, cloned item becomes real items, and the relationship between the items no longer exist. This is not an issue, because on the web database, this relationship is not needed. But remember that all changes to cloned items must be individually published, and just publishing the parent item does not publish the cloned items.

In respect to this, the cloned items can have their own workflow, even if field value changes are forced (via an event handler). So in fact, a parent item can be changed, approved and published, while the cloned items are still in a draft mode. To solve this scenario, cloned items can be made to follow the same approval process by copying the workflow properties during the event handler process which auto-accepts the notifications (more in Part 2).

4. Appearance of cloned items

As of version 6.6, the cloned items are always in grey. There is no way to configure a different style for this, other than writing custom code for it. You can add your processor to the uiCloneItems pipeline processor, and change style of the item as such:

clonedItem.Fields[Sitecore.FieldIDs.Style].Value = "color:#666666; font-style: italic";

More details about how to write the event handlers for auto-accept and their usages coming in Part 2.

An edited version of this post also appears on The Runtime Report.

Rich Text Editor style sheets in a Multi Site Sitecore solution

In the Sitecore web.config, there is a setting to point to a style sheet file, so that Rich Text Editors (RTE) all can point to a set of classes that all content editors can use. The setting is:

<setting name="WebStylesheet" value="/default.css" />

Style classes show up based on the style sheet pointed to in the web.config setting

The default setting is ‘default.css’, which comes with Sitecore – you can either add to this file, or change it to point to your own custom .css file. This all works great – unless you have a multisite solution that requires content editors to have different style sheets for each site. The setting allows you to point to only one .css file, and sometimes you may be able to get away by putting @import statements, but if you need the Rich Text Editor to be site aware while in the content editor, you’ll need to extend the control that comes with Sitecore.

In pre-Sitecore 6.4, the control that was used was Telerik RADEditor – there is an article on SDN that explains how to extend the control that if you are using versions prior to 6.4 (or 6.3).

In Sitecore 6.5, it uses a slightly different version of this control and the implementation isn’t quite the same; When ‘Show Editor’ button is clicked on the RTE control, a page pops up with the control loaded. The page that pops up is Editor.aspx, and it is located at: [your webroot]\sitecore\shell\Controls\Rich Text Editor. The first thing we need to do is to make a class to extend the Sitecore.Shell.Controls.RichTextEditor.EditorPage

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Web;
using Sitecore.Shell.Controls.RichTextEditor;
using Sitecore.Security.Accounts;
using Sitecore.Data.Items;
using Sitecore.Configuration;
using Telerik.Web.UI;
using System.Threading;
using System.Net;
using System.Configuration;

namespace MyCustomNamespace
{
    public class RTEStyleLoader : Sitecore.Shell.Controls.RichTextEditor.EditorPage
    {
        
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!base.IsPostBack && !string.IsNullOrEmpty(WebUtil.GetQueryString("id")))
            {
                string id = WebUtil.GetQueryString("id");
                Item item = Sitecore.Context.ContentDatabase.GetItem(id);

                //This is here so that the CSS file doesn't cache
                string strNoCache = "?noCache=" + DateTime.Now.Ticks.ToString();
                
                //DO YOUR LOGIC HERE TO DETERMINE THE STYLE SHEET THAT SHOULD BE USED
                string siteCssPath = "/Styles/SiteSpecific.css"

                this.Editor.CssFiles.Add(new EditorCssFile(siteCssPath));

            }
        }

    }

}

Step-by-step Code Breakdown

1. First, make a new class in your project that inherits from Sitecore.Shell.Controls.RichTextEditor.EditorPage

public class MultiSiteEditorPage : Sitecore.Shell.Controls.RichTextEditor.EditorPage

2. Override the OnLoad method of the control

protected override void OnLoad(EventArgs e)

3. Make sure the logic in here is executed only when there is no postback and the item ID exists (when you click ‘Show Editor’, the ID of the item being edited is passed into the control)

if (!base.IsPostBack && !string.IsNullOrEmpty(WebUtil.GetQueryString("id")))

4. Get the item in context using the ID from the URL

string id = WebUtil.GetQueryString("id");
Item item = Sitecore.Context.ContentDatabase.GetItem(id);

5. Once you have your item ID and the item, determine the site in context, and then use the style sheet path to add a new css file into the RTE control. You can actually add multiple CSS files into the control – the result will be a concatenation of all the files.

//DO YOUR LOGIC HERE TO DETERMINE THE STYLE SHEET THAT SHOULD BE USED
string siteCssPath = "/Styles/SiteSpecific.css"

this.Editor.CssFiles.Add(new EditorCssFile(siteCssPath));

You can make the class in your Sitecore project or in a custom class library (although in a custom class library you may have issues trying to determine the item, etc). Once the class is complete, go to the Editor.aspx page, and change the page directive to point to your class:

change:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EditorPage.aspx.cs" Inherits="Sitecore.Shell.Controls.RichTextEditor.EditorPage" %>

to this:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="RTEStyleLoader.aspx.cs" Inherits="HMyCustomNamespace.RTEStyleLoader" %>

That should be it. Now if you load the Content Editor, any items with the RTE control should show the list of classes based on the style sheet that was loaded.


Note: This is a change to components that comprise of Sitecore’s core functionality, and there are different schools of thought as to whether one should customize Sitecore to this level, as any future changes to how Sitecore implements the RTE control may break the concept shown here. Given the requirement, this is the best way I’ve found to get this done (let me know if you know a better way). Make sure all changes of this sort are documented, and it shouldn’t be an issue. You can check how future implementations are done by looking in the RTE control folder.