Home » Customization » Unlock Sitecore Users’ Items During Logout

Unlock Sitecore Users’ Items During Logout

Sitecore Technology MVP 2016
Sitecore MVP 2015
Sitecore MVP 2014

Enter your email address to follow this blog and receive notifications of new posts by email.

Tweets

The other day I saw a post in one of the SDN forums asking how one could go about building a solution to unlock items locked by a user when he/she logs out of Sitecore.

What immediately came to mind was building a new processor for the logout pipeline — this pipeline can be found at /configuration/sitecore/processors/logout in your Sitecore instance’s Web.config — but had to research how one would programmatically get all Sitecore items locked by the current user.

After a bit of fishing in Sitecore.Kernel.dll and Sitecore.Client.dll, I found a query in Sitecore.Client.dll that will give me all locked items for the current user:

fast-query-locked-items

Now all we need to do is add it into a custom logout pipeline processor:

using System;
using System.Collections.Generic;
using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.Logout;

namespace Sitecore.Sandbox.Pipelines.Logout
{
    public class UnlockMyItems
    {
        public void Process(LogoutArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            UnlockMyItemsIfAny();
        }

        private void UnlockMyItemsIfAny()
        {
            IEnumerable<Item> lockedItems = GetMyLockedItems();
            if (!CanProcess(lockedItems))
            {
                return;
            }

            foreach (Item lockedItem in lockedItems)
            {
                Unlock(lockedItem);
            }
        }

        private static IEnumerable<Item> GetMyLockedItems()
        {
            return Context.ContentDatabase.SelectItems(GetMyLockedItemsQuery());
        }

        private static string GetMyLockedItemsQuery()
        {
            return string.Format("fast://*[@__lock='%\"{0}\"%']", Context.User.Name);
        }

        private static bool CanProcess(IEnumerable<Item> lockedItems)
        {
            return lockedItems != null
                    && lockedItems.Any()
                    && lockedItems.Select(item => item.Locking.HasLock()).Any();
        }

        private void Unlock(Item item)
        {
            Assert.ArgumentNotNull(item, "item");
            if (!item.Locking.HasLock())
            {
                return;
            }

            try
            {
                item.Editing.BeginEdit();
                item.Locking.Unlock();
                item.Editing.EndEdit();
            }
            catch (Exception ex)
            {
                Log.Error(this.ToString(), ex, this);
            }
        }
    }
}

The class above grabs all items locked by the current user in the context content database. If none are found, we don’t move forward on processing.

When there are locked items for the current user, the code checks to see if each item is locked before unlocking, just in case some other account unlocks the item before we unlock it — I don’t know what would happen if we try to unlock an item that isn’t locked. If you know, please share in a comment.

I then injected the above pipeline processor into the logout pipeline using the following patch configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <processors>
      <logout>
        <processor patch:after="*[@type='Sitecore.Pipelines.Logout.CheckModified, Sitecore.Kernel']" type="Sitecore.Sandbox.Pipelines.Logout.UnlockMyItems, Sitecore.Sandbox"/>
      </logout>
    </processors>
  </sitecore>
</configuration>

Let’s test-drive this.

I first logged into Sitecore using my ‘mike’ account, and chose the Home item to lock:

lets-lock-home-item

It is now locked:

home-is-locked-1

In another session, I logged in using another account, and saw that ‘mike’ had locked the Home item:

home-is-locked-2

I switched back to the other session under the ‘mike’ user, and logged out:

IF

When I logged back in, I saw that the Home item was no longer locked:

item-now-unlocked

If you have any thoughts or suggestions on making this better, please share in a comment below.

Advertisements

6 Comments

  1. dsolovay says:

    Nice example of customizing UI behavior using pipelines. Here’s a possible refinement: Add a profile property to disable this functionality on a per user basis, so a user could ask for permission to keep items locked.

  2. […] Unlock Sitecore Users’ Items During Logout […]

  3. Hey Mike, we implemented this, and it’s great, but we’re finding that it does not unlock all language versions of the item the user has locked — before I dive in and see about how to do this, thought I’d ask if you might have a suggestion?

    • Hi Caroline,

      I dug into the Unlock() method in Sitecore.Data.Locking.ItemLocking, and it appears it’s just unlocking the Item in the language set for the Item instance.

      You might be able to grab the item in each of its languages, and then unlock it (I haven’t tested this code, and I’m not even sure it compiles: I wrote it quickly in Notepad++):

      public class UnlockMyItems
      {
      	public void Process(LogoutArgs args)
      	{
      		Assert.ArgumentNotNull(args, "args");
      		UnlockMyItemsIfAny();
      	}
      
      	private void UnlockMyItemsIfAny()
      	{
      		IEnumerable lockedItems = GetMyLockedItems();
      		if (!CanProcess(lockedItems))
      		{
      			return;
      		}
      
      		foreach (Item lockedItem in lockedItems)
      		{
      			UnlockInAllLanguages(lockedItem);
      		}
      	}
      
      	private static IEnumerable GetMyLockedItems()
      	{
      		return Context.ContentDatabase.SelectItems(GetMyLockedItemsQuery());
      	}
      
      	private static string GetMyLockedItemsQuery()
      	{
      		return string.Format("fast://*[@__lock='%\"{0}\"%']", Context.User.Name);
      	}
      
      	private static bool CanProcess(IEnumerable lockedItems)
      	{
      		return lockedItems != null
      				&& lockedItems.Any()
      				&& lockedItems.Select(item => item.Locking.HasLock()).Any();
      	}
      
      	private void UnlockInAllLanguages(Item item)
      	{
      	   Assert.ArgumentNotNull(item, "item");
      	   foreach(Language language in item.Languages)
      	   {
      			Item itemInLanguage = item.Database.GetItem(item.ID, language);
                              //TODO: you'll want to put a check to see if the user has itemInLanguage locked
      			Unlock(itemInLanguage);
      	   }
      	}
      	
      	protected virtual void Unlock(Item item)
      	{
      		Assert.ArgumentNotNull(item, "item");
      		if (!item.Locking.HasLock())
      		{
      			return;
      		}
      
      		try
      		{
      			item.Editing.BeginEdit();
      			item.Locking.Unlock();
      			item.Editing.EndEdit();
      		}
      		catch (Exception ex)
      		{
      			Log.Error(this.ToString(), ex, this);
      		}
      	}
      }
      
  4. Siva says:

    Hi,

    Nice article. But I have small question , What if user was logged out due to session expiry but not manual log off. ??. Will the above steps work?.

    Thanks in advance,
    Siva

Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: