Skip to main content

Capturing A User's Last Login Date Into The CME

Requirement: Indentify a user's last login date into the CME and store it for the purpose of later creating a report with different users and their last login date.

This post only covers the storing of a last login and not the report itself.

There are several ways of intercepting the when a user logs in:
  1. HTTP Module
  2. Global.asax
  3. Event System
I tried the Global.asax and event system approaches with more or less success. I did not try the HTTP Module, which I believe would work the best.

For storing the last login date, I used Application Data on the User object.

1. HTTP Module
The idea is to write a HTTP Module that intercepts each request made to the CME. The module would check if the session has just been initiated, and if so, it would store the current DateTime in the current User object as AppData. One could get the User object by using the Core Service.

2. Global.asax
First of all, this approach doesn't really work :-(

The idea is to intercept the session object creation in the CME global.asax, then during this method identify the current User by using the Core Service, then store DateTime.Now into the user's AppData.

When placing such a Global.asax in the CME web-app's %TRIDION_HOME%\web\WebUI\WebRoot folder, the CME won't really load and I got timeout exceptions on GetList.

I used the code below:

public void Session_OnStart(object sender, EventArgs e)
{
    using (var client = new CoreServiceSession())
    {
        try
        {
            UserData user = client.GetCurrentUser();
            var adapter = new ApplicationDataAdapter("LastLogin", DateTime.Now);
            client.SaveApplicationData(user.Id, new ApplicationData[] { adapter.ApplicationData });
        }
        catch (Exception ex)
        {
            // handle error or ignore it
        }
    }
}

3. Event System
The idea here is to create an Event System handler that intercepts the load of a User object. When the user logs into the CME, its User object is loaded, therefore the event handler is called.

The challenge of this approach is to identify which load event to intercept and which to ignore. There is no easy way to detect a new session. I tried using the Tridion Session.ContextData to store a flag, then check for its presence in a subsequent handler call, but it seems the Session is recreated for each handler call, so the Session.ContextData I receive is always empty across different events.

Finally, I settled for a kind of hybrid approach, where I cache a User login for a period of time (let's say 1hour). The event handler won't save another last login date until this hour expires.

I noticed the User load handler is called many times! I mean, with every selection of a different Folder in the CME, the handler is called about 10-15 times. Obviously, this might pose a performance issue, so be careful what you do in the handler code.

Sample event handler code below: 

[TcmExtension("Last Login Event System")]
public class Events : TcmExtension
{

    public Events()
    {
        EventSystem.Subscribe<User, LoadEventArgs>(OnUserLoad, EventPhases.TransactionCommitted);
    }

    private void OnUserLoad(User user, LoadEventArgs eventArgs, EventPhases phase)
    {
        Logger.Write("OnUserLoad occurred", "Last Login Event System", LoggingCategory.General, TraceEventType.Warning);
        Session session = user.Session;
        if (session.User.Id.Equals(user.Id))
        {
            DateTime dateNow = DateTime.Now;
            if (isUpdate(user, dateNow))
            {
                ApplicationDataAdapter adapter = new ApplicationDataAdapter("LastLogin", dateNow);
                user.SaveApplicationData(adapter.ApplicationData);
            }
        }
    }

    private bool isUpdate(User user, DateTime dateNow)
    {
        ApplicationData appData = user.LoadApplicationData("LastLogin");
        if (appData == null)
        {
            return true;
        }

        DateTime lastLogin = appData.GetAs<DateTime>();
        if (dateNow.Subtract(lastLogin).TotalHours > 1)
        {
            return true;
        }

        return false;
    }
}

Comments

Nivlong said…
Woot! That was almost too clear and easy to follow and understand, Mihai. Thanks for the background and example.

With a little help from Google, I realize I didn't know what I didn't know. But now... I know! :-D

I describe how I got background on the Event System and AppData here: http://www.tridiondeveloper.com/sdl-tridion-event-system-newbie-perspective. If I can help other blog, others can help me code.
Mihai Cădariu said…
Isn't it cool that you can simply Google for Tridion samples these days? :)
Speaking of which, I just blogged about Core Service client sample code: http://yatb.mitza.net/2012/03/core-service-client-sample-code.html

Popular posts from this blog

Running sp_updatestats on AWS RDS database

Part of the maintenance tasks that I perform on a MSSQL Content Manager database is to run stored procedure sp_updatestats . exec sp_updatestats However, that is not supported on an AWS RDS instance. The error message below indicates that only the sa  account can perform this: Msg 15247 , Level 16 , State 1 , Procedure sp_updatestats, Line 15 [Batch Start Line 0 ] User does not have permission to perform this action. Instead there are several posts that suggest using UPDATE STATISTICS instead: https://dba.stackexchange.com/questions/145982/sp-updatestats-vs-update-statistics I stumbled upon the following post from 2008 (!!!), https://social.msdn.microsoft.com/Forums/sqlserver/en-US/186e3db0-fe37-4c31-b017-8e7c24d19697/spupdatestats-fails-to-run-with-permission-error-under-dbopriveleged-user , which describes a way to wrap the call to sp_updatestats and execute it under a different user: create procedure dbo.sp_updstats with execute as 'dbo' as

Scaling Policies

This post is part of a bigger topic Autoscaling Publishers in AWS . In a previous post we talked about the Auto Scaling Groups , but we didn't go into details on the Scaling Policies. This is the purpose of this blog post. As defined earlier, the Scaling Policies define the rules according to which the group size is increased or decreased. These rules are based on instance metrics (e.g. CPU), CloudWatch custom metrics, or even CloudWatch alarms and their states and values. We defined a Scaling Policy with Steps, called 'increase_group_size', which is triggered first by the CloudWatch Alarm 'Publish_Alarm' defined earlier. Also depending on the size of the monitored CloudWatch custom metric 'Waiting for Publish', the Scaling Policy with Steps can add a difference number of instances to the group. The scaling policy sets the number of instances in group to 1 if there are between 1000 and 2000 items Waiting for Publish in the queue. It also sets the

I Have Gone Dark

Maybe it's the Holidays, but my mood has gone pretty dark. That is, regarding the look and feel of my computer and Tridion CME, of course. What I did was to dim the lights on the operating system, so I installed Placebo themes for Windows 7 . I went for the Ashtray look -- great name :) My VM looks now like this: But, once you change the theme on Windows, you should 'match' the theme of your applications. Some skin easily, some not. The Office suite has an in-built scheme, which can be set to Black , but it doesn't actually dim the ribbon tool bars -- it looks quite weird. Yahoo Messenger is skinnable, but you can't change the big white panels where you actually 'chat'. Skype is not skinnable at all. For Chrome, there are plenty of grey themes. Now i'm using Pro Grey . But then I got into changing the theme of websites. While very few offer skinnable interfaces (as GMail does), I had to find a way to darken the websites... Enter Stylish -- a pl