Users_Record_Model   F
last analyzed

Complexity

Total Complexity 201

Size/Duplication

Total Lines 1111
Duplicated Lines 0 %

Test Coverage

Coverage 46.27%

Importance

Changes 0
Metric Value
wmc 201
eloc 506
dl 0
loc 1111
ccs 223
cts 482
cp 0.4627
rs 2
c 0
b 0
f 0

55 Methods

Rating   Name   Duplication   Size   Complexity  
A getRealId() 0 6 3
A encryptPassword() 0 3 1
A getEditViewUrl() 0 5 1
A getModuleName() 0 7 2
A getRoleDetail() 0 15 3
A getUserGroups() 0 3 1
A cleanCache() 0 7 1
A getPreferenceEditViewUrl() 0 3 1
A getSwitchUsersUrl() 0 3 1
B cleanAttachments() 0 14 8
A getPrivileges() 0 8 2
A isActive() 0 7 2
A getDeleteUrl() 0 5 1
A getGroups() 0 11 3
B getDefaultValue() 0 19 7
A get() 0 6 2
A getActiveAdminUsers() 0 8 2
A getActivityView() 0 3 1
A fakeEncryptPassword() 0 5 1
A saveToDb() 0 18 5
B doLogin() 0 25 7
A doLoginByAuthMethod() 0 8 2
B getHeadLocks() 0 31 9
A getSubordinateUsers() 0 19 5
A getCount() 0 7 2
A deleteUserPermanently() 0 20 3
A delete() 0 14 2
A getInstanceFromUserObject() 0 8 2
A getRole() 0 9 2
A isAdminUser() 0 6 3
A set() 0 8 2
A getInstanceFromFile() 0 12 2
A getLocks() 0 12 4
B verifyPasswordChange() 0 27 7
A getDayStartsPicklistValues() 0 22 4
C validate() 0 22 13
B getRecordListViewLinksLeftSide() 0 63 9
A getBodyLocks() 0 3 1
A getParentRoles() 0 12 3
A getDisplayName() 0 3 1
A getInstanceByName() 0 7 2
C afterSaveToDb() 0 35 16
A getCurrentUserModel() 0 16 4
A getAll() 0 17 3
A getDetailViewUrl() 0 5 1
B getCurrentUserActivityReminderInSeconds() 0 28 8
A verifyPassword() 0 3 1
A getAuthDetail() 0 14 3
A getPreferenceDetailViewUrl() 0 3 1
A getDuplicateRecordUrl() 0 5 1
A getProfiles() 0 15 4
B getValuesForSave() 0 45 11
A getModule() 0 3 1
A updateLabel() 0 15 4
B save() 0 35 10

How to fix   Complexity   

Complex Class

Complex classes like Users_Record_Model often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Users_Record_Model, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
 /* +***********************************************************************************
4
 * The contents of this file are subject to the vtiger CRM Public License Version 1.0
5
 * ("License"); You may not use this file except in compliance with the License
6
 * The Original Code is:  vtiger CRM Open Source
7
 * The Initial Developer of the Original Code is vtiger.
8
 * Portions created by vtiger are Copyright (C) vtiger.
9
 * All Rights Reserved.
10
 * Contributor(s): YetiForce S.A.
11
 * *********************************************************************************** */
12
13
class Users_Record_Model extends Vtiger_Record_Model
14
{
15
	/** {@inheritdoc} */
16
	public function getModule(): Vtiger_Module_Model
17
	{
18
		return $this->module ?? Users_Module_Model::getCleanInstance('Users');
19
	}
20
21
	public function getRealId()
22
	{
23
		if (App\Session::has('baseUserId') && '' != App\Session::get('baseUserId')) {
24
			return App\Session::get('baseUserId');
25
		}
26 5773
		return $this->getId();
27
	}
28 5773
29 1
	/**
30
	 * Gets the value of the key . First it will check whether specified key is a property if not it
31 5773
	 *  will get from normal data attribure from base class.
32
	 *
33
	 * @param string $key - property or key name
34
	 *
35
	 * @return <object>
0 ignored issues
show
Documentation Bug introduced by
The doc comment <object> at position 0 could not be parsed: Unknown type name '<' at position 0 in <object>.
Loading history...
36
	 */
37
	public function get($key)
38
	{
39
		if (property_exists($this, $key)) {
40
			return $this->{$key};
41
		}
42 5789
		return parent::get($key);
43
	}
44 5789
45 12
	/**
46
	 * Sets the value of the key . First it will check whether specified key is a property if not it
47 5783
	 * will set from normal set from base class.
48
	 *
49
	 * @param string $key   - property or key name
50
	 * @param string $value
51
	 */
52
	public function set($key, $value)
53
	{
54
		if (property_exists($this, $key)) {
55
			$this->{$key} = $value;
56
		}
57 5777
		parent::set($key, $value);
58
59 5777
		return $this;
60 2
	}
61
62 5777
	/**
63
	 * Function to get the Detail View url for the record.
64 5777
	 *
65
	 * @return string - Record Detail View Url
66
	 */
67
	public function getDetailViewUrl()
68
	{
69
		$module = $this->getModule();
70
71
		return 'index.php?module=' . $this->getModuleName() . '&parent=Settings&view=' . $module->getDetailViewName() . '&record=' . $this->getId();
72
	}
73
74
	/**
75
	 * Function to get the Detail View url for the Preferences page.
76
	 *
77
	 * @return string - Record Detail View Url
78
	 */
79
	public function getPreferenceDetailViewUrl()
80
	{
81
		return 'index.php?module=' . $this->getModuleName() . '&view=PreferenceDetail&record=' . $this->getId();
82
	}
83
84
	/**
85
	 * Function to get the Edit View url for the record.
86
	 *
87
	 * @return string - Record Edit View Url
88
	 */
89
	public function getEditViewUrl()
90
	{
91
		$module = $this->getModule();
92
93
		return 'index.php?module=' . $this->getModuleName() . '&parent=Settings&view=' . $module->getEditViewName() . '&record=' . $this->getId();
94
	}
95
96
	/**
97
	 * Function to get the Edit View url for the Preferences page.
98
	 *
99
	 * @return string - Record Detail View Url
100
	 */
101
	public function getPreferenceEditViewUrl()
102
	{
103
		return 'index.php?module=' . $this->getModuleName() . '&view=PreferenceEdit&record=' . $this->getId();
104
	}
105
106
	/**
107
	 * Function to get the Delete Action url for the record.
108
	 *
109
	 * @return string - Record Delete Action Url
110
	 */
111
	public function getDeleteUrl()
112
	{
113
		$module = $this->getModule();
114
115
		return 'index.php?module=' . $this->getModuleName() . '&parent=Settings&view=' . $module->getDeleteActionName() . 'User&record=' . $this->getId();
116
	}
117
118
	/**
119
	 * Function to check whether the user is an Admin user.
120
	 *
121
	 * @return bool true/false
122
	 */
123
	public function isAdminUser()
124
	{
125
		if ('on' === $this->get('is_admin') || 1 == $this->get('is_admin')) {
126
			return true;
127
		}
128 6
		return false;
129
	}
130 6
131 6
	/**
132
	 * Function to get the module name.
133
	 *
134
	 * @return string Module Name
135
	 */
136
	public function getModuleName(): string
137
	{
138
		if ($this->getModule()) {
139
			return parent::getModuleName();
140
		}
141 5772
		//get from the class propety module_name
142
		return $this->get('module_name');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->get('module_name') could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
143 5772
	}
144 5772
145 5772
	/**
146
	 * Function to save the user record model.
147
	 *
148
	 * @throws \Exception
149
	 */
150
	public function save()
151
	{
152
		$entityInstance = $this->getModule()->getEntityInstance();
153
		$entityInstance->column_fields['user_name'] = $this->get('user_name');
0 ignored issues
show
Bug Best Practice introduced by
The property column_fields does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
154
		if (!$this->isNew() && empty($this->getPreviousValue())) {
155
			App\Log::info('ERR_NO_DATA');
156 5770
			return false;
157
		}
158 5770
		if ($this->getPreviousValue('user_password')) {
159 5770
			$this->set('date_password_change', date('Y-m-d H:i:s'));
160 5770
			if ($this->isNew() || (false === $this->getPreviousValue('force_password_change') && ($relId = App\User::getCurrentUserRealId()) && $relId !== $this->getId())) {
161 1
				$this->set('force_password_change', 1);
162 1
			}
163
		}
164 5769
		$eventHandler = new App\EventHandler();
165
		$eventHandler->setRecordModel($this);
166
		$eventHandler->setModuleName($this->getModuleName());
167 5769
		if ($this->getHandlerExceptions()) {
168 5769
			$eventHandler->setExceptions($this->getHandlerExceptions());
169 5769
		}
170 5769
		$eventHandler->trigger('UserBeforeSave');
171
		$db = \App\Db::getInstance();
172
		$transaction = $db->beginTransaction();
173 5769
		try {
174 5769
			$this->validate();
175 5769
			$this->saveToDb();
176
			$this->afterSaveToDb();
177 5769
			(new App\BatchMethod(['method' => '\App\PrivilegeUtil::recalculateSharingRulesByUser', 'params' => [$this->getId()]]))->save();
178 5769
			$transaction->commit();
179 5769
		} catch (\Exception $e) {
180 5769
			$transaction->rollBack();
181 5769
			throw $e;
182
		}
183
		$eventHandler->trigger('UserAfterSave');
184
		\App\Cache::clearOpcache();
185
	}
186 5769
187 5769
	/**
188 5769
	 * Save data to the database.
189
	 */
190
	public function saveToDb()
191
	{
192
		$entityInstance = $this->getModule()->getEntityInstance();
193 5769
		$db = \App\Db::getInstance();
194
		$valuesForSave = $this->getValuesForSave();
195 5769
		foreach ($valuesForSave as $tableName => $tableData) {
196 5769
			if ($this->isNew()) {
197 5769
				$db->createCommand()->insert($tableName, [$entityInstance->tab_name_index[$tableName] => $this->getId()] + $tableData)->execute();
198 5769
			} else {
199 5769
				$db->createCommand()->update($tableName, $tableData, [$entityInstance->tab_name_index[$tableName] => $this->getId()])->execute();
200 3
			}
201
		}
202 5766
		if (App\Config::module('Users', 'CHECK_LAST_USERNAME') && isset($valuesForSave['vtiger_users']['user_name'])) {
203
			\App\Db::getInstance('log')->createCommand()->insert('l_#__username_history', [
204
				'user_name' => $valuesForSave['vtiger_users']['user_name'],
205 5769
				'user_id' => $this->getId(),
206 4
				'date' => date('Y-m-d H:i:s'),
207 4
			])->execute();
208
		}
209 5769
	}
210
211
	/**
212
	 * Prepare value to save.
213
	 *
214
	 * @return array
215
	 */
216 5769
	public function getValuesForSave()
217
	{
218
		$forSave = [
219
			'vtiger_users' => [
220 5769
				'date_modified' => date('Y-m-d H:i:s'),
221 5769
				'reminder_next_time' => date('Y-m-d H:i'),
222 5769
				'modified_user_id' => \App\User::getCurrentUserRealId(),
223
			],
224
		];
225 5769
		$moduleModel = $this->getModule();
226 5769
		$saveFields = $moduleModel->getFieldsForSave($this);
227 5769
		if (!$this->isNew()) {
228 5766
			$saveFields = array_intersect($saveFields, array_keys($this->changes));
229
		}
230 5769
		if ($this->has('changeUserPassword') || $this->isNew()) {
231 3
			$saveFields[] = 'user_password';
232
			$saveFields[] = 'force_password_change';
233 5769
		}
234 5769
		foreach ($saveFields as $fieldName) {
235 5769
			$fieldModel = $moduleModel->getFieldByName($fieldName);
236 5769
			if ($fieldModel) {
237 5769
				$uitypeModel = $fieldModel->getUITypeModel();
238 5769
				$value = $this->get($fieldName);
239 5769
				$uitypeModel->validate($value);
240 3
				if (null === $value || '' === $value) {
241 3
					$defaultValue = $fieldModel->getDefaultFieldValue();
242 3
					if ('' !== $defaultValue) {
243 3
						$value = $defaultValue;
244 3
					} elseif ($default = $this->getDefaultValue($fieldName)) {
245
						$value = $default;
246 3
					} else {
247
						$value = $uitypeModel->getDBValue($value, $this);
248 3
					}
249
					$this->set($fieldName, $value);
250 5769
				}
251
				$forSave[$fieldModel->getTableName()][$fieldModel->getColumnName()] = $uitypeModel->convertToSave($value, $this);
252
			}
253 5769
		}
254 3
		if ($this->isNew()) {
255 3
			$this->setId(\App\Db::getInstance()->getUniqueID('vtiger_users'));
256 3
			$now = date('Y-m-d H:i:s');
257 3
			$forSave['vtiger_users']['date_entered'] = $now;
258
			$forSave['vtiger_users']['date_password_change'] = $now;
259 5769
		}
260
		return $forSave;
261
	}
262
263
	/**
264
	 * Get default value.
265
	 *
266
	 * @param string $fieldName
267
	 *
268
	 * @return mixed
269 3
	 */
270
	protected function getDefaultValue($fieldName)
271 3
	{
272 3
		switch ($fieldName) {
273 3
			case 'currency_id':
274 3
				return CurrencyField::getDBCurrencyId();
275 3
			case 'accesskey':
276 3
				return \App\Encryption::generatePassword(20, 'lbn');
277 3
			case 'language':
278 3
				return \App\Language::getLanguage();
279 3
			case 'time_zone':
280 3
				return App\Fields\DateTime::getTimeZone();
281
			case 'theme':
282 3
				return Vtiger_Viewer::DEFAULTTHEME;
283 1
			case 'is_admin':
284
				return 'off';
285 3
			default:
286
				break;
287 3
		}
288
		return false;
289
	}
290
291
	/**
292
	 * Validation of modified data.
293
	 *
294
	 * @throws \App\Exceptions\SaveRecord
295 5769
	 */
296
	public function validate()
297 5769
	{
298 5769
		if ($this->isNew() || false !== $this->getPreviousValue('roleid') || false !== $this->getPreviousValue('user_name')) {
299 3
			$query = (new App\Db\Query())->from('vtiger_users')
300
				->leftJoin('vtiger_user2role', 'vtiger_user2role.userid = vtiger_users.id')
301 5766
				->where(['vtiger_users.user_name' => $this->get('user_name'), 'vtiger_user2role.roleid' => $this->get('roleid')]);
302
			if (false === $this->isNew()) {
303
				$query->andWhere(['<>', 'vtiger_users.id', $this->getId()]);
304 5766
			}
305 1
			if ($query->exists()) {
306
				throw new \App\Exceptions\SaveRecord('ERR_USER_EXISTS||' . $this->get('user_name'), 406);
307
			}
308 5769
		}
309 4
		if (!$this->isNew() && false !== $this->getPreviousValue('user_password') && App\User::getCurrentUserId() === $this->getId()) {
310 4
			if (App\User::checkPreviousPassword($this->getId(), $this->get('user_password'))) {
311 4
				throw new \App\Exceptions\SaveRecord('ERR_PASSWORD_HAS_ALREADY_BEEN_USED', 406);
312 4
			}
313 1
		}
314
		if (!$this->isNew() && 'on' === $this->getPreviousValue('is_admin')) {
315 4
			$isExists = (new App\Db\Query())->from('vtiger_users')->where(['is_admin' => 'on'])->andWhere(['<>', 'id', $this->getId()])->exists();
316
			if (!$isExists) {
317
				throw new \App\Exceptions\SaveRecord('ERR_REMOVING_LAST_ADMIN', 406);
318 4
			}
319 1
		}
320
	}
321 4
322
	/**
323 5769
	 * Function after save to database.
324
	 */
325
	public function afterSaveToDb()
326
	{
327
		$dbCommand = \App\Db::getInstance()->createCommand();
328
		$this->cleanAttachments();
329 5769
		if ($this->isNew() || false !== $this->getPreviousValue('roleid') || false !== $this->getPreviousValue('is_admin')) {
330
			\App\Privilege::setAllUpdater();
331
			if (!$this->isNew() && false !== $this->getPreviousValue('roleid')) {
332
				$dbCommand->delete('vtiger_module_dashboard_widgets', ['userid' => $this->getId()])->execute();
333
			}
334 5769
		}
335
		if (false !== $this->getPreviousValue('user_password') && ($this->isNew() || App\User::getCurrentUserId() === $this->getId())) {
336 5769
			\App\Db::getInstance('log')->createCommand()->insert('l_#__userpass_history', [
337 5769
				'pass' => \App\Encryption::createHash($this->get('user_password')),
338
				'user_id' => $this->getId(),
339
				'date' => date('Y-m-d H:i:s'),
340
			])->execute();
341
			$this->getModule()->saveLoginHistory(strtolower($this->get('user_name')), 'LBL_PASSWORD_CHANGED');
0 ignored issues
show
Bug introduced by
The method saveLoginHistory() does not exist on Vtiger_Module_Model. It seems like you code against a sub-type of Vtiger_Module_Model such as Users_Module_Model. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

341
			$this->getModule()->/** @scrutinizer ignore-call */ saveLoginHistory(strtolower($this->get('user_name')), 'LBL_PASSWORD_CHANGED');
Loading history...
342
		}
343
		if (false !== $this->getPreviousValue('language') && App\User::getCurrentUserRealId() === $this->getId()) {
344 5769
			App\Session::set('language', $this->get('language'));
345
		}
346
		\App\UserPrivilegesFile::createUserPrivilegesfile($this->getId());
347 5769
		\App\UserPrivilegesFile::createUserSharingPrivilegesfile($this->getId());
348 5769
		if (App\Config::performance('ENABLE_CACHING_USERS')) {
349 5769
			\App\PrivilegeFile::createUsersFile();
350
		}
351
		if ($this->getPreviousValue('sync_caldav') || $this->isNew()) {
352 5769
			$dbCommand->update('vtiger_activity', ['dav_status' => 1])->execute();
353 3
		}
354
		if ($this->getPreviousValue('sync_carddav') || $this->isNew()) {
355 5769
			$dbCommand->update('vtiger_contactdetails', ['dav_status' => 1])->execute();
356 3
			$dbCommand->update('vtiger_ossemployees', ['dav_status' => 1])->execute();
357 3
		}
358
		self::cleanCache($this->getId());
359 5769
		$this->updateLabel();
360
	}
361
362
	/**
363
	 * Clear user cache.
364
	 *
365
	 * @param int $userId
366
	 */
367
	public static function cleanCache(int $userId = 0)
368 8
	{
369
		\App\Cache::delete('UserImageById', $userId);
370 8
		\App\Cache::delete('UserIsExists', $userId);
371 8
		\App\Cache::delete('UserIsExistsInactive', $userId);
372
		\App\Cache::delete('NumberOfUsers', '');
373
		\App\Cache::delete('ActiveAdminId', '');
374 8
	}
375 8
376 7
	/**
377
	 * Clear temporary attachments.
378 8
	 */
379 2
	public function cleanAttachments()
380
	{
381
		foreach ($this->getModule()->getFieldsByType(['image', 'multiImage'], true) as $fieldName => $fieldModel) {
382 8
			$currentData = [];
383
			if ($this->get($fieldName) && ($this->isNew() || false !== $this->getPreviousValue($fieldName))) {
384
				$currentData = \App\Fields\File::parse(\App\Json::decode($this->get($fieldName)));
385
				\App\Fields\File::cleanTemp(array_keys($currentData));
386
			}
387
			if ($previousValue = $this->getPreviousValue($fieldName)) {
388
				$previousData = \App\Json::decode($previousValue);
389
				foreach ($previousData as $item) {
390
					if (!isset($currentData[$item['key']])) {
391
						\App\Fields\File::cleanTemp($item['key']);
392
						\App\Fields\File::loadFromInfo(['path' => $item['path']])->delete();
393
					}
394 2
				}
395
			}
396 2
		}
397 2
	}
398 2
399 2
	/**
400
	 * Static Function to get the instance of the User Record model for the current user.
401 2
	 *
402
	 * @return Users_Record_Model instance
403
	 */
404
	protected static $currentUserModels = [];
405
406
	public static function getCurrentUserModel()
407
	{
408
		$currentUser = \App\User::getCurrentUserModel();
409
		if ($currentUser->getId()) {
410
			// Optimization to avoid object creation every-time
411 1
			// Caching is per-id as current_user can get swapped at runtime (ex. workflow)
412
			$currentUserModel = null;
413 1
			if (isset(static::$currentUserModels[$currentUser->getId()])) {
414 1
				$currentUserModel = static::$currentUserModels[$currentUser->getId()];
415 1
			}
416 1
			if (!$currentUserModel) {
417 1
				static::$currentUserModels[$currentUser->getId()] = $currentUserModel = static::getInstanceFromUserObject($currentUser);
418
			}
419 1
			return $currentUserModel;
420 1
		}
421 1
		return new self();
422 1
	}
423 1
424
	/**
425 1
	 * Static Function to get the instance of the User Record model from the given Users object.
426
	 *
427 1
	 * @param mixed $currentUser
428
	 *
429
	 * @return Users_Record_Model instance
430
	 */
431
	public static function getInstanceFromUserObject($currentUser)
432
	{
433
		$userDetails = array_map('\App\Purifier::decodeHtml', $currentUser->getDetails());
434
		$userModel = new self();
435
		foreach ($userDetails as $key => $value) {
436
			$userModel->{$key} = $value;
437
		}
438
		return $userModel->setData($userDetails)->setModule('Users')->setId($currentUser->getId());
439
	}
440
441
	/**
442
	 * Static Function to get the instance of all the User Record models.
443
	 *
444
	 * @param bool $onlyActive
445
	 *
446
	 * @return <Array> - List of Users_Record_Model instances
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Array> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Array>.
Loading history...
447
	 */
448
	public static function getAll($onlyActive = true)
449
	{
450
		$query = (new \App\Db\Query())
451
			->select(['id'])
452
			->from('vtiger_users');
453
		if ($onlyActive) {
454
			$query->where(['status' => 'Active']);
455
		}
456
		$users = [];
457
		$dataReader = $query->createCommand()->query();
458
		while ($userId = $dataReader->readColumn(0)) {
459
			$userModel = self::getInstanceFromUserObject(\App\User::getUserModel($userId));
460
			$users[(int) $userModel->getId()] = $userModel;
461
		}
462
		$dataReader->close();
463
464
		return $users;
465
	}
466
467
	/**
468
	 * Function returns the Subordinate users.
469
	 *
470
	 * @return <Array>
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Array> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Array>.
Loading history...
471
	 */
472 3
	public function getSubordinateUsers()
473
	{
474 3
		$privilegesModel = $this->get('privileges');
475 3
476 3
		if (empty($privilegesModel)) {
477
			$privilegesModel = Users_Privileges_Model::getInstanceById($this->getId());
478 2
			$this->set('privileges', $privilegesModel);
0 ignored issues
show
Bug introduced by
$privilegesModel of type Users_Privileges_Model is incompatible with the type string expected by parameter $value of Users_Record_Model::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

478
			$this->set('privileges', /** @scrutinizer ignore-type */ $privilegesModel);
Loading history...
479 2
		}
480 2
481 2
		$subordinateUsers = [];
482
		$subordinateRoleUsers = $privilegesModel->get('subordinate_roles_users');
483 2
		if ($subordinateRoleUsers) {
484 2
			foreach ($subordinateRoleUsers as $users) {
485
				foreach ($users as $user) {
486 2
					$subordinateUsers[$user] = $privilegesModel->getDisplayName();
487
				}
488
			}
489
		}
490
		return $subordinateUsers;
491
	}
492
493
	/**
494
	 * Function returns the Users Current Role.
495
	 *
496
	 * @return string
497
	 */
498
	public function getRole()
499
	{
500
		$privilegesModel = $this->get('privileges');
501
502
		if (empty($privilegesModel)) {
503
			$privilegesModel = Users_Privileges_Model::getInstanceById($this->getId());
504
			$this->set('privileges', $privilegesModel);
0 ignored issues
show
Bug introduced by
$privilegesModel of type Users_Privileges_Model is incompatible with the type string expected by parameter $value of Users_Record_Model::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

504
			$this->set('privileges', /** @scrutinizer ignore-type */ $privilegesModel);
Loading history...
505
		}
506
		return $privilegesModel->get('roleid');
507
	}
508
509
	public function getRoleDetail()
510
	{
511
		$roleDetail = $this->get('roleDetail');
512
		if (!empty($roleDetail)) {
513
			return $this->get('roleDetail');
514
		}
515
		$privileges = $this->get('privileges');
516
		if (empty($privileges)) {
517
			$privilegesModel = Users_Privileges_Model::getInstanceById($this->getId());
518
			$this->set('privileges', $privilegesModel);
0 ignored issues
show
Bug introduced by
$privilegesModel of type Users_Privileges_Model is incompatible with the type string expected by parameter $value of Users_Record_Model::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

518
			$this->set('privileges', /** @scrutinizer ignore-type */ $privilegesModel);
Loading history...
519
		}
520
		$roleModel = Settings_Roles_Record_Model::getInstanceById($this->get('privileges')->get('roleid'));
521
		$this->set('roleDetail', $roleModel);
522
523
		return $roleModel;
524
	}
525
526
	/**
527
	 * Function returns the Users Current Role.
528
	 *
529
	 * @return string
530
	 */
531
	public function getProfiles()
532
	{
533
		$userProfiles = $this->get('profiles');
534
		if (empty($userProfiles)) {
535
			$privilegesModel = Users_Privileges_Model::getInstanceById($this->getId());
536
			$userProfiles = $privilegesModel->get('profiles');
537
			$this->set('profiles', $userProfiles);
538
		}
539
		$profiles = [];
540
		if (!empty($userProfiles)) {
541
			foreach ($userProfiles as $profile) {
542
				$profiles[$profile] = Settings_Profiles_Record_Model::getInstanceById($profile);
543
			}
544
		}
545
		return $profiles;
546
	}
547
548
	public function getGroups()
549
	{
550
		if (empty($this->get('groups'))) {
551
			if ($this->isAdminUser()) {
552
				$userGroups = App\PrivilegeUtil::getAllGroupsByUser($this->getId());
553
			} else {
554
				$userGroups = $this->getPrivileges()->get('groups');
555
			}
556
			$this->set('groups', $userGroups);
557
		}
558
		return $this->get('groups');
559
	}
560
561
	public function getParentRoles()
562
	{
563
		if (empty($this->get('parentRoles'))) {
564
			if ($this->isAdminUser()) {
565
				$userParentRoles = \App\PrivilegeUtil::getParentRole($this->getRole());
566
			} else {
567
				$privilegesModel = $this->getPrivileges();
568
				$userParentRoles = $privilegesModel->get('parent_roles');
569
			}
570
			$this->set('parentRoles', $userParentRoles);
571
		}
572
		return $this->get('parentRoles');
573
	}
574
575
	/**
576
	 * Function to get privillage model.
577
	 *
578
	 * @return $privillage model
0 ignored issues
show
Documentation Bug introduced by
The doc comment $privillage at position 0 could not be parsed: Unknown type name '$privillage' at position 0 in $privillage.
Loading history...
579
	 */
580
	public function getPrivileges()
581
	{
582
		$privilegesModel = $this->get('privileges');
583
		if (empty($privilegesModel)) {
584
			$privilegesModel = Users_Privileges_Model::getInstanceById($this->getId());
585
			$this->set('privileges', $privilegesModel);
0 ignored issues
show
Bug introduced by
$privilegesModel of type Users_Privileges_Model is incompatible with the type string expected by parameter $value of Users_Record_Model::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

585
			$this->set('privileges', /** @scrutinizer ignore-type */ $privilegesModel);
Loading history...
586
		}
587
		return $privilegesModel;
588
	}
589
590
	/**
591
	 * Function to get user default activity view.
592
	 *
593
	 * @return string
594
	 */
595
	public function getActivityView()
596
	{
597
		return $this->get('activity_view');
598
	}
599
600
	/**
601
	 * Function to get the Day Starts picklist values.
602
	 *
603
	 * @return array
604
	 */
605
	public function getDayStartsPicklistValues()
606
	{
607
		$hourFormats = $this->getField('hour_format')->getPicklistValues();
608
		$startHour = $this->getField('start_hour')->getPicklistValues();
609
		$defaultValues = [
610
			'00:00' => '12:00 AM', '01:00' => '01:00 AM', '02:00' => '02:00 AM', '03:00' => '03:00 AM', '04:00' => '04:00 AM', '05:00' => '05:00 AM',
611
			'06:00' => '06:00 AM', '07:00' => '07:00 AM', '08:00' => '08:00 AM', '09:00' => '09:00 AM', '10:00' => '10:00 AM', '11:00' => '11:00 AM', '12:00' => '12:00 PM',
612
			'13:00' => '01:00 PM', '14:00' => '02:00 PM', '15:00' => '03:00 PM', '16:00' => '04:00 PM', '17:00' => '05:00 PM', '18:00' => '06:00 PM', '19:00' => '07:00 PM',
613
			'20:00' => '08:00 PM', '21:00' => '09:00 PM', '22:00' => '10:00 PM', '23:00' => '11:00 PM',
614
		];
615
		$picklistDependencyData = [];
616
		foreach ($hourFormats as $value) {
617
			if (24 == $value) {
618
				$picklistDependencyData['hour_format'][$value]['start_hour'] = $startHour;
619
			} else {
620
				$picklistDependencyData['hour_format'][$value]['start_hour'] = $defaultValues;
621
			}
622
		}
623
		if (empty($picklistDependencyData['hour_format']['__DEFAULT__']['start_hour'])) {
624
			$picklistDependencyData['hour_format']['__DEFAULT__']['start_hour'] = $defaultValues;
625
		}
626
		return $picklistDependencyData;
627
	}
628
629
	/**
630
	 * Function to get user groups.
631
	 *
632
	 * @param int $userId
633
	 *
634
	 * @return array - groupId's
635
	 */
636
	public static function getUserGroups($userId)
637
	{
638
		return App\PrivilegeUtil::getUserGroups($userId);
639
	}
640
641
	/**
642
	 * Function returns the users activity reminder in seconds.
643
	 *
644
	 * @return string
645
	 */
646
647
	/**
648
	 * Function returns the users activity reminder in seconds.
649
	 *
650
	 * @return string
651
	 */
652
	public function getCurrentUserActivityReminderInSeconds()
653
	{
654
		$activityReminder = $this->reminder_interval;
0 ignored issues
show
Bug Best Practice introduced by
The property reminder_interval does not exist on Users_Record_Model. Did you maybe forget to declare it?
Loading history...
655
		$activityReminderInSeconds = '';
656
		if ('None' != $activityReminder) {
657
			preg_match('/([0-9]+)[\s]([a-zA-Z]+)/', $activityReminder, $matches);
658
			if ($matches) {
659
				$number = $matches[1];
660
				$string = $matches[2];
661
				if ($string) {
662
					switch ($string) {
663
						case 'Minute':
664
						case 'Minutes':
665
							$activityReminderInSeconds = $number * 60;
666
							break;
667
						case 'Hour':
668
							$activityReminderInSeconds = $number * 60 * 60;
669
							break;
670
						case 'Day':
671
							$activityReminderInSeconds = $number * 60 * 60 * 24;
672
							break;
673
						default:
674
							$activityReminderInSeconds = '';
675
					}
676
				}
677
			}
678
		}
679
		return $activityReminderInSeconds;
680
	}
681
682
	/** {@inheritdoc} */
683
	public function getRecordListViewLinksLeftSide()
684
	{
685
		$links = $recordLinks = [];
686
		if ($this->isViewable()) {
687
			$recordLinks['LBL_SHOW_COMPLETE_DETAILS'] = [
688
				'linktype' => 'LIST_VIEW_ACTIONS_RECORD_LEFT_SIDE',
689
				'linklabel' => 'LBL_SHOW_COMPLETE_DETAILS',
690
				'linkurl' => $this->getFullDetailViewUrl(),
691
				'linkicon' => 'fas fa-th-list',
692
				'linkclass' => 'btn-sm btn-default',
693
				'linkhref' => true,
694
			];
695
		}
696 2
		if ($this->isEditable() && $this->isActive()) {
697
			$recordLinks['LBL_EDIT'] = [
698 2
				'linktype' => 'LIST_VIEW_ACTIONS_RECORD_LEFT_SIDE',
699
				'linklabel' => 'LBL_EDIT',
700
				'linkurl' => $this->getEditViewUrl(),
701
				'linkicon' => 'yfi yfi-full-editing-view',
702
				'linkclass' => 'btn-sm btn-default',
703 2
				'linkhref' => true,
704 2
			];
705 2
			if ($this->isPermitted('DuplicateRecord')) {
706
				$recordLinks['LBL_DUPLICATE'] = [
707 2
					'linktype' => 'LIST_VIEW_ACTIONS_RECORD_LEFT_SIDE',
708
					'linklabel' => 'LBL_DUPLICATE',
709
					'linkurl' => $this->getDuplicateRecordUrl(),
710
					'linkicon' => 'fas fa-clone',
711
					'linkclass' => 'btn-outline-dark btn-sm',
712
					'title' => \App\Language::translate('LBL_DUPLICATE_RECORD'),
713
				];
714
			}
715
		}
716
		if ($this->privilegeToDelete()) {
717
			if ($this->isActive()) {
718
				$recordLinks['LBL_DELETE_RECORD_COMPLETELY'] = [
719
					'linktype' => 'LIST_VIEW_ACTIONS_RECORD_LEFT_SIDE',
720
					'linklabel' => 'LBL_DELETE_RECORD_COMPLETELY',
721
					'linkicon' => 'fas fa-eraser',
722
					'linkclass' => 'btn-sm btn-primary deleteRecordButton',
723
				];
724
			} else {
725
				$recordLinks['LBL_DELETE_USER_PERMANENTLY'] = [
726
					'linktype' => 'LIST_VIEW_ACTIONS_RECORD_LEFT_SIDE',
727
					'linklabel' => 'LBL_DELETE_USER_PERMANENTLY',
728
					'linkurl' => 'javascript:Settings_Users_List_Js.deleteUserPermanently(' . $this->getId() . ', event)',
729
					'linkicon' => 'fas fa-eraser',
730
					'linkclass' => 'btn-sm btn-dark',
731
				];
732
			}
733
		}
734
		foreach ($recordLinks as $key => $recordLink) {
735
			$links[$key] = Vtiger_Link_Model::getInstanceFromValues($recordLink);
736
		}
737
		if (!$this->isActive()) {
738
			$links['BUTTONS'][] = Vtiger_Link_Model::getInstanceFromValues([
739
				'linklabel' => 'LBL_RESTORE',
740
				'linkicon' => 'fas fa-sync-alt',
741
				'linkclass' => 'btn btn-sm btn-light',
742
				'linkurl' => 'javascript:Settings_Users_List_Js.restoreUser(' . $this->getId() . ', event)',
743
			]);
744
		}
745
		return \App\Utils::changeSequence($links, App\Config::module($this->getModuleName(), 'recordListViewButtonSequence', []));
746
	}
747
748 2
	/** Checking if the record is active.
749
	 *
750 2
	 * @return bool
751 2
	 */
752
	public function isActive(): bool
753 2
	{
754
		$active = false;
755 2
		if ('Active' === $this->get('status')) {
756 2
			$active = true;
757 2
		}
758 2
		return $active;
759 2
	}
760 2
761
	/**
762 2
	 * Function to get the users count.
763 2
	 *
764 2
	 * @param bool $onlyActive - If true it returns count of only acive users else only inactive users
765
	 *
766 2
	 * @return int number of users
767
	 */
768
	public static function getCount($onlyActive = false)
769
	{
770
		$query = (new App\Db\Query())->from('vtiger_users');
771
		if ($onlyActive) {
772
			$query->where(['status' => 'Active']);
773 2
		}
774
		return $query->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->count() also could return the type string which is incompatible with the documented return type integer.
Loading history...
775 2
	}
776
777
	/**
778
	 * Funtion to get Duplicate Record Url.
779
	 *
780
	 * @return string
781
	 */
782
	public function getDuplicateRecordUrl()
783
	{
784
		$module = $this->getModule();
785
786
		return 'index.php?module=' . $this->getModuleName() . '&parent=Settings&view=' . $module->getEditViewName() . '&record=' . $this->getId() . '&isDuplicate=true';
787
	}
788
789
	/**
790
	 * Function to get instance of user model by name.
791
	 *
792
	 * @param string $userName
793
	 *
794
	 * @return Users_Record_Model
795
	 */
796
	public static function getInstanceByName($userName)
797
	{
798
		$id = (new \App\Db\Query())->select(['id'])->from('vtiger_users')->where(['or', ['user_name' => $userName], ['user_name' => strtolower($userName)]])->scalar();
799
		if ($id) {
800
			return self::getInstanceById($id, 'Users');
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $recordId of Vtiger_Record_Model::getInstanceById(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

800
			return self::getInstanceById(/** @scrutinizer ignore-type */ $id, 'Users');
Loading history...
801
		}
802
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type Users_Record_Model.
Loading history...
803
	}
804
805
	/**
806
	 * Function to get instance of user model by id from file.
807
	 *
808
	 * @param int $userId
809
	 *
810
	 * @return Users_Record_Model
811
	 */
812
	public static function getInstanceFromFile($userId)
813
	{
814
		if (empty($userId)) {
815
			\App\Log::error('No user id: ' . $userId);
816
817
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type Users_Record_Model.
Loading history...
818
		}
819
		$valueMap = \App\User::getPrivilegesFile($userId);
820
		$instance = new self();
821
		$instance->setData($valueMap['user_info']);
822
823
		return $instance;
824
	}
825
826
	/**
827
	 * Function to delete the current Record Model.
828
	 */
829
	public function delete()
830
	{
831
		$db = \App\Db::getInstance();
832
		$transaction = $db->beginTransaction();
833
		try {
834
			$db->createCommand()->update('vtiger_users', [
835
				'status' => 'Inactive',
836
				'date_modified' => date('Y-m-d H:i:s'),
837
				'modified_user_id' => App\User::getCurrentUserRealId(),
838
			], ['id' => $this->getId()])->execute();
839
			$transaction->commit();
840
		} catch (\Exception $e) {
841
			$transaction->rollBack();
842
			throw $e;
843
		}
844
	}
845
846
	public function getActiveAdminUsers()
847
	{
848
		$dataReader = (new App\Db\Query())->select(['id'])->from('vtiger_users')->where(['status' => 'Active', 'is_admin' => 'on'])->createCommand()->query();
849
		$users = [];
850
		while ($id = $dataReader->readColumn(0)) {
851
			$users[$id] = self::getInstanceFromUserObject(\App\User::getUserModel($id));
852
		}
853
		return $users;
854
	}
855
856
	/**
857
	 * Function to delete user permanemtly from CRM and
858
	 * assign all record which are assigned to that user
859
	 * and not transfered to other user to other user.
860
	 *
861
	 * @param int $userId
862
	 * @param int $newOwnerId
863
	 */
864
	public static function deleteUserPermanently($userId, $newOwnerId)
865
	{
866
		$dbCommand = \App\Db::getInstance()->createCommand();
867 3
		$dbCommand->update('vtiger_crmentity', ['smcreatorid' => $newOwnerId, 'smownerid' => $newOwnerId], ['smcreatorid' => $userId, 'setype' => 'ModComments'])->execute();
868
		//update history details in vtiger_modtracker_basic
869 3
		$dbCommand->update('vtiger_modtracker_basic', ['whodid' => $newOwnerId], ['whodid' => $userId])->execute();
870
		//update comments details in vtiger_modcomments
871
		$dbCommand->update('vtiger_modcomments', ['userid' => $newOwnerId], ['userid' => $userId])->execute();
872
		$dbCommand->delete('vtiger_users', ['id' => $userId])->execute();
873
		$dbCommand->delete('vtiger_module_dashboard_widgets', ['userid' => $userId])->execute();
874
		\App\PrivilegeUtil::deleteRelatedSharingRules($userId, 'Users');
875
		$fileName = "user_privileges/sharing_privileges_{$userId}.php";
876
		if (file_exists($fileName)) {
877
			unlink($fileName);
878
		}
879 2
		$fileName = "user_privileges/user_privileges_{$userId}.php";
880
		if (file_exists($fileName)) {
881 2
			unlink($fileName);
882
		}
883
		self::cleanCache($userId);
884
	}
885
886
	/**
887
	 * Function to get the Display Name for the record.
888
	 *
889
	 * @return string - Entity Display Name for the record
890
	 */
891
	public function getDisplayName()
892
	{
893
		return \App\Purifier::encodeHtml(\vtlib\Deprecated::getFullNameFromArray($this->getModuleName(), $this->getData()));
894
	}
895
896
	public function getSwitchUsersUrl()
897
	{
898
		return 'index.php?module=' . $this->getModuleName() . '&view=SwitchUsers&id=' . $this->getId();
899
	}
900
901
	public function getLocks()
902
	{
903 2
		if ($this->has('locks')) {
904
			return $this->get('locks');
905 2
		}
906 2
		require 'user_privileges/locks.php';
907 2
		if ($this->getId() && \array_key_exists($this->getId(), $locks)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $locks seems to be never defined.
Loading history...
908
			$this->set('locks', $locks[$this->getId()]);
909
910
			return $locks[$this->getId()];
911
		}
912
		return [];
913 2
	}
914 2
915 2
	/**
916
	 * Get locks content.
917
	 *
918
	 * @return string
919
	 */
920 2
	public function getBodyLocks(): string
921 2
	{
922
		return \App\Utils::getLocksContent($this->getLocks());
923
	}
924 2
925 2
	public function getHeadLocks()
926
	{
927 2
		$return = '';
928
		foreach ($this->getLocks() as $lock) {
929
			switch ($lock) {
930
				case 'copy':
931
					$return = ' document.oncopy = lockFunction;';
932
					break;
933
				case 'cut':
934
					$return = ' document.oncut = lockFunction;';
935
					break;
936
				case 'paste':
937
					$return = ' document.onpaste = lockFunction;';
938
					break;
939
				case 'contextmenu':
940
					$return = ' document.oncontextmenu = function(event) {if(event.button==2){return false;}}; document.oncontextmenu = lockFunction;';
941 2
					break;
942
				case 'selectstart':
943 2
					$return = ' document.onselectstart = lockFunction; document.onselect = lockFunction;';
944 2
					break;
945
				case 'drag':
946
					$return = ' document.ondragstart = lockFunction; document.ondrag = lockFunction;';
947
					break;
948 2
				default:
949
					break;
950
			}
951
		}
952
		if ($return) {
953
			$return = 'function lockFunction() {return false;}' . $return;
954
		}
955
		return $return;
956 2
	}
957
958 2
	/**
959 1
	 * Encrypt user password.
960
	 *
961 1
	 * @param string $password User password
962 1
	 *
963 1
	 * @return string Encrypted password
964 1
	 */
965
	public function encryptPassword($password)
966 1
	{
967 1
		return password_hash($password, PASSWORD_BCRYPT, ['cost' => App\Config::security('USER_ENCRYPT_PASSWORD_COST')]);
968
	}
969 1
970
	/**
971
	 * Verify user password.
972
	 *
973
	 * @param string $password
974
	 *
975
	 * @return bool
976
	 */
977
	public function verifyPassword($password)
978
	{
979
		return password_verify($password, $this->get('user_password'));
980
	}
981
982
	/**
983
	 * Slower logon for security purposes.
984
	 *
985
	 * @param string $password
986
	 */
987
	public function fakeEncryptPassword($password)
988
	{
989
		$this->getAuthDetail();
990
		\Settings_Password_Record_Model::getUserPassConfig();
991
		password_verify($password, $this->encryptPassword($password));
992
	}
993
994
	/**
995
	 * The function to log on to the system.
996
	 *
997
	 * @param string $password The password of the user to authenticate
998
	 *
999
	 * @return bool true if the user is authenticated, false otherwise
1000
	 */
1001
	public function doLogin($password)
1002
	{
1003
		$userName = $this->get('user_name');
1004
		$row = (new App\Db\Query())->select(['id', 'deleted'])->from('vtiger_users')->where(['or', ['user_name' => $userName], ['user_name' => strtolower($userName)]])->limit(1)->one();
1005
		if (!$row || 0 !== (int) $row['deleted']) {
1006
			$this->fakeEncryptPassword($password);
1007
			\App\Log::info('User not found: ' . $userName, 'UserAuthentication');
1008
			return false;
1009
		}
1010
		$this->set('id', $row['id']);
1011
		$userRecordModel = static::getInstanceFromFile($row['id']);
1012
		if ('Active' !== $userRecordModel->get('status')) {
1013
			\App\Log::info('Inactive user :' . $userName, 'UserAuthentication');
1014
			return false;
1015
		}
1016
		$result = $userRecordModel->doLoginByAuthMethod($password);
1017
		if (null !== $result) {
1018
			return $result;
1019
		}
1020
		if (null === $result && $userRecordModel->verifyPassword($password)) {
1021
			\App\Session::set('UserAuthMethod', 'PASSWORD');
1022
			return true;
1023
		}
1024
		\App\Log::info('Invalid password. User: ' . $userName, 'UserAuthentication');
1025
		return false;
1026
	}
1027
1028
	/**
1029
	 * User authorization based on authorization methods.
1030
	 *
1031
	 * @param string $password
1032
	 *
1033
	 * @return bool|null
1034
	 */
1035
	protected function doLoginByAuthMethod($password)
1036
	{
1037
		$auth = $this->getAuthDetail();
1038
		if ('true' === $auth['ldap']['active']) {
1039
			$authMethod = new Users_Ldap_Authmethod($this);
1040
			return $authMethod->process($auth['ldap'], $password);
1041
		}
1042
		return null;
1043
	}
1044
1045
	/**
1046
	 * Get authorization detail.
1047
	 *
1048
	 * @return array
1049
	 */
1050
	protected function getAuthDetail()
1051
	{
1052
		if (\App\Cache::has('getAuthMethods', 'config')) {
1053
			return \App\Cache::get('getAuthMethods', 'config');
1054
		}
1055
		$dataReader = (new \App\Db\Query())->from('yetiforce_auth')->createCommand()->query();
1056
		$auth = [];
1057
		while ($row = $dataReader->read()) {
1058
			$auth[$row['type']][$row['param']] = $row['value'];
1059
		}
1060
		$dataReader->close();
1061
		\App\Cache::save('getAuthMethods', 'config', $auth);
1062
1063
		return $auth;
1064
	}
1065
1066
	/**
1067
	 * Verify  password change.
1068
	 *
1069
	 * @param App\User $userModel
1070
	 *
1071
	 * @return void
1072
	 */
1073
	public function verifyPasswordChange(App\User $userModel): void
1074
	{
1075
		$passConfig = \Settings_Password_Record_Model::getUserPassConfig();
1076
		$time = (int) $passConfig['change_time'];
1077
		if (1 === (int) $userModel->getDetail('force_password_change')) {
1078
			\App\Session::set('ShowUserPasswordChange', 2);
1079
			\App\Process::addEvent([
1080
				'name' => 'ShowUserPasswordChange',
1081
				'priority' => 3,
1082
				'type' => 'modal',
1083
				'url' => 'index.php?module=Users&view=PasswordModal&mode=change&record=' . $userModel->getId(),
1084
			]);
1085
			return;
1086
		}
1087
		$lastChange = strtotime($userModel->getDetail('date_password_change'));
1088
		if (0 !== $time && (!$lastChange || strtotime("-$time day") > $lastChange)) {
1089
			$time += (int) $passConfig['lock_time'];
1090
			if (!$lastChange || strtotime("-$time day") > $lastChange) {
1091
				\App\Session::set('ShowUserPasswordChange', 2);
1092
			} else {
1093
				\App\Session::set('ShowUserPasswordChange', 1);
1094
			}
1095
			\App\Process::addEvent([
1096
				'name' => 'ShowUserPasswordChange',
1097
				'priority' => 3,
1098
				'type' => 'modal',
1099
				'url' => 'index.php?module=Users&view=PasswordModal&mode=change&record=' . $userModel->getId(),
1100
			]);
1101
		}
1102
	}
1103
1104
	/**
1105
	 * Update record label.
1106
	 *
1107
	 * @return void
1108
	 */
1109
	public function updateLabel(): void
1110
	{
1111
		$metaInfo = \App\Module::getEntityInfo($this->getModuleName());
1112
		$labelName = [];
1113
		foreach ($metaInfo['fieldnameArr'] as $columnName) {
1114
			$fieldModel = $this->getModule()->getFieldByColumn($columnName);
1115
			$labelName[] = $fieldModel->getDisplayValue($this->get($fieldModel->getName()), $this->getId(), $this, true);
1116
		}
1117
		$label = \App\TextUtils::textTruncate(implode($metaInfo['separator'] ?? ' ', $labelName), 250, false);
1118
		if (!empty($label)) {
1119
			$db = \App\Db::getInstance();
1120
			if (!(new \App\Db\Query())->from('u_#__users_labels')->where(['id' => $this->getId()])->exists()) {
1121
				$db->createCommand()->insert('u_#__users_labels', ['id' => $this->getId(), 'label' => $label])->execute();
1122
			} else {
1123
				$db->createCommand()->update('u_#__users_labels', ['label' => $label], ['id' => $this->getId()])->execute();
1124
			}
1125
		}
1126
	}
1127
}
1128