Test Setup Failed
Pull Request — developer (#17272)
by Arkadiusz
25:44
created

Vtiger_Fields_Action::validateForField()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 14
ccs 0
cts 0
cp 0
rs 9.9332
c 0
b 0
f 0
cc 4
nc 2
nop 1
crap 20
1
<?php
2
3
/**
4
 * Fields Action Class.
5
 *
6
 * @package Action
7
 *
8
 * @copyright YetiForce S.A.
9
 * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
10
 * @author    Mariusz Krzaczkowski <[email protected]>
11
 * @author    Radosław Skrzypczak <[email protected]>
12
 */
13
class Vtiger_Fields_Action extends \App\Controller\Action
14
{
15
	use \App\Controller\ExposeMethod;
16
17
	/**
18
	 * Field model instance.
19
	 *
20
	 * @var Vtiger_Field_Model
21
	 */
22
	protected $fieldModel;
23
24
	/** {@inheritDoc} */
25
	public function __construct()
26
	{
27
		parent::__construct();
28
		$this->exposeMethod('getOwners');
29
		$this->exposeMethod('getReference');
30
		$this->exposeMethod('getUserRole');
31
		$this->exposeMethod('findAddress');
32
		$this->exposeMethod('validateForField');
33
		$this->exposeMethod('validateByMode');
34
		$this->exposeMethod('verifyPhoneNumber');
35
		$this->exposeMethod('changeFavoriteOwner');
36
		$this->exposeMethod('validateFile');
37
		$this->exposeMethod('getCopyValue');
38
	}
39
40
	/**
41
	 * Function to check permission.
42
	 *
43
	 * @param \App\Request $request
44
	 *
45
	 * @throws \App\Exceptions\NoPermitted
46
	 */
47
	public function checkPermission(App\Request $request)
48
	{
49
		$mode = $request->getMode();
50
		if ('verifyPhoneNumber' !== $mode) {
51
			$userPrivilegesModel = Users_Privileges_Model::getCurrentUserPrivilegesModel();
52
			if (!$userPrivilegesModel->hasModulePermission($request->getModule())) {
0 ignored issues
show
Bug introduced by
$request->getModule() of type string is incompatible with the type integer expected by parameter $mixed of Users_Privileges_Model::hasModulePermission(). ( Ignorable by Annotation )

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

52
			if (!$userPrivilegesModel->hasModulePermission(/** @scrutinizer ignore-type */ $request->getModule())) {
Loading history...
53
				throw new \App\Exceptions\NoPermitted('LBL_PERMISSION_DENIED', 406);
54
			}
55
			if ('getReference' !== $mode && !\App\Privilege::isPermitted($request->getModule(), 'EditView')) {
56
				throw new \App\Exceptions\NoPermitted('LBL_PERMISSION_DENIED', 406);
57
			}
58
		}
59
		if ('findAddress' !== $mode && 'getReference' !== $mode && 'validateByMode' !== $mode && 'getCopyValue' !== $mode) {
60
			$this->fieldModel = Vtiger_Module_Model::getInstance($request->getModule())->getFieldByName($request->getByType('fieldName', 2));
0 ignored issues
show
Documentation Bug introduced by
It seems like Vtiger_Module_Model::get...ByType('fieldName', 2)) can also be of type false. However, the property $fieldModel is declared as type Vtiger_Field_Model. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
61
			if (!$this->fieldModel || !$this->fieldModel->isViewable()) {
62
				throw new \App\Exceptions\NoPermitted('ERR_NO_PERMISSIONS_TO_FIELD', 406);
63
			}
64
		}
65
	}
66
67
	/**
68
	 * Get owners for ajax owners list.
69
	 *
70
	 * @param \App\Request $request
71
	 *
72
	 * @throws \App\Exceptions\NoPermitted
73
	 */
74
	public function getOwners(App\Request $request): void
75
	{
76
		if (!App\Config::performance('SEARCH_OWNERS_BY_AJAX')) {
77
			throw new \App\Exceptions\NoPermitted('LBL_PERMISSION_DENIED', 406);
78
		}
79
80
		if ('owner' !== $this->fieldModel->getFieldDataType() && 'sharedOwner' !== $this->fieldModel->getFieldDataType()
81
			&& 'userCreator' !== $this->fieldModel->getFieldDataType()) {
82
			throw new \App\Exceptions\NoPermitted('ERR_NO_PERMISSIONS_TO_FIELD');
83
		}
84
85
		$moduleName = $request->getModule();
86
		$searchValue = $request->getByType('value', 'Text');
87
		if ($request->has('result')) {
88
			$result = $request->getArray('result', 'Standard');
89
		} else {
90
			$result = ['users', 'groups'];
91
		}
92
		$response = new Vtiger_Response();
93
		if (empty($searchValue)) {
94
			$response->setResult(['items' => []]);
95
		} else {
96
			$owner = App\Fields\Owner::getInstance($moduleName);
97
			$owner->find($searchValue);
98
99
			$data = [];
100
			if (\in_array('users', $result)) {
101
				$users = $owner->getAccessibleUsers('', 'owner');
102
				if (!empty($users)) {
103
					$data[] = ['name' => \App\Language::translate('LBL_USERS'), 'type' => 'optgroup'];
104
					foreach ($users as $key => $value) {
105
						$data[] = ['id' => $key, 'name' => $value];
106
					}
107
				}
108
			}
109
			if (\in_array('groups', $result)) {
110
				$grup = $owner->getAccessibleGroups('', 'owner', true);
111
				if (!empty($grup)) {
112
					$data[] = ['name' => \App\Language::translate('LBL_GROUPS'), 'type' => 'optgroup'];
113
					foreach ($grup as $key => $value) {
114
						$data[] = ['id' => $key, 'name' => $value];
115
					}
116
				}
117
			}
118
			$response->setResult(['items' => $data]);
119
		}
120
		$response->emit();
121
	}
122
123
	/**
124
	 * Search user roles.
125
	 *
126
	 * @param \App\Request $request
127
	 *
128
	 * @throws \App\Exceptions\NoPermitted
129
	 */
130
	public function getUserRole(App\Request $request): void
131
	{
132
		if (!App\Config::performance('SEARCH_ROLES_BY_AJAX')) {
133
			throw new \App\Exceptions\NoPermitted('LBL_PERMISSION_DENIED', 406);
134
		}
135
		if ('userRole' !== $this->fieldModel->getFieldDataType()) {
136
			throw new \App\Exceptions\NoPermitted('ERR_NO_PERMISSIONS_TO_FIELD');
137
		}
138
		$searchValue = $request->getByType('value', 'Text');
139
		$response = new Vtiger_Response();
140
		if (empty($searchValue)) {
141
			$response->setResult(['items' => []]);
142
		} else {
143
			$rows = $this->fieldModel->getUITypeModel()->getSearchValues($searchValue);
0 ignored issues
show
Bug introduced by
The method getSearchValues() does not exist on Vtiger_Base_UIType. It seems like you code against a sub-type of Vtiger_Base_UIType such as Vtiger_UserRole_UIType. ( Ignorable by Annotation )

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

143
			$rows = $this->fieldModel->getUITypeModel()->/** @scrutinizer ignore-call */ getSearchValues($searchValue);
Loading history...
144
			foreach ($rows as $key => $value) {
145
				$data[] = ['id' => $key, 'name' => $value];
146
			}
147
			$response->setResult(['items' => $data]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data seems to be defined by a foreach iteration on line 144. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
148
		}
149
		$response->emit();
150
	}
151
152
	/**
153
	 * @param \App\Request $request
154
	 *
155
	 * @throws \App\Exceptions\NoPermitted
156
	 */
157
	public function getReference(App\Request $request): void
158
	{
159
		if ($request->has('fieldName')) {
160
			$fieldModel = Vtiger_Module_Model::getInstance($request->getModule())->getFieldByName($request->getByType('fieldName', 2));
161
			if (empty($fieldModel) || !$fieldModel->isActiveField() || !$fieldModel->isViewEnabled()) {
162
				throw new \App\Exceptions\NoPermitted('ERR_NO_PERMISSIONS_TO_FIELD', 406);
163
			}
164
			$searchInModule = $fieldModel->getReferenceList();
165
		} elseif ($request->has('relationId') && ($relation = \App\Relation::getById($request->getInteger('relationId'))) && $relation['related_modulename'] === $request->getModule()) {
166
			if (\in_array($relation['related_modulename'], ['getDependentsList', 'getManyToMany', 'getRelatedList'])) {
167
				$searchInModule = $relation['related_modulename'];
168
			} else {
169
				$typeRelationModel = \Vtiger_Relation_Model::getInstanceById($relation['relation_id'])->getTypeRelationModel();
170
				if (method_exists($typeRelationModel, 'getConfigAdvancedConditionsByColumns')) {
171
					$searchInModule = $typeRelationModel->getConfigAdvancedConditionsByColumns()['relatedModules'] ?? $relation['related_modulename'];
172
				} else {
173
					$searchInModule = $relation['related_modulename'];
174
				}
175
			}
176
		} else {
177
			throw new \App\Exceptions\NoPermitted('ERR_NO_PERMISSIONS_TO_FIELD', 406);
178
		}
179
		$response = new Vtiger_Response();
180
		$limit = \App\Config::search('GLOBAL_SEARCH_AUTOCOMPLETE_LIMIT');
181
		$searchValue = \App\RecordSearch::getSearchField()->getUITypeModel()->getDbConditionBuilderValue($request->getByType('value', \App\Purifier::TEXT), '');
182
		$data = $modules = [];
183
		if ('Users' === $searchInModule[0]) {
184
			foreach (\App\User::searchByLabel($searchValue, $limit) as $row) {
0 ignored issues
show
Unused Code introduced by
The call to App\User::searchByLabel() has too many arguments starting with $limit. ( Ignorable by Annotation )

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

184
			foreach (\App\User::/** @scrutinizer ignore-call */ searchByLabel($searchValue, $limit) as $row) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
185
				$modules['Users'][] = ['crmid' => $row['id'], 'label' => $row['label']];
186
			}
187
		} else {
188
			$rows = (new \App\RecordSearch($searchValue, $searchInModule, $limit))->setMode(\App\RecordSearch::LABEL_MODE)->search();
189
			foreach ($rows as $row) {
190
				$modules[$row['setype']][] = $row;
191
			}
192
		}
193
194
		foreach ($modules as $moduleName => $rows) {
195
			$data[] = ['name' => App\Language::translateSingleMod($moduleName, $moduleName), 'type' => 'optgroup'];
196
			foreach ($rows as $row) {
197
				$data[] = ['id' => $row['crmid'], 'name' => \App\Purifier::encodeHtml($row['label'])];
198
			}
199
		}
200
		$response->setResult(['items' => $data]);
201
		$response->emit();
202
	}
203
204
	/**
205
	 * Verify phone number.
206
	 *
207
	 * @param \App\Request $request
208
	 *
209
	 * @throws \App\Exceptions\NoPermitted
210
	 */
211
	public function verifyPhoneNumber(App\Request $request): void
212
	{
213
		if ('phone' !== $this->fieldModel->getFieldDataType()) {
214
			throw new \App\Exceptions\NoPermitted('ERR_NO_PERMISSIONS_TO_FIELD');
215
		}
216
		$response = new Vtiger_Response();
217
		$data = ['isValidNumber' => false];
218
		if ($request->isEmpty('phoneCountry', true)) {
219
			$data['message'] = \App\Language::translate('LBL_NO_PHONE_COUNTRY');
220
		}
221
		if (empty($data['message'])) {
222
			try {
223
				$data = App\Fields\Phone::verifyNumber($request->getByType('phoneNumber', 'Text'), $request->getByType('phoneCountry', 1));
224
			} catch (\App\Exceptions\FieldException $e) {
225
				$data = ['isValidNumber' => false];
226
			}
227
		}
228
		if (!$data['isValidNumber'] && empty($data['message'])) {
229
			$data['message'] = \App\Language::translate('LBL_INVALID_PHONE_NUMBER');
230
		}
231
		$response->setResult($data);
232
		$response->emit();
233
	}
234
235
	/**
236
	 * Find address.
237
	 *
238
	 * @param \App\Request $request
239
	 */
240
	public function findAddress(App\Request $request): void
241
	{
242
		$instance = \App\Map\Address::getInstance($request->getByType('type'));
243
		$response = new Vtiger_Response();
244
		if ($instance) {
0 ignored issues
show
introduced by
$instance is of type App\Map\Address\Base, thus it always evaluated to true.
Loading history...
245
			$response->setResult($instance->find($request->getByType('value', 'Text')));
246
		}
247
		$response->emit();
248
	}
249
250
	/**
251
	 * Change favorite owner state.
252
	 *
253
	 * @param \App\Request $request
254
	 *
255
	 * @throws \App\Exceptions\IllegalValue
256
	 * @throws \App\Exceptions\NoPermitted
257
	 * @throws \yii\db\Exception
258
	 */
259
	public function changeFavoriteOwner(App\Request $request): void
260
	{
261
		if (!App\Config::module('Users', 'FAVORITE_OWNERS') || (\App\User::getCurrentUserRealId() !== \App\User::getCurrentUserId())) {
262
			throw new \App\Exceptions\NoPermitted('LBL_PERMISSION_DENIED', 406);
263
		}
264
		$moduleName = $request->getModule();
265
		$ownerField = \App\Fields\Owner::getInstance($moduleName);
266
		$result = $ownerField->changeFavorites($this->fieldModel->getFieldDataType(), $request->getInteger('owner'));
267
		$message = $result ? 'LBL_MODIFICATION_SUCCESSFUL_AND_RELOAD' : 'LBL_MODIFICATION_FAILURE';
268
		$message = \App\Language::translate($this->fieldModel->getFieldLabel(), $moduleName) . ': ' . \App\Language::translate($message, $moduleName);
0 ignored issues
show
Deprecated Code introduced by
The function Vtiger_Field_Model::getFieldLabel() has been deprecated: 7.0 Use $this->getLabel() ( Ignorable by Annotation )

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

268
		$message = \App\Language::translate(/** @scrutinizer ignore-deprecated */ $this->fieldModel->getFieldLabel(), $moduleName) . ': ' . \App\Language::translate($message, $moduleName);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
269
		$response = new Vtiger_Response();
270
		$response->setResult(['result' => $result, 'message' => $message]);
271
		$response->emit();
272
	}
273
274
	/**
275
	 * Validate the field name and value.
276
	 *
277
	 * @param \App\Request $request
278
	 *
279
	 * @throws \App\Exceptions\NoPermitted
280
	 */
281
	public function validateForField(App\Request $request): void
282
	{
283
		$fieldModel = Vtiger_Module_Model::getInstance($request->getModule())->getFieldByName($request->getByType('fieldName', 2));
284
		if (!$fieldModel || !$fieldModel->isActiveField() || !$fieldModel->isViewEnabled()) {
285
			throw new \App\Exceptions\NoPermitted('ERR_NO_PERMISSIONS_TO_FIELD', 406);
286
		}
287
		$recordModel = \Vtiger_Record_Model::getCleanInstance($fieldModel->getModuleName());
288
		$fieldModel->getUITypeModel()->setValueFromRequest($request, $recordModel, 'fieldValue');
289
		$response = new Vtiger_Response();
290
		$response->setResult([
291
			'raw' => $recordModel->get($fieldModel->getName()),
292
			'display' => $recordModel->getDisplayValue($fieldModel->getName()),
293
		]);
294
		$response->emit();
295
	}
296
297
	/**
298
	 * Validate the value based on the type of purify.
299
	 *
300
	 * @param \App\Request $request
301
	 *
302
	 * @throws \App\Exceptions\NoPermitted
303
	 */
304
	public function validateByMode(App\Request $request): void
305
	{
306
		if ($request->isEmpty('purifyMode') || !$request->has('value')) {
307
			throw new \App\Exceptions\NoPermitted('ERR_ILLEGAL_VALUE', 406);
308
		}
309
		$response = new Vtiger_Response();
310
		$response->setResult([
311
			'raw' => $request->getByType('value', $request->getByType('purifyMode')),
312
		]);
313
		$response->emit();
314
	}
315
316
	/**
317
	 * Validate file.
318
	 *
319
	 * @param App\Request $request
320
	 *
321
	 * @return void
322
	 */
323
	public function validateFile(App\Request $request): void
324
	{
325
		$validate = false;
326
		if ($request->has('base64')) {
327
			$fileInstance = \App\Fields\File::loadFromBase($request->getByType('base64', 'base64'), ['validateAllowedFormat' => 'image']);
328
			if ($fileInstance && $fileInstance->validate()) {
329
				$validate = true;
330
			} else {
331
				$validateError = $fileInstance->validateError;
332
			}
333
		}
334
		$response = new Vtiger_Response();
335
		$response->setResult([
336
			'validate' => $validate,
337
			'validateError' => $validateError ?? null,
338
		]);
339
		$response->emit();
340
	}
341
342
	/**
343
	 * Gets copy value.
344
	 *
345
	 * @param App\Request $request
346
	 */
347
	public function getCopyValue(App\Request $request)
348
	{
349
		$recordModel = \Vtiger_Record_Model::getInstanceById($request->getInteger('record'), $request->getModule());
350
		$response = new Vtiger_Response();
351
		$response->setResult(['success' => true, 'text' => $recordModel->get($request->getByType('fieldName', 'Alnum'))]);
352
		$response->emit();
353
	}
354
}
355