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:
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:
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")]
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:
- HTTP Module
- Global.asax
- Event System
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
}
}
}
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);
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
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.
Speaking of which, I just blogged about Core Service client sample code: http://yatb.mitza.net/2012/03/core-service-client-sample-code.html