Passed
Push — developer ( 3e6d7c...adc0a6 )
by Mariusz
19:16
created

Vtiger_Relation_Model::privilegeToDelete()   B

Complexity

Conditions 11
Paths 31

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 14
rs 7.3166
ccs 0
cts 8
cp 0
cc 11
nc 31
nop 2
crap 132

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/* +***********************************************************************************
3
 * The contents of this file are subject to the vtiger CRM Public License Version 1.0
4
 * ("License"); You may not use this file except in compliance with the License
5
 * The Original Code is:  vtiger CRM Open Source
6
 * The Initial Developer of the Original Code is vtiger.
7
 * Portions created by vtiger are Copyright (C) vtiger.
8
 * All Rights Reserved.
9
 * Contributor(s): YetiForce S.A.
10
 * *********************************************************************************** */
11
12
class Vtiger_Relation_Model extends \App\Base
13
{
14
	/**
15
	 *  Cached instances.
16
	 *
17
	 * @var Vtiger_Relation_Model[]
18
	 */
19
	protected static $cachedInstances = [];
20
	/**
21
	 * Cached instances by relation id.
22
	 *
23
	 * @var Vtiger_Relation_Model[]
24
	 */
25
	protected static $cachedInstancesById = [];
26
	protected $parentModule = false;
27
	protected $relatedModule = false;
28
29
	/** @var \App\Relation\RelationAbstraction Class that includes basic operations on relations */
30
	protected $typeRelationModel;
31
32
	/** @var array Custom view list */
33
	protected $customViewList;
34
35 1
	/** @var array Event handler exceptions */
36
	protected $handlerExceptions = [];
37 1
38
	/** @var int one to many */
39
	const RELATION_O2M = 1;
40
41
	/** @var int Many to many and many to one */
42
	const RELATION_M2M = 2;
43
44
	/** @var int Advanced reference */
45
	const RELATION_AR = 3;
46
47 4
	/** @var int Multi reference */
48
	const RELATION_MR = 4;
49 4
50
	/**
51 4
	 * Function returns the relation id.
52
	 *
53
	 * @return int
54
	 */
55
	public function getId()
56
	{
57
		return $this->get('relation_id');
58
	}
59 2
60
	/**
61 2
	 * Function sets the relation's parent module model.
62
	 *
63
	 * @param Vtiger_Module_Model $moduleModel
64 2
	 *
65
	 * @return Vtiger_Relation_Model
66
	 */
67
	public function setParentModuleModel($moduleModel)
68
	{
69
		$this->parentModule = $moduleModel;
70
		return $this;
71
	}
72
73
	/**
74 2
	 * Function that returns the relation's parent module model.
75
	 *
76 2
	 * @return Vtiger_Module_Model
77
	 */
78 2
	public function getParentModuleModel()
79
	{
80
		if (empty($this->parentModule)) {
81
			$this->parentModule = Vtiger_Module_Model::getInstance($this->get('tabid'));
82
		}
83
		return $this->parentModule;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->parentModule also could return the type boolean which is incompatible with the documented return type Vtiger_Module_Model.
Loading history...
84
	}
85
86 3
	/**
87
	 * Gets parent record model.
88 3
	 *
89 1
	 * @return Vtiger_Record_Model|null
90
	 */
91 3
	public function getParentRecord(): ?Vtiger_Record_Model
92
	{
93
		return $this->get('parentRecord') ?? null;
94
	}
95
96
	/**
97
	 * Set relation's parent module model.
98
	 *
99 1
	 * @param Vtiger_Module_Model $relationModel
100
	 *
101 1
	 * @return $this
102 1
	 */
103
	public function setRelationModuleModel($relationModel)
104
	{
105 1
		$this->relatedModule = $relationModel;
106
		return $this;
107
	}
108
109
	/**
110
	 * Function that returns the relation's related module model.
111
	 *
112
	 * @return Vtiger_Module_Model
113
	 */
114
	public function getRelationModuleModel()
115
	{
116
		if (!$this->relatedModule) {
117
			$this->relatedModule = Vtiger_Module_Model::getInstance($this->get('related_tabid'));
118
		}
119
		return $this->relatedModule;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->relatedModule also could return the type true which is incompatible with the documented return type Vtiger_Module_Model.
Loading history...
120
	}
121
122
	/**
123
	 * Get relation module name.
124
	 *
125
	 * @return string
126
	 */
127
	public function getRelationModuleName()
128
	{
129
		$relationModuleName = $this->get('relatedModuleName');
130
		if (!empty($relationModuleName)) {
131
			return $relationModuleName;
132
		}
133
		return $this->getRelationModuleModel()->getName();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getRelationModuleModel()->getName() returns the type boolean which is incompatible with the documented return type string.
Loading history...
134
	}
135
136
	/**
137
	 * Get actions.
138
	 *
139
	 * @return string[]
140
	 */
141
	public function getActions()
142
	{
143
		if (\is_array($this->get('actions'))) {
144
			return $this->get('actions');
145
		}
146
		// No actions for Activity history
147
		if ('Activity History' === $this->get('c')) {
148
			return [];
149
		}
150
		$actions = explode(',', strtolower($this->get('actions')));
151
		$this->set('actions', $actions);
152
		return $actions;
153
	}
154
155
	/**
156
	 * Check if action is supported.
157
	 *
158
	 * @param string $actionName
159
	 *
160
	 * @return bool
161
	 */
162
	public function isActionSupported($actionName)
163
	{
164
		return \in_array(strtolower($actionName), $this->getActions());
165 2
	}
166
167 2
	/**
168 2
	 * Is record selection action available.
169
	 *
170
	 * @return bool
171
	 */
172
	public function isSelectActionSupported()
173
	{
174
		return $this->isActionSupported('select');
175
	}
176
177
	/**
178 2
	 * Is record add action available.
179
	 *
180 2
	 * @return bool
181 2
	 */
182
	public function isAddActionSupported()
183
	{
184
		return $this->isActionSupported('add') && $this->getRelationModuleModel()->isPermitted('CreateView');
185
	}
186
187
	/**
188
	 * Check favorite.
189
	 */
190
	public function isFavorites()
191 2
	{
192
		return 1 == $this->get('favorites') ? true : false;
193 2
	}
194 1
195
	/**
196 2
	 * Check related view type.
197
	 *
198
	 * @param string $type
199
	 *
200
	 * @return bool
201
	 */
202
	public function isRelatedViewType($type)
203
	{
204
		return false !== strpos($this->get('view_type'), $type);
205
	}
206
207
	/**
208
	 * Show user who created relation.
209
	 *
210
	 * @return bool
211
	 */
212
	public function showCreatorDetail()
213
	{
214
		if (0 === $this->get('creator_detail') || self::RELATION_M2M !== $this->getRelationType()) {
215
			return false;
216
		}
217
		return (bool) $this->get('creator_detail');
218
	}
219
220
	/**
221
	 * Show comments in related module.
222
	 *
223
	 * @return bool
224
	 */
225
	public function showComment()
226
	{
227
		if (0 === $this->get('relation_comment') || self::RELATION_M2M !== $this->getRelationType()) {
228
			return false;
229
		}
230
		return (bool) $this->get('relation_comment');
231
	}
232
233
	/**
234
	 * Get query generator instance.
235
	 *
236
	 * @return \App\QueryGenerator
237
	 */
238
	public function getQueryGenerator(): App\QueryGenerator
239
	{
240
		if (!$this->has('query_generator')) {
241
			$this->set('query_generator', new \App\QueryGenerator($this->getRelationModuleName()));
242
		}
243
		return $this->get('query_generator');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->get('query_generator') could return the type null which is incompatible with the type-hinted return App\QueryGenerator. Consider adding an additional type-check to rule them out.
Loading history...
244
	}
245
246
	/**
247 2
	 * Get relation type.
248
	 *
249 2
	 * @return int
250 2
	 */
251 2
	public function getRelationType()
252
	{
253 2
		if (!$this->has('relationType')) {
254
			$this->set('relationType', $this->getTypeRelationModel()->getRelationType());
255
		}
256
		return $this->get('relationType');
257
	}
258
259
	/**
260
	 * Get related view type.
261
	 *
262
	 * @return string[]
263
	 */
264
	public function getRelatedViewType(): array
265 2
	{
266 2
		return explode(',', $this->get('view_type')) ?? [];
267 2
	}
268 2
269 2
	/**
270 2
	 * Get custom view.
271
	 *
272
	 * @return string[]
273 2
	 */
274 2
	public function getCustomView(): array
275 2
	{
276 2
		if ($this->isEmpty('custom_view')) {
277 2
			return [];
278 2
		}
279
		return explode(',', $this->get('custom_view')) ?? [];
280 2
	}
281
282 1
	/**
283
	 * Get relation model instance.
284
	 *
285
	 * @param Vtiger_Module_Model $parentModuleModel
286
	 * @param Vtiger_Module_Model $relatedModuleModel
287
	 * @param bool|int            $relationId
288 2
	 *
289
	 * @return $this|bool
290 2
	 */
291 2
	public static function getInstance($parentModuleModel, $relatedModuleModel, $relationId = false)
292 2
	{
293 2
		$relKey = $parentModuleModel->getId() . '_' . $relatedModuleModel->getId() . '_' . $relationId;
294
		if (isset(self::$cachedInstances[$relKey])) {
295
			return self::$cachedInstances[$relKey] ? clone self::$cachedInstances[$relKey] : self::$cachedInstances[$relKey];
296 2
		}
297 2
		if (('ModComments' == $relatedModuleModel->getName() && $parentModuleModel->isCommentEnabled()) || 'Documents' == $parentModuleModel->getName()) {
298 2
			$moduleName = 'ModComments' == $relatedModuleModel->getName() ? $relatedModuleModel->getName() : $parentModuleModel->getName();
299
			$relationModelClassName = Vtiger_Loader::getComponentClassName('Model', 'Relation', $moduleName);
300
			$relationModel = new $relationModelClassName();
301 2
			$relationModel->setParentModuleModel($parentModuleModel)->setRelationModuleModel($relatedModuleModel);
302
			if (method_exists($relationModel, 'setExceptionData')) {
303
				$relationModel->setExceptionData();
304
			}
305
			self::$cachedInstances[$relKey] = $relationModel;
306
			return clone $relationModel;
307
		}
308
		if (empty($relationId)) {
309
			$row = current(\App\Relation::getByModule($parentModuleModel->getName(), true, $relatedModuleModel->getName()));
0 ignored issues
show
Bug introduced by
$relatedModuleModel->getName() of type boolean is incompatible with the type null|string expected by parameter $relatedModuleName of App\Relation::getByModule(). ( Ignorable by Annotation )

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

309
			$row = current(\App\Relation::getByModule($parentModuleModel->getName(), true, /** @scrutinizer ignore-type */ $relatedModuleModel->getName()));
Loading history...
Bug introduced by
$parentModuleModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Relation::getByModule(). ( Ignorable by Annotation )

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

309
			$row = current(\App\Relation::getByModule(/** @scrutinizer ignore-type */ $parentModuleModel->getName(), true, $relatedModuleModel->getName()));
Loading history...
310
		} else {
311 2
			$row = \App\Relation::getById($relationId);
312
			if (1 === $row['presence'] || $row['tabid'] !== $parentModuleModel->getId() || $row['related_tabid'] !== $relatedModuleModel->getId()) {
313 2
				$row = [];
314
			}
315
		}
316
		if ($row) {
317 2
			$row['modulename'] = $row['related_modulename'];
318
			$relationModelClassName = Vtiger_Loader::getComponentClassName('Model', 'Relation', $parentModuleModel->get('name'));
319
			$relationModel = new $relationModelClassName();
320
			$relationModel->setData($row)->setParentModuleModel($parentModuleModel)->setRelationModuleModel($relatedModuleModel);
321
			$relationModel->set('relatedModuleName', $row['related_modulename']);
322 2
			self::$cachedInstances[$relKey] = $relationModel;
323 2
			self::$cachedInstancesById[$row['relation_id']] = $relationModel;
324 2
			return clone $relationModel;
325
		}
326 2
		self::$cachedInstances[$relKey] = false;
327
		return false;
328
	}
329
330 2
	/**
331
	 * Get relation model instance by relation id.
332
	 *
333 2
	 * @param int $relationId
334 2
	 *
335 2
	 * @return self|bool
336
	 */
337 2
	public static function getInstanceById(int $relationId)
338
	{
339
		if (!isset(self::$cachedInstancesById[$relationId])) {
340
			$row = \App\Relation::getById($relationId);
341
			$relationModel = false;
342
			if ($row) {
343
				$row['modulename'] = $row['related_modulename'];
344
				$parentModuleModel = Vtiger_Module_Model::getInstance($row['tabid']);
345 2
				$relationModelClassName = Vtiger_Loader::getComponentClassName('Model', 'Relation', $parentModuleModel->getName());
346
				$relationModel = new $relationModelClassName();
347 2
				$relationModel->setData($row)->setParentModuleModel($parentModuleModel)->setRelationModuleModel(Vtiger_Module_Model::getInstance($row['related_modulename']));
348 2
				$relationModel->set('relatedModuleName', $row['related_modulename']);
349
				if (method_exists($relationModel, 'setExceptionData')) {
350 1
					$relationModel->setExceptionData();
351 1
				}
352
			}
353 1
			self::$cachedInstancesById[$relationId] = $relationModel;
354
		}
355
		return self::$cachedInstancesById[$relationId] ? clone self::$cachedInstancesById[$relationId] : null;
356 1
	}
357
358
	/**
359
	 * Get type relation model.
360
	 *
361 1
	 * @return \App\Relation\RelationAbstraction
362 1
	 */
363 1
	public function getTypeRelationModel(): App\Relation\RelationAbstraction
364
	{
365
		if (!isset($this->typeRelationModel)) {
366
			$name = ucfirst($this->get('name'));
367
			$relationClassName = Vtiger_Loader::getComponentClassName('Relation', $name, $this->getParentModuleModel()->getName(), false);
368
			if (!$relationClassName) {
369
				$relationClassName = Vtiger_Loader::getComponentClassName('Relation', $name, $this->getRelationModuleName());
370 1
			}
371 1
			if (class_exists($relationClassName)) {
372 1
				$this->typeRelationModel = new $relationClassName();
373 1
				$this->typeRelationModel->relationModel = &$this;
374
			} else {
375
				App\Log::error("Not exist relation: {$name} in " . __METHOD__);
376 1
				throw new \App\Exceptions\NotAllowedMethod('LBL_NOT_EXIST_RELATION: ' . $name);
377
			}
378 1
		}
379 1
		return $this->typeRelationModel;
380
	}
381 1
382
	/**
383
	 * Get query form relation.
384
	 *
385
	 * @throws \App\Exceptions\NotAllowedMethod
386
	 *
387
	 * @return \App\QueryGenerator
388
	 */
389
	public function getQuery()
390
	{
391
		if (empty($this->get('parentRecord'))) {
392
			App\Log::error('No value parentRecord in ' . __METHOD__);
393 2
			throw new \App\Exceptions\IllegalValue('ERR_NO_VALUE||parentRecord');
394
		}
395 2
		$queryGenerator = $this->getQueryGenerator();
396 2
		$queryGenerator->setSourceRecord($this->get('parentRecord')->getId());
397
		$this->getTypeRelationModel()->getQuery();
398 2
		if ($this->showCreatorDetail()) {
399 2
			$queryGenerator->setCustomColumn('rel_created_user');
400 2
			$queryGenerator->setCustomColumn('rel_created_time');
401 2
		}
402 2
		if ($this->showComment()) {
403 2
			$queryGenerator->setCustomColumn('rel_comment');
404 2
		}
405 2
		$fields = array_keys($this->getQueryFields());
406 2
		$fields[] = 'id';
407 2
		$queryGenerator->setFields($fields);
408
		return $queryGenerator;
409
	}
410
411 2
	/**
412 1
	 * Get query fields.
413 1
	 *
414 1
	 * @return Vtiger_Field_Model[] with field name as key
415 1
	 */
416 1
	public function getQueryFields()
417
	{
418
		if ($this->has('QueryFields')) {
419
			return $this->get('QueryFields');
420
		}
421
		$relatedListFields = [];
422
		$relatedModuleModel = $this->getRelationModuleModel();
423 2
		// Get fields from panel
424
		foreach (App\Field::getFieldsFromRelation($this->getId()) as $fieldName) {
425 2
			$relatedListFields[$fieldName] = $relatedModuleModel->getFieldByName($fieldName);
426
		}
427
		if ($relatedListFields) {
428
			$this->set('QueryFields', $relatedListFields);
429
			return $relatedListFields;
430
		}
431
		$queryGenerator = $this->getQueryGenerator();
432
		$entity = $queryGenerator->getEntityModel();
433
		if (!empty($entity->relationFields)) {
434
			// Get fields from entity model
435
			foreach ($entity->relationFields as $fieldName) {
436
				$relatedListFields[$fieldName] = $relatedModuleModel->getFieldByName($fieldName);
437
			}
438
		} else {
439
			// Get fields from default CustomView
440
			$queryGenerator->initForDefaultCustomView(true, true);
441
			foreach ($queryGenerator->getFields() as $fieldName) {
442
				if ('id' !== $fieldName) {
443
					$relatedListFields[$fieldName] = $relatedModuleModel->getFieldByName($fieldName);
444
				}
445
			}
446
			$relatedListFields['id'] = true;
447
		}
448
		if ($relatedListFields) {
449
			$this->set('QueryFields', $relatedListFields);
450
			return $relatedListFields;
451
		}
452
		$this->set('QueryFields', $relatedListFields);
453
		return $relatedListFields;
454
	}
455
456
	/**
457
	 * Function to get relation field for relation module and parent module.
458
	 *
459
	 * @return Vtiger_Field_Model
460
	 */
461
	public function getRelationField()
462
	{
463
		if ($this->has('RelationField')) {
464
			return $this->get('RelationField');
465
		}
466
		$relatedModuleModel = $this->getRelationModuleModel();
467
		$parentModuleName = $this->getParentModuleModel()->getName();
468
		$relatedModelFields = $relatedModuleModel->getFields();
469
		if (!$this->isEmpty('field_name') && isset($relatedModelFields[$this->get('field_name')])) {
470
			$relationField = $relatedModelFields[$this->get('field_name')];
471
		} else {
472
			$fieldRel = App\Field::getRelatedFieldForModule($relatedModuleModel->getName(), $parentModuleName);
473
			if (isset($fieldRel['fieldid'])) {
474
				foreach ($relatedModelFields as $fieldModel) {
475
					if ($fieldModel->getId() === $fieldRel['fieldid']) {
476
						$relationField = $fieldModel;
477
						break;
478
					}
479
				}
480
			}
481
		}
482
		if (empty($relationField)) {
483
			$relationField = false;
484
			foreach ($relatedModelFields as $fieldModel) {
485
				if ($fieldModel->isReferenceField()) {
486
					$referenceList = $fieldModel->getReferenceList();
487
					if (!empty($referenceList) && \in_array($parentModuleName, $referenceList)) {
488
						$relationField = $fieldModel;
489
						break;
490
					}
491
				}
492
			}
493
		}
494
		if (empty($relationField) && !$this->isEmpty('field_name') && $relatedModuleModel->isInventory()) {
495
			$relationField = Vtiger_Inventory_Model::getInstance($relatedModuleModel->getName())->getField($this->get('field_name'));
0 ignored issues
show
Bug introduced by
$relatedModuleModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of Vtiger_Inventory_Model::getInstance(). ( Ignorable by Annotation )

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

495
			$relationField = Vtiger_Inventory_Model::getInstance(/** @scrutinizer ignore-type */ $relatedModuleModel->getName())->getField($this->get('field_name'));
Loading history...
496
		}
497
		$this->set('RelationField', $relationField ?: false);
498
		return $relationField;
499
	}
500
501
	/**
502
	 * Get relation inventory fields.
503
	 *
504
	 * @return Vtiger_Basic_InventoryField[]
505
	 */
506
	public function getRelationInventoryFields()
507
	{
508
		if (!$this->has('RelationInventoryFields')) {
509
			$this->set('RelationInventoryFields', []);
510
			if ($this->getRelationModuleModel()->isInventory()) {
511
				$columns = (new \App\Db\Query())
512
					->select(['fieldname'])
513
					->from('a_#__relatedlists_inv_fields')
514
					->where(['relation_id' => $this->getId()])
515
					->orderBy('sequence')
516
					->column();
517
				$inventoryFields = Vtiger_Inventory_Model::getInstance($this->getRelationModuleModel()->getName())->getFields();
0 ignored issues
show
Bug introduced by
$this->getRelationModuleModel()->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of Vtiger_Inventory_Model::getInstance(). ( Ignorable by Annotation )

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

517
				$inventoryFields = Vtiger_Inventory_Model::getInstance(/** @scrutinizer ignore-type */ $this->getRelationModuleModel()->getName())->getFields();
Loading history...
518
				$fields = [];
519
				foreach ($columns as &$column) {
520
					if (!empty($inventoryFields[$column]) && $inventoryFields[$column]->isVisible()) {
521
						$fields[$column] = $inventoryFields[$column];
522
					}
523
				}
524
				$this->set('RelationInventoryFields', $fields);
525
			}
526
		}
527
		return $this->get('RelationInventoryFields');
528
	}
529
530
	/**
531
	 * Function which will specify whether the relation is editable.
532
	 *
533
	 * @return bool
534
	 */
535
	public function isEditable(): bool
536
	{
537
		return $this->getRelationModuleModel()->isPermitted('EditView');
538
	}
539
540
	/**
541
	 * Function which will specify whether the relation is deletable.
542
	 *
543
	 * @param \Vtiger_Record_Model|null $recordModel
544
	 * @param int|null                  $recordId
545
	 *
546
	 * @return bool
547
	 */
548
	public function privilegeToDelete(Vtiger_Record_Model $recordModel = null, int $recordId = null): bool
549
	{
550
		if ($returnVal = $this->getRelationModuleModel()->isPermitted('RemoveRelation')) {
551
			if ($this->getRelationType() === static::RELATION_O2M && ($fieldModel = $this->getRelationField())) {
552
				if (!$recordModel && $recordId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $recordId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
553
					$recordModel = \Vtiger_Record_Model::getInstanceById($recordId);
554
				}
555
				$returnVal = !$fieldModel->isMandatory() && $fieldModel->isEditable() && !$fieldModel->isEditableReadOnly() && (!$recordModel || $recordModel->isEditable());
556
			}
557
			if (\in_array($this->getRelationType(), [static::RELATION_AR, static::RELATION_MR])) {
558
				$returnVal = false;
559
			}
560
		}
561
		return $returnVal;
562
	}
563
564
	/**
565
	 * Function which will specify whether the tree element is deletable.
566
	 *
567
	 * @return bool
568
	 */
569
	public function privilegeToTreeDelete(): bool
570
	{
571
		return $this->getRelationModuleModel()->isPermitted('RemoveRelation');
572
	}
573
574
	/**
575
	 * Get list url for record.
576
	 *
577
	 * @param Vtiger_Module_Model $parentRecordModel
578
	 *
579
	 * @return string
580
	 */
581
	public function getListUrl(Vtiger_Record_Model $parentRecordModel): string
582
	{
583
		$url = 'index.php?module=' . $this->getParentModuleModel()->get('name') . '&relatedModule=' . $this->get('modulename') .
584
			'&view=Detail&record=' . $parentRecordModel->getId() . '&mode=showRelatedList&relationId=' . $this->getId();
585
		if ('Calendar' == $this->get('modulename')) {
586
			$url .= '&time=current';
587
		}
588
		return $url;
589
	}
590
591
	/**
592
	 * Get create url from parent record.
593
	 *
594
	 * @param bool $fullView
595
	 *
596
	 * @return string
597
	 */
598
	public function getCreateViewUrl(bool $fullView = false)
599
	{
600
		$parentRecord = $this->getParentRecord();
601
		$relatedModuleModel = $this->getRelationModuleModel();
602
		if (!$fullView && $relatedModuleModel->isQuickCreateSupported()) {
603
			$createViewUrl = $relatedModuleModel->getQuickCreateUrl();
604
		} else {
605
			$createViewUrl = $relatedModuleModel->getCreateRecordUrl();
606
		}
607
		$createViewUrl .= '&sourceModule=' . $parentRecord->getModule()->getName() . '&sourceRecord=' . $parentRecord->getId() . '&relationOperation=true&relationId=' . $this->getId();
608
		if ($this->isDirectRelation()) {
609
			$relationField = $this->getRelationField();
610
			$createViewUrl .= '&' . $relationField->getName() . '=' . $parentRecord->getId();
611
		}
612
613
		return $createViewUrl;
614
	}
615
616
	/**
617
	 * Get delete url from parent record.
618
	 *
619
	 * @param int $relatedRecordId
620
	 *
621
	 * @return string
622
	 */
623
	public function getDeleteUrl(int $relatedRecordId)
624
	{
625
		$parentModuleName = $this->getParentModuleModel()->getName();
626
		$relatedModuleName = $this->getRelationModuleModel()->getName();
627
		$recordId = $this->getParentRecord()->getId();
628
629
		return "index.php?module={$parentModuleName}&related_module={$relatedModuleName}&action=RelationAjax&mode=deleteRelation&related_record_list=[{$relatedRecordId}]&src_record={$recordId}&relationId={$this->getId()}";
630
	}
631
632
	/**
633
	 * Add relation.
634
	 *
635
	 * @param int       $sourceRecordId
636
	 * @param int|int[] $destinationRecordIds
637
	 * @param mixed     $params
638
	 */
639
	public function addRelation($sourceRecordId, $destinationRecordIds, $params = false)
640
	{
641
		$result = false;
642
		$sourceModuleName = $this->getParentModuleModel()->getName();
643
		$relationModel = $this->getTypeRelationModel();
644
		if (!\is_array($destinationRecordIds)) {
645
			$destinationRecordIds = [$destinationRecordIds];
646
		}
647
		$data = [
648
			'CRMEntity' => $this->getParentModuleModel()->getEntityInstance(),
649
			'sourceModule' => $sourceModuleName,
650
			'sourceRecordId' => $sourceRecordId,
651
			'destinationModule' => $this->getRelationModuleModel()->getName(),
652
			'relationId' => $this->getId(),
653
		];
654
		$eventHandler = (new \App\EventHandler())->setModuleName($this->getRelationModuleModel()->getName());
655
		$eventHandlerBySource = (new \App\EventHandler())->setModuleName($sourceModuleName);
656
		if (!empty($this->getHandlerExceptions())) {
657
			$eventHandler->setExceptions($this->getHandlerExceptions());
658
		}
659
		foreach ($destinationRecordIds as $destinationRecordId) {
660
			$data['destinationRecordId'] = $destinationRecordId;
661
			$eventHandler->setParams($data)->trigger('EntityBeforeLink');
662
			$eventHandlerBySource->setParams($data)->trigger('EntityBeforeLinkForSource');
663
			if ($result = $relationModel->create($sourceRecordId, $destinationRecordId)) {
664
				\CRMEntity::trackLinkedInfo($sourceRecordId);
665
				\CRMEntity::trackLinkedInfo($destinationRecordId);
666
				$eventHandler->trigger('EntityAfterLink');
667
				$eventHandler->trigger('EntityAfterLinkForSource');
668
			}
669
		}
670
		return $result;
671
	}
672
673
	/**
674
	 * Set handler exceptions.
675
	 *
676
	 * @param array $exceptions
677
	 *
678
	 * @return $this
679
	 */
680
	public function setHandlerExceptions(array $exceptions)
681
	{
682
		$this->handlerExceptions = $exceptions;
683
		return $this;
684
	}
685
686
	/**
687
	 * get handler exceptions.
688
	 *
689
	 * @return array
690
	 */
691
	public function getHandlerExceptions(): array
692
	{
693
		return $this->handlerExceptions;
694
	}
695
696
	/**
697
	 * Transfer.
698
	 *
699
	 * @param array $recordsToTransfer
700
	 */
701
	public function transfer(array $recordsToTransfer)
702
	{
703
		$relationModel = $this->getTypeRelationModel();
704
		$eventHandler = new \App\EventHandler();
705
		$eventHandler->setModuleName($this->getParentModuleModel()->getName());
706
		$toRecordId = $this->get('parentRecord')->getId();
707
		$params = ['sourceRecordId' => $toRecordId, 'sourceModule' => $eventHandler->getModuleName(), 'destinationModule' => $this->getRelationModuleModel()->getName()];
708
709
		foreach ($recordsToTransfer as $relatedRecordId => $fromRecordId) {
710
			$params['destinationRecordId'] = $relatedRecordId;
711
			$eventHandler->setParams($params);
712
			$eventHandler->trigger('EntityBeforeTransferUnLink');
713
			if ($relationModel->transfer($relatedRecordId, $fromRecordId, $toRecordId)) {
714
				\CRMEntity::trackLinkedInfo([$toRecordId, $fromRecordId]);
715
				$eventHandler->trigger('EntityAfterTransferLink');
716
			}
717
		}
718
	}
719
720
	/**
721
	 * Transfer tree relation.
722
	 *
723
	 * @param array $relationRecords
724
	 */
725
	public function transferTree(array $relationRecords)
726
	{
727
		$recordId = $this->get('parentRecord')->getId();
728 2
		$dbCommand = \App\Db::getInstance()->createCommand();
729
		foreach ($relationRecords as $tree => $fromId) {
730 2
			if ($dbCommand->update('u_#__crmentity_rel_tree', ['crmid' => $recordId], ['crmid' => $fromId, 'relmodule' => $this->getRelationModuleModel()->getId(), 'tree' => $tree])->execute()) {
731 2
				$dbCommand->update('vtiger_crmentity', ['modifiedtime' => date('Y-m-d H:i:s'), 'modifiedby' => \App\User::getCurrentUserId()], ['crmid' => [$fromId, $recordId]])->execute();
732
			}
733
		}
734 2
	}
735 2
736 2
	/**
737 2
	 * Delete relation.
738 2
	 *
739 2
	 * @param int $relId
740 1
	 */
741
	public function transferDelete(int $relId)
742 2
	{
743 2
		$recordId = $this->get('parentRecord')->getId();
744
		$params = ['sourceRecordId' => $recordId,
745 2
			'sourceModule' => $this->getParentModuleModel()->getName(),
746 2
			'destinationModule' => $this->getRelationModuleModel()->getName(),
747
			'destinationRecordId' => $relId, ];
748 2
		$eventHandler = new \App\EventHandler();
749 2
		$eventHandler->setModuleName($this->getParentModuleModel()->getName());
750 2
		$eventHandler->setParams($params);
751 2
		$eventHandler->trigger('EntityBeforeTransferUnLink');
752
		if ($this->getTypeRelationModel()->delete($recordId, $relId)) {
753 2
			$eventHandler->trigger('EntityAfterTransferUnLink');
754
		}
755
	}
756 2
757 2
	/**
758 2
	 * Delete relation.
759
	 *
760 2
	 * @param int $sourceRecordId
761
	 * @param int $relatedRecordId
762
	 *
763
	 * @return bool
764
	 */
765
	public function deleteRelation($sourceRecordId, $relatedRecordId)
766
	{
767
		$sourceModuleName = $this->getParentModuleModel()->getName();
768
		$destinationModuleName = $this->getRelationModuleModel()->getName();
769
		$result = false;
770
		if ('ModComments' === $destinationModuleName) {
0 ignored issues
show
introduced by
The condition 'ModComments' === $destinationModuleName is always false.
Loading history...
771
			include_once 'modules/ModTracker/ModTracker.php';
772
			ModTracker::unLinkRelation($sourceModuleName, $sourceRecordId, $destinationModuleName, $relatedRecordId);
773
			$result = true;
774
		} elseif (!($this->getRelationField() && $this->getRelationField()->isMandatory())) {
775
			$destinationModuleFocus = $this->getRelationModuleModel()->getEntityInstance();
776
			$params = [
777
				'CRMEntity' => $destinationModuleFocus,
778
				'sourceModule' => $sourceModuleName,
779
				'sourceRecordId' => $sourceRecordId,
780
				'destinationModule' => $destinationModuleName,
781
				'destinationRecordId' => $relatedRecordId,
782
				'relatedName' => $this->get('name'),
783
				'relationId' => $this->getId(),
784
			];
785
			$eventHandler = (new \App\EventHandler())->setModuleName($destinationModuleName)->setParams($params);
786
			$eventHandlerBySource = (new \App\EventHandler())->setModuleName($sourceModuleName)->setParams($params);
787
			$eventHandler->trigger('EntityBeforeUnLink');
788
			$eventHandlerBySource->trigger('EntityBeforeUnLinkForSource');
789
			if ($result = $this->getTypeRelationModel()->delete($sourceRecordId, $relatedRecordId)) {
790
				$destinationModuleFocus->trackUnLinkedInfo($sourceRecordId);
791
				$eventHandler->trigger('EntityAfterUnLink');
792
				$destinationModuleFocus->trackUnLinkedInfo($relatedRecordId);
793
				$eventHandlerBySource->trigger('EntityAfterUnLinkForSource');
794
			}
795
		}
796
797
		return $result;
798
	}
799
800
	/**
801
	 * Function to add tree type relation.
802
	 *
803
	 * @param int    $crmid
804
	 * @param string $tree
805
	 */
806
	public function addRelationTree($crmid, $tree)
807
	{
808
		App\Db::getInstance()->createCommand()->insert('u_#__crmentity_rel_tree', [
809
			'crmid' => $crmid,
810
			'tree' => $tree,
811
			'module' => $this->getParentModuleModel()->getId(),
812
			'relmodule' => $this->getRelationModuleModel()->getId(),
813
			'rel_created_user' => App\User::getCurrentUserId(),
814
			'rel_created_time' => date('Y-m-d H:i:s'),
815
		])->execute();
816
	}
817
818
	/**
819
	 * Function to delete tree type relation.
820
	 *
821
	 * @param int    $crmid
822
	 * @param string $tree
823
	 */
824
	public function deleteRelationTree($crmid, $tree)
825
	{
826
		App\Db::getInstance()->createCommand()
827
			->delete('u_#__crmentity_rel_tree', ['crmid' => $crmid, 'tree' => $tree, 'module' => $this->getParentModuleModel()->getId(), 'relmodule' => $this->getRelationModuleModel()->getId()])
828
			->execute();
829
	}
830
831
	/**
832
	 * Query tree category relation.
833
	 *
834
	 * @return \App\Db\Query
835
	 */
836
	public function getRelationTreeQuery()
837
	{
838
		$template = [];
839
		foreach ($this->getRelationModuleModel()->getFieldsByType('tree') as $field) {
840
			if ($field->isActiveField()) {
841
				$template[] = $field->getFieldParams();
842
			}
843
		}
844
		return (new \App\Db\Query())
845
			->select(['ttd.*', 'rel.crmid', 'rel.rel_created_time', 'rel.rel_created_user', 'rel.rel_comment'])
846
			->from('vtiger_trees_templates_data ttd')
847
			->innerJoin('u_#__crmentity_rel_tree rel', 'rel.tree = ttd.tree')
848
			->where(['ttd.templateid' => $template, 'rel.crmid' => $this->get('parentRecord')->getId(), 'rel.relmodule' => $this->getRelationModuleModel()->getId()]);
849
	}
850
851
	/**
852
	 * Tree category relation.
853
	 *
854
	 * @return array
855
	 */
856
	public function getRelationTree()
857
	{
858
		return $this->getRelationTreeQuery()->all();
859
	}
860
861
	/**
862
	 * Is the tree type relation available.
863
	 *
864
	 * @return bool
865
	 */
866
	public function isTreeRelation()
867
	{
868
		if (\in_array($this->getRelationModuleModel()->getName(), ['OutsourcedProducts', 'Products', 'Services', 'OSSOutsourcedServices'])) {
869
			foreach ($this->getRelationModuleModel()->getFieldsByType('tree') as $field) {
870
				if ($field->isActiveField()) {
871
					return true;
872
				}
873
			}
874
		}
875
		return false;
876
	}
877
878
	public function isDirectRelation()
879
	{
880
		return self::RELATION_O2M == $this->getRelationType();
881
	}
882
883
	/**
884
	 * Getting all relations.
885
	 *
886
	 * @param \Vtiger_Module_Model $moduleModel
887
	 * @param bool                 $selected
888
	 * @param bool                 $onlyActive
889
	 * @param bool                 $permissions
890
	 * @param string               $key
891
	 *
892
	 * @return \Vtiger_Relation_Model[]
893
	 */
894
	public static function getAllRelations(Vtiger_Module_Model $moduleModel, bool $selected = true, bool $onlyActive = true, bool $permissions = true, string $key = 'relation_id')
895
	{
896
		$relationModels = [];
897
		$relationModelClassName = Vtiger_Loader::getComponentClassName('Model', 'Relation', $moduleModel->get('name'));
898
		$privilegesModel = Users_Privileges_Model::getCurrentUserPrivilegesModel();
899
		foreach (\App\Relation::getByModule($moduleModel->getName(), $onlyActive) as $row) {
0 ignored issues
show
Bug introduced by
$moduleModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Relation::getByModule(). ( Ignorable by Annotation )

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

899
		foreach (\App\Relation::getByModule(/** @scrutinizer ignore-type */ $moduleModel->getName(), $onlyActive) as $row) {
Loading history...
900
			if ($selected && 1 === $row['presence']) {
901
				continue;
902
			}
903
			$row['modulename'] = $row['related_modulename'];
904
			$row['moduleid'] = $row['related_tabid'];
905
			// Skip relation where target module does not exits or is no permitted for view.
906
			if ($permissions && !$privilegesModel->hasModuleActionPermission($row['related_modulename'], 'DetailView')) {
907
				continue;
908
			}
909
			$relationModel = new $relationModelClassName();
910
			$relationModel->setData($row)->setParentModuleModel($moduleModel)->set('relatedModuleName', $row['related_modulename']);
911
			$relationModels[$row[$key]] = $relationModel;
912
		}
913
		return $relationModels;
914 1
	}
915
916 1
	/**
917 1
	 * Get autocomplete fields.
918 1
	 *
919 1
	 * @param \Vtiger_Record_Model $recordModel
920 1
	 *
921
	 * @return array
922 1
	 */
923 1
	public function getAutoCompleteField($recordModel): array
924 1
	{
925
		$fields = [];
926
		$fieldsReferenceList = [];
927 1
		$excludedModules = ['Users'];
928
		$relatedModel = $this->getRelationModuleModel();
929 1
		if ($relationField = $this->getRelationField()) {
930
			$fields[$relationField->getName()] = $recordModel->getId();
931
		}
932
		$parentModelFields = $this->getParentModuleModel()->getFields();
933
		foreach ($parentModelFields as $fieldName => $fieldModel) {
934
			if ($fieldModel->isReferenceField()) {
935
				$referenceList = $fieldModel->getReferenceList();
936 1
				foreach ($referenceList as $module) {
937
					if (!\in_array($module, $excludedModules) && 'userCreator' !== !$fieldModel->getFieldDataType()) {
938
						$fieldsReferenceList[$module] = $fieldModel;
939 1
					}
940
				}
941 1
			}
942 1
		}
943 1
		$relatedModelFields = $relatedModel->getFields();
944
		foreach ($relatedModelFields as $fieldName => $fieldModel) {
945 1
			if ($fieldModel->isReferenceField()) {
946
				$referenceList = $fieldModel->getReferenceList();
947
				foreach ($referenceList as $module) {
948
					if (\array_key_exists($module, $fieldsReferenceList) && $module != $recordModel->getModuleName()) {
949 1
						$parentFieldModel = $fieldsReferenceList[$module];
950 1
						$relId = $recordModel->get($parentFieldModel->getName());
951
						if (!empty($relId) && \App\Record::isExists($relId)) {
952 1
							$fields[$fieldName] = $relId;
953
						}
954
					}
955
				}
956
			}
957
		}
958
959
		return $fields;
960
	}
961
962
	public function getRestrictionsPopupField($recordModel)
963
	{
964
		$fields = [];
965
		$map = [];
966
		$relatedModel = $this->getRelationModuleModel();
967
		$relatedModuleName = $relatedModel->getName();
968
		$parentModuleName = $this->getParentModuleModel()->getName();
969
970
		if (\array_key_exists("$relatedModuleName::$parentModuleName", $map)) {
971
			$fieldMap = $map["$relatedModuleName::$parentModuleName"];
972
			$fieldModel = $recordModel->getField($fieldMap[1]);
973
			$value = $fieldModel->getEditViewDisplayValue($recordModel->get($fieldMap[1]), $recordModel);
974
			$fields = ['key' => $fieldMap[0], 'name' => strip_tags($value)];
975
		}
976
		return $fields;
977
	}
978
979
	/**
980
	 * Function to set presence relation.
981
	 *
982
	 * @param int    $relationId
983
	 * @param string $status
984
	 */
985
	public static function updateRelationPresence($relationId, $status)
986
	{
987
		\App\Db::getInstance()->createCommand()->update('vtiger_relatedlists', ['presence' => !$status ? 1 : 0], ['relation_id' => $relationId])->execute();
988
		\App\Relation::clearCacheById($relationId);
989
	}
990
991
	/**
992
	 * Function to set presence relation.
993
	 *
994
	 * @param int   $relationId
995
	 * @param array $customView
996
	 */
997
	public static function updateRelationCustomView(int $relationId, array $customView): void
998
	{
999
		\App\Db::getInstance()->createCommand()->update('vtiger_relatedlists', ['custom_view' => implode(',', $customView)], ['relation_id' => $relationId])->execute();
1000
		\App\Relation::clearCacheById($relationId);
1001
	}
1002
1003
	/**
1004
	 * Function to set custom view orderby .
1005
	 *
1006
	 * @param int  $relationId
1007
	 * @param bool $customViewOrderBy
1008
	 */
1009
	public static function updateRelationCustomViewOrderBy(int $relationId, bool $customViewOrderBy): void
1010
	{
1011
		\App\Db::getInstance()->createCommand()->update('vtiger_relatedlists', ['custom_view_orderby' => $customViewOrderBy], ['relation_id' => $relationId])->execute();
1012
		\App\Relation::clearCacheById($relationId);
1013
	}
1014
1015
	/**
1016
	 * Removes relation between modules.
1017
	 *
1018
	 * @param int $relationId
1019
	 */
1020
	public static function removeRelationById($relationId)
1021
	{
1022
		if ($relationId) {
1023
			$dbCommand = App\Db::getInstance()->createCommand();
1024
			$dbCommand->delete('vtiger_relatedlists', ['relation_id' => $relationId])->execute();
1025
			$dbCommand->delete('vtiger_relatedlists_fields', ['relation_id' => $relationId])->execute();
1026
			App\Db::getInstance('admin')->createCommand()->delete('a_yf_relatedlists_inv_fields', ['relation_id' => $relationId])->execute();
1027
			$widgets = (new \App\Db\Query())->select(['id', 'tabid'])->from('vtiger_widgets')->where(['and', ['type' => 'RelatedModule'], ['like', 'data', "\"relation_id\":{$relationId},"]])->createCommand()->queryAllByGroup();
1028
			foreach ($widgets as $widgetId => $tabId) {
1029
				$dbCommand->delete('vtiger_widgets', ['id' => $widgetId])->execute();
1030
				\App\Cache::delete('ModuleWidgets', $tabId);
1031
			}
1032
		}
1033
		\App\Relation::clearCacheById($relationId);
1034
	}
1035
1036
	/**
1037
	 * Function to save sequence of relation.
1038
	 *
1039
	 * @param array $modules
1040
	 */
1041
	public static function updateRelationSequence($modules)
1042
	{
1043
		$dbCommand = App\Db::getInstance()->createCommand();
1044
		foreach ($modules as $module) {
1045
			$dbCommand->update('vtiger_relatedlists', ['sequence' => (int) $module['index'] + 1], ['relation_id' => $module['relationId']])->execute();
1046
			\App\Relation::clearCacheById((int) $module['relationId']);
1047
		}
1048
	}
1049
1050
	/**
1051
	 * Update module related fields.
1052
	 *
1053
	 * @param int   $relationId
1054
	 * @param array $fields
1055
	 *
1056
	 * @throws \yii\db\Exception
1057
	 */
1058
	public static function updateModuleRelatedFields(int $relationId, $fields)
1059
	{
1060
		$db = \App\Db::getInstance();
1061
		$transaction = $db->beginTransaction();
1062
		try {
1063
			$db->createCommand()->delete('vtiger_relatedlists_fields', ['relation_id' => $relationId])->execute();
1064
			if ($fields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1065
				$addedFields = [];
1066
				foreach ($fields as $key => $field) {
1067
					if (\in_array($field['id'], $addedFields)) {
1068
						continue;
1069
					}
1070
					$db->createCommand()->insert('vtiger_relatedlists_fields', [
1071
						'relation_id' => $relationId,
1072
						'fieldid' => $field['id'],
1073
						'sequence' => $key,
1074
					])->execute();
1075
					$addedFields[] = $field['id'];
1076
				}
1077
			}
1078
			$transaction->commit();
1079
		} catch (\Throwable $e) {
1080
			$transaction->rollBack();
1081
			throw $e;
1082
		}
1083
		\App\Relation::clearCacheById($relationId);
1084
	}
1085
1086
	public static function updateModuleRelatedInventoryFields($relationId, $fields)
1087
	{
1088
		$db = \App\Db::getInstance('admin');
1089
		$db->createCommand()->delete('a_#__relatedlists_inv_fields', ['relation_id' => $relationId])->execute();
1090
		if ($fields) {
1091
			foreach ($fields as $key => $field) {
1092
				$db->createCommand()->insert('a_#__relatedlists_inv_fields', [
1093
					'relation_id' => $relationId,
1094
					'fieldname' => $field,
1095
					'sequence' => $key,
1096
				])->execute();
1097
			}
1098
		}
1099
		\App\Cache::clear();
1100
	}
1101
1102
	public function isActive()
1103
	{
1104
		return 0 == $this->get('presence') ? true : false;
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->get('presence') of type mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
1105
	}
1106
1107
	public function getFields($type = false)
1108
	{
1109
		$fields = $this->get('fields');
1110
		if (!$fields) {
1111
			$fields = [];
1112
			$relatedModel = $this->getRelationModuleModel();
1113
			$relatedModelFields = $relatedModel->getFields();
1114
1115
			foreach ($relatedModelFields as $fieldModel) {
1116
				if ($fieldModel->isViewable()) {
1117
					$fields[] = $fieldModel;
1118
				}
1119
			}
1120
			$this->set('fields', $fields);
1121
		}
1122
		if ($type) {
1123
			foreach ($fields as $key => $fieldModel) {
1124
				if ($fieldModel->getFieldDataType() != $type) {
1125
					unset($fields[$key]);
1126
				}
1127
			}
1128
		}
1129
		return $fields;
1130
	}
1131
1132
	/**
1133
	 * Gets relation data fields.
1134
	 *
1135
	 * @return array
1136
	 */
1137
	public function getRelationFields(): array
1138
	{
1139
		return method_exists($this->getTypeRelationModel(), 'getFields') ? $this->getTypeRelationModel()->getFields() : [];
1140
	}
1141
1142
	/**
1143
	 * Set conditions for relation fields.
1144
	 *
1145
	 * @param array $conditions
1146
	 *
1147
	 * @return self
1148
	 */
1149
	public function setRelationConditions(array $conditions): self
1150
	{
1151
		$group = 'and';
1152
		$relFields = $this->getRelationFields();
1153
		foreach ($conditions as $groupInfo) {
1154
			if (empty($groupInfo) || !array_filter($groupInfo)) {
1155
				$group = 'or';
1156
				continue;
1157
			}
1158
			$dataGroup = [$group];
1159
			foreach ($groupInfo as $fieldSearchInfo) {
1160
				[$fieldName, $operator, $fieldValue] = array_pad($fieldSearchInfo, 3, false);
1161
				$field = $relFields[$fieldName] ?? null;
1162
				if (!$field || (($className = '\App\Conditions\QueryFields\\' . ucfirst($field->getFieldDataType()) . 'Field') && !class_exists($className))) {
1163
					continue;
1164
				}
1165
				$queryField = new $className($this->getQueryGenerator(), $field);
1166
				$queryField->setValue($fieldValue);
1167
				$queryField->setOperator($operator);
1168
				$condition = $queryField->getCondition();
1169
				if ($condition) {
1170
					$dataGroup[] = $condition;
1171
				}
1172
			}
1173
			$this->getQueryGenerator()->addNativeCondition($dataGroup);
1174
		}
1175
		return $this;
1176
	}
1177
1178
	public static function getReferenceTableInfo($moduleName, $refModuleName)
1179
	{
1180
		$temp = [$moduleName, $refModuleName];
1181
		sort($temp);
1182
		$tableName = 'u_yf_' . strtolower($temp[0]) . '_' . strtolower($temp[1]);
1183
1184
		if ($temp[0] == $moduleName) {
1185
			$baseColumn = 'relcrmid';
1186
			$relColumn = 'crmid';
1187
		} else {
1188
			$baseColumn = 'crmid';
1189
			$relColumn = 'relcrmid';
1190
		}
1191
		return ['table' => $tableName, 'module' => $temp[0], 'base' => $baseColumn, 'rel' => $relColumn];
1192
	}
1193
1194
	public function updateFavoriteForRecord($action, $data)
1195
	{
1196
		$db = App\Db::getInstance();
1197
		$moduleName = $this->getParentModuleModel()->get('name');
1198
		$result = false;
1199
		if ('add' === $action) {
1200
			$result = $db->createCommand()->insert('u_#__favorites', [
1201
				'data' => date('Y-m-d H:i:s'),
1202
				'crmid' => $data['crmid'],
1203
				'module' => $moduleName,
1204
				'relcrmid' => $data['relcrmid'],
1205
				'relmodule' => $this->getRelationModuleName(),
1206
				'userid' => App\User::getCurrentUserId(),
1207
			])->execute();
1208
		} elseif ('delete' === $action) {
1209
			$result = $db->createCommand()->delete('u_#__favorites', [
1210
				'crmid' => $data['crmid'],
1211
				'module' => $moduleName,
1212
				'relcrmid' => $data['relcrmid'],
1213
				'relmodule' => $this->getRelationModuleName(),
1214
				'userid' => App\User::getCurrentUserId(),
1215
			])->execute();
1216
		}
1217
		return $result;
1218
	}
1219
1220
	public static function updateStateFavorites($relationId, $status)
1221
	{
1222
		\App\Db::getInstance()->createCommand()->update('vtiger_relatedlists', ['favorites' => $status], ['relation_id' => $relationId])->execute();
1223
		\App\Relation::clearCacheById($relationId);
1224
	}
1225
1226
	/**
1227
	 * Get custom view list.
1228
	 *
1229
	 * @return string[]
1230
	 */
1231
	public function getCustomViewList(): array
1232
	{
1233
		if (isset($this->customViewList)) {
1234
			return $this->customViewList;
1235
		}
1236
		$cv = [];
1237
		$selectedCv = $this->getCustomView();
1238
		if (empty($selectedCv) || \in_array('relation', $selectedCv)) {
1239
			$cv['relation'] = \App\Language::translate('LBL_RECORDS_FROM_RELATION');
1240
			unset($selectedCv[array_search('relation', $selectedCv)]);
1241
		}
1242
		if ($selectedCv) {
1243
			$moduleName = $this->getRelationModuleName();
1244
			$all = CustomView_Record_Model::getAll($moduleName);
1245
			if (\in_array('all', $selectedCv)) {
1246
				unset($selectedCv[array_search('all', $selectedCv)]);
1247
				foreach ($all as $cvId => $cvModel) {
1248
					$cv[$cvId] = \App\Language::translate($cvModel->get('viewname'), $moduleName);
1249
				}
1250
			} elseif (\in_array('private', $selectedCv)) {
1251
				unset($selectedCv[array_search('private', $selectedCv)]);
1252
				foreach ($all as $cvId => $cvModel) {
1253
					if ($cvModel->isMine()) {
1254
						$cv[$cvId] = \App\Language::translate($cvModel->get('viewname'), $moduleName);
1255
					}
1256
				}
1257
			}
1258
			foreach ($selectedCv as $cvId) {
1259
				if (isset($all[$cvId])) {
1260
					$cv[$cvId] = \App\Language::translate($all[$cvId]->get('viewname'), $moduleName);
1261
				}
1262
			}
1263
		}
1264
		return $this->customViewList = $cv;
1265
	}
1266
1267
	/**
1268
	 * Get sort orderby for custom view.
1269
	 *
1270
	 * @param int|string $cvId
1271
	 *
1272
	 * @return array
1273
	 */
1274
	public function getCustomViewOrderBy($cvId): array
1275
	{
1276
		$orderBy = [];
1277
		if ($cvId && is_numeric($cvId) && $this->get('custom_view_orderby') && ($customViewRecordModel = CustomView_Record_Model::getInstanceById($cvId))) {
1278
			$orderBy = $customViewRecordModel->getSortOrderBy();
1279
		}
1280
		return $orderBy;
1281
	}
1282
}
1283