Last Login Users data

On a first look, I don’t see that data in the database. The “users” table doesn’t even seem to store the “last login time”.

I suppose you could store that data using a Workflow, and surely you could with a logic_hook:

http://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_6.5/Module_Framework/Logic_Hooks/User_Hooks/

Maybe there’s an add-on already made for this? Try searching the SuiteCRM Store…

good luck

We tweaked the login information using logic hooks

We add cookies (for integration with another application) and also log last login
I added a custom field “userlastlogin” to the user record…
here is the code

<?php

if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

class logic_hooks_class
{
    function after_login_method($bean, $event, $arguments)
    {
        //logic
        global $current_user, $timedate;
        setcookie("SuiteCRMUserID", $current_user->id, time()+360000, "/" );
        setcookie("AccountingValidCies", $current_user->accesstocies_c,time()+360000, "/")        ;

        $focus = new User();
        $focus->retrieve($current_user->id)
        $focus->userlastlogin_c = $timedate->getInstance()->nowDb();
        $focus->save();
        
    }

    function login_failed_method($bean, $event, $arguments)
    {
        //logic
        setcookie("SuiteCRMUserID",  " login failed ", time()-36000,"/");

    }

    function before_logout_method($bean, $event, $arguments)
    {
        //logic
        setcookie("SuiteCRMUserID",  " bye! ", time()-36000,"/");


    }
}

?>
4 Likes

Thank you for posting this

I am looking to do something similar.

Were do you put this php file? I am on SuiteCRM 7.10 if it maters.

Also, is there a way to keep a running record of logins / logouts for each user? I am assuming a new database field connected to the user and a means of displaying it. If you have answers to both, great, but for now could work with just the database setup and php file code and use a database query tool to see the results.

Have you ever done this?

I am happy to help,
find documentation on logic hooks there are plenty
you put it in /custom/modules/users…
historical information?, yes you will need a new table linked to the user table…

Well, the good news is, I did not crash the system :slight_smile:

The bad news is, I am not getting the field populated.

I added the field and made it readonly
I added the field to both the editviewdefs and detailviewdefs layouts
I added the script (modified per below since I only needed the login field, not the cookies)
I Repair and Rebuild the system

So the field does show on the layouts and the field is readonly, but when the user logs in and out, and back in again, no data is shown in the new field.

Any ideas what I am missing?

See below for what I did.

Using the SuiteCRM GUI as Admin

SuiteCRM -> Admin -> Studio -> Users (module)
Fields -> Add
	Data Type: Date/Time
	Name: userlastlogin   (when it is stored, SuiteCRM will call it userlastlogin_c to show it was a user-custom field)
	Display Label: Last Login Date/Time:
	Inline Edit: UnCheck
	Importable: No
Layouts -> EditView
	Sync to Detail view: Check
	Scroll Down to row with Address Country taking full width and shrink to 1/2 width
	Drag and drop "Last Login Date/Time:" filed to space opened up

From the CLI


cd {root directory of SuiteCRM installation}
cd custom/modules/Users/metadata
nano editviewdefs.php
	Change
		array (
		  0 => 'address_country',
		  1 =>
		  array (
			'name' => 'userlastlogin_c',
			'label' => 'LBL_USERLASTLOGIN',
		  ),
		),
	to
				array (
				  0 => 'address_country',
				  1 =>
				  array (
					'name' => 'userlastlogin_c',
					'label' => 'LBL_USERLASTLOGIN',
					'type' => 'readonly',
				  ),
				),
cd {root directory of SuiteCRM installation}
cd custom/modules/Users
nano record-last-login-date-time-for-user.php
	<?php

	if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

	class logic_hooks_class
	{
		function after_login_method($bean, $event, $arguments)
		{
			global $current_user, $timedate;
			$focus = new User();
			$focus->retrieve($current_user->id)
			$focus->userlastlogin_c = $timedate->getInstance()->nowDb();
			$focus->save();
			
		}
	}

	?>

From the SuiteCRM GUI

 
SuiteCRM -> Admin -> System (section) -> Repair -> Quick Repair and Rebuild

From the CLI


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

But, as I said, the login date/time is not being populated to the field.

Any ideas?

OK,

Apparently, in addition to creating the file that does the action, you have to actually link that file to the hook.

So, do all the above and then …

I have seen two versions of how that is done, but neither one is working for me right now.

Version 1:
The file at /var/www/html/suiteayudacrm/custom/modules/Users/logic_hooks.php curently contains

<?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');
?>

so I added to that

$hook_array['after_login'][] = Array(99, 'Record last user login date and time', 'logic_hooks_class.php','logic_hooks_class', 'after_login_method');

Making the full file now

<?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(99, 'Record last user login date and time', 'logic_hooks_class.php','logic_hooks_class', 'after_login_method');
?>

I also changed the name of the file from record-last-login-date-time-for-user.php to logic_hooks_class.php

After a Quick Repair and a login/logout/login still no entry in the new field

Version 2
There was another post that said to add entries using the extensions framework, which appeared to be talking about a way to add logic hook actions and protect the edits from future upgrades wiping them out, but I could not really follow the whole code suggestions there.

So I am close, but no cigar.

Any ideas?

In case it matters, I did manually add to the SQL database an entry for userlastlogin_c for one of my users and when I brought up that user’s profile, it does show properly, so the issue is not the field or layout edits; it is the logic of my coding.

At this point I have exhausted my level of expertise (I know, that did not take much) so any help would be appreciated.

One thing I did figure out

The path I declared to the file logic_hooks_class.php assumed the current working directory was the same directory as the calling file but it is not. Curse those relative paths …

So I edited the path to the logic_hooks_class.php file and made it relative to the SutieCRM root directory as in

$hook_array['after_login'][] = Array(99, 'Record last user login date and time', 'custom/modules/Users/logic_hooks_class.php','logic_hooks_class', 'after_login_method');

I think that did now make a difference and I think the file is now being called since it did change how I have to login; I get a blank screen then refresh the URL and I am in.

The userlastlogin_c field is NOT being populated though.

So although that is not in itself good, at least I now think the hook is calling the file.

But now I need to correct what the code in the file is doing.

Hopefully you have some ideas.

You’re not supposed to add the field on the database, you should do it from Studio (or manually in the vardefs).

When you do a QR&R, you need to scroll to the bottom and click a button that says “sync database with vardefs” if there is one there. Did you check this?

I did add the field using Studio and it does show properly.

I manually added DATA to the database to check if it was displaying properly in the layout, which it is.

The issue is getting the script, triggered by the after_login hook, to populate the database, which it is currently NOT doing.

Ah ok, sorry.

Can you check the returns from each function, to see which part is not working? The “retrieve”, etc.

I have it working!

Thank you for pointing me in the right direction.

So, in one posting, here is what I did in SuiteCRM 7.10.11 to have the User->Profile page display a field showing when the User last logged in

Using the SuiteCRM GUI (this part I had correct all along - Thanks to the Sugar?Suite CRM development folks …)

SuiteCRM -> Admin -> Studio -> Users (module)
Fields -> Add
	Data Type: Date/Time
	Name: userlastlogin   (when it is stored, SuiteCRM will call it userlastlogin_c to show it was a user-custom field)
	Display Label: Last Login Date/Time:
	Inline Edit: UnCheck
	Importable: No
Layouts -> EditView
	Sync to Detail view: Check
	Scroll Down to row with "Address Country" taking full width and shrink it to 1/2 width
	Drag and drop "Last Login Date/Time:" field to space opened up

From the CLI (had to change this part)

cd {root directory of SuiteCRM installation}
cd custom/modules/Users/metadata
nano editviewdefs.php
	Change
		array (
		  0 => 'address_country',
		  1 =>
		  array (
			'name' => 'userlastlogin_c',
			'label' => 'LBL_USERLASTLOGIN',
		  ),
		),
	to
		array (
		  0 => 'address_country',
		  1 =>
		  array (
			'name' => 'userlastlogin_c',
			'label' => 'LBL_USERLASTLOGIN',
			'type' => 'readonly',
		  ),
		),
cd {root directory of SuiteCRM installation}
cd custom/modules/Users
nano 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;
					$bean->userlastlogin_c = $timedate->nowDb();
					$bean->save();
			}
	}

	?>
cd {root directory of SuiteCRM installation}
cd custom/modules/Users
nano 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 line)
		<?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(99, 'Record last user login date and time', 'custom/modules/Users/record_user_login.php','record_user_login_class', 'record_user_login_method');
		?>

Back to the SuiteCRM GUI

SuiteCRM -> Admin -> System (section) -> Repair -> Quick Repair and Rebuild

And back to 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

It works!

Thanks to all who posted code snippets in this and other forums.

Open Source RULES!

1 Like

I have been using the above with a custom Report to see who has been logging in but I realized that without any indication of how long they were logged in, it was incomplete information.

So, I added another field for collecting logout data added a script to populate that, and and edited the report to show both. It provides a more useful set of information. See new, complete code, below.

If anyone has figured out how to store this information in a table so we can view a history of the login/logout per User, feel free to add to this. I know it could be done by direct access to the sql code, but I am hoping to stay within the $bean-> structure.

So, to track and report on user login/logout activity:

  1. Create new fields in the User module to store the most recent login/logout per User
  2. Add the new fields to the Editview and Detailview of the User module
  3. Add script to populate the fields on login and logout
  4. Create a Report to show the most recent login and logout by User
  1. Create new fields in the User module to store the most recent login/logout per User
    Using the SuiteCRM Web Interface: SuiteCRM -> Admin -> Studio -> Users (module)

Fields -> Add
	Data Type: Date/Time
	Name: userlastlogin   (when it is stored, SuiteCRM will call it userlastlogin_c to show it was a user-custom field)
	Display Label: Last Login:
	Inline Edit: UnCheck
	Importable: No
	(wait, it takes a while)
Fields -> Add
	Data Type: Date/Time
	Name: userlastlogout   (when it is stored, SuiteCRM will call it userlastlogout_c to show it was a user-custom field)
	Display Label: Last Logout:
	Inline Edit: UnCheck
	Importable: No
	(wait, it takes a while)
  1. Add the new fields to the Editview and Detailview of the User module
    Using the SuiteCRM Web Interface: SuiteCRM -> Admin -> Studio -> Users (module) -> Layouts -> EditView

	Sync to Detail view: Check
	Scroll Down to row with "Address Country" taking full width and shrink it to 1/2 width
	Add a row after the "Address Country" row
	Drag and drop "Last Login" field to new row then shrink to 1/2 width
	Drag and drop "Last Logout" field to new row on right side

Using the CLI

cd {root directory of SuiteCRM installation}
cd custom/modules/Users/metadata
nano editviewdefs.php
	Change
		array (
		  0 => 'address_country',
		  1 =>
		  array (
			'name' => 'userlastlogin_c',
			'label' => 'LBL_USERLASTLOGIN',
		  ),
		),
	to
		array (
		  0 => 'address_country',
		  1 =>
		  array (
			'name' => 'userlastlogin_c',
			'label' => 'LBL_USERLASTLOGIN',
			'type' => 'readonly',
		  ),
		),
	and
		array (
		  0 => 'address_country',
		  1 =>
		  array (
			'name' => 'userlastlogout_c',
			'label' => 'LBL_USERLASTLOGOUT',
		  ),
		),
	to
		array (
		  0 => 'address_country',
		  1 =>
		  array (
			'name' => 'userlastlogout_c',
			'label' => 'LBL_USERLASTLOGOUT',
			'type' => 'readonly',
		  ),
		),
  1. Add script to populate the fields on login and logout
    Using the CLI
cd {root directory of SuiteCRM installation}
cd custom/modules/Users
nano 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;
					$bean->userlastlogin_c = $timedate->nowDb();
					$bean->save();
			}
	}

	?>
nano 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;
					$bean->userlastlogout_c = $timedate->nowDb();
					$bean->save();
			}
	}

	?>
chown www-data:www-data record_user_login.php record_user_logout.php
chmod +x record_user_login.php record_user_logout.php
nano 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');
		?>

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

4)Create a Report to show the most recent login and logout by User
Using the SuiteCRM web interface

SuiteCRM -> All -> Reports -> Create Report
	Name: Last Login / Logout
	Module: Users
	Moule Tree: Users (select)
	Fileds: First Name, Last Name, User Name, Last Login, Last Logout

Logout
Login
SuiteCRM - All -> Reports -> Click on Last Login / Logout
It should now show your last login and logout (the ones you just did)

That’s it!

From now on, just go to Reports-> Last Login / Logout and you get a list of the most recent login and logout for each user.

You can infer that if the last login is after the last logout, the User is currently logged in, but that is not always true so only use it as an indicator.

2 Likes

Thanks, once again. You might like to know that I have this post referenced here so one day we can start moving more of these things into our Documentation Site (Technical blog) section.

https://github.com/salesagility/SuiteDocs/issues/206

So, I have the new module setup, the relationship table and the code to populate the tables so I can now create a historical log of the users’ login and logout activity.

Problem is, the subpanel in Users that shows the user activity has a “Create” dropdown button on it that would allow a User to add in additional, nonsense entries.

I cannot figure out how to delete the button in the subpanel and I’d also like to make it so the editview, quick create and full create forms are not availalbe, but I don’t know how to do that. Do you know how I can do that and I’ll add to my instructions and post the complete solution here?

What is “the relationship table”? I searched your previous instructions but I didn’t see the creation of a relationship (or maybe I missed it :blink:)

Have you tried customizing the subpaneldefs of where you want to remove the “create button”?

I did just figure out how to edit the Layoutdefs for the subpanel and have successfully removed the Action buttons from it so that is now good. (Why is it that after working all day on something and then, having no success, posting for help, you then find an answer (to at least part of the question) ?)

I’ll include the subpanel edits in my final documentation when I post here the final solution.

There was no need for a relationship table in the above solution since I was just storing the most recent login/logout in each user’s dbase table and the normal setup for adding fields into the Users module allowed that to happen with the right hooks automatically populating the data.

The solution I wanted and have now figured out (except the disabling of Editview, … to make it read-only) was to store the history of login/logout by user so I could see not only the most recent login/logout but the full history. So for that, I need a one-to-many relationship from Users (one) to a new module I created called useractivity (many) and that is where I have a relationship table. Yes, I know for one-to-many I could do it without a relationship table, but that’s how SuiteCRM sets it up when I use Module Builder and Studio so I went with the flow.

So all that is now remaining is to figure out how to disable any view that would allow C®UD operations by the user (Create,Update,Delete) and only allow Read operations, so I am trying to figure out how to disable Editview, QuickCreate View, … .

I assume if I just delete the views I will create broken links so looking for a better way.

If all you need is a subpanel (not the list view) I suggest dropping the relatinship (so, no new module, and no unwanted edit views etc), and just creating a new subpanel under users with a custom SQL query to fetch your data directly from the database.

This example might help:
https://suitecrm.com/suitecrm/forum/suitecrm-7-0-discussion/19222-subpanels-cases#66668

I am going to save that post for another application so thank you.

In this case, yes, I do want a listview so the administrator can get a list of all users login/logout history.

I just figured out a (backdoor?) way to make everything read-only so I’ll put together the instructions and post.
If I am screwing something up with what I do, PLEASE let me know.

Thank you for your quick replies.

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:

  1. Create a new module to hold the login/logout data
  2. Relate the new module to the Users’ module
  3. Create the script that auto-populates the login/logout data based on user actions
  1. 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
  1. 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
  1. 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.

2 Likes