Showing posts with label customization. Show all posts
Showing posts with label customization. Show all posts

Thursday, July 05, 2012

Sitecore Pre-Production Preview


It’s time to revisit my old blog series from a couple of years ago. I’ve blogged about ways to setup a pre-production preview environment back in 2009 and 2010. I suggest reviewing those before reading this post further, as I am not going to provide a lot of context. Since the last post, I was getting even more questions about how to set this up, so here is another take on the same problem that involves less customization, and in my book, it’s much easier to setup.

The idea is simple and rotates around the notion of running kind of a “Live mode” on separate Pre-Production Preview server.
This server will source out content from the master database instead of “pre-prod web” database. This will let you get away from using publishing, thus reducing complexity.

Wednesday, February 22, 2012

Cascading renderings in Sitecore Page Editor [Experimental]


Ok, here is something completely experimental. Consider the following real world example from Sitecore’s website. You have a section called “Customers” and you have a left rail where the leftnav typically sits. Now your marketers want the “call-to-action” rendering to be placed below the leftnav:
 SNAGHTML9837b0a

…and what’s important, have it cascade to all child pages (let’s take this as an example).
Another thing they want to make sure is to lock it down on those child pages so nobody can do anything with it in Page Editor (move, change properties, etc,)

Pretty interesting scenario, heh?

Friday, August 19, 2011

Sitecore Output Caching: Kick it up a Notch


image Ever wondered how Sitecore caches works? It is pretty cool, actually. Sitecore can cache the output of any presentation component that it is aware of (Sublayout, WebControl, XSL Rendering). Basically, this output may vary on data context (Context Item or Datasource), device, whether user is logged in or not, parameters passed to the rendering, query string and Context user:
This gives a ton of options and in most cases this is sufficient, but what if the output of your rendering depends on inner logic embedded within the control? Sometimes you could solve this with VaryByParm and by passing different parameters to the rendering, but what if that’s not the case?
This is exactly the use case I have been presented during another onsite visit with a customer.
Here is a very simple way of extending the cache variation logic for a Sublayout.

Tuesday, February 01, 2011

Sitecore Product URLs Amazon Style


If you are a Sitecore CMS developer and you love shopping on Amazon, you probably had the same thought: what would it take to re-create Amazon.com in Sitecore? Completely from scratch. Ok, maybe it’s just me.

Thursday, January 06, 2011

Color Picker Field for Sitecore


Here is a late Christmas present for the Sitecore community. A “ColorPicker” custom field that helps storing color codes in Sitecore.

The idea is simple: have a single line text box for color code storage and present the user with a good looking dialog to pick the color.

Technically speaking, there are two ways of color picking implemented.
The first approach is using native Internet Explorer’s color picker dialog and it is getting called via JavaScript.
The second one is using a custom built XamlSharp dialog with a ColorPicker control from ComponentArt. If you are using non IE browsers, the second dialog will be called.

Here is a short video about how it works. Turn those mics on!

Color Picker Module for Sitecore

Here are the installation steps:

1. Download the appropriate Sitecore package.
If you are planning on building the project yourself, use the “ColorPicker field items only” package.

2. Install it via Sitecore desktop.

3. After the installation, new custom field called “Color Picker” should be registered with the system.

To verify it, switch to “core” database and go to the following location: “/sitecore/system/Field types/Simple Types”

The “Color Picker” field should be there and should look like this:

clip_image001

Short user guide:

1. If the installation was successful, you can start using it.

2. Find the template you want to play with.

3. Create a field of type “Color Picker”

image

4. Create an item from this template.

5. Enjoy.

image

This shared source module has been implemented in collaboration with Artem Loboda (compent.dk).

Developed and tested on 6.4 Update-1. Expected to work with 6.3. There could be compatibility issues with earlier releases.

All the sources are published to our Shared Source Library. Thanks for the help, Jimmie :-)
http://trac.sitecore.net/ColorPicker/browser/Trunk

Friday, November 12, 2010

Sitecore Searcher and Advanced Database Crawler


Hi there,
Today I am proud to announce a preview release of a component that extends the standard Sitecore Searching mechanisms, specifically, the relatively “new” Sitecore.Search namespace introduced in 6.0 and provides easy search querying APIs. If you are not sure what I am talking about, check out this recently published document on SDN and also Ivan’s blog posts about it.

Friday, October 15, 2010

How to Sort your Multilist Field


Greetings!

Just recently one of the Sitecore implementation partners asked me whether there is a way to have the list of available items on a Multilist field sorted by updated date.
To clarify, by default, the left side is sorted by item name, it is by design.

image

So I did some digging, and have found that it is a very frequent feature request. Another thing I’ve found is that since Sitecore 6 it is actually pretty easy to change this behavior.

First thing I need for that is subclass MultilistEx class which implements is UI part of the Multilist field. This class has a method called GetSelectedItems (introduced since Sitecore 6 release) where the list of unselected items is constructed. Since this method is marked as protected virtual, I can easily override it:

namespace Sitecore.SharedSource.CustomFields
{
   public class SortedMultilist : Sitecore.Shell.Applications.ContentEditor.MultilistEx
   {
      protected override void GetSelectedItems(Item[] sources, out ArrayList selected, out IDictionary unselected)
      {
         Assert.ArgumentNotNull(sources, "sources");
         ListString str = new ListString(this.Value);
         unselected = new SortedList(StringComparer.Ordinal);
         selected = new ArrayList(str.Count);
         int index = 0;
         while (index < str.Count)
         {
            selected.Add(str[index]);
            index++;
         }
         foreach (Item item in sources)
         {
            string str2 = item.ID.ToString();
            index = str.IndexOf(str2);
            if (index >= 0)
            {
               selected[index] = item;
            }
            else
            {
               unselected.Add(GetItemKey(item), item);
            }
         }
      }
      protected virtual string GetItemKey(Item item)
      {
         return MainUtil.GetSortKey(item[FieldIDs.Created]);
      }
   }
}

The only thing I change, actually, is the line where items are added to the “unselected” list. Since the key is used for sorting, I move this outside to a separate method “GetItemKey” where instead of constructing sort key by item.Name as in default implementation, I simply read the “created” date.

After this class is compiled, all I need to define the new “Sorted Multilist” field under core:/sitecore/system/Field types/List Types:

image

The easiest way to do it is to duplicate the existing Multilist item ;-)

I should mention that adding complex sorting rules will definitely slow down you multilist, so you have to be very careful.

Also, there is a very handy FilteredMultilist that is already on shared source. It gives you ability to refine the left side of Multilist, so you may never need it sorted at all.

no_filter[1]

Enjoy!

Friday, September 03, 2010

Optimizing Sitecore Link Database Rebuild


Greetings! One of the topics I covered during my Dreamcore presentation on Data Access Techniques with Sitecore CMS was Link Database. While it is a great feature that enables fast and efficient retrieval of item relations, there is one caveat, it requires some maintenance and configuration to be working properly in multi server environment. Many customers face this challenge, and while there is a number of ways you can tackle this, the approach I want to share today seems like the most appropriate to me.

The idea behind the scenes is to separate the Link Database used on the Content Delivery side from the one used on the Content Management side by introducing a second link database. This database will be updated during item publishing. This way if CMS decides to update its own Link DB, the one used on the front-end will not be affected.

So, here is what you need to do:

1. Add the FrontEndLinkDatabase definition below the default <LinkDatabase> in web.config file of the CMS server:

<!-- LINK DATABASE -->
<LinkDatabase type="Sitecore.Data.$(database).$(database)LinkDatabase,
                    Sitecore.Kernel">
   <param connectionStringName="core" />
</LinkDatabase>
<!-- FRONT END LINK DATABASE -->
<FrontEndLinkDatabase type="Sitecore.Data.$(database).$(database)LinkDatabase,
                            Sitecore.$(database)">
   <param desc="connection" ref="connections/web" />
</FrontEndLinkDatabase>

2. Compile the following code that contains two Sitecore event handlers (publish:itemprocessed and publish:itemprocessing)

using System;
using Sitecore.Configuration;
using Sitecore.Links;
using Sitecore.Publishing;
using Sitecore.Publishing.Pipelines.PublishItem;
using Sitecore.Diagnostics;
namespace Sitecore.SharedSource.LinkDatabaseRebuilder
{
   public class EventHandler
   {
      protected static LinkDatabase FrontEndLinkDatabase
      {
         get
         {
            return (Factory.CreateObject("FrontEndLinkDatabase", true) 
                     as LinkDatabase);
         }
      }
      public string Database { get; set; }
      public void OnItemProcessed(object sender, EventArgs args)
      {
         var context = ((ItemProcessedEventArgs)args).Context;
         Assert.IsNotNull(context, 
         "Cannot get PublishItem context");
         Assert.IsNotNull(FrontEndLinkDatabase,
         "Cannot resolve FrontEndLinkDatabase from config");
         if (context.PublishOptions.TargetDatabase.Name.Equals(Database))
         {
            var item = context.PublishHelper.GetTargetItem(context.ItemId);
            // if an item was not unpublished, 
            // the call below will reintroduce the reference
            // removed within OnItemProcessing method
            if (item != null)
            {
               FrontEndLinkDatabase.UpdateReferences(item);
            }
         }
      }
      public void OnItemProcessing(object sender, EventArgs args)
      {
         var context = ((ItemProcessingEventArgs)args).Context;
         Assert.IsNotNull(context,
         "Cannot get PublishItem context");
         Assert.IsNotNull(FrontEndLinkDatabase, 
         "Cannot resolve FrontEndLinkDatabase from config");
         if (context.PublishOptions.TargetDatabase.Name.Equals(Database))
         {
            if (context.Action == PublishAction.DeleteTargetItem)
            {
               var item = context.PublishHelper.GetTargetItem(context.ItemId);
               Assert.IsNotNull(item, "Source item cannot be found");
               FrontEndLinkDatabase.RemoveReferences(item);
            }
         }
      }
   }
}

3. Last thing to do is to define the two event handler mapping in web.config.

<events>
   <event name="publish:itemProcessing">
    <handler type="Sitecore.SharedSource.LinkDatabaseRebuilder.EventHandler, 
Sitecore.SharedSource.LinkDatabaseRebuilder"
method="OnItemProcessing">
      <database>web</database>
     </handler>
   </event>
   <event name="publish:itemProcessed">
     <handler type="Sitecore.SharedSource.LinkDatabaseRebuilder.EventHandler,
Sitecore.SharedSource.LinkDatabaseRebuilder"
method="OnItemProcessed">
       <database>web</database>
      </handler>
   </event>
 </events>

That’s all you need to do. For the sake of simplicity, here is the link to the whole VS project where you can find the auto-include file.

While this solution has been tested on 6.2, it is expected to work on all Sitecore 6 installations.

Let me know if it does not ;-)

Thursday, July 29, 2010

Sitecore Logging Part 3. Adding custom parameters to the log.


Summer…I am on a blog posting spree :-)

This is a continuation of the sage about SQL logging for Sitecore CMS. As I previously blogged, you can easily have Sitecore log to a SQL database instead of a flat text file.
Now what if we take it one step forward and have Sitecore output more information than we had before, including Sitecore Context User, Sitecore Context Item Id and raw server URL?
Well, after some digging, here is the solution for you.

Thursday, July 22, 2010

File Download Dialog Box for Sitecore Media Item


It is not a secret that Sitecore CMS can deliver media content in a dynamic fashion.
You can do pretty sick things with our MediaRequestHandler (as an example of that, check out what Alistair Deneys done with it in the past).
Today, however, I wanted to cover a fairly easy aspect of this functionality.

Friday, April 02, 2010

Sitecore Installation Wizard – disable search index update during install


OK, now after my April Fools joke, let’s talk about serious things around Sitecore .NET CMS. Such as how to speed up installation of Sitecore packages.
One of the things that improve the performance of the package installation is disabling the search index update temporarily via the following setting:

<setting name="Indexing.UpdateInterval" value="00:00:00" />

Setting this interval to all zeros actively disables the background index update job that maintains Sitecore’s search index.

While you can certainly do this manually, it is not always feasible since you need to edit web.config (have access to the server, etc) plus it restarts the application.

What you can do however is to add some logic to the Installation Wizard, specifically a flag to temporarily disable the search index updates during the package installation:
clip_image002

To make this happen, do the following:

1. Create the following class in your project and compile it.

namespace SCUSAINC.Custom.Packager
{
   using System;
   using Sitecore.Reflection;
   using Sitecore.Web.UI.HtmlControls;
   using Sitecore.Web.UI.Sheer;
   using Sitecore.Shell.Applications.Install.Dialogs.InstallPackage;

   public class CustomInstallPackageForm : InstallPackageForm
   {
      [HandleMessage("installer:setTaskId")]
      private void OnSetTaskId(Message message)
      {
         var obj = this as InstallPackageForm;
         ReflectionUtil.CallMethod(typeof(InstallPackageForm), obj, "SetTaskID", true, true, new object[] { message });
      }

      [HandleMessage("installer:commitingFiles")]
      private void OnCommittingFiles(Message message)
      {
         var obj = this as InstallPackageForm;
         ReflectionUtil.CallMethod(typeof(InstallPackageForm), obj, "OnCommittingFiles", true, true, new object[] { message });
      }

      protected Checkbox DisableIndexing;

      protected override void OnLoad(EventArgs e)
      {
         DisableIndexing.Checked = false;
         base.OnLoad(e);
      }

      protected override void OnNext(object sender, EventArgs formEventArgs)
      {
         base.OnNext(sender, formEventArgs);
         if (Active == "Installing" && DisableIndexing.Checked)
         {
            Sitecore.Configuration.Settings.Indexing.Enabled = false;
         }
      }

      protected override void EndWizard()
      {
         base.EndWizard();
         if (DisableIndexing.Checked)
         {
            DisableIndexing.Checked = false;
            Sitecore.Configuration.Settings.Indexing.Enabled = true;
         }
      }
   }
}

2. Copy the following file to the /sitecore/shell/override folder: \sitecore\shell\Applications\Install\Dialogs\Install package\Install Package.xml

3. Open this XML file

4. Change the CodeBeside reference in the XML source file to your custom name:

<WizardForm Application="Tools/Installer/InstallationWizard" CodeBeside="SCUSAINC.Custom.Packager.CustomInstallPackageForm,SCUSAINC.Custom ">

5. Add the checkbox definition to the XML source:

<WizardFormPage ID="Ready" Header="Ready to Install" Text="The wizard is ready to install the package. Click Install to install the package." Icon="People/32x32/Box_Software.png">
   <WizardFormIndent>
     <GridPanel Columns="2" CellPadding="2" Width="100%"></GridPanel>
     <Checkbox ID="DisableIndexing" Header="Disable search index update during the install" Checked="True" />
   </WizardFormIndent>
</WizardFormPage>

Based on my tests, this significantly decreases the installation time (up to a half), especially on high volume packages in terms of content items.

Special thanks to Paul from Sitecore Support and Sergey from the development team for the assistance with this!

Tuesday, March 09, 2010

Read only view of Rich Text field for Sitecore editor


Today I want to share an idea of a lightweight customization for Sitecore CMS that meant to increase usability of the rich text field for your editors. The idea is inspired by great feedback I received recently while being in the field.

The basic problem reported by one of the editors who uses Sitecore on the daily basis, is that when a content item is approved in workflow there is no way to see what’s inside of a rich text field easily. All field buttons are grayed out thus disabled:
disabled rich text field
…so the only way is to click “Lock and Edit” which creates a new version:
lock-and-edit

As you can imagine, this is not generally acceptable. If your non-admin editor user simply wants to see what’s in there, you don’t want a new version to be created.

Now it is worth mentioning that the item which is approved, unlocked is disabled for editing only if “RequireLockBeforeEditing” setting is set to true which is often the case, since you would want to have “Lock and Edit” in place so new version of the approved content is created.

Same goes along with the items that are locked. If there is a locked item, I would still want to see the content of the rich text field in read-only mode.

So one of the easiest ways I could think of to address this was enable “preview” of the rich text field in a modal dialog on double click.

Tech support provided the following solution which enabled the double click on disabled rich text field and showed the “preview” version of the field in a modal dialog:image

To make it work, follow these steps:
1. Open Preview.aspx under /sitecore/shell/Controls/Rich Text Editor.
2. Locate the scEdit() function and comment out the first IF statement:

function scEdit() {
        //if (scDisabled == "1") {
         // return;
       // }
        
var form = scGetForm();
...

3. Compile the following code:

namespace Company.Web.Fields
{
   using Sitecore.Diagnostics;
   using Sitecore.Shell.Applications.ContentEditor.RichTextEditor;
   using Sitecore.Web.UI.Sheer;

   public class RichText : Sitecore.Shell.Applications.ContentEditor.RichText
   {
      protected new void EditText(ClientPipelineArgs args)
      {
         Assert.ArgumentNotNull(args, "args");
         if (Disabled && !args.IsPostBack)
         {
            var url = new RichTextEditorUrl
                         {
                            Conversion = RichTextEditorUrl.HtmlConversion.Runtime,
                            Disabled = Disabled,
                            FieldID = FieldID,
                            ID = ID,
                            ItemID = ItemID,
                            Language = ItemLanguage,
                            Mode = string.Empty,
                            Source = Source,
                            Url = "/sitecore/shell/Controls/Rich Text Editor/Preview.aspx",
                            Value = Value,
                            Version = ItemVersion
                         };

            var str = url.GetUrl();
            SheerResponse.ShowModalDialog(str.ToString(), "800px", "500px", string.Empty, true);
            args.WaitForPostBack();
         }

         base.EditText(args);
      }
   }
}

4. In web.config, add your own controlSource reference to the namespace above:

<controlSources>
      <source mode="on" namespace="Sitecore.Web.UI.XmlControls" folder="/sitecore/shell/override" deep="true" />
...
       <source mode="on" namespace="Company.Web.Fields" assembly=" Company.Web" prefix="custom" />
</controlSources>

5. In the core database, locate the Rich Text field item (/sitecore/system/Field types/Simple Types/Rich Text) and adjust the value of the “control” field:
custom:RichText
Note that the prefix is used from the controlSource reference above.

Simple and lightweight customization that should bring some value. Sitecore rocks!

Tested on 6.1, expected to work on 6.0.x and 6.2.

Wednesday, February 24, 2010

Insert Media Item dialog in rich text field – remember last selection


Just last week I was at a customer talking to end users, authors who use the Sitecore CMS every day. It was was a very productive session, some quite valuable feedback was gathered.

One of the reported inconveniences with the rich text editor was inability for the “Insert Media Item” dialog to remember the previously selected location. It always falls back to the media library root when opened.
Insert Media Item - new

With active  content entry, especially media related, this dialog is used quite often.
Plus as the media library grows significantly, it takes more time to browse through the media library structure to locate the much needed media.

Thanks to Sitecore’s customizability, you can easily change that. By simply overriding the InsertImage XML control with new code that remembers the previous location.

namespace SCUSAINC.Shell.Applications.Dialogs
{
   using System;
   using Sitecore;
   using Sitecore.IO;
   using Sitecore.Data.Items;
   using Sitecore.Data;
   using Sitecore.Resources.Media;
   using Sitecore.Diagnostics;

   class InsertImageForm : Sitecore.Shell.Controls.RichTextEditor.InsertImage.InsertImageForm
   {
      protected override void OnLoad(EventArgs e)
      {
         Assert.ArgumentNotNull(e, "e");
         if (!Context.ClientPage.IsEvent)
         {
            var prevMedia = Context.ClientPage.Session["prevMedia"];
            DataContext.GetFromQueryString();
            Item folder;
            if (prevMedia != null)
            {
               folder = Context.ContentDatabase.GetItem(ID.Parse(prevMedia.ToString()));
               if (folder != null)
               {
                  DataContext.SetFolder(folder.Uri);
               }
            }
         }
         base.OnLoad(e);
      }

      protected override void OnOK(object sender, EventArgs args)
      {
         base.OnOK(sender, args);

         var filename = Filename.Value;
         if (filename.Length != 0)
         {
            var root = DataContext.GetRoot();
            if ((root != null) && (root.ID != root.Database.GetRootItem().ID))
            {
               filename = FileUtil.MakePath(root.Paths.Path, filename, '/');
            }
            MediaItem item = DataContext.GetItem(filename);
            if ((item != null) && ((MediaManager.GetMedia(MediaUri.Parse(item)) is ImageMedia)))
            {
               Context.ClientPage.Session.Add("prevMedia", item.ID.ToString());
            }
         }
      }
   }
}

After the code is compiled, you will need to copy the source file of the InsertImage XML control that can be found here: \sitecore\shell\Controls\Rich Text Editor\InsertImage\ to the \sitecore\shell\Override\ folder and make the following change:

<CodeBeside Type="SCUSAINC.Shell.Applications.Dialogs.InsertImageForm,SCUSAINC"/>

Huge thanks goes to Kirill T from the customer service for the solution!

Related reading:
http://sdn.sitecore.net/Scrapbook/Customize%20an%20XML%20Control.aspx

Enjoy and please do share if you like it! ;-)

Tuesday, December 22, 2009

Publish to pre-production web database


In my experience with enterprise level implementations, there is often a need for a separate “stage environment” for final content preview or a pre-production phase of workflow.

It is also often the case that the environmental limitations or restrictions make the content delivery part of the authoring environment suitable for such purpose. For example, your e-commerce infrastructure could not be available from the authoring instance. One of the possible ways to approach such a requirement is to configure a stand-alone staging instance of Sitecore which will be used for content delivery instance and is commonly configured exactly like one of the servers in the web farm. There is one exception though – the database that is used for content delivery is generally different from the one that is used in production content delivery.

With this approach, now the question is how to plug in workflow in a way that content could go via this “stage” environment and then only upon final approval will be able to get to “production”.

Now there is a problem that you cannot get get items published until they are in final workflow state. You could have two workflow states “Staged” and “Published”, both marked as final and with auto publish actions connected.
I don’t like this approach since it goes against the workflow nature – only one state should be marked as final.

Alternatively, you can plug in a “copy item” workflow action to the “Staged” workflow state where you can programmatically copy an item from master database to stage web.

While this approach does seem legitimate, it works around the need of publishing thus any additional processing you may be having in publish(item) pipeline would not work.

I looked into the option of having publishing process ignore workflow when publishing to stage. While this seemed like a dangerous path to go, I have soon discovered that the PublishHelper’s GetVersionToPublish method already accepts this notion of passing a “requireapproval” flag to the underlying publishing mechanism. Since it is always passes “true”, I started looking into ways to make this flag dynamic and figured that setting it on the level of PublishOptions could be a good idea. For example, from the code of my workflow action I would be able to define whether I want to have workflow respected or not.

Here is what you will need to do to make it work.

First, override the default PipelinePublishProvider and plug it into the web.config:

<publishManager defaultProvider="default" enabled="true">
    <providers>
        <clear />
         <!--<add name="default" type="Sitecore.Publishing.PipelinePublishProvider, Sitecore.Kernel" />-->
         <add name="default" type="SCUSAINC.Publishing.ExtendedPublishProvider, SCUSAINC.Publishing" />
     </providers>
</publishManager>

Secondly, you will need to override the CreatePublishHelper class:

public class ExtendedPublishProvider : PipelinePublishProvider
    {
        public override PublishHelper CreatePublishHelper(PublishOptions options)
        {
            Assert.ArgumentNotNull(options, "options");
            if (options is ExtendedPublishOptions)
                return new ExtendedPublishHelper(options as ExtendedPublishOptions);

            return new PublishHelper(options);
        }
    }

After that, introduce two more classes – ExtendedPublishHelper and ExtendedPublishOptions:

public class ExtendedPublishHelper : PublishHelper
    {
        private readonly ExtendedPublishOptions _options;

        public ExtendedPublishHelper(ExtendedPublishOptions options)
            : base(options)
        {
            _options = options;
        }

        public override Item GetVersionToPublish(Item sourceItem)
        {
            Assert.ArgumentNotNull(sourceItem, "sourceItem");
            if (Options is ExtendedPublishOptions)
            {
                return sourceItem.Publishing.GetValidVersion(Options.PublishDate, _options.RequireApproval);
            }

            return sourceItem.Publishing.GetValidVersion(Options.PublishDate, true);
        }
    }

public class ExtendedPublishOptions : PublishOptions
    {
        public ExtendedPublishOptions(Database sourceDatabase, Database targetDatabase, PublishMode mode, Language language, DateTime publishDate, bool requireApproval)
            : base(sourceDatabase, targetDatabase, mode, language, publishDate)
        {
            RequireApproval = requireApproval;
        }

        public bool RequireApproval { get; set; }
    }

Now you are ready to launch publishing with workflow settings completely ignored. For example, this is how you can do it from the workflow action:

var database = Factory.GetDatabase(databaseName);

var options = new ExtendedPublishOptions(dataItem.Database, database, PublishMode.SingleItem, dataItem.Language, DateTime.Now, true)
{
         RootItem = dataItem,
         Deep = true;
};

new Publisher(options).PublishAsync();

That’s it!

Tested on Sitecore 6.1. Expected to work with 6.2.

Monday, December 14, 2009

Get all published items in Sitecore 6.1/6.2


You may need to get all items that were processes by a publishing operation for different reasons, for example, pull certain information for each item and notify CDN of a change so cache could be purged. AKAMAI can be configured to behave that way.
Currently we have two articles on the subject. This one describes how to use the PublishEngine, second article suggests approach with custom PublishLog. Both are not compatible with 6.x. So I’ve looked into the options for the latest Sitecore with tech support. Our findings included introduction of a new PublishProcessor which examines the Queue property of PublishContext, but this did not work properly for smart publishing and I wanted a bulletproof yet simple approach applicable to all possible publishing modes.
Then I remembered that we are already doing similar things for indexing. The update index job processes only changed items and gets that information from the History storage.

Friday, May 08, 2009

Altering the behavior of the tree view control


You may have noticed this too. When you create a template, the Base Template dropdown may be tricky to operate with. If you are trying to find a template in the subfolders to inherit from and miss the expansion plus, the tree will collapse and you will have to start over. This has been bugging me for a while so I asked the tech support if there is an easy way to fix this and not have the tree to collapse.
Luckily, there is an easy fix, but you will have to mess with the Sitecore.js file located under /sitecore/shell/controls.

What you need to do is to add a global variable in Sitecore.js:

var iAmTree = null;

Then straight after line 563 within scSitecore.prototype.postEvent = function(tag, evt, parameters) add the following:

iAmTree = false;                                                                                      
if (ctl.className.toString() == "scTreeItem") iAmTree = true;  

And finally within scRequest.prototype.resume = function() alter this IF statement:

if (this.closePopups && typeof(scForm) != "undefined") {
    scForm.browser.closePopups("Request");
  }

to this:

if (this.closePopups && typeof(scForm) != "undefined" && iAmTree != true) {
    scForm.browser.closePopups("Request");
  }

This will also affect the Droptree field’s behavior and possibly more areas.

And you may have guessed – create a backup of Sitecore.js file and use the solution on your own risk ;-)

Special thanks goes to Ruslan for providing the fix.

Thursday, April 09, 2009

How to troubleshoot SQL connection issues


Had a problem with remote web app not connection to newly installed SQL Server. Was getting generic “A network-related or instance-specific error occurred while establishing a connection to SQL Server…”

Found this great article with the breakdown of possible scenarios, very useful. Mine was #6, SQLBrowser not running. Was clicking too fast through the installation ;-)

Friday, January 09, 2009

How to control the sort of languages in the selector


A great article was just published by Igor regarding the way to control the language sorting. By default, the languages that show up in that list are not sorted by name, but by the date they were created.
In the multilingual solutions with more than 10 languages this may cause some confusion from the editors perspective that have access to multiple languages. If there are not security settings specified on the language level, this may be a bit of an inconvenience.
Using this solution, you can control the language sort order by either name as it is shown in the example or by other parameters, such as number of versions of example.

Kudos goes to Igor for the solution!

Thursday, December 18, 2008

Who moved the item to the workflow state?


Applies to Sitecore 6.0.
In the workflow scenarios with "rejection" commands when an item is moved to a previous workflow state, sometimes necessary to notify the user who submitted the item in the first place.
Since Sitecore keeps track of workflow activities and writes everything into WorkflowHistory table, there are also APIs to retrieve such information.
The code snippet below shows how it can be done.

protected override string GetRecipient(WorkflowPipelineArgs args)
 {
    IWorkflowProvider workflowProvider = Context.ContentDatabase.WorkflowProvider;
 
    if (workflowProvider != null)
    {
       IWorkflow workflow = workflowProvider.GetWorkflow(args.DataItem[FieldIDs.Workflow]);
 
       if (workflow != null)
       {
          WorkflowEvent[] history = workflow.GetHistory(args.DataItem);
          if (history.Length > 0)
          {
             WorkflowEvent wfEvent = history[history.Length - 1];
             string fullUserName = wfEvent.User;
 
             if (User.Exists(fullUserName))
             {
                System.Web.Security.MembershipUser mUser = Membership.GetUser(fullUserName);
 
                if (mUser.Email != string.Empty)
                {
                   return mUser.Email;
                }
             }
          }
       }
    }
 
    return string.Empty;
 }

This method can be a part of workflow email action that is associated with the "Reject" command.
Related links:

Don't you just love Sitecore workflow?