Would anybody be prepared to write a macro that I can use to produce a list of users showing when they last logged in? I'm aware that you can use com.atlassian.jira.bc.security.login.LoginInfo to do this, but I don't have the skills to write the macro myself. Any help would be appreciated.
Thanks
EDIT: If this you're just interested in the code, I have created a 'master' version that I frequently update following contributions from Remo Siegwart, Bruce Schlueter, and myself: https://github.com/stevegoldberg/ConfluenceMacros/tree/master/Last%20Login
The following user macro should display all users and their last successful login date:
## @noparams #set($containerManagerClass = $content.class.forName('com.atlassian.spring.container.ContainerManager')) #set($getInstanceMethod = $containerManagerClass.getDeclaredMethod('getInstance',null)) #set($containerManager = $getInstanceMethod.invoke(null,null)) #set($containerContext = $containerManager.containerContext) #set($loginManager = $containerContext.getComponent('loginManager')) #set($users = $userAccessor.getUsers()) <table class="confluenceTable"> <tr> <th class="confluenceTh">User</th> <th class="confluenceTh">Last Successful Login Date</th> </tr> #foreach($user in $users) <tr> <td class="confluenceTd">#usernameLink($user.name)</td> <td class="confluenceTd">$action.dateFormatter.formatDateTime($loginManager.getLoginInfo($user).lastSuccessfulLoginDate)</td> </tr> #end </table>
Hope this helps
That's incredibly useful, thanks. I may pester you again for some modifications if I can't figure them out myself.
Cheers
Great, I'm glad that it's useful for you!
Here is some more information about the API of the objects in use:
This information should help you to customize the report to your needs.
Well seeing as you're being so helpful, what I want to know is if it is possible to add a third column which would show whether the user has been disabled? We want to use the macro to quickly see who's inactive and can have their account disabled/deleted and a tick or cross next to each user to show if they are enabled or disabled would be helpful. Is this possible?
Thats a great little script..
Please can you tell me how I would get a count of user logins e.g. user1 has logged in 123 times.. ?
Thx
As far as I know it's not possible by default in Confluence. Confluence only stores a "total failed login count", but no total login count.
Little bug for Crowd users: '$user' should be '$user.name' for the last successful login date:
<td class="confluenceTd">$action.dateFormatter.formatDateTime($loginManager.getLoginInfo($user.name).lastSuccessfulLoginDate)</td>
If I modify the code to include that change, will it still work for non-Crowd users?
Not sure, don't think so. We are using Crowd and I was referring to the code you've posted at (Feb 08 at 11:15 AM).
But I've noticed that the version above that was not for Crowd.
I tried this on 3.5.17 and it shows empty fields for the last login, what might be the problem?
Try '$user.name' instead of '$user', seems to be a v3.5.x related issue...
<td class="confluenceTd">$action.dateFormatter.formatDateTime($loginManager.getLoginInfo($user.name).lastSuccessfulLoginDate)</td>
Hi Remo - excellent macro.
Is there a way to display only those that have not logged in to the system for the last 30 days? Also, can the column titles become clickable for sorting (desc/asc)?Thanks in advance.
Just add an if-clause and compare the last login date with the current date:
## @noparams #set($containerManagerClass = $content.class.forName('com.atlassian.spring.container.ContainerManager')) #set($getInstanceMethod = $containerManagerClass.getDeclaredMethod('getInstance',null)) #set($containerManager = $getInstanceMethod.invoke(null,null)) #set($containerContext = $containerManager.containerContext) #set($loginManager = $containerContext.getComponent('loginManager')) #set($users = $userAccessor.getUsers()) #set($thirtyDaysInMilliseconds = 30 * 24 * 60 * 60 * 1000) #set($thirtyDaysInThePastInMilliseconds = $content.currentDate.time - $thirtyDaysInMilliseconds) <table class="confluenceTable"> <tr> <th class="confluenceTh">User</th> <th class="confluenceTh">Last Successful Login Date</th> </tr> #foreach($user in $users) #set($lastLoginDate = $loginManager.getLoginInfo($user).lastSuccessfulLoginDate) #if($thirtyDaysInThePastInMilliseconds > $lastLoginDate.time) <tr> <td class="confluenceTd">#usernameLink($user.name)</td> <td class="confluenceTd">$action.dateFormatter.formatDateTime($lastLoginDate)</td> </tr> #end #end </table>
Table columns should be sortable by default since Confluence version 4.3.
Thanks Remo it works!
One little kink though, we are on Confluence 4.2.7 so the columns won't sort. Anyway you could extend help by providing extra code to the one you wrote above?Kind regards,
Doods
Sadly, there is no easy way to do that in a user macro. You would need to write a custom plugin for that. So, I would suggest to wait for 4.3.
Hi Remo,
a really nice job you did with your macro. What I noticed is that the sort order seems to brake if we add the time to the line, so we just keep the date like this :
<td class="confluenceTd">$action.dateFormatter.formatGivenString('dd.MM.yyyy', $crowdUser.createdDate)</td>
That is enough for us as we do not really need the exact time and the day suffices.
Another point concerning the last column "Active". Is there a possibility to sort this? I cant get it working...
Any idea would be great, like adding "True" or "False" and hide this and just show the image.
Instead of using tick and cross icons, I just use the status macro:
#if($userAccessor.isDeactivated($user)) <ac:macro ac:name="status"> <ac:parameter ac:name="colour">Red</ac:parameter> <ac:parameter ac:name="title">NO</ac:parameter> </ac:macro> #else <ac:macro ac:name="status"> <ac:parameter ac:name="colour">Green</ac:parameter> <ac:parameter ac:name="title">YES</ac:parameter> </ac:macro> #set($totalactiveusers = $totalactiveusers + 1) #end
Thanks, that works like a charme.
We are just setting up our wiki and we would like to know who never logged in. I tried something like
#if($loginManager.getLoginInfo($user).lastSuccessfulLoginDate ==' ') or this #if($loginManager.getLoginInfo($user).lastSuccessfulLoginDate ==null)
But I just get an empty table. How can I check for null values?
Try the following:
#if(!$loginManager.getLoginInfo($user).lastSuccessfulLoginDate)
Hi and thanks all the above!
I'm doing this in Confluence 3.3 and $lastLoginDate.time doesn't work. (But $content.currentDate.time works fine.) Any suggestions on how else I might get $loginManager.getLoginInfo($user.name).lastSuccessfulLoginDate) in milliseconds?
$lastLoginDate.time
Also, how do I format my compare date that is in milliseconds into a formatted date?
I'm using the code from here as the basis of my macro, so no JQuery or javascript is involved.
Any help would be greatly appreciated!
Remo, very useful User Macro. Do you know how to filter only users of some given space?
Cheers, Stefan
Hi Selcuk, you can use $userAccessor.getGroupNamesForUserName($username) to get the groups a user is part of and then use a loop to print them out as you want. E.g.:
#set ($groups = $userAccessor.getGroupNamesForUserName($username)) <ul> #foreach ($group in $groups) <li>$group</li> #end </ul> #end
What do you mean by 'space'? Do you mean 'group' or do you mean something more complicated like the users who can access a particular space?
Steve, I mean all users with permissions to a specific space (kind of community). Groups, single users.
Try this. It displays last login of user permitted to current space: {code}## Macro title: Last Login By Current Space ## Macro has a body: N ## Body processing: No macro body ## ## Macro to display the last login date of users who have access to the current space ## @noparams #set($containerManagerClass = $content.class.forName('com.atlassian.spring.container.ContainerManager')) #set($getInstanceMethod = $containerManagerClass.getDeclaredMethod('getInstance',null)) #set($containerManager = $getInstanceMethod.invoke(null,null)) #set($containerContext = $containerManager.containerContext) #set($loginManager = $containerContext.getComponent('loginManager')) #set($users = $userAccessor.getUsers()) <table class="confluenceTable"> <tr> <th class="confluenceTh">User</th> <th class="confluenceTh">Last Successful Login</th> </tr> #foreach($user in $users) ## list the last login date of users who can view the current space #if ($permissionHelper.canView($user, $space)) <tr> <td class="confluenceTd">#usernameLink($user.name)</td> #if (!$loginManager.getLoginInfo($user).lastSuccessfulLoginDate) <td class="confluenceTd" style="background-color:#ff0000"> <strong>NEVER</strong> </td> #else <td class="confluenceTd">$action.dateFormatter.formatDateTime($loginManager.getLoginInfo($user.name).lastSuccessfulLoginDate)</td> #end </tr> #end #end </table>{code}
Sorted by date. Sorry, can't get the spaces right.
## @param group:title=Group|type=string|required=true|desc=Group Name|default=confluence-users#set($containerManagerClass = $content.class.forName('com.atlassian.spring.container.ContainerManager'))#set($getInstanceMethod = $containerManagerClass.getDeclaredMethod('getInstance',null))#set($containerManager = $getInstanceMethod.invoke(null,null))#set($containerContext = $containerManager.containerContext)#set($loginManager = $containerContext.getComponent('loginManager'))#set($group = $userAccessor.getGroup($paramgroup))#set($usernames = $userAccessor.getMemberNames($group))#set($rows= [])#foreach($username in $usernames) #set($user = $userAccessor.getUser($username)) #set($foo = $rows.add( {"name" : $user.name, "date" : $loginManager.getLoginInfo($user).lastSuccessfulLoginDate} ))#end#set($ii = $rows.size() - 2)#foreach($i in [0..$ii]) #set($jj = $rows.size() - $i - 2) #foreach($j in [0..$jj]) #set($inc = $j + 1) #if ($rows.get($j).get("date")) #if ($rows.get($inc).get("date")) #if ($rows.get($j).get("date").before($rows.get($inc).get("date"))) #set($tmp = $rows.get($j)) #set($foo = $rows.set($j, $rows.get($inc))) #set($foo = $rows.set($inc, $tmp)) #end #end #else #set($tmp = $rows.get($j)) #set($foo = $rows.set($j, $rows.get($inc))) #set($foo = $rows.set($inc, $tmp)) #end #end#end<h2>$group</h2><table class="confluenceTable"> <tr> <th class="confluenceTh">Count</th> <th class="confluenceTh">User</th> <th class="confluenceTh">Last Successful Login Date</th> </tr> #foreach($row in $rows) <tr> <td class="confluenceTd">$velocityCount</td> <td class="confluenceTd">#usernameLink($row.get("name"))</td> <td class="confluenceTd">$action.dateFormatter.formatDateTime($row.get("date"))</td> </tr> #end</table>
Hi Folks,
Could you please advise for Bitbucket. I need to find last login date of users in bitbucket.
com.atlassian.jira.bc.security.login.LoginInfo - this one is for jira, can't find one for bitbucket. Please advice.
For Bitbucket, I use the REST API (rest/api/1.0/admin/users) to get lastAuthenticationTimestamp (ms from 1/1/1970).
Do I just need to paste the above code snipped into Create UserMacro from the Admin console? If so what Macro Processing Body should I select: No Macro Body, Escaped, Unrendered, Rendered
How do I consume this code withing my Confluence?
Hi SimratPal,
you have to write a User Macro
https://confluence.atlassian.com/doc/writing-user-macros-4485.html
Only works with Confluence Server though.
Is it possible to use this on a confluence page but to view the user's last login date to Jira? Our confluence uses Jira for user management. Great macro though!
is it possible to prevent specific groups from being listed. i.e. the confluence-users group will have too many users to list
Thank you so much, Remo! We are always close to our user limit, and this will be immensely helpful in identifying inactive users to be disabled. Really, thank you!!
I am getting the error using the macro shared by @remo
page isnt working because it took too long to respond.HTTP ERROR 504
Seems like this broke for Confluence 7, no more loginManager. Anyone know the fix?
I found out that the updated_date in the Confluence [dbo].[cwd_user] table actually shows last log in date/time.
Very useful.
Bah. They updated this to show no macro solution in pre-7 Confluence. Guess who's staying at 6.15.9 for awhile...
https://confluence.atlassian.com/confkb/how-to-get-a-list-of-active-users-counting-towards-the-confluence-license-298978076.html
Hello,
I try to do this on Confluence 7.2.1 and the column Last successful login date is still black.
Have you an idea on how to solved it?
Regards
Is there a version of this fix that includes the last login date?
Has anyone been able to get this User Macro working for 7.x?
If so can you please share the code. I expect the ContainerManager has moved, or has a new class name perhaps, but I can't find it.
I managed to get my User Macro working in 7.x. For those interested here is my code:
## @Param group:title=Group|type=string|required=true|desc=Group to show
#set( $containerManagerClass=$content.class.forName('com.atlassian.spring.container.ContainerManager') )#set( $getInstanceMethod=$containerManagerClass.getDeclaredMethod('getInstance',null) )#set( $containerManager=$getInstanceMethod.invoke(null,null) )#set( $containerContext=$containerManager.containerContext )#set( $loginManager = $containerContext.getComponent('loginManager') )#set( $crowdService = $containerContext.getComponent('crowdService') )
#set($timeNow=$content.currentDate.time)#set($group = $userAccessor.getGroup($paramgroup))
#if($group)#set($usernames = $userAccessor.getMemberNames($group))
<table class="confluenceTable" width="660px"><tr><th class="confluenceTh " width="220px">Name</th><th class="confluenceTh" width="110px">Create date</th><th class="confluenceTh" width="110px">Last Successful Login</th><th class="confluenceTh" width="110px">Last Failed Login</th><th class="confluenceTh" width="110px" style="text-align:center;">Activated</th><th class="confluenceTh" width="110px" style="text-align:center;">Days</th></tr>#foreach($username in $usernames)#set($t3="9999")#set($user = $userAccessor.getUserByName($username))#set($crowdUser = $crowdService.getUser($user.name))#set($lastLogin = $loginManager.getLoginInfo($user).lastSuccessfulLoginDate)
<tr><td class="confluenceTd"><a target="_tab" href="/admin/users/viewuser.action?username=$username">$user.getFullName()</a></td><td class="confluenceTd" style="text-align:center;">$action.dateFormatter.formatGivenString('yyyy.MM.dd', $crowdUser.createdDate)</td><td class="confluenceTd" style="text-align:center;"><div>$action.dateFormatter.formatGivenString('yyyy.MM.dd',$lastLogin)</div></td><td class="confluenceTd" style="text-align:center;">$action.dateFormatter.formatGivenString('yyyy.MM.dd',$loginManager.getLoginInfo($user).lastFailedLoginDate)</td><td class="confluenceTd" style="text-align:center;">#if($userAccessor.isDeactivated($user))<ac:macro ac:name="status"><ac:parameter ac:name="colour">Red</ac:parameter><ac:parameter ac:name="title">No</ac:parameter></ac:macro>#else<ac:macro ac:name="status"><ac:parameter ac:name="colour">Green</ac:parameter><ac:parameter ac:name="title">Yes</ac:parameter></ac:macro>#set($totalactiveusers = $totalactiveusers + 1)#end</td>
#if($loginManager.getLoginInfo($user).lastSuccessfulLoginDate)#set($t3= $timeNow - $loginManager.getLoginInfo($user).lastSuccessfulLoginDate.time)#set($t3=$t3/1000/24/60/60)#end<td class="confluenceTd" style="text-align:center;">#if($t3 < 31)<ac:macro ac:name="status"><ac:parameter ac:name="colour">Green</ac:parameter><ac:parameter ac:name="title">$t3</ac:parameter></ac:macro>#elseif($t3 < 365)<ac:macro ac:name="status"><ac:parameter ac:name="colour">Yellow</ac:parameter><ac:parameter ac:name="title">$t3</ac:parameter></ac:macro>#else<ac:macro ac:name="status"><ac:parameter ac:name="colour">Red</ac:parameter><ac:parameter ac:name="title">$t3</ac:parameter></ac:macro>#end</td></tr>#end</table>#end
hey @KC Integrations
Thanks for updating it and sharing but for some reason mine is not showing the Create, Last Login and Last Failed login columns. Days is always showing red and 9999. Do you happen to know what could I be doing wrong? I just copied and pasted your code. Is your still working?
Thanks!
Hi @Diego ,
I think you need to enable some classes from the velocity context. Edit the file:
and at the bottom, comment out some classes, here is a copy of the last section of the file: WEB-INF/classes/velocity.properties
# ----------------------------------------------------------------------------# SECURE INTROSPECTOR# ----------------------------------------------------------------------------# If selected, prohibits methods in certain classes and packages from being# accessed. If you are adding new package or class restriction, please also# add it to /confluence-core/confluence/src/etc/java/org/apache/velocity/runtime/defaults/velocity.properties# The list is maintained at:# https://extranet.atlassian.com/display/CSPF/Restrict+packages+and+classes+usage+from+velocity+files# ----------------------------------------------------------------------------
#introspector.restrict.packages = java.lang.reflect,\introspector.restrict.packages = \com.atlassian.cache,\com.atlassian.confluence.util.http,\com.atlassian.failurecache,\com.atlassian.vcache,\com.atlassian.sal.api.net,\com.google.common.cache,\com.google.common.net,\com.hazelcast,java.jms,\java.rmi,\javax.management,\javax.naming,\org.apache.commons.httpclient,\org.apache.httpcomponents.httpclient,\org.apache.http.client,\org.ehcache,\com.google.common.reflect,\com.sun.jmx,com.sun.jna,\javax.xml,jdk.nashorn,\net.bytebuddy,\net.sf.cglib,org.apache.bcel,\org.javassist,org.ow2.asm,\sun,\com.atlassian.activeobjects,\com.atlassian.hibernate,\#java.sql,\javax.persistence,\#javax.sql,\liquibase,\net.java.ao,\net.sf.hibernate,\org.hibernate,\com.atlassian.confluence.setup.bandana,\com.atlassian.filestore,\com.atlassian.media,\com.google.common.io,java.io,\java.nio,java.util.jar,\java.util.zip,\org.apache.commons.io,\com.atlassian.confluence.impl.util.sandbox,\com.atlassian.confluence.util.io,\com.atlassian.confluence.util.sandbox,\com.atlassian.quartz,\com.atlassian.scheduler,\com.atlassian.utils.process,\com.atlassian.util.concurrent,\io.atlassian.util.concurrent,\java.util.concurrent,\org.apache.commons.exec,\org.springframework.util.concurrent,\org.quartz,\oshi
#introspector.restrict.classes = java.lang.Class,\introspector.restrict.classes = \java.lang.ClassLoader,\java.lang.Compiler,\java.lang.InheritableThreadLocal,\java.lang.Package,\java.lang.Process,\java.lang.Runtime,\java.lang.RuntimePermission,\java.lang.SecurityManager,\java.lang.System,\java.lang.Thread,\java.lang.ThreadGroup,\java.lang.ThreadLocal, \com.atlassian.applinks.api.ApplicationLinkRequestFactory,\com.atlassian.confluence.util.ConfluenceUberClassLoader,\com.atlassian.core.util.ClassLoaderUtils,\com.atlassian.core.util.ClassHelper
Hello same here, we are on 7.18.3.
It looks like you're new here. Sign in or register to get started.