Here are the instructions to create a history of logins and logouts for your users. With this solution you get a list of all the login/logout actions by each user, not just the most recent login/logout actions by each user.
Some similarities in the coding part and if you wish, you can do both (see the coding above) but these instructions assume you are just going to create the historical records.
This creates a subpanel in each user’s page so they can see their own login/logout actions and creates a listview on the new module page accessible by the Administrator (and anyone else to whom you give access) that shows the login/logout history for all users. It also prevents users from Creating,Updating or Deleting entries; all entries are system generated based on user login/logout actions.
Steps to create this solution:
- Create a new module to hold the login/logout data
- Relate the new module to the Users’ module
- Create the script that auto-populates the login/logout data based on user actions
- Create a new module to hold the login/logout data
As an administrator, using SuiteCRM->Admin->Developer Tools->Module Builder
New Package
Package Name: useractivitylog
Author:
Prefix: rcual
Description: Record user login and logout activity
ReadMe: This package requires custom script to populate the new table and relationships
Save
New Module
Module Name: useractivity
Label: User Activity
Importing: unchecked
Navigation: Checked
Type: Basic
Save
Customize new module -> expand useractivylog and useractivity on left menu
Add Fields
Type: Text
Field Name: useractivitytype
Display Label: Activity Type
Inline Edit: No
Importable: No
Save
Type: datetime
Field Name: useractivitytime
Display Label: Activity Type
Inline Edit: No
Importable: No
Save
Edit the Available Subpanels -> Default (for some reason you have to do this 2x and save 2x)
Move Name from Default column to Hidden Column
Move Remove from Default column to Hidden Column
Move Edit from Default column to Hidden Column
Move Date Modified from Default column to Hidden Column
Move Activity Type from Hidden column to Default column
Move Activity Date/Time from Hidden column to Default column
Edit the layouts
Listview
Move all existing fields out of Default column to Hidden column
Move from the Hidden column to the Default Column: Users, Created By, Activty Type and Activity Date/Time
Save
Save and Deploy the package
(Result is a module/bean called rcual_useractivity)
Repair
Quick Repair and Rebuild
- Relate the new module to the Users’ module
Use Studio Tools to add a relationship between Users and User Activity
Use studio; do NOT do this from within Module Builder since you want the Primary module to be users
The primary module is the one which has the subpanel showing the relationships
SuiteCRM->Admin->Developer Tools->Studio->Users->Relationships->Add Relationship
Type: one to Many
Related Module: User Activity
(result is link called users_rcual_useractivity_1 in bean rcual_useractivity)
(link name can be found in cache/modules/Users/Uservardefs.php)
Save and Deploy
Now go back in Studio to add the user to the listview of the new module rcual_useractivity
SuiteCRM->Admin->Developer Tools->Studio->rcual_useractivity->Layouts->listview
Drag Users from Hidden to Default
Save
Repair and Rebuild the installation
Rebuild Relationships
Quick Repair and Rebuild
Make the views read-only (ie prevent any user Create,Update or Delete operations) to the useractivty data
Remove the action buttons from the useractivity subpanel in Users so the display is read-only
Edit custom/Extensions/modules/Users/Ext/Layoutdefs/users_rcual_useractivity_1_Users.php
Comment out the array which defines the action buttons in the useractivity subpanel
so it now looks like (note the /* and */ markers commenting out the whole array definition section)
<?php
// created: 2019-05-13 16:28:41
$layout_defs["Users"]["subpanel_setup"]['users_rcual_useractivity_1'] = array (
'order' => 100,
'module' => 'rcual_useractivity',
'subpanel_name' => 'default',
'sort_order' => 'asc',
'sort_by' => 'id',
'title_key' => 'LBL_USERS_RCUAL_USERACTIVITY_1_FROM_RCUAL_USERACTIVITY_TITLE',
'get_subpanel_data' => 'users_rcual_useractivity_1',
'top_buttons' =>
array (
/*
0 =>
array (
'widget_class' => 'SubPanelTopButtonQuickCreate',
),
1 =>
array (
'widget_class' => 'SubPanelTopSelectButton',
'mode' => 'MultiSelect',
),
*/
),
);
Remove the Menu item for Creating useractivity from the useractivity module so it is read-only
Edit modules/rcual-useractivity/Menu.php
Note that this is NOT upgrade safe, but it is a custom module so SuiteCRM upgrades will not touch this module
Comment out the if section that presents the Edit menu item
so it now looks like (note the /* and */ markers commenting out the whole if section for the edit menu item)
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
global $mod_strings, $app_strings, $sugar_config;
/*
if(ACLController::checkAccess('rcual_useractivity', 'edit', true)){
$module_menu[]=array('index.php?module=rcual_useractivity&action=EditView&return_module=rcual_useractivity&return_action=DetailView', $mod_strings['LNK_NEW_RECORD'], 'Add', 'rcual_useractivity');
}
*/
if(ACLController::checkAccess('rcual_useractivity', 'list', true)){
$module_menu[]=array('index.php?module=rcual_useractivity&action=index&return_module=rcual_useractivity&return_action=DetailView', $mod_strings['LNK_LIST'],'View', 'rcual_useractivity');
}
Note that the above code hides the navigation option but a user could still directly access the editview by entering
https://{server_address}/index.php?module=rcual_useractivity&action=editview
so to prevent that (again, not upgrade-safe, but see above for why this is not a problem)
Create a new file at modules/rcual-useractivity/views/view.edit.php (you will likely need to create the views directory)
<?php
require_once('include/MVC/View/views/view.edit.php');
class rcual_useractivityViewEdit extends SugarView{
function display(){
if(!$this->bean->fetched_row){
sugar_die('Data for this module is created by the system so users have no edit capability');
}
parent::display();
}
}
Admin->Repair->Repair and Rebuild
- Create the script that auto-populates the login/logout data based on user actions
Create a new file at custom/modules/Users/record_user_login.php
<?php
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
class record_user_login_class
{
function record_user_login_method($bean, $event, $arguments)
{
global $timedate;
// Create the entries for the historical list of logic/logout times kept in the User Activity module
$activityBean = BeanFactory::newBean('rcual_useractivity');
$activityBean->useractivitytype = "login";
$activityBean->useractivitytime = $timedate->nowDb();
$activityBean->save();
// Load the relationship between the Contacts and the Notes module
$bean->load_relationship('users_rcual_useractivity_1');
// Relate the new entry to the user
$bean->users_rcual_useractivity_1->add($activityBean);
}
}
?>
Create a new file at custom/modules/Users/record_user_logout.php
<?php
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
class record_user_logout_class
{
function record_user_logout_method($bean, $event, $arguments)
{
global $timedate;
// Create the entries for the historical list of logic/logout times kept in the User Activity module
$activityBean = BeanFactory::newBean('rcual_useractivity');
$activityBean->useractivitytype = "logout";
$activityBean->useractivitytime = $timedate->nowDb();
$activityBean->save();
// Load the relationship between the Contacts and the Notes module
$bean->load_relationship('users_rcual_useractivity_1');
// Relate the new entry to the user
$bean->users_rcual_useractivity_1->add($activityBean);
}
}
?>
Edit the existing file at custom/modules/Users/logic_hooks.php
Change
<?php
// Do not store anything in this file that is not part of the array or the hook version. This file will
// be automatically rebuilt in the future.
$hook_version = 1;
$hook_array = Array();
// position, file, function
$hook_array['after_login'] = Array();
$hook_array['after_login'][] = Array(1, 'SugarFeed old feed entry remover', 'modules/SugarFeed/SugarFeedFlush.php','SugarFeedFlush', 'flushStaleEntries');
?>
to (ie add the last 2 lines)
<?php
// Do not store anything in this file that is not part of the array or the hook version. This file will
// be automatically rebuilt in the future.
$hook_version = 1;
$hook_array = Array();
// position, file, function
$hook_array['after_login'] = Array();
$hook_array['after_login'][] = Array(1, 'SugarFeed old feed entry remover', 'modules/SugarFeed/SugarFeedFlush.php','SugarFeedFlush', 'flushStaleEntries');
$hook_array['after_login'][] = Array(98, 'Record last user login date and time', 'custom/modules/Users/record_user_login.php','record_user_login_class', 'record_user_login_method');
$hook_array['before_logout'][] = Array(99, 'Record last user logout date and time', 'custom/modules/Users/record_user_logout.php','record_user_logout_class', 'record_user_logout_method');
?>
Do a final clean-up
Repair and Rebuild
Using the SuiteCRM web interface
SuiteCRM -> Admin -> System (section) -> Repair -> Quick Repair and Rebuild
(when the screen refreshes, scroll to the bottom in case it requires a manual sync of vardefs and database)
Using the CLI (maybe not necessary every time, but it never hurts to ensure correct permissions)
cd {root directory of SuiteCRM installation}
chown -R www-data:www-data .
chmod -R 755 .
chmod -R 775 cache custom modules themes data upload
chmod 775 config_override.php 2>/dev/null
That’s it!
If you change any names of modules or fields, you’ll have to change the instructions to match
Anyone who sees any issues cause by this or has any suggestion for improvement, please feel free to add your comments/feedback.