Passed
Push — developer ( eaef4c...8edcad )
by Radosław
27:06 queued 08:09
created

Vtiger_RelationListView_Model::getHeaders()   D

Complexity

Conditions 18
Paths 64

Size

Total Lines 48
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 34
c 1
b 0
f 0
dl 0
loc 48
ccs 0
cts 27
cp 0
rs 4.8666
cc 18
nc 64
nop 0
crap 342

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
 /* +***********************************************************************************
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 Vtiger_RelationListView_Model extends \App\Base
14
{
15
	/**
16
	 * Relation model instance.
17
	 *
18
	 * @var Vtiger_Relation_Model
19
	 */
20
	protected $relationModel;
21
22
	/**
23
	 * Parent record model instance.
24
	 *
25
	 * @var Vtiger_Record_Model
26
	 */
27
	protected $parentRecordModel;
28
29
	/**
30
	 * Related module model instance.
31
	 *
32
	 * @var Vtiger_Module_Model
33
	 */
34
	protected $relatedModuleModel;
35
36
	/**
37
	 * Mandatory columns.
38
	 *
39
	 * @var array
40
	 */
41
	protected $mandatoryColumns = [];
42
43
	/**
44
	 * Set relation model instance.
45
	 *
46
	 * @param Vtiger_Relation_Model $relation
47
	 *
48
	 * @return $this
49 1
	 */
50
	public function setRelationModel($relation)
51 1
	{
52
		$this->relationModel = $relation;
53 1
		return $this;
54
	}
55
56
	/**
57
	 * Get relation model instance.
58
	 *
59
	 * @return Vtiger_Relation_Model
60
	 */
61 1
	public function getRelationModel()
62
	{
63 1
		return $this->relationModel;
64
	}
65
66
	/**
67
	 * Set parent record model instance.
68
	 *
69
	 * @param Vtiger_Record_Model $parentRecord
70
	 *
71
	 * @return $this
72
	 */
73 1
	public function setParentRecordModel($parentRecord)
74
	{
75 1
		$this->parentRecordModel = $parentRecord;
76
77 1
		return $this;
78
	}
79
80
	/**
81
	 * Get parent record model instance.
82
	 *
83
	 * @return Vtiger_Record_Model
84
	 */
85 1
	public function getParentRecordModel()
86
	{
87 1
		return $this->parentRecordModel;
88
	}
89
90
	/**
91
	 * Set related module model instance.
92
	 *
93
	 * @param Vtiger_Module_Model $relatedModuleModel
94
	 *
95
	 * @return $this
96
	 */
97 1
	public function setRelatedModuleModel($relatedModuleModel)
98
	{
99 1
		$this->relatedModuleModel = $relatedModuleModel;
100
101 1
		return $this;
102
	}
103
104
	/**
105
	 * Get related module model instance.
106
	 *
107
	 * @return Vtiger_Module_Model
108
	 */
109 1
	public function getRelatedModuleModel()
110
	{
111 1
		return $this->relatedModuleModel;
112
	}
113
114
	/**
115
	 * Get query generator instance.
116
	 *
117
	 * @return \App\QueryGenerator
118
	 */
119 1
	public function getQueryGenerator()
120
	{
121 1
		return $this->getRelationModel()->getQueryGenerator();
122
	}
123
124
	/**
125
	 * Function to identify if the module supports quick search or not.
126
	 */
127
	public function isQuickSearchEnabled(): bool
128
	{
129
		return $this->has('quickSearchEnabled') ? $this->get('quickSearchEnabled') : true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->has('quick...kSearchEnabled') : true could return the type null which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
130
	}
131
132
	/**
133 1
	 * Get relation list view model instance.
134
	 *
135 1
	 * @param Vtiger_Record_Model $parentRecordModel
136 1
	 * @param string              $relationModuleName
137 1
	 * @param bool|int            $relationId
138
	 * @param int|string          $cvId
139 1
	 *
140 1
	 * @return self
141 1
	 */
142
	public static function getInstance(Vtiger_Record_Model $parentRecordModel, string $relationModuleName, $relationId = false, $cvId = 0)
143 1
	{
144 1
		$parentModuleModel = $parentRecordModel->getModule();
145 1
		$className = Vtiger_Loader::getComponentClassName('Model', 'RelationListView', $parentModuleModel->getName());
146 1
		$instance = new $className();
147
		if ($relationId) {
148 1
			$relationModelInstance = Vtiger_Relation_Model::getInstanceById($relationId);
149 1
		} else {
150 1
			$relationModelInstance = Vtiger_Relation_Model::getInstance($parentModuleModel, Vtiger_Module_Model::getInstance($relationModuleName), $relationId);
151 1
		}
152
		if (!$relationModelInstance) {
0 ignored issues
show
introduced by
$relationModelInstance is of type Vtiger_Relation_Model, thus it always evaluated to true.
Loading history...
153 1
			return false;
154
		}
155
		$instance->setParentRecordModel($parentRecordModel);
156
		$instance->setRelatedModuleModel($relationModelInstance->getRelationModuleModel());
157
		$queryGenerator = new \App\QueryGenerator($relationModelInstance->getRelationModuleModel()->getName());
158
		if (is_numeric($cvId)) {
159
			$instance->set('viewId', $cvId);
160
		}
161
		$relationModelInstance->set('query_generator', $queryGenerator);
162
		$relationModelInstance->set('parentRecord', $parentRecordModel);
163 1
		$instance->setRelationModel($relationModelInstance);
164
		return $instance;
165 1
	}
166
167
	/**
168 1
	 * Function to get Relation query.
169 1
	 *
170 1
	 * @param mixed $returnQueryGenerator
171 1
	 *
172 1
	 * @return \App\Db\Query|\App\QueryGenerator
173 1
	 */
174 1
	public function getRelationQuery($returnQueryGenerator = false)
175
	{
176
		if ($this->has('Query')) {
177
			return $this->get('Query');
178
		}
179 1
		$this->loadCustomView();
180
		$this->loadCondition();
181
		$this->loadOrderBy();
182 1
		$relationModelInstance = $this->getRelationModel();
183 1
		if (!empty($relationModelInstance) && $relationModelInstance->get('name')) {
184 1
			$queryGenerator = $relationModelInstance->getQuery();
185
			$relationModuleName = $queryGenerator->getModule();
186
			if (isset($this->mandatoryColumns[$relationModuleName])) {
187
				foreach ($this->mandatoryColumns[$relationModuleName] as &$columnName) {
188
					$queryGenerator->setCustomColumn($columnName);
189
				}
190
			}
191
			if ($returnQueryGenerator) {
192 1
				return $queryGenerator;
193
			}
194 1
			$query = $queryGenerator->createQuery();
195 1
			$this->set('Query', $query);
196 1
			return $query;
197
		}
198
		throw new \App\Exceptions\AppException('>>> No relationModel instance, requires verification 2 <<<');
199 1
	}
200
201
	/**
202 1
	 * Load list view conditions.
203 1
	 */
204
	public function loadCondition()
205 1
	{
206
		$relatedModuleName = $this->getRelatedModuleModel()->getName();
207
		$queryGenerator = $this->getRelationModel()->getQueryGenerator();
208 1
		if ($entityState = $this->get('entityState')) {
209
			$queryGenerator->setStateCondition($entityState);
210
		}
211
		if ($relatedModuleName === $this->get('src_module') && !$this->isEmpty('src_record')) {
212
			$queryGenerator->addCondition('id', $this->get('src_record'), 'n');
213
		}
214
		if ($searchParams = $this->getArray('search_params')) {
215
			$queryGenerator->parseAdvFilter($searchParams);
216
		}
217 1
		if (!$this->isEmpty('search_key')) {
218
			$queryGenerator->addCondition($this->get('search_key'), $this->get('search_value'), $this->get('operator'));
219 1
		}
220 1
		if ($searchParams = $this->getArray('search_rel_params')) {
221 1
			$this->getRelationModel()->setRelationConditions($searchParams);
222 1
		}
223 1
	}
224
225 1
	/**
226 1
	 * Load custom view.
227 1
	 */
228
	public function loadCustomView()
229
	{
230
		if ($this->has('viewId')) {
231 1
			$cvId = $this->get('viewId');
232
		} else {
233 1
			$cvId = array_key_first($this->getRelationModel()->getCustomViewList());
234 1
		}
235 1
		if ('relation' !== $cvId) {
236 1
			$this->getRelationModel()->getQueryGenerator()->initForCustomViewById($cvId);
237 1
		}
238 1
	}
239 1
240
	/**
241
	 * Function to get the related list view entries.
242 1
	 *
243 1
	 * @param Vtiger_Paging_Model $pagingModel
244
	 *
245
	 * @return Vtiger_Record_Model[]
246
	 */
247
	public function getEntries(Vtiger_Paging_Model $pagingModel)
248
	{
249
		$pageLimit = $pagingModel->getPageLimit();
250
		$query = $this->getRelationQuery();
251 1
		$query->limit($pageLimit + 1)->offset($pagingModel->getStartIndex());
0 ignored issues
show
Bug introduced by
The method limit() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

251
		$query->/** @scrutinizer ignore-call */ 
252
          limit($pageLimit + 1)->offset($pagingModel->getStartIndex());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method offset() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

251
		$query->limit($pageLimit + 1)->/** @scrutinizer ignore-call */ offset($pagingModel->getStartIndex());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
252
		$rows = $query->all();
0 ignored issues
show
Bug introduced by
The method all() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

252
		/** @scrutinizer ignore-call */ 
253
  $rows = $query->all();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
253 1
254
		$count = \count($rows);
255
		if ($count > $pageLimit) {
256
			array_pop($rows);
257
			$pagingModel->set('nextPageExists', true);
258 1
		} else {
259
			$pagingModel->set('nextPageExists', false);
260 1
		}
261 1
		$relatedRecordList = $this->getRecordsFromArray($rows);
262
		$pagingModel->calculatePageRange(\count($relatedRecordList));
263
		return $relatedRecordList;
264
	}
265
266
	/**
267
	 * Gets all entries.
268
	 *
269
	 * @return \Vtiger_Record_Model[]
270
	 */
271 1
	public function getAllEntries(): array
272
	{
273
		return $this->getRecordsFromArray($this->getRelationQuery()->all());
274
	}
275
276
	/**
277
	 * Get models of records from array.
278 1
	 *
279
	 * @param array $rows
280 1
	 *
281 1
	 * @return \Vtiger_Record_Model[]
282 1
	 */
283 1
	public function getRecordsFromArray(array $rows)
284
	{
285
		$listViewRecordModels = $relatedFields = [];
286
		$moduleModel = $this->getRelationModel()->getRelationModuleModel();
287 1
		$recordId = $this->getParentRecordModel()->getId();
288
		foreach ($this->getQueryGenerator()->getRelatedFields() as $fieldInfo) {
289
			$relatedFields[$fieldInfo['relatedModule']][$fieldInfo['sourceField']][] = $fieldInfo['relatedField'];
290
		}
291
		foreach ($rows as $row) {
292
			if ($recordId == $row['id']) {
293
				continue;
294
			}
295
			$extRecordModel = [];
296
			foreach ($relatedFields as $relatedModuleName => $fields) {
297
				foreach ($fields as $sourceField => $field) {
298
					$recordData = [
299
						'id' => $row[$sourceField . $relatedModuleName . 'id'] ?? 0,
300
					];
301
					foreach ($field as $relatedFieldName) {
302
						$recordData[$relatedFieldName] = $row[$sourceField . $relatedModuleName . $relatedFieldName];
303
						unset($row[$sourceField . $relatedModuleName . $relatedFieldName]);
304
					}
305
					$extRecordModel[$sourceField][$relatedModuleName] = Vtiger_Module_Model::getInstance($relatedModuleName)->getRecordFromArray($recordData);
306
				}
307
			}
308
			$recordModel = $moduleModel->getRecordFromArray($row);
309
			$recordModel->ext = $extRecordModel;
310
			$this->getEntryExtend($recordModel);
311
			$listViewRecordModels[$row['id']] = $recordModel;
312
		}
313
		return $listViewRecordModels;
314
	}
315
316
	/**
317
	 * Function extending recordModel object with additional information.
318
	 *
319
	 * @param Vtiger_Record_Model $recordModel
320
	 */
321
	public function getEntryExtend(Vtiger_Record_Model $recordModel)
0 ignored issues
show
Unused Code introduced by
The parameter $recordModel is not used and could be removed. ( Ignorable by Annotation )

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

321
	public function getEntryExtend(/** @scrutinizer ignore-unused */ Vtiger_Record_Model $recordModel)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
322
	{
323
	}
324
325
	/**
326
	 * Set list view order by.
327
	 */
328
	public function loadOrderBy()
329
	{
330
		$orderBy = $this->get('orderby');
331
		if (!empty($orderBy) && \is_array($orderBy)) {
332
			foreach ($orderBy as $fieldName => $sortFlag) {
333
				$field = $this->getRelationModel()->getRelationModuleModel()->getFieldByName($fieldName);
334
				if ($field || 'id' === $fieldName) {
335
					$this->getRelationModel()->getQueryGenerator()->setOrder($fieldName, $sortFlag);
336
				} else {
337
					\App\Log::warning("[RelationListView] Incorrect value of sorting: '$fieldName'");
338
				}
339
			}
340
		}
341
	}
342
343
	/**
344
	 * Get header fields.
345
	 *
346
	 * @return Vtiger_Field_Model[]
347
	 */
348
	public function getHeaders()
349
	{
350
		$fields = [];
351
		foreach (App\Field::getFieldsFromRelation($this->getRelationModel()->getId()) as $fieldName) {
352
			$fields[$fieldName] = $this->relatedModuleModel->getFieldByName($fieldName);
353
		}
354
		if (!$fields && $this->get('viewId')) {
355
			$moduleModel = $this->getRelationModel()->getRelationModuleModel();
356
			$customView = App\CustomView::getInstance($moduleModel->getName());
357
			foreach ($customView->getColumnsListByCvid($this->get('viewId')) as $fieldInfo) {
358
				$fieldName = $fieldInfo['field_name'];
359
				$sourceFieldName = $fieldInfo['source_field_name'] ?? '';
360
				$fieldModel = Vtiger_Field_Model::getInstance($fieldName, Vtiger_Module_Model::getInstance($fieldInfo['module_name']));
361
				if (!$fieldModel) {
362
					\App\Log::warning("The field does not exist: '$fieldName' | Module: " . $this->getRelationModel()->getRelationModuleModel()->getName(), __METHOD__);
363
					continue;
364
				}
365
				if (!$fieldModel->isActiveField() || ($sourceFieldName && !$moduleModel->getFieldByName($sourceFieldName)->isActiveField())) {
366
					continue;
367
				}
368
				if ($sourceFieldName) {
369
					$fieldModel->set('source_field_name', $sourceFieldName);
370
				}
371
				$fields[$fieldModel->getFullName()] = $fieldModel;
372
			}
373
		}
374
		if (empty($fields)) {
375
			$fields = $this->getRelationModel()->getQueryFields();
376
		}
377
		unset($fields['id']);
378
		foreach ($fields as $fieldName => $fieldModel) {
379
			if (!$fieldModel) {
380
				\App\Log::warning("The field does not exist: '$fieldName' | Module: " . $this->getRelationModel()->getRelationModuleModel()->getName(), __METHOD__);
381
				unset($fields[$fieldName]);
382
			} elseif (!$fieldModel->isViewable() && !$fieldModel->get('fromOutsideList')) {
383
				unset($fields[$fieldName]);
384
			}
385
		}
386
		if ($relFields = $this->getRelationModel()->getRelationFields()) {
387
			foreach ($relFields as $fieldName => $fieldModel) {
388
				if (!$fieldModel) {
389
					\App\Log::warning("The field does not exist: '$fieldName' | Module: " . $this->getRelationModel()->getRelationModuleModel()->getName(), __METHOD__);
390
					unset($relFields[$fieldName]);
391
				}
392
			}
393
			$fields = array_merge($fields, $relFields);
394
		}
395
		return $fields;
396
	}
397
398
	/**
399
	 * Function to get Total number of record in this relation.
400
	 *
401
	 * @return int
402
	 */
403
	public function getRelatedEntriesCount()
404
	{
405
		return $this->getRelationQuery()->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getRelationQuery()->count() also could return the type string which is incompatible with the documented return type integer.
Loading history...
Bug introduced by
The method count() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

405
		return $this->getRelationQuery()->/** @scrutinizer ignore-call */ count();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
406
	}
407
408
	/**
409
	 * Get tree view model.
410
	 *
411
	 * @return Vtiger_TreeCategoryModal_Model
412
	 */
413
	public function getTreeViewModel()
414
	{
415
		return Vtiger_TreeCategoryModal_Model::getInstance($this->getRelatedModuleModel());
0 ignored issues
show
Bug Best Practice introduced by
The expression return Vtiger_TreeCatego...etRelatedModuleModel()) returns the type Vtiger_TreeView_Model which is incompatible with the documented return type Vtiger_TreeCategoryModal_Model.
Loading history...
416
	}
417
418
	/**
419
	 * Get tree headers.
420
	 *
421
	 * @return string[]
422
	 */
423
	public function getTreeHeaders()
424
	{
425
		$fields = $this->getTreeViewModel()->getTreeField();
426
427
		return [
428
			'name' => $fields['fieldlabel'],
429
		];
430
	}
431
432
	/**
433
	 * Get tree entries.
434
	 *
435
	 * @return array[]
436
	 */
437
	public function getTreeEntries()
438
	{
439
		$relModuleName = $this->getRelatedModuleModel()->getName();
440
		$relationModelInstance = $this->getRelationModel();
441
		$template = $this->getTreeViewModel()->getTemplate();
442
		$showCreatorDetail = $relationModelInstance->get('creator_detail');
443
		$showComment = $relationModelInstance->get('relation_comment');
444
445
		$rows = $relationModelInstance->getRelationTree();
446
		$trees = [];
447
		foreach ($rows as &$row) {
448
			$pieces = explode('::', $row['parentTree']);
449
			end($pieces);
450
			$parent = prev($pieces);
451
			$parentName = '';
452
			if ($row['depth'] > 0) {
453
				$treeDetail = App\Fields\Tree::getValueByTreeId($template, $parent);
0 ignored issues
show
Bug introduced by
$template of type type is incompatible with the type integer expected by parameter $templateId of App\Fields\Tree::getValueByTreeId(). ( Ignorable by Annotation )

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

453
				$treeDetail = App\Fields\Tree::getValueByTreeId(/** @scrutinizer ignore-type */ $template, $parent);
Loading history...
454
				$parentName = '(' . App\Language::translate($treeDetail['name'], $relModuleName) . ') ';
0 ignored issues
show
Bug introduced by
$relModuleName of type boolean is incompatible with the type string expected by parameter $moduleName of App\Language::translate(). ( Ignorable by Annotation )

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

454
				$parentName = '(' . App\Language::translate($treeDetail['name'], /** @scrutinizer ignore-type */ $relModuleName) . ') ';
Loading history...
455
			}
456
			$tree = [
457
				'id' => $row['tree'],
458
				'name' => $parentName . App\Language::translate($row['name'], $relModuleName),
459
				'parent' => 0 == $parent ? '#' : $parent,
460
			];
461
			if ($showCreatorDetail) {
462
				$tree['rel_created_user'] = \App\Fields\Owner::getLabel($row['rel_created_user']);
463
				$tree['rel_created_time'] = App\Fields\DateTime::formatToDisplay($row['rel_created_time']);
464
			}
465
			if ($showComment) {
466
				$tree['rel_comment'] = $row['rel_comment'];
467
			}
468
			if (!empty($row['icon'])) {
469
				$tree['icon'] = $row['icon'];
470
			}
471
			$trees[] = $tree;
472
		}
473
		return $trees;
474
	}
475
476
	/**
477
	 * Function to get Total number of record in this relation.
478
	 *
479
	 * @return int
480
	 */
481
	public function getRelatedTreeEntriesCount()
482
	{
483
		$recordId = $this->getParentRecordModel()->getId();
484
		$relModuleId = $this->getRelatedModuleModel()->getId();
485
		$treeViewModel = $this->getTreeViewModel();
486
		$template = $treeViewModel->getTemplate();
487
488
		return (new \App\Db\Query())->from('vtiger_trees_templates_data ttd')->innerJoin('u_#__crmentity_rel_tree rel', 'rel.tree = ttd.tree')
0 ignored issues
show
Bug Best Practice introduced by
The expression return new App\Db\Query(...$relModuleId))->count() also could return the type string which is incompatible with the documented return type integer.
Loading history...
489
			->where(['ttd.templateid' => $template, 'rel.crmid' => $recordId, 'rel.relmodule' => $relModuleId])->count();
490
	}
491
492
	/**
493
	 * Get create url from parent record.
494
	 *
495
	 * @param bool $fullView
496
	 *
497
	 * @return string
498
	 */
499
	public function getCreateViewUrl(bool $fullView = false)
500
	{
501
		$createViewUrl = $this->getRelationModel()->getCreateViewUrl($fullView);
502
		if (!empty(Config\Relation::$addSearchParamsToCreateView) && ($searchParams = $this->getArray('search_params')) && isset($searchParams['and']) && \is_array($searchParams['and'])) {
0 ignored issues
show
Bug introduced by
The type Config\Relation was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
503
			foreach ($searchParams['and'] as $row) {
504
				if ('e' === $row['comparator']) {
505
					$createViewUrl .= "&{$row['field_name']}={$row['value']}";
506
				}
507
			}
508
		}
509
		return $createViewUrl;
510
	}
511
512
	/**
513
	 * Function to get the links for related list.
514
	 *
515
	 * @return Vtiger_Link_Model[]
516
	 */
517
	public function getLinks(): array
518
	{
519
		$parentRecordModel = $this->getParentRecordModel();
520
		$relationModelInstance = $this->getRelationModel();
521
		$relatedModuleModel = $relationModelInstance->getRelationModuleModel();
522
		$relatedLink = [
523
			'RELATEDLIST_VIEWS' => [
524
				Vtiger_Link_Model::getInstanceFromValues([
525 1
					'linktype' => 'RELATEDLIST_VIEWS',
526
					'linklabel' => 'LBL_RECORDS_LIST',
527 1
					'view' => 'List',
528 1
					'linkicon' => 'far fa-list-alt',
529
				]),
530 1
				Vtiger_Link_Model::getInstanceFromValues([
531 1
					'linktype' => 'RELATEDLIST_VIEWS',
532 1
					'linklabel' => 'LBL_RECORDS_PREVIEW_LIST',
533 1
					'view' => 'ListPreview',
534 1
					'linkicon' => 'fas fa-desktop',
535
				]),
536
			],
537 1
		];
538 1
		if (!$parentRecordModel->isReadOnly()) {
539
			$selectLinks = $this->getSelectRelationLinks();
540
			foreach ($selectLinks as $selectLinkModel) {
541
				$selectLinkModel->set('_selectRelation', true)->set('_module', $relatedModuleModel);
542
			}
543
			$relatedLink['LISTVIEWBASIC'] = $selectLinks;
544
			$relatedLink = array_merge_recursive($relatedLink, $this->getAddRelationLinks());
545
			if ('Documents' === $relatedModuleModel->getName()) {
0 ignored issues
show
introduced by
The condition 'Documents' === $relatedModuleModel->getName() is always false.
Loading history...
546
				$relatedLink['RELATEDLIST_MASSACTIONS'][] = Vtiger_Link_Model::getInstanceFromValues([
547
					'linktype' => 'RELATEDLIST_MASSACTIONS',
548
					'linklabel' => 'LBL_MASS_DOWNLOAD',
549
					'linkurl' => "javascript:Vtiger_RelatedList_Js.triggerMassDownload('index.php?module={$parentRecordModel->getModuleName()}&action=RelationAjax&mode=massDownload&src_record={$parentRecordModel->getId()}&relatedModule=Documents&mode=multiple','sendByForm')",
550
					'linkclass' => '',
551
					'linkicon' => 'fas fa-download',
552
				]);
553
			}
554
			if ($relatedModuleModel->isPermitted('MassSendSMS') && ($smsNotifierModel = \Vtiger_Module_Model::getInstance('SMSNotifier'))->isSMSActiveForModule($relatedModuleModel->getName())) {
0 ignored issues
show
Bug introduced by
The method isSMSActiveForModule() does not exist on Vtiger_Module_Model. It seems like you code against a sub-type of Vtiger_Module_Model such as SMSNotifier_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

554
			if ($relatedModuleModel->isPermitted('MassSendSMS') && ($smsNotifierModel = \Vtiger_Module_Model::getInstance('SMSNotifier'))->/** @scrutinizer ignore-call */ isSMSActiveForModule($relatedModuleModel->getName())) {
Loading history...
555
				$relatedLink['RELATEDLIST_MASSACTIONS'][] = Vtiger_Link_Model::getInstanceFromValues([
556
					'linktype' => 'RELATEDLIST_MASSACTIONS',
557
					'linklabel' => 'LBL_MASS_SEND_SMS',
558
					'linkdata' => ['url' => $smsNotifierModel->getMassSMSUrlForModule($relatedModuleModel->getName()), 'type' => 'modal'],
0 ignored issues
show
Bug introduced by
The method getMassSMSUrlForModule() does not exist on Vtiger_Module_Model. It seems like you code against a sub-type of Vtiger_Module_Model such as SMSNotifier_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

558
					'linkdata' => ['url' => $smsNotifierModel->/** @scrutinizer ignore-call */ getMassSMSUrlForModule($relatedModuleModel->getName()), 'type' => 'modal'],
Loading history...
559
					'linkicon' => 'fas fa-comment-sms',
560
					'linkclass' => 'js-mass-record-event',
561
				]);
562
			}
563
			if ($relatedModuleModel->isPermitted('QuickExportToExcel')) {
564
				$relatedLink['RELATEDLIST_MASSACTIONS'][] = Vtiger_Link_Model::getInstanceFromValues([
565
					'linktype' => 'RELATEDLIST_MASSACTIONS',
566
					'linklabel' => 'LBL_QUICK_EXPORT',
567
					'linkurl' => "javascript:Vtiger_RelatedList_Js.triggerMassAction('index.php?module={$parentRecordModel->getModuleName()}&action=RelationAjax&mode=exportToExcel&src_record={$parentRecordModel->getId()}&relatedModule={$relatedModuleModel->getName()}&relationId={$this->getRelationModel()->getId()}&isSortActive=true','sendByForm')",
568
					'linkclass' => '',
569
					'linkicon' => 'fas fa-file-export',
570
				]);
571
			}
572
			if ($relatedModuleModel->isPermitted('ExportPdf')) {
573
				$handlerClass = Vtiger_Loader::getComponentClassName('Model', 'PDF', $relatedModuleModel->getName());
574
				$pdfModel = new $handlerClass();
575
				if ($pdfModel->getActiveTemplatesForModule($relatedModuleModel->getName(), 'RelatedList')) {
576
					$relatedLink['RELATEDLIST_BASIC'][] = Vtiger_Link_Model::getInstanceFromValues([
577
						'linktype' => 'RELATEDLIST_BASIC',
578
						'linkdata' => [
579
							'type' => 'modal',
580
							'url' => "index.php?module={$parentRecordModel->getModuleName()}&view=PDF&fromview=RelatedList",
581
						],
582
						'linkclass' => 'btn-light js-mass-record-event',
583
						'linkicon' => 'fas fa-file-pdf',
584
						'linkhint' => \App\Language::translate('LBL_EXPORT_PDF'),
585
					]);
586
				}
587
			}
588
		}
589
		$eventHandler = new App\EventHandler();
590
		$eventHandler->setRecordModel($parentRecordModel);
591
		$eventHandler->setModuleName($relatedModuleModel->getName());
592
		$eventHandler->setParams([
593
			'relatedLink' => $relatedLink,
594
			'viewInstance' => $this,
595
		]);
596
		$eventHandler->trigger('RelationListLinks');
597
		return $eventHandler->getParam('relatedLink');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $eventHandler->getParam('relatedLink') could return the type App\EventHandler|null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
598
	}
599
600
	/**
601
	 * Function to get the select links for related list.
602
	 *
603
	 * @return Vtiger_Link_Model[]
604
	 */
605
	public function getSelectRelationLinks(): array
606
	{
607
		if (!$this->getRelationModel()->isSelectActionSupported() || $this->getParentRecordModel()->isReadOnly()) {
608
			return [];
609
		}
610
		$relatedModel = $this->getRelationModel()->getRelationModuleModel();
611
		if (!$relatedModel->isPermitted('DetailView')) {
612
			return [];
613
		}
614
		return [
615
			Vtiger_Link_Model::getInstanceFromValues([
616
				'linktype' => 'LISTVIEWBASIC',
617
				'linklabel' => \App\Language::translate('LBL_SELECT_RELATION', $relatedModel->getName()),
0 ignored issues
show
Bug introduced by
$relatedModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Language::translate(). ( Ignorable by Annotation )

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

617
				'linklabel' => \App\Language::translate('LBL_SELECT_RELATION', /** @scrutinizer ignore-type */ $relatedModel->getName()),
Loading history...
618
				'linkurl' => '',
619
				'linkicon' => 'fas fa-level-up-alt',
620
			]),
621
		];
622
	}
623
624
	/**
625
	 * Function to get the add links for related list.
626
	 *
627
	 * @return Vtiger_Link_Model[][]
628
	 */
629
	public function getAddRelationLinks(): array
630
	{
631
		$relationModelInstance = $this->getRelationModel();
632
		if (!$relationModelInstance->isAddActionSupported() || $this->getParentRecordModel()->isReadOnly()) {
633
			return [];
634
		}
635
		$relatedModel = $relationModelInstance->getRelationModuleModel();
636
		$addLinkModel = [
637
			'LISTVIEWBASIC' => [
638
				Vtiger_Link_Model::getInstanceFromValues([
639
					'linktype' => 'LISTVIEWBASIC',
640
					'linklabel' => App\Language::translate('LBL_ADD_RELATION', $relatedModel->getName()),
0 ignored issues
show
Bug introduced by
$relatedModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Language::translate(). ( Ignorable by Annotation )

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

640
					'linklabel' => App\Language::translate('LBL_ADD_RELATION', /** @scrutinizer ignore-type */ $relatedModel->getName()),
Loading history...
641
					'linkurl' => $this->getCreateViewUrl(),
642
					'linkqcs' => $relatedModel->isQuickCreateSupported(),
643
					'linkicon' => 'fas fa-plus',
644
				]),
645
			]
646
		];
647
		if ('Documents' === $relatedModel->getName()) {
0 ignored issues
show
introduced by
The condition 'Documents' === $relatedModel->getName() is always false.
Loading history...
648
			$addLinkModel['RELATEDLIST_BASIC'][] = Vtiger_Link_Model::getInstanceFromValues([
649
				'linktype' => 'LISTVIEWBASIC',
650
				'linklabel' => App\Language::translate('LBL_MASS_ADD', 'Documents'),
651
				'linkdata' => [
652
					'url' => 'index.php?module=Documents&view=MassAddDocuments&sourceView=Detail',
653
					'cb' => 'Documents_MassAddDocuments_Js.register',
654
					'view' => 'Detail',
655
				],
656
				'linkclass' => 'btn-light js-show-modal',
657
				'linkicon' => 'yfi-document-templates',
658
				'showLabel' => 1,
659
			]);
660
		}
661
		return $addLinkModel;
662
	}
663
664
	public function getFavoriteRecords()
665
	{
666
		return (new App\Db\Query())->select(['relcrmid'])->from('u_#__favorites')
667
			->where([
668
				'module' => $this->getParentRecordModel()->getModuleName(),
669
				'relmodule' => $this->getRelatedModuleModel()->getName(),
670
				'crmid' => $this->getParentRecordModel()->getId(),
671
				'userid' => App\User::getCurrentUserId(),
672
			])
673
			->column();
674
	}
675
676
	/**
677
	 * Set fileds.
678
	 *
679
	 * @param string|string[] $fields
680
	 */
681
	public function setFields($fields)
682
	{
683
		if (\is_string($fields)) {
684
			$fields = explode(',', $fields);
685
		}
686
		$relatedListFields = [];
687
		$relFields = $this->getRelationModel()->getRelationFields();
688
		foreach ($fields as $fieldName) {
689
			$fieldModel = $this->relatedModuleModel->getFieldByName($fieldName);
690
			if ($fieldModel) {
691
				$relatedListFields[$fieldName] = $fieldModel;
692
			} elseif (isset($relFields[$fieldName])) {
693
				$relatedListFields[$fieldName] = $relFields[$fieldName];
694
			} elseif (false !== strpos($fieldName, ':')) {
695
				[$relatedFieldName, $relatedModule, $sourceField] = array_pad(explode(':', $fieldName), 3, null);
696
				if (($model = \Vtiger_Module_Model::getInstance($relatedModule)) && ($fieldModel = $model->getFieldByName($relatedFieldName)) && $fieldModel->isActiveField()) {
697
					$fieldModel->set('source_field_name', $sourceField);
698
					$relatedListFields[$fieldName] = $fieldModel;
699
				}
700
			}
701
		}
702
		$this->relationModel->set('QueryFields', $relatedListFields);
703
	}
704
705
	/**
706
	 * Get widgets instances.
707
	 *
708
	 * @param int $recordId
709
	 *
710
	 * @return array
711
	 */
712
	public function getWidgets(int $recordId): array
713
	{
714
		$widgets = [];
715
		$moduleModel = $this->getRelatedModuleModel();
716
		foreach ($this->getWidgetsList() as $widgetCol) {
717
			foreach ($widgetCol as $widget) {
718
				$widgetName = Vtiger_Loader::getComponentClassName('Widget', $widget['type'], $moduleModel->getName());
719
				if (class_exists($widgetName)) {
720
					$widgetInstance = new $widgetName($moduleModel->getName(), $moduleModel, $recordId, $widget);
721
					$widgetObject = $widgetInstance->getWidget();
722
					if (\count($widgetObject) > 0) {
723
						$widgets[$widgetObject['wcol']][] = $widgetObject;
724
					}
725
				}
726
			}
727
		}
728
		return $widgets;
729
	}
730
731
	/**
732
	 * Get widgets list.
733
	 *
734
	 * @return array
735
	 */
736
	public function getWidgetsList(): array
737
	{
738
		$relationId = $this->getRelationModel()->getId();
739
		if (\App\Cache::has('RelatedModuleWidgets', $relationId)) {
740
			return \App\Cache::get('RelatedModuleWidgets', $relationId);
741
		}
742
		$query = (new App\Db\Query())->from('a_#__relatedlists_widgets')->where(['relation_id' => $relationId]);
743
		$dataReader = $query->orderBy(['sequence' => SORT_ASC])->createCommand()->query();
744
		$widgets = [1 => [], 2 => [], 3 => []];
745
		while ($row = $dataReader->read()) {
746
			$row['data'] = \App\Json::decode($row['data']);
747
			$widgets[$row['wcol']][$row['id']] = $row;
748
		}
749
		$dataReader->close();
750
		App\Cache::save('RelatedModuleWidgets', $relationId, $widgets);
751
		return $widgets;
752
	}
753
754
	/**
755
	 * Check if widgets exist.
756
	 *
757
	 * @return bool
758
	 */
759
	public function isWidgetsList(): bool
760
	{
761
		$widgets = $this->getWidgetsList();
762
		return !empty($widgets[1]) || !empty($widgets[2]) || !empty($widgets[3]);
763
	}
764
765
	/**
766
	 * Locked fields according to parameters passed.
767
	 *
768
	 * @param App\Request $request
769
	 *
770
	 * @return void
771
	 */
772
	public function loadSearchLockedFields(App\Request $request): void
773
	{
774
		$moduleModel = $this->getRelationModel()->getRelationModuleModel();
775
		if (!$request->isEmpty('lockedFields')) {
776
			foreach ($request->getArray('lockedFields') as $value) {
777
				$moduleModel->getFieldByName($value)->set('searchLockedFields', true);
778
			}
779
		}
780
		if (!$request->isEmpty('lockedEmptyFields')) {
781
			foreach ($request->getArray('lockedEmptyFields') as $value) {
782
				if (strpos($value, ':')) {
783
					[$fieldName, $moduleName] = explode(':', $value);
784
					$moduleModel = \Vtiger_Module_Model::getInstance($moduleName);
785
					$value = $fieldName;
786
				}
787
				$moduleModel->getFieldByName($value)->set('searchLockedEmptyFields', true);
788
			}
789
		}
790
		if (!$request->isEmpty('search_params')) {
791
			foreach ($request->getArray('search_params') as $values) {
792
				foreach ($values as $value) {
793
					if ('y' === $value[1]) {
794
						$moduleModel->getFieldByName($value[0])->set('searchLockedEmptyFields', true);
795
					}
796
				}
797
			}
798
		}
799
	}
800
}
801