Sitecore


When you’ve got larger Sitercore installs where there is a team of graphic artists dedicated to maintaining the media library you can get cases of content editors linking to images that haven’t been published yet.

Off the back of those kind of situations I wrote a custom validator to test if the item is published on any of the target databases. It could easily be repurposed for any of the link fields and the warning could be raised to an error if needed.

using System.Collections.Generic;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Data.Validators;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.SecurityModel;
using Sitecore.Shell.Applications.ContentEditor;
namespace example
{
    public class UnpublishedMediaValidator : StandardValidator
    {
        protected override ValidatorResult Evaluate()
        {
            ItemUri itemUri = base.ItemUri;
            if (itemUri != null)
            {
                Field field = base.GetField();
                if (field == null)
                    return ValidatorResult.Valid;
                string str = field.Value;
                if (string.IsNullOrEmpty(str))
                    return ValidatorResult.Valid;
                if (string.Compare(str, "<image />") == 0)
                    return ValidatorResult.Valid;
                string itemId = new XmlValue(str, "image").GetAttribute("mediaid");
                if (string.IsNullOrEmpty(itemId))
                    return ValidatorResult.Valid;
                Database currentDatabase = Factory.GetDatabase(itemUri.DatabaseName);
                Assert.IsNotNull(currentDatabase, itemUri.DatabaseName);
                Item localItem = currentDatabase.GetItem(itemId);
                if (localItem == null)
                    return ValidatorResult.Valid;
                foreach(Database targetDatabase in GetTargets(localItem))
                {
                    Item targetItem = targetDatabase.GetItem(localItem.ID);
                    if(targetItem == null)
                    {
                        base.Text = Translate.Text("The field \"{0}\" links to an unpublished item.", new object[] { field.DisplayName });
                        return base.GetFailedResult(ValidatorResult.Warning);
                    }
                }
            }
            return ValidatorResult.Valid;
        }
        private List<Database> GetTargets(Item item)
        {
            using (new SecurityDisabler())
            {
                Item item2 = item.Database.Items["/sitecore/system/publishing targets"];
                if (item2 != null)
                {
                    List<Database> list = new List<Database>();
                    foreach (Item item3 in item2.Children)
                    {
                        string name = item3["Target database"];
                        if (name.Length > 0)
                        {
                            Database database = Factory.GetDatabase(name, false);
                            if (database != null)
                            {
                                list.Add(database);
                            }
                            else
                            {
                                Log.Warn("Unknown database in PublishAction: " + name, this);
                            }
                        }
                    }
                    return list;
                }
            }
            return new List<Database>();
        }
        protected override ValidatorResult GetMaxValidatorResult()
        {
            return base.GetFailedResult(ValidatorResult.Warning);
        }
        public override string Name
        {
            get
            {
                return "Unpublished Items.";
            }
        }
    }
}

Implementing it is fairly straight forward, just compile the code, create a new Validation Rule item in /sitecore/system/Settings/Validation Rules/Field Rules/ (namespace.class,assembly) and add to the Validator Bar rule on the Image Field Type in /sitecore/system/Settings/Validation Rules/Field Types/Image

As I said it would be easy to repurpose this for any link type, useful in conjunction with more complex workflows.

This is something I see come up a lot when developing Sitecore websites, how best to reference your items, by path or by guid. Almost every method in the Sitecore API will accept both path and guid as will xslt (paths often been XPATH statements in that case). Both have their pros and cons and both are more suited for particular situations that the other.

The biggest benefit of paths is their human readability, even a non-technical person can find an item based on its path. If however you’ve spent a whole project referencing items by path and you need to change the site item hierarchy you’ll run afoul of a potential refactoring nightmare, doubly so if those paths are created dynamically. Guids on the other hand are the exact opposite, they’re essentially immune to 90% of those refactoring issues but they’re otherwise uttering incomprehensible (i.e. 5EBC6046-5012-4C78-BAC9-5852A8E26918)

So the long and short of it is that it would be nice to use guids everywhere for more robust code but you’ll need to hope everyone remembers to put a friendly comment in explaining what the guid is for. The middle ground solution I’ve found which really nails the issue for everything but xslt (more on that later) is a static settings class like below:

using Sitecore.Data;
namespace Example.Business
{
    public static class SiteSettings
    {
        public static class TemplateIds
        {
            public static TemplateID Client { get { return new TemplateID( new ID("5EBC6046-5012-4C78-BAC9-5852A8E26918"));} }
            public static TemplateID Branch { get { return new TemplateID( new ID("{35E75C72-4985-4E09-88C3-0EAC6CD1E64F}"));} }
            public static TemplateID Fees { get { return new TemplateID(new ID("{334CA6C3-B6CB-4A86-80C4-1BD1892408BB}")); } }
            public static TemplateID Service { get { return new TemplateID(new ID("{2FB9F53A-CCE2-4873-A376-BE1984FE0DDB}"));} }
            public static TemplateID Section { get { return new TemplateID(new ID("9F9AE8B7-159D-468E-9732-68BB7E9D53A4")); } }
            public static TemplateID Article { get { return new TemplateID(new ID("CCDAE7B5-656C-4E64-90D1-7A6BDC66C697")); } }
            public static TemplateID Author { get { return new TemplateID(new ID("D3B7113E-639E-494C-8F4D-65A4CADCE11A")); } }
        }
        public static class ItemIds
        {
            public static ID ArticleRoot{ get { return new ID("7438EA73-B93A-476E-B6A0-9FE4C382AE66"); } }
            public static ID Branches { get { return new ID("BAD98E0E-C1B5-4598-AC13-21B06218B30C"); } }
            public static ID SiteRoot { get { return new ID("D3978CE5-BBC7-4201-9D53-46B53E039365"); } }
        }
        public static class WorkflowIds
        {
            public static ID NewSection { get { return new ID("1FE1D278-6463-47AD-9813-E82C82F87753"); } }
            public static ID NewComponent { get { return new ID("777348EE-A748-47EC-B771-9BC0F5A0C636"); } }
        }
        public static class WorkflowStateIds
        {
            public static ID LoginDetailsSent { get { return new ID("DE94B240-198D-40BE-8621-236EF74DB395"); } }
            public static ID Approved { get { return new ID("3521F34D-F15D-404D-9EAB-DF124F89EE56"); } }
        }
    }
}

Straight forward and simple, the only downside is that it cant be used for your xslt renderings but more on that later…

« Previous Page

Follow

Get every new post delivered to your Inbox.