Vtiger_Detail_View::showModuleBasicView()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 37
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 37
ccs 0
cts 23
cp 0
rs 9.1128
c 0
b 0
f 0
cc 5
nc 4
nop 1
crap 30
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
/**
13
 * Class Vtiger_Detail_View.
14
 *
15
 * @package View
16
 */
17
class Vtiger_Detail_View extends Vtiger_Index_View
18
{
19
	use \App\Controller\ExposeMethod;
20
21
	/** @var Vtiger_DetailView_Model Record model instance. */
22
	public $record;
23
24
	/** @var Vtiger_DetailRecordStructure_Model Detail record structure model instance. */
25
	protected $recordStructure;
26
27
	/** @var string Default request mode */
28
	public $defaultMode = '';
29
30
	/** @var string Page title. */
31
	protected $pageTitle = 'LBL_VIEW_DETAIL';
32
33
	/**
34
	 * Construct.
35
	 */
36
	public function __construct()
37
	{
38
		parent::__construct();
39
		$this->exposeMethod('showDetailViewByMode');
40
		$this->exposeMethod('showModuleDetailView');
41
		$this->exposeMethod('showModuleSummaryView');
42
		$this->exposeMethod('showModuleBasicView');
43
		$this->exposeMethod('showRecentActivities');
44
		$this->exposeMethod('showRecentComments');
45
		$this->exposeMethod('showRelatedList');
46
		$this->exposeMethod('showChildComments');
47
		$this->exposeMethod('showParentComments');
48
		$this->exposeMethod('showAllComments');
49
		$this->exposeMethod('showThreadComments');
50
		$this->exposeMethod('showSearchComments');
51
		$this->exposeMethod('getActivities');
52
		$this->exposeMethod('showRelatedProductsServices');
53
		$this->exposeMethod('showRelatedRecords');
54
		$this->exposeMethod('showRelatedTree');
55
		$this->exposeMethod('showRecentRelation');
56
		$this->exposeMethod('showOpenStreetMap');
57
		$this->exposeMethod('showInventoryDetails');
58
		$this->exposeMethod('showChat');
59
		$this->exposeMethod('processWizard');
60
		$this->exposeMethod('showModTrackerByField');
61
		$this->exposeMethod('showCharts');
62
		$this->exposeMethod('showInventoryEntries');
63
		$this->exposeMethod('showPDF');
64
	}
65
66
	/**
67
	 * Function to check permission.
68
	 *
69
	 * @param \App\Request $request
70
	 *
71
	 * @throws \App\Exceptions\NoPermittedToRecord
72
	 */
73
	public function checkPermission(App\Request $request)
74
	{
75
		if ($request->isEmpty('record')) {
76
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
77
		}
78
		$this->record = Vtiger_DetailView_Model::getInstance($request->getModule(), $request->getInteger('record'));
79
		if (!$this->record->getRecord()->isViewable()) {
80
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
81
		}
82
	}
83
84
	public function preProcess(App\Request $request, $display = true)
85
	{
86
		parent::preProcess($request, false);
87
88
		$moduleName = $request->getModule();
89
		$recordId = $request->getInteger('record');
90
		$recordModel = $this->record->getRecord();
91
		$this->recordStructure = Vtiger_RecordStructure_Model::getInstanceFromRecordModel($recordModel, Vtiger_RecordStructure_Model::RECORD_STRUCTURE_MODE_DETAIL);
92
		$fieldsInHeader = $this->recordStructure->getFieldInHeader();
0 ignored issues
show
Bug introduced by
The method getFieldInHeader() does not exist on Vtiger_RecordStructure_Model. It seems like you code against a sub-type of Vtiger_RecordStructure_Model such as Vtiger_DetailRecordStructure_Model. ( Ignorable by Annotation )

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

92
		/** @scrutinizer ignore-call */ 
93
  $fieldsInHeader = $this->recordStructure->getFieldInHeader();
Loading history...
93
94
		$eventHandler = new App\EventHandler();
95
		$eventHandler->setRecordModel($recordModel);
96
		$eventHandler->setModuleName($moduleName);
97
		$viewLinks = $this->record->getDetailViewLinks([
98
			'MODULE' => $moduleName,
99
			'RECORD' => $recordId,
100
			'VIEW' => $request->getByType('view', 2),
101
		]);
102
		$eventHandler->setParams([
103
			'viewLinks' => $viewLinks,
104
		]);
105
		$eventHandler->trigger('DetailViewBefore');
106
		$viewLinks = $eventHandler->getParams()['viewLinks'];
107
108
		$this->record->getWidgets();
109
		$viewer = $this->getViewer($request);
110
		$viewer->assign('RECORD', $recordModel);
111
		$moduleModel = $this->record->getModule();
112
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
113
		$selectedTabLabel = $request->getByType('tab_label', 'Text');
114
		$requestMode = $request->getByType('requestMode');
115
		$mode = $request->getMode();
116
		if (empty($selectedTabLabel) && !empty($requestMode)) {
117
			if ('full' == $requestMode) {
118
				$selectedTabLabel = 'LBL_RECORD_DETAILS';
119
			} else {
120
				$selectedTabLabel = 'LBL_RECORD_SUMMARY';
121
			}
122
		} elseif (empty($requestMode) && empty($mode)) {
123
			$selectedTabLabel = App\Config::module($moduleName, 'DEFAULT_VIEW_RECORD');
124
			if (empty($selectedTabLabel)) {
125
				if ('Detail' === $currentUserModel->get('default_record_view')) {
126
					$selectedTabLabel = 'LBL_RECORD_DETAILS';
127
				} else {
128
					if ($moduleModel->isSummaryViewSupported() && $this->record->widgetsList) {
129
						$selectedTabLabel = 'LBL_RECORD_SUMMARY';
130
					} else {
131
						$selectedTabLabel = 'LBL_RECORD_DETAILS';
132
					}
133
				}
134
			}
135
		}
136
		$detailViewLabel = [];
137
		if (isset($viewLinks['DETAILVIEWRELATED']) && \is_array($viewLinks['DETAILVIEWRELATED'])) {
138
			foreach ($viewLinks['DETAILVIEWRELATED']  as $link) {
139
				$detailViewLabel[] = $link->getLabel();
140
			}
141
		}
142
		if (isset($viewLinks['DETAILVIEWTAB']) && \is_array($viewLinks['DETAILVIEWTAB'])) {
143
			foreach ($viewLinks['DETAILVIEWTAB']  as $link) {
144
				$detailViewLabel[] = $link->getLabel();
145
				if ($link->getLabel() === $selectedTabLabel) {
146
					$params = vtlib\Functions::getQueryParams($link->getUrl());
147
					$this->defaultMode = $params['mode'];
148
				}
149
			}
150
			if (!\in_array($selectedTabLabel, $detailViewLabel)) {
151
				$selectedTabLabel = 'LBL_RECORD_SUMMARY';
152
			}
153
		}
154
		$viewer->assign('SELECTED_TAB_LABEL', $selectedTabLabel);
155
		$viewer->assign('MODULE_MODEL', $moduleModel);
156
		$viewer->assign('DETAILVIEW_LINKS', $viewLinks);
157
		$viewer->assign('DETAILVIEW_WIDGETS', $this->record->widgets);
158
		$viewer->assign('FIELDS_HEADER', $fieldsInHeader);
159
		$viewer->assign('CUSTOM_FIELDS_HEADER', $this->record->getCustomHeaderFields());
160
		$viewer->assign('IS_EDITABLE', $this->record->getRecord()->isEditable());
161
		$viewer->assign('IS_DELETABLE', $this->record->getRecord()->privilegeToMoveToTrash());
162
		$viewer->assign('VIEW_MODEL', $this->record);
163
		$viewer->assign('QUICK_LINKS', $this->record->getSideBarLinks([
164
			'MODULE' => $moduleName,
165
			'ACTION' => $request->getByType('view', 1),
166
		]));
167
		$viewer->assign('DEFAULT_RECORD_VIEW', $currentUserModel->get('default_record_view'));
168
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
169
		$viewer->assign('RECORD_ACTIVITY_NOTIFIER', $recordId && \App\Config::performance('recordActivityNotifier', false) && $moduleModel->isTrackingEnabled() && $moduleModel->isPermitted('RecordActivityNotifier'));
170
		if ($display) {
171
			$this->preProcessDisplay($request);
172
		}
173
	}
174
175
	/** {@inheritdoc} */
176
	public function preProcessTplName(App\Request $request)
177
	{
178
		return 'Detail/PreProcess.tpl';
179
	}
180
181
	public function process(App\Request $request)
182
	{
183
		$mode = $request->getMode();
184
		if (!empty($mode)) {
185
			echo $this->invokeExposedMethod($mode, $request);
186
			return;
187
		}
188
		$defaultMode = $this->defaultMode;
189
		if ('showDetailViewByMode' === $defaultMode) {
190
			$currentUserModel = Users_Record_Model::getCurrentUserModel();
191
			$this->record->getWidgets();
192
			if (!('Summary' === $currentUserModel->get('default_record_view') && $this->record->widgetsList)) {
193
				$defaultMode = 'showModuleDetailView';
194
			}
195
		}
196
		if (!$defaultMode) {
197
			$defaultMode = 'showDetailViewByMode';
198
		}
199
		echo $this->{$defaultMode}($request);
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->$defaultMode($request) can contain request data and is used in output context(s) leading to a potential security vulnerability.

3 paths for user data to reach this point

  1. Path: Read from $_REQUEST, and Request::__construct() is called in app/Request.php on line 728
  1. Read from $_REQUEST, and Request::__construct() is called
    in app/Request.php on line 728
  2. Enters via parameter $rawValues
    in app/Request.php on line 110
  3. $rawValues is assigned to property Request::$rawValues
    in app/Request.php on line 112
  4. Read from property Request::$rawValues, and Data is passed through purifyByType(), and $this->purifiedValuesByType[$key][$type] = App\Purifier::purifyByType($this->rawValues[$key], $type, $convert) is returned
    in app/Request.php on line 170
  5. Time::formatToDB() is called
    in modules/Settings/BusinessHours/actions/Save.php on line 30
  6. Enters via parameter $time
    in app/Fields/Time.php on line 41
  7. DateTimeField::__construct() is called
    in app/Fields/Time.php on line 44
  8. Enters via parameter $value
    in include/fields/DateTimeField.php on line 31
  9. $value is assigned to property DateTimeField::$datetime
    in include/fields/DateTimeField.php on line 38
  10. Read from property DateTimeField::$datetime, and Data is passed through explode(), and explode(' ', $this->datetime, 2) is assigned to $value
    in include/fields/DateTimeField.php on line 47
  11. Data is passed through convertToDBFormat(), and self::convertToDBFormat($value[0]) is assigned to $insert_date
    in include/fields/DateTimeField.php on line 53
  12. $insert_date is returned
    in include/fields/DateTimeField.php on line 55
  13. $this->getDBInsertDateValue() . ' ' . $this->getDBInsertTimeValue() is returned
    in include/fields/DateTimeField.php on line 66
  14. new DateTimeField($valueList[0] . ' ' . $dbTimeValue)->getDBInsertDateTimeValue() is returned
    in app/Fields/DateTime.php on line 76
  15. App\Fields\DateTime::formatToDb($nowInUserFormat) is assigned to $nowInDBFormat
    in modules/Home/models/Module.php on line 177
  16. Data is passed through explode(), and explode(' ', $nowInDBFormat) is assigned to $currentDate
    in modules/Home/models/Module.php on line 178
  2. Path: DateTimeField::__construct() is called in app/Fields/Time.php on line 44
  1. DateTimeField::__construct() is called
    in app/Fields/Time.php on line 44
  2. Enters via parameter $value
    in include/fields/DateTimeField.php on line 31
  3. $value is assigned to property DateTimeField::$datetime
    in include/fields/DateTimeField.php on line 38
  4. Read from property DateTimeField::$datetime, and Data is passed through explode(), and explode(' ', $this->datetime, 2) is assigned to $value
    in include/fields/DateTimeField.php on line 47
  5. Data is passed through convertToDBFormat(), and self::convertToDBFormat($value[0]) is assigned to $insert_date
    in include/fields/DateTimeField.php on line 53
  6. $insert_date is returned
    in include/fields/DateTimeField.php on line 55
  7. new DateTimeField($value)->getDBInsertDateValue() is returned
    in app/Fields/Date.php on line 168
  8. App\Validator::dateInUserFormat($input) ? $convert ? App\Fields\Date::formatToDB($input) : $input : null is assigned to $value
    in app/Purifier.php on line 451
  9. $value is returned
    in app/Purifier.php on line 569
  10. App\Purifier::purifyByType($this->rawValues[$key], $type, $convert) is assigned to property Request::$purifiedValuesByType
    in app/Request.php on line 170
  11. Read from property Request::$purifiedValuesByType, and $this->purifiedValuesByType[$key][$type] is returned
    in app/Request.php on line 167
  12. Time::formatToDB() is called
    in modules/Settings/BusinessHours/actions/Save.php on line 29
  13. Enters via parameter $time
    in app/Fields/Time.php on line 41
  14. DateTimeField::__construct() is called
    in app/Fields/Time.php on line 44
  15. Enters via parameter $value
    in include/fields/DateTimeField.php on line 31
  16. $value is assigned to property DateTimeField::$datetime
    in include/fields/DateTimeField.php on line 38
  17. Read from property DateTimeField::$datetime, and Data is passed through explode(), and explode(' ', $this->datetime, 2) is assigned to $value
    in include/fields/DateTimeField.php on line 47
  18. Data is passed through convertToDBFormat(), and self::convertToDBFormat($value[0]) is assigned to $insert_date
    in include/fields/DateTimeField.php on line 53
  19. $insert_date is returned
    in include/fields/DateTimeField.php on line 55
  20. $this->getDBInsertDateValue() . ' ' . $this->getDBInsertTimeValue() is returned
    in include/fields/DateTimeField.php on line 66
  21. new DateTimeField($valueList[0] . ' ' . $dbTimeValue)->getDBInsertDateTimeValue() is returned
    in app/Fields/DateTime.php on line 76
  22. App\Fields\DateTime::formatToDb($nowInUserFormat) is assigned to $nowInDBFormat
    in modules/Home/models/Module.php on line 177
  23. Data is passed through explode(), and explode(' ', $nowInDBFormat) is assigned to $currentDate
    in modules/Home/models/Module.php on line 178
  3. Path: Read from $_REQUEST, and Request::__construct() is called in api/webservice/Core/Request.php on line 56
  1. Read from $_REQUEST, and Request::__construct() is called
    in api/webservice/Core/Request.php on line 56
  2. Enters via parameter $rawValues
    in app/Request.php on line 110
  3. $rawValues is assigned to property Request::$rawValues
    in app/Request.php on line 112
  4. Read from property Request::$rawValues, and Data is passed through purifyByType(), and $this->purifiedValuesByType[$key][$type] = App\Purifier::purifyByType($this->rawValues[$key], $type, $convert) is returned
    in app/Request.php on line 170
  5. Time::formatToDB() is called
    in modules/Settings/BusinessHours/actions/Save.php on line 29
  6. Enters via parameter $time
    in app/Fields/Time.php on line 41
  7. DateTimeField::__construct() is called
    in app/Fields/Time.php on line 44
  8. Enters via parameter $value
    in include/fields/DateTimeField.php on line 31
  9. $value is assigned to property DateTimeField::$datetime
    in include/fields/DateTimeField.php on line 38
  10. Read from property DateTimeField::$datetime, and Data is passed through explode(), and explode(' ', $this->datetime, 2) is assigned to $value
    in include/fields/DateTimeField.php on line 47
  11. Data is passed through convertToDBFormat(), and self::convertToDBFormat($value[0]) is assigned to $insert_date
    in include/fields/DateTimeField.php on line 53
  12. $insert_date is returned
    in include/fields/DateTimeField.php on line 55
  13. $this->getDBInsertDateValue() . ' ' . $this->getDBInsertTimeValue() is returned
    in include/fields/DateTimeField.php on line 66
  14. new DateTimeField($valueList[0] . ' ' . $dbTimeValue)->getDBInsertDateTimeValue() is returned
    in app/Fields/DateTime.php on line 76
  15. App\Fields\DateTime::formatToDb($nowInUserFormat) is assigned to $nowInDBFormat
    in modules/Home/models/Module.php on line 177
  16. Data is passed through explode(), and explode(' ', $nowInDBFormat) is assigned to $currentDate
    in modules/Home/models/Module.php on line 178

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
200
	}
201
202
	public function postProcess(App\Request $request, $display = true)
203
	{
204
		$moduleName = $request->getModule();
205
		$viewer = $this->getViewer($request);
206
		$viewer->assign('MODULE_MODEL', $this->record->getModule());
207
		$viewer->view('Detail/PostProcess.tpl', $moduleName);
208
		parent::postProcess($request);
209
	}
210
211
	/**
212
	 * Function to get the list of Css models to be included.
213
	 *
214
	 * @param \App\Request $request
215
	 *
216
	 * @return Vtiger_CssScript_Model[]
217
	 */
218
	public function getHeaderCss(App\Request $request)
219
	{
220
		return array_merge(parent::getHeaderCss($request), $this->checkAndConvertCssStyles([
221
			'~libraries/leaflet/dist/leaflet.css',
222
			'~libraries/leaflet.markercluster/dist/MarkerCluster.Default.css',
223
			'~libraries/leaflet.markercluster/dist/MarkerCluster.css',
224
			'~libraries/leaflet.awesome-markers/dist/leaflet.awesome-markers.css',
225
		]));
226
	}
227
228
	/**
229
	 * Function to get the list of Script models to be included.
230
	 *
231
	 * @param \App\Request $request
232
	 *
233
	 * @return Vtiger_JsScript_Model[]
234
	 */
235
	public function getFooterScripts(App\Request $request)
236
	{
237
		$moduleName = $request->getModule();
238
		$jsFileNames = [
239
			'~libraries/split.js/dist/split.js',
240
			'modules.Vtiger.resources.List',
241
			'modules.Vtiger.resources.ListSearch',
242
			"modules.$moduleName.resources.ListSearch",
243
			'modules.Vtiger.resources.RelatedList',
244
			"modules.$moduleName.resources.RelatedList",
245
			'modules.Vtiger.resources.Widgets',
246
			'~libraries/leaflet/dist/leaflet.js',
247
			'~libraries/leaflet.markercluster/dist/leaflet.markercluster.js',
248
			'~libraries/leaflet.awesome-markers/dist/leaflet.awesome-markers.js',
249
			'modules.OpenStreetMap.resources.Map',
250
			'modules.Vtiger.resources.dashboards.Widget',
251
			'~libraries/chart.js/dist/Chart.js',
252
		];
253
		if (\App\Privilege::isPermitted('Chat')) {
254
			$jsFileNames[] = '~layouts/basic/modules/Chat/resources/Chat.js';
255
		}
256
		return array_merge(
257
			parent::getFooterScripts($request),
258
			$this->checkAndConvertJsScripts($jsFileNames)
259
		);
260
	}
261
262
	public function showDetailViewByMode(App\Request $request)
263
	{
264
		if ('full' === $request->getByType('requestMode', 1)) {
265
			return $this->showModuleDetailView($request);
266
		}
267
		return $this->showModuleBasicView($request);
268
	}
269
270
	/**
271
	 * Function shows the entire detail for the record.
272
	 *
273
	 * @param \App\Request $request
274
	 *
275
	 * @return <type>
0 ignored issues
show
Documentation Bug introduced by
The doc comment <type> at position 0 could not be parsed: Unknown type name '<' at position 0 in <type>.
Loading history...
276
	 */
277
	public function showModuleDetailView(App\Request $request)
278
	{
279
		$moduleName = $request->getModule();
280
		$recordModel = $this->record->getRecord();
281
		if (!$this->recordStructure) {
282
			$this->recordStructure = Vtiger_RecordStructure_Model::getInstanceFromRecordModel($recordModel, Vtiger_RecordStructure_Model::RECORD_STRUCTURE_MODE_DETAIL);
283
		}
284
		$structuredValues = $this->recordStructure->getStructure();
285
286
		$moduleModel = $recordModel->getModule();
287
288
		$viewer = $this->getViewer($request);
289
		$viewer->assign('VIEW', $request->getByType('view', 1));
290
		$viewer->assign('RECORD', $recordModel);
291
		$viewer->assign('RECORD_STRUCTURE', $structuredValues);
292
		$viewer->assign('VIEW_MODEL', $this->record);
293
		$viewer->assign('BLOCK_LIST', $moduleModel->getBlocks());
294
		$viewer->assign('USER_MODEL', Users_Record_Model::getCurrentUserModel());
295
		$viewer->assign('IS_AJAX_ENABLED', $this->isAjaxEnabled($recordModel));
296
		$viewer->assign('MODULE_TYPE', $moduleModel->getModuleType());
297
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
298
		if ($request->getBoolean('toWidget')) {
299
			return $viewer->view('Detail/Widget/BlockView.tpl', $moduleName, true);
300
		}
301
		return $viewer->view('Detail/FullContents.tpl', $moduleName, true);
302
	}
303
304
	public function showModuleSummaryView(App\Request $request)
305
	{
306
		$moduleName = $request->getModule();
307
		$recordModel = $this->record->getRecord();
308
		$recordStrucure = Vtiger_RecordStructure_Model::getInstanceFromRecordModel($recordModel, Vtiger_RecordStructure_Model::RECORD_STRUCTURE_MODE_SUMMARY);
309
310
		$moduleModel = $recordModel->getModule();
311
		$viewer = $this->getViewer($request);
312
		$viewer->assign('RECORD', $recordModel);
313
		$viewer->assign('BLOCK_LIST', $moduleModel->getBlocks());
314
		$viewer->assign('USER_MODEL', Users_Record_Model::getCurrentUserModel());
315
		$viewer->assign('VIEW', $request->getByType('view', 1));
316
		$viewer->assign('VIEW_MODEL', $this->record);
317
		$viewer->assign('IS_AJAX_ENABLED', $this->isAjaxEnabled($recordModel));
318
		$viewer->assign('SUMMARY_RECORD_STRUCTURE', $recordStrucure->getStructure());
319
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
320
		if (\is_callable($moduleName . '_Record_Model', 'getStructure')) {
0 ignored issues
show
Bug introduced by
'getStructure' of type string is incompatible with the type boolean expected by parameter $syntax_only of is_callable(). ( Ignorable by Annotation )

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

320
		if (\is_callable($moduleName . '_Record_Model', /** @scrutinizer ignore-type */ 'getStructure')) {
Loading history...
321
			$viewer->assign('SUMMARY_RECORD_STRUCTURE', $recordStrucure->getStructure());
322
		}
323
		return $viewer->view('Detail/Widget/GeneralInfo.tpl', $moduleName, true);
324
	}
325
326
	/**
327
	 * Function shows basic detail for the record.
328
	 *
329
	 * @param \App\Request $request
330
	 */
331
	public function showModuleBasicView(App\Request $request)
332
	{
333
		$recordId = $request->getInteger('record');
334
		$moduleName = $request->getModule();
335
		$recordModel = $this->record->getRecord();
336
		$viewLinks = $this->record->getDetailViewLinks([
337
			'MODULE' => $moduleName,
338
			'RECORD' => $recordId,
339
			'VIEW' => $request->getByType('view', 2),
340
		]);
341
		$this->record->getWidgets();
342
		$viewer = $this->getViewer($request);
343
		$viewer->assign('RECORD', $recordModel);
344
		$viewer->assign('MODULE_SUMMARY', $this->showModuleSummaryView($request));
345
		$viewer->assign('DETAILVIEW_WIDGETS', $this->record->widgets);
346
		$viewer->assign('DETAILVIEW_LINKS', $viewLinks);
347
		$viewer->assign('USER_MODEL', Users_Record_Model::getCurrentUserModel());
348
		$viewer->assign('IS_AJAX_ENABLED', $this->isAjaxEnabled($recordModel));
349
350
		$viewer->assign('VIEW', $request->getByType('view', 1));
351
		if (!$this->recordStructure) {
352
			$this->recordStructure = Vtiger_RecordStructure_Model::getInstanceFromRecordModel($recordModel, Vtiger_RecordStructure_Model::RECORD_STRUCTURE_MODE_DETAIL);
353
		}
354
		$structuredValues = $this->recordStructure->getStructure();
355
		$moduleModel = $recordModel->getModule();
356
		$viewer->assign('RECORD_STRUCTURE', $structuredValues);
357
		$viewer->assign('MODULE_MODEL', $moduleModel);
358
		$viewer->assign('BLOCK_LIST', $moduleModel->getBlocks());
359
		$viewer->assign('VIEW_MODEL', $this->record);
360
		$viewer->assign('MODULE_TYPE', $moduleModel->getModuleType());
361
		$viewer->assign('HIERARCHY_VALUE', $this->getHierarchyValue($request));
362
		$viewer->assign('HIERARCHY', \App\ModuleHierarchy::getModuleLevel($moduleName));
363
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
364
		if ($moduleModel->isSummaryViewSupported() && $this->record->widgetsList) {
365
			return $viewer->view('DetailViewSummaryView.tpl', $moduleName, true);
366
		}
367
		return $viewer->view('Detail/FullContents.tpl', $moduleName, true);
368
	}
369
370
	/**
371
	 * Function returns recent changes made on the record.
372
	 *
373
	 * @param \App\Request $request
374
	 *
375
	 * @throws \App\Exceptions\NoPermittedToRecord
376
	 */
377
	public function showRecentActivities(App\Request $request)
378
	{
379
		$moduleName = $request->getModule();
380
		include_once 'modules/ModTracker/ModTracker.php';
381
		$type = 'changes';
382
		$parentRecordId = $request->getInteger('record');
383
		$pageNumber = $request->getInteger('page');
384
		$limit = $request->getInteger('limit');
385
		$whereCondition = $request->getArray('whereCondition', 'Standard');
386
		if (empty($pageNumber)) {
387
			$pageNumber = 1;
388
		}
389
		$pagingModel = new Vtiger_Paging_Model();
390
		$pagingModel->set('page', $pageNumber);
391
		if (!empty($limit)) {
392
			$pagingModel->set('limit', $limit);
393
		} else {
394
			$limit = App\Config::module('ModTracker', 'NUMBER_RECORDS_ON_PAGE');
395
			$pagingModel->set('limit', $limit);
396
		}
397
		if (!empty($whereCondition)) {
398
			$type = \is_array($whereCondition) ? current($whereCondition) : $whereCondition;
0 ignored issues
show
introduced by
The condition is_array($whereCondition) is always true.
Loading history...
399
		}
400
		$recentActivities = ModTracker_Record_Model::getUpdates($parentRecordId, $pagingModel, $type);
401
		$pagingModel->calculatePageRange(\count($recentActivities));
402
403
		if ($pagingModel->getCurrentPage() == ceil(ModTracker_Record_Model::getTotalRecordCount($parentRecordId, $type) / $pagingModel->getPageLimit())) {
404
			$pagingModel->set('nextPageExists', false);
405
		} else {
406
			$pagingModel->set('nextPageExists', true);
407
		}
408
		if ('changes' === $type) {
409
			$newChange = $request->has('newChange') ? $request->getBoolean('newChange') : ModTracker_Record_Model::isNewChange($parentRecordId);
410
		} else {
411
			$newChange = false;
412
		}
413
		$viewer = $this->getViewer($request);
414
		$viewer->assign('TYPE', $type);
415
		$viewer->assign('NEW_CHANGE', $newChange ?? null);
416
		$viewer->assign('PARENT_RACORD_ID', $parentRecordId);
417
		$viewer->assign('RECENT_ACTIVITIES', $recentActivities);
418
		$viewer->assign('MODULE_MODEL', Vtiger_Module_Model::getInstance($moduleName));
419
		$viewer->assign('MODULE_BASE_NAME', 'ModTracker');
420
		$viewer->assign('PAGING_MODEL', $pagingModel);
421
		$viewer->assign('VIEW_MODEL', $this->record);
422
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
423
		$defaultView = App\Config::module('ModTracker', 'DEFAULT_VIEW');
424
		if ('List' == $defaultView) {
425
			$tplName = 'RecentActivities.tpl';
426
		} else {
427
			$tplName = 'RecentActivitiesTimeLine.tpl';
428
		}
429
		if (!$request->getBoolean('skipHeader')) {
430
			$viewer->view('RecentActivitiesHeader.tpl', $moduleName);
431
		}
432
		return $viewer->view($tplName, $moduleName, true);
433
	}
434
435
	/**
436
	 * Function returns latest comments.
437
	 *
438
	 * @param \App\Request $request
439
	 *
440
	 * @throws \App\Exceptions\NoPermittedToRecord
441
	 *
442
	 * @return string
443
	 */
444
	public function showRecentComments(App\Request $request)
445
	{
446
		if (!\App\Privilege::isPermitted('ModComments')) {
447
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
448
		}
449
		$parentId = $request->getInteger('record');
450
		$pageNumber = $request->getInteger('page');
451
		$limit = $request->getInteger('limit');
452
		$moduleName = $request->getModule();
453
		if (empty($pageNumber)) {
454
			$pageNumber = 1;
455
		}
456
		$pagingModel = new Vtiger_Paging_Model();
457
		$pagingModel->set('page', $pageNumber);
458
		if (!empty($limit)) {
459
			$pagingModel->set('limit', $limit);
460
		}
461
		$parentCommentModels = ModComments_Record_Model::getAllParentComments($parentId, $moduleName, $this->getHierarchy($request), $pagingModel);
462
		$pagingModel->calculatePageRange(\count($parentCommentModels));
463
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
464
		$modCommentsModel = Vtiger_Module_Model::getInstance('ModComments');
465
		$viewer = $this->getViewer($request);
466
		$viewer->assign('PARENT_RECORD', $parentId);
467
		$viewer->assign('PARENT_COMMENTS', $parentCommentModels);
468
		$viewer->assign('CURRENTUSER', $currentUserModel);
469
		$viewer->assign('PAGING_MODEL', $pagingModel);
470
		$viewer->assign('COMMENTS_MODULE_MODEL', $modCommentsModel);
471
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
472
		$viewer->assign('CURRENT_COMMENT', null);
473
		return $viewer->view('RecentComments.tpl', $moduleName, true);
474
	}
475
476
	/**
477
	 * Function returns related records.
478
	 *
479
	 * @param \App\Request $request
480
	 *
481
	 * @throws \App\Exceptions\NoPermittedToRecord
482
	 *
483
	 * @return string
484
	 */
485
	public function showRelatedList(App\Request $request)
486
	{
487
		$moduleName = $request->getModule();
488
		$relatedModuleName = $request->getByType('relatedModule', 2);
489
		$targetControllerClass = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $targetControllerClass is dead and can be removed.
Loading history...
490
		if (!\App\Privilege::isPermitted($relatedModuleName)) {
491
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
492
		}
493
		// Added to support related list view from the related module, rather than the base module.
494
		if (!($targetControllerClass = Vtiger_Loader::getComponentClassName('View', 'In' . $moduleName . 'Relation', $relatedModuleName, false)) && !($targetControllerClass = Vtiger_Loader::getComponentClassName('View', 'InRelation', $relatedModuleName, false))) {
495
			// Default related list
496
			$targetControllerClass = Vtiger_Loader::getComponentClassName('View', 'RelatedList', $moduleName);
497
		}
498
		if ($targetControllerClass) {
499
			$targetController = new $targetControllerClass();
500
			return $targetController->process($request);
501
		}
502
	}
503
504
	/**
505
	 * Function sends the child comments for a comment.
506
	 *
507
	 * @param \App\Request $request
508
	 *
509
	 * @throws \App\Exceptions\NoPermittedToRecord
510
	 *
511
	 * @return mixed
512
	 */
513
	public function showChildComments(App\Request $request)
514
	{
515
		if (!\App\Privilege::isPermitted('ModComments')) {
516
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
517
		}
518
		$parentCommentId = $request->getInteger('commentid');
519
		$parentCommentModel = ModComments_Record_Model::getInstanceById($parentCommentId);
520
		$childComments = $parentCommentModel->getChildComments();
521
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
522
		$modCommentsModel = Vtiger_Module_Model::getInstance('ModComments');
523
		$viewer = $this->getViewer($request);
524
		$viewer->assign('PARENT_COMMENTS', $childComments);
525
		$viewer->assign('CHILD_COMMENTS', true);
526
		$viewer->assign('NO_COMMENT_FORM', true);
527
		$viewer->assign('CURRENTUSER', $currentUserModel);
528
		$viewer->assign('COMMENTS_MODULE_MODEL', $modCommentsModel);
529
		$viewer->assign('CURRENT_COMMENT', null);
530
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
531
		return $viewer->view('CommentsList.tpl', $request->getModule(), true);
532
	}
533
534
	/**
535
	 * Function sends the parent comment for a comment.
536
	 *
537
	 * @param \App\Request $request
538
	 *
539
	 * @throws \App\Exceptions\NoPermittedToRecord
540
	 *
541
	 * @return mixed
542
	 */
543
	public function showParentComments(App\Request $request)
544
	{
545
		if (!\App\Privilege::isPermitted('ModComments')) {
546
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
547
		}
548
		$parentCommentModel = ModComments_Record_Model::getInstanceById($request->getInteger('commentid'));
549
		$parentThreadComments = $parentCommentModel->getParentComments();
550
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
551
		$modCommentsModel = Vtiger_Module_Model::getInstance('ModComments');
552
		$viewer = $this->getViewer($request);
553
		$viewer->assign('PARENT_COMMENTS', $parentThreadComments);
554
		$viewer->assign('SHOW_CHILD_COMMENTS', true);
555
		$viewer->assign('NO_COMMENT_FORM', true);
556
		$viewer->assign('CURRENTUSER', $currentUserModel);
557
		$viewer->assign('COMMENTS_MODULE_MODEL', $modCommentsModel);
558
		$viewer->assign('CURRENT_COMMENT', null);
559
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
560
		return $viewer->view('CommentsList.tpl', $request->getModule(), true);
561
	}
562
563
	/**
564
	 * Function send all the comments in thead.
565
	 *
566
	 * @param \App\Request $request
567
	 *
568
	 * @throws \App\Exceptions\NoPermittedToRecord
569
	 *
570
	 * @return mixed
571
	 */
572
	public function showThreadComments(App\Request $request)
573
	{
574
		if (!\App\Privilege::isPermitted('ModComments')) {
575
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
576
		}
577
		$parentRecordId = $request->getInteger('record');
578
		$commentRecordId = $request->getInteger('commentid');
579
		$moduleName = $request->getModule();
580
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
581
		$parentCommentModels = ModComments_Record_Model::getAllParentComments($parentRecordId, $moduleName);
582
		$currentCommentModel = Vtiger_Record_Model::getInstanceById($commentRecordId);
583
584
		$viewer = $this->getViewer($request);
585
		$viewer->assign('CURRENTUSER', $currentUserModel);
586
		$viewer->assign('PARENT_COMMENTS', $parentCommentModels);
587
		$viewer->assign('CURRENT_COMMENT', $currentCommentModel);
588
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
589
		return $viewer->view('ShowThreadComments.tpl', $moduleName, true);
590
	}
591
592
	/**
593
	 * Function sends all the comments for a parent(Accounts, Contacts etc).
594
	 *
595
	 * @param \App\Request $request
596
	 *
597
	 * @throws \App\Exceptions\NoPermittedToRecord
598
	 *
599
	 * @return mixed
600
	 */
601
	public function showAllComments(App\Request $request)
602
	{
603
		if (!\App\Privilege::isPermitted('ModComments')) {
604
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
605
		}
606
		$parentRecordId = $request->getInteger('record');
607
		$commentRecordId = $request->getInteger('commentid');
608
		$moduleName = $request->getModule();
609
		$hierarchy = $this->getHierarchy($request);
610
		$hierarchyValue = $this->getHierarchyValue($request);
611
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
612
		$modCommentsModel = Vtiger_Module_Model::getInstance('ModComments');
613
		$parentCommentModels = ModComments_Record_Model::getAllParentComments($parentRecordId, $moduleName, $hierarchy);
614
		$currentCommentModel = [];
615
		if (!empty($commentRecordId)) {
616
			$currentCommentModel = Vtiger_Record_Model::getInstanceById($commentRecordId);
617
		}
618
		$viewer = $this->getViewer($request);
619
		$viewer->assign('CURRENTUSER', $currentUserModel);
620
		$viewer->assign('PARENT_RECORD', $parentRecordId);
621
		$viewer->assign('HIERARCHY', \App\ModuleHierarchy::getModuleLevel($moduleName));
622
		$viewer->assign('HIERARCHY_VALUE', $hierarchyValue);
623
		$viewer->assign('COMMENTS_MODULE_MODEL', $modCommentsModel);
624
		$viewer->assign('PARENT_COMMENTS', $parentCommentModels);
625
		$viewer->assign('CURRENT_COMMENT', $currentCommentModel);
626
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
627
		return $viewer->view('ShowAllComments.tpl', $moduleName, true);
628
	}
629
630
	/**
631
	 * Function send all the comments with search value.
632
	 *
633
	 * @param \App\Request $request
634
	 *
635
	 * @throws \App\Exceptions\NoPermittedToRecord
636
	 *
637
	 * @return mixed
638
	 */
639
	public function showSearchComments(App\Request $request)
640
	{
641
		if (!\App\Privilege::isPermitted('ModComments')) {
642
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
643
		}
644
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
645
		$recordId = $request->getInteger('record');
646
		$moduleName = $request->getModule();
647
		$isWidget = false;
648
		if (!$request->isEmpty('is_widget', true)) {
649
			$isWidget = $request->getBoolean('is_widget');
650
		}
651
		if ($request->isEmpty('search_key', true)) {
652
			return $this->showAllComments($request);
653
		}
654
		$searchValue = $request->getByType('search_key', 'Text');
655
		$parentCommentModels = ModComments_Record_Model::getSearchComments($recordId, $moduleName, $searchValue, $isWidget, $this->getHierarchy($request));
656
657
		$viewer = $this->getViewer($request);
658
		if (!empty($parentCommentModels)) {
659
			$modCommentsModel = Vtiger_Module_Model::getInstance('ModComments');
660
			$viewer->assign('COMMENTS_MODULE_MODEL', $modCommentsModel);
661
			$viewer->assign('CURRENTUSER', $currentUserModel);
662
			$viewer->assign('PARENT_COMMENTS', $parentCommentModels);
663
			$viewer->assign('CURRENT_COMMENT', false);
664
			$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
665
			$viewer->assign('NO_COMMENT_FORM', true);
666
			$viewer->assign('MODULE', $moduleName);
667
			if (false === $isWidget) {
668
				$viewer->assign('SHOW_CHILD_COMMENTS', true);
669
			} else {
670
				$viewer->assign('BUTTON_SHOW_PARENT', true);
671
			}
672
			return $viewer->view('CommentsList.tpl', $moduleName, true);
673
		}
674
		return $viewer->view('NoComments.tpl', $moduleName, true);
675
	}
676
677
	/**
678
	 * Returns value source to display comments.
679
	 *
680
	 * @param \App\Request $request
681
	 *
682
	 * @return mixed
683
	 */
684
	private function getHierarchyValue(App\Request $request)
685
	{
686
		$moduleName = $request->getModule();
687
		$hierarchyValue = $request->getExploded('hierarchy', ',', 'Standard');
688
		$cacheName = 'DEFAULT_SOURCE_COMMENTS_' . $moduleName;
689
		if (empty($hierarchyValue)) {
690
			if (App\Session::has($cacheName)) {
691
				$hierarchyValue = App\Session::get($cacheName);
692
			} else {
693
				$hierarchyValue = \Config\Modules\ModComments::$defaultSource;
0 ignored issues
show
Bug introduced by
The type Config\Modules\ModComments 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...
694
			}
695
		} else {
696
			App\Session::set($cacheName, $hierarchyValue);
697
		}
698
		return $hierarchyValue;
699
	}
700
701
	/**
702
	 * Get comments hierarchy.
703
	 *
704
	 * @param \App\Request $request
705
	 * @param array        $hierarchyValue
706
	 *
707
	 * @return array
708
	 */
709
	public function getHierarchy(App\Request $request, array $hierarchyValue = [])
710
	{
711
		if (empty($hierarchyValue)) {
712
			$hierarchyValue = $this->getHierarchyValue($request);
713
		}
714
		$moduleName = $request->getModule();
715
		$hierarchy = [];
716
		$level = \App\ModuleHierarchy::getModuleLevel($moduleName);
717
		if (0 === $level) {
718
			$hierarchy = \in_array('related', $hierarchyValue) ? [1, 2, 3, 4] : [];
719
		} elseif (1 === $level) {
720
			$hierarchy = \in_array('related', $hierarchyValue) ? [2, 3] : [];
721
		}
722
		if (\in_array('current', $hierarchyValue)) {
723
			$hierarchy[] = $level;
724
		}
725
		return $hierarchy;
726
	}
727
728
	/**
729
	 * Function to get Ajax is enabled or not.
730
	 *
731
	 * @param Vtiger_Record_Model record model
732
	 * @param mixed $recordModel
733
	 *
734
	 * @return bool true/false
735
	 */
736
	public function isAjaxEnabled($recordModel)
737
	{
738
		return $recordModel->isEditable();
739
	}
740
741
	/**
742
	 * Function to get activities.
743
	 *
744
	 * @param \App\Request $request
745
	 *
746
	 * @throws \App\Exceptions\NoPermittedToRecord
747
	 *
748
	 * @return <List of activity models>
0 ignored issues
show
Documentation Bug introduced by
The doc comment <List at position 0 could not be parsed: Unknown type name '<' at position 0 in <List.
Loading history...
749
	 */
750
	public function getActivities(App\Request $request)
751
	{
752
		$userPrivilegesModel = Users_Privileges_Model::getCurrentUserPrivilegesModel();
753
		if (!$userPrivilegesModel->hasModulePermission('Calendar')) {
0 ignored issues
show
Bug introduced by
'Calendar' 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

753
		if (!$userPrivilegesModel->hasModulePermission(/** @scrutinizer ignore-type */ 'Calendar')) {
Loading history...
754
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
755
		}
756
		$moduleName = $request->getModule();
757
		$pageNumber = $request->getInteger('page');
758
		$pageLimit = $request->getInteger('limit');
759
		if (empty($pageNumber)) {
760
			$pageNumber = 1;
761
		}
762
		$pagingModel = new Vtiger_Paging_Model();
763
		$pagingModel->set('page', $pageNumber);
764
		if (!empty($pageLimit)) {
765
			$pagingModel->set('limit', $pageLimit);
766
		} else {
767
			$pagingModel->set('limit', 10);
768
		}
769
		$recordModel = $this->record->getRecord();
770
		$relatedActivities = [];
771
		$relationListView = Vtiger_RelationListView_Model::getInstance($recordModel, 'Calendar');
772
		if ($relationListView) {
0 ignored issues
show
introduced by
$relationListView is of type Vtiger_RelationListView_Model, thus it always evaluated to true.
Loading history...
773
			$moduleModel = $relationListView->getQueryGenerator()->getModuleModel();
774
			$relationListView->getRelationModel()->set('QueryFields', $moduleModel->getFields());
775
			$searchParams = App\Condition::validSearchParams($moduleModel->getName(), $request->getArray('search_params'));
0 ignored issues
show
Bug introduced by
$moduleModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Condition::validSearchParams(). ( Ignorable by Annotation )

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

775
			$searchParams = App\Condition::validSearchParams(/** @scrutinizer ignore-type */ $moduleModel->getName(), $request->getArray('search_params'));
Loading history...
776
			if (!empty($searchParams) && \is_array($searchParams)) {
777
				$transformedSearchParams = $relationListView->getQueryGenerator()->parseBaseSearchParamsToCondition($searchParams);
778
				$relationListView->set('search_params', $transformedSearchParams);
779
			}
780
			if (!$request->has('orderby')) {
781
				$relationListView->set('orderby', ['date_start' => \App\Db::ASC, 'time_start' => \App\Db::ASC]);
782
			} else {
783
				$relationListView->set('orderby', $request->getArray('orderby', \App\Purifier::STANDARD, [], \App\Purifier::SQL));
784
			}
785
			if (App\Config::relation('SHOW_RECORDS_COUNT') && $pagingModel->isEmpty('totalCount')) {
786
				$pagingModel->set('totalCount', $relationListView->getRelationQuery()->count());
0 ignored issues
show
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

786
				$pagingModel->set('totalCount', $relationListView->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...
787
			}
788
789
			$relatedActivities = $relationListView->getEntries($pagingModel);
790
			foreach ($relatedActivities as $recordModel) {
791
				$recordModel->set('selectedusers', (new \App\Db\Query())->select(['inviteesid'])->from('u_#__activity_invitation')->where(['activityid' => $recordModel->getId()])->column());
792
			}
793
		}
794
		$viewer = $this->getViewer($request);
795
		$viewer->assign('RECORD', $recordModel);
796
		$viewer->assign('PAGING_MODEL', $pagingModel);
797
		$viewer->assign('PAGE_NUMBER', $pageNumber);
798
		$viewer->assign('ACTIVITIES', $relatedActivities);
799
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
800
		return $viewer->view('RelatedActivities.tpl', $moduleName, true);
801
	}
802
803
	/**
804
	 * Function returns related records based on related moduleName.
805
	 *
806
	 * @param \App\Request $request
807
	 *
808
	 * @throws \App\Exceptions\NoPermittedToRecord
809
	 *
810
	 * @return string
811
	 */
812
	public function showRelatedRecords(App\Request $request)
813
	{
814
		$parentId = $request->getInteger('record');
815
		$pageNumber = $request->getInteger('page');
816
		$relationId = $request->isEmpty('relationId') ? false : $request->getInteger('relationId');
817
		$limit = 10;
818
		$relatedModuleName = $request->getByType('relatedModule', 2);
819
		$columns = 0;
820
		$moduleName = $request->getModule();
821
		$searchParams = App\Condition::validSearchParams($relatedModuleName, $request->getArray('search_params'));
822
		$totalCount = $request->getInteger('totalCount');
823
		if (empty($pageNumber)) {
824
			$pageNumber = 1;
825
		}
826
		$pagingModel = new Vtiger_Paging_Model();
827
		$pagingModel->set('page', $pageNumber);
828
		if (!$request->isEmpty('limit', true)) {
829
			$limit = $request->getInteger('limit');
830
		}
831
		$pagingModel->set('limit', $limit);
832
		if (is_numeric($relatedModuleName)) {
833
			$relatedModuleName = \App\Module::getModuleName($relatedModuleName);
0 ignored issues
show
Bug introduced by
$relatedModuleName of type string is incompatible with the type integer expected by parameter $tabId of App\Module::getModuleName(). ( Ignorable by Annotation )

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

833
			$relatedModuleName = \App\Module::getModuleName(/** @scrutinizer ignore-type */ $relatedModuleName);
Loading history...
834
		}
835
		if (!Users_Privileges_Model::getCurrentUserPrivilegesModel()->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

835
		if (!Users_Privileges_Model::getCurrentUserPrivilegesModel()->hasModulePermission(/** @scrutinizer ignore-type */ $request->getModule())) {
Loading history...
836
			throw new \App\Exceptions\NoPermitted('LBL_PERMISSION_DENIED', 406);
837
		}
838
		if (!\App\Privilege::isPermitted($relatedModuleName)) {
839
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
840
		}
841
		$parentRecordModel = Vtiger_Record_Model::getInstanceById($parentId, $moduleName);
842
		$cvId = $request->isEmpty('cvId', true) ? 0 : $request->getByType('cvId', 'Alnum');
843
		$relationListView = Vtiger_RelationListView_Model::getInstance($parentRecordModel, $relatedModuleName, $relationId, $cvId);
844
		if ($fieldRelation = $request->getArray('fromRelation', \App\Purifier::ALNUM, [], \App\Purifier::STANDARD)) {
845
			if (($parentId = $parentRecordModel->get($fieldRelation['relatedField'])) && \App\Record::isExists($parentId)) {
846
				$moduleName = \App\Record::getType($parentId);
847
				$parentRecordModel = Vtiger_Record_Model::getInstanceById($parentId, $moduleName);
848
				$relationId = $fieldRelation['relationId'];
849
				$relationListView = Vtiger_RelationListView_Model::getInstance($parentRecordModel, $relatedModuleName, $relationId);
850
			} else {
851
				$relationListView->getQueryGenerator()->addNativeCondition((new \yii\db\Expression('0 > 1')));
0 ignored issues
show
Bug introduced by
new yii\db\Expression('0 > 1') of type yii\db\Expression is incompatible with the type array expected by parameter $condition of App\QueryGenerator::addNativeCondition(). ( Ignorable by Annotation )

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

851
				$relationListView->getQueryGenerator()->addNativeCondition(/** @scrutinizer ignore-type */ (new \yii\db\Expression('0 > 1')));
Loading history...
852
			}
853
		}
854
		$relationModel = $relationListView->getRelationModel();
855
		if ($relationModel->isFavorites() && \App\Privilege::isPermitted($moduleName, 'FavoriteRecords')) {
856
			$favorites = $relationListView->getFavoriteRecords();
857
			if (!empty($favorites)) {
858
				$relationListView->getQueryGenerator()->addNativeCondition(['vtiger_crmentity.crmid' => $favorites]);
859
			}
860
		}
861
		if (!empty($searchParams)) {
862
			$searchParams = $relationListView->getQueryGenerator()->parseBaseSearchParamsToCondition($searchParams);
863
			$relationListView->set('search_params', $searchParams);
864
		}
865
		$orderBy = $request->getArray('orderby', \App\Purifier::STANDARD, [], \App\Purifier::SQL);
866
		if (empty($orderBy)) {
867
			$moduleInstance = CRMEntity::getInstance($relatedModuleName);
868
			if ($moduleInstance->default_order_by && $moduleInstance->default_sort_order) {
869
				$orderBy = [];
870
				foreach ((array) $moduleInstance->default_order_by as $value) {
871
					$orderBy[$value] = $moduleInstance->default_sort_order;
872
				}
873
			}
874
		}
875
		if (!empty($orderBy)) {
876
			$relationListView->set('orderby', $orderBy);
877
		}
878
		$viewer = $this->getViewer($request);
879
		$viewType = !$request->isEmpty('viewType') ? $request->getByType('viewType') : '';
880
		if ('ListWithSummary' === $viewType) {
881
			$header = $relationListView->getHeaders();
882
			if ($summaryHeaders = $relationListView->getRelatedModuleModel()->getSummaryViewFieldsList()) {
883
				$relationListView->setFields(array_keys($summaryHeaders));
884
				$summaryHeaders = $relationListView->getHeaders();
885
			}
886
			$viewer->assign('RELATED_SUMMARY_HEADERS', $summaryHeaders);
887
			if ($request->has('fields')) {
888
				$relationListView->setFields($request->getExploded('fields'));
889
				$header = $relationListView->getHeaders();
890
				$relationListView->setFields(array_keys(array_merge($summaryHeaders, $header)));
891
			}
892
		} else {
893
			if ($request->has('fields')) {
894
				$relationListView->setFields($request->getExploded('fields'));
895
			}
896
			$header = $relationListView->getHeaders();
897
		}
898
		$models = $relationListView->getEntries($pagingModel);
899
		$links = $relationListView->getLinks();
900
		$relatedModuleModel = $relationModel->getRelationModuleModel();
901
		$relationField = $relationModel->getRelationField();
902
		$noOfEntries = \count($models);
903
904
		if ($request->has('col')) {
905
			$columns = $request->getInteger('col');
906
			$header = array_splice($header, 0, $columns);
907
		}
908
		$viewer->assign('TYPE_VIEW', $viewType);
909
		$viewer->assign('MODULE', $moduleName);
910
		$viewer->assign('LIMIT', $limit);
911
		$viewer->assign('RELATED_RECORDS', $models);
912
		$viewer->assign('RELATED_HEADERS', $header);
913
		$viewer->assign('RELATED_MODULE', $relatedModuleModel);
914
		$viewer->assign('RELATED_MODULE_NAME', $relatedModuleName);
915
		$viewer->assign('PAGING_MODEL', $pagingModel);
916
		$viewer->assign('USER_MODEL', Users_Record_Model::getCurrentUserModel());
917
		$viewer->assign('PARENT_RECORD', $parentRecordModel);
918
		$viewer->assign('RELATED_LIST_LINKS', $links);
919
		$viewer->assign('RELATED_ENTIRES_COUNT', $noOfEntries);
920
		$viewer->assign('RELATION_FIELD', $relationField);
921
		if (App\Config::performance('LISTVIEW_COMPUTE_PAGE_COUNT')) {
922
			$totalCount = $relationListView->getRelatedEntriesCount();
923
		}
924
		if (empty($totalCount)) {
925
			$totalCount = 0;
926
		}
927
		$pagingModel->set('totalCount', (int) $totalCount);
928
		$viewer->assign('TOTAL_ENTRIES', (int) $totalCount);
929
		$pageCount = $pagingModel->getPageCount();
930
		$startPaginFrom = $pagingModel->getStartPagingFrom();
931
		$viewer->assign('VIEW_MODEL', $relationListView);
932
		$viewer->assign('PAGE_COUNT', $pageCount);
933
		$viewer->assign('PAGE_NUMBER', $pageNumber);
934
		$viewer->assign('START_PAGIN_FROM', $startPaginFrom);
935
		$viewer->assign('PAGING_MODEL', $pagingModel);
936
		$viewer->assign('ORDER_BY', $orderBy);
937
		$viewer->assign('COLUMNS', $columns);
938
		$viewer->assign('IS_EDITABLE', $relationModel->isEditable());
939
		$viewer->assign('INVENTORY_FIELDS', $relationModel->getRelationInventoryFields());
940
		$viewer->assign('SHOW_CREATOR_DETAIL', $relationModel->showCreatorDetail());
941
		$viewer->assign('SHOW_COMMENT', $relationModel->showComment());
942
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
943
		$viewer->assign('NO_RESULT_TEXT', $request->getBoolean('no_result_text'));
944
		$viewer->assign('RELATION_ID', $relationId);
945
		return $viewer->view('SummaryWidgets.tpl', $moduleName, true);
946
	}
947
948
	public function showRelatedTree(App\Request $request)
949
	{
950
		$moduleName = $request->getModule();
951
		$parentId = $request->getInteger('record');
952
		$relatedModuleName = $request->getByType('relatedModule', 2);
953
		if (!\App\Privilege::isPermitted($relatedModuleName)) {
954
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
955
		}
956
		$parentRecordModel = Vtiger_Record_Model::getInstanceById($parentId, $moduleName);
957
		$relationListView = Vtiger_RelationListView_Model::getInstance($parentRecordModel, $relatedModuleName);
958
		$relationModel = $relationListView->getRelationModel();
959
960
		$header = $relationListView->getTreeHeaders();
961
		$entries = $relationListView->getTreeEntries();
962
963
		$viewer = $this->getViewer($request);
964
		$viewer->assign('MODULE', $moduleName);
965
		$viewer->assign('RECORDID', $parentId);
966
		$viewer->assign('VIEW_MODEL', $relationListView);
967
		$viewer->assign('RELATED_MODULE_NAME', $relatedModuleName);
968
		$viewer->assign('RELATED_RECORDS', $entries);
969
		$viewer->assign('RELATED_HEADERS', $header);
970
		$viewer->assign('SHOW_CREATOR_DETAIL', (bool) $relationModel->get('creator_detail'));
971
		$viewer->assign('SHOW_COMMENT', (bool) $relationModel->get('relation_comment'));
972
		$viewer->assign('USER_MODEL', Users_Record_Model::getCurrentUserModel());
973
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
974
		return $viewer->view('RelatedTreeContent.tpl', $moduleName, true);
975
	}
976
977
	public function showRelatedProductsServices(App\Request $request)
978
	{
979
		$recordId = $request->getInteger('record');
980
		$moduleName = $request->getModule();
981
		$recordModel = $this->record->getRecord();
982
		$viewer = $this->getViewer($request);
983
		$viewer->assign('RECORDID', $recordId);
984
		$viewer->assign('RECORD', $recordModel);
985
		$viewer->assign('VIEW_MODEL', $this->record);
986
		$viewer->assign('DETAILVIEW_LINKS', $this->record->getDetailViewLinks([
987
			'MODULE' => $moduleName,
988
			'RECORD' => $recordId,
989
			'VIEW' => $request->getByType('view', 2),
990
		]));
991
		$viewer->assign('IS_AJAX_ENABLED', $this->isAjaxEnabled($recordModel));
992
		$viewer->assign('LIMIT', 0);
993
		if (!$this->recordStructure) {
994
			$this->recordStructure = Vtiger_RecordStructure_Model::getInstanceFromRecordModel($recordModel, Vtiger_RecordStructure_Model::RECORD_STRUCTURE_MODE_DETAIL);
995
		}
996
		$structuredValues = $this->recordStructure->getStructure();
997
		$moduleModel = $recordModel->getModule();
998
		$relations = \Vtiger_Relation_Model::getAllRelations($moduleModel, false, true, true, 'modulename');
999
		foreach ($relations as $relation) {
1000
			$relation->set('parentRecord', $recordModel);
1001
		}
1002
		$viewer->assign('RECORD_STRUCTURE', $structuredValues);
1003
		$viewer->assign('RELATIONS', $relations);
1004
		$viewer->assign('BLOCK_LIST', $moduleModel->getBlocks());
1005
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
1006
		return $viewer->view('DetailViewProductsServicesContents.tpl', $moduleName, true);
1007
	}
1008
1009
	/**
1010
	 * Function returns related records based on related moduleName.
1011
	 *
1012
	 * @param \App\Request $request
1013
	 *
1014
	 * @return string
1015
	 */
1016
	public function showInventoryEntries(App\Request $request)
1017
	{
1018
		$recordId = $request->getInteger('record');
1019
		$pageNumber = $request->getInteger('page');
1020
		$moduleName = $request->getModule();
1021
		if (empty($pageNumber)) {
1022
			$pageNumber = 1;
1023
		}
1024
		$pagingModel = new Vtiger_Paging_Model();
1025
		$pagingModel->set('page', $pageNumber);
1026
		$limit = $request->isEmpty('limit', true) ? 10 : $request->getInteger('limit');
1027
		$pagingModel->set('limit', $limit);
1028
		$entries = \Vtiger_Inventory_Model::getInventoryDataById($recordId, $moduleName, $pagingModel);
1029
		$inventoryModel = \Vtiger_Inventory_Model::getInstance($moduleName);
1030
		$viewer = $this->getViewer($request);
1031
		$columns = $request->getExploded('fields');
1032
		$header = [];
1033
		foreach ($columns as $fieldName) {
1034
			$fieldModel = $inventoryModel->getField($fieldName);
1035
			if ($fieldModel && $fieldModel->isVisibleInDetail()) {
1036
				$header[$fieldName] = $fieldModel;
1037
			}
1038
		}
1039
		$viewer->assign('LIMIT', $limit);
1040
		$viewer->assign('ENTRIES', $entries);
1041
		$viewer->assign('HEADER_FIELD', $header);
1042
		$viewer->assign('PAGING_MODEL', $pagingModel);
1043
		return $viewer->view('Detail/Widget/InventoryBlock.tpl', $moduleName, true);
1044
	}
1045
1046
	/**
1047
	 * Function shows PDF.
1048
	 *
1049
	 * @param \App\Request $request
1050
	 *
1051
	 * @return string
1052
	 */
1053
	public function showPDF(App\Request $request)
1054
	{
1055
		$recordId = $request->getInteger('record');
1056
		$templateId = $request->getInteger('template');
1057
		$moduleName = $request->getModule();
1058
		$viewer = $this->getViewer($request);
1059
		$viewer->assign('RECORD_ID', $recordId);
1060
		$viewer->assign('TEMPLATE', $templateId);
1061
		$viewer->assign('MODULE_NAME', $moduleName);
1062
		return $viewer->view('Detail/Widget/PDFViewer.tpl', $moduleName, true);
1063
	}
1064
1065
	/**
1066
	 * Show recent relation.
1067
	 *
1068
	 * @param \App\Request $request
1069
	 *
1070
	 * @return string
1071
	 */
1072
	public function showRecentRelation(App\Request $request)
1073
	{
1074
		$pageNumber = $request->getInteger('page');
1075
		$limitPage = $request->getInteger('limit');
1076
		$moduleName = $request->getModule();
1077
1078
		if (empty($pageNumber)) {
1079
			$pageNumber = 1;
1080
		}
1081
		if (empty($limitPage)) {
1082
			$limitPage = 10;
1083
		}
1084
		$pagingModel = new Vtiger_Paging_Model();
1085
		$pagingModel->set('page', $pageNumber);
1086
		$pagingModel->set('limit', $limitPage);
1087
		$config = OSSMail_Module_Model::getComposeParameters();
1088
		$histories = Vtiger_HistoryRelation_Widget::getHistory($request, $pagingModel);
1089
		$viewer = $this->getViewer($request);
1090
		$viewer->assign('VIEW_MODEL', $this->record);
1091
		$viewer->assign('RECORD_ID', $request->getInteger('record'));
1092
		$viewer->assign('HISTORIES', $histories);
1093
		$viewer->assign('PAGING_MODEL', $pagingModel);
1094
		$viewer->assign('POPUP', $config['popup']);
1095
		$viewer->assign('NO_MORE', $request->getBoolean('noMore'));
1096
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
1097
		$viewer->assign('IS_FULLSCREEN', $request->getBoolean('isFullscreen'));
1098
		return $viewer->view('HistoryRelation.tpl', $moduleName, true);
1099
	}
1100
1101
	/**
1102
	 * Show open street map.
1103
	 *
1104
	 * @param \App\Request $request
1105
	 *
1106
	 * @throws \App\Exceptions\IllegalValue
1107
	 * @throws \App\Exceptions\NoPermittedToRecord
1108
	 *
1109
	 * @return \html
1110
	 */
1111
	public function showOpenStreetMap(App\Request $request)
1112
	{
1113
		if (!\App\Privilege::isPermitted('OpenStreetMap')) {
1114
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
1115
		}
1116
		$moduleName = $request->getModule();
1117
		$recordId = $request->getInteger('record');
1118
		$coordinateModel = OpenStreetMap_Coordinate_Model::getInstance();
1119
		$coordinates = $coordinateModel->readCoordinates($recordId);
1120
		$viewer = $this->getViewer($request);
1121
		$viewer->assign('COORRDINATES', $coordinates);
1122
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
1123
		return $viewer->view('DetailViewMap.tpl', $moduleName, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $viewer->view('De...pl', $moduleName, true) returns the type string which is incompatible with the documented return type html.
Loading history...
1124
	}
1125
1126
	/**
1127
	 * Show chat for record.
1128
	 *
1129
	 * @param \App\Request $request
1130
	 *
1131
	 * @throws \App\Exceptions\NoPermitted
1132
	 * @throws \App\Exceptions\NoPermittedToRecord
1133
	 */
1134
	public function showChat(App\Request $request)
1135
	{
1136
		if (\App\User::getCurrentUserId() !== \App\User::getCurrentUserRealId()) {
1137
			throw new \App\Exceptions\NoPermitted('ERR_NOT_ACCESSIBLE', 406);
1138
		}
1139
		$viewer = $this->getViewer($request);
1140
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
1141
		$viewer->view('Detail/Chat.tpl', 'Chat');
1142
	}
1143
1144
	/** {@inheritdoc} */
1145
	public function getPageTitle(App\Request $request)
1146
	{
1147
		$moduleName = $request->getModule();
1148
		return \App\Language::translate($moduleName, $moduleName) . ' ' . $this->record->getRecord()->getDisplayName();
1149
	}
1150
1151
	/**
1152
	 * Show inventory details from record for specified module.
1153
	 *
1154
	 * @param \App\Request $request
1155
	 *
1156
	 * @throws \App\Exceptions\IllegalValue
1157
	 *
1158
	 * @return string
1159
	 */
1160
	public function showInventoryDetails(App\Request $request)
1161
	{
1162
		$moduleName = $request->getModule();
1163
		$recordModel = Vtiger_Record_Model::getInstanceById($request->getInteger('record'), $moduleName);
1164
		$viewer = $this->getViewer($request);
1165
		$viewer->assign('RECORD', $recordModel);
1166
		$viewer->assign('VIEW', 'Detail');
1167
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
1168
		return $viewer->view('Detail/InventoryView.tpl', $moduleName, true);
1169
	}
1170
1171
	/**
1172
	 * Show process wizard for record.
1173
	 *
1174
	 * @param \App\Request $request
1175
	 *
1176
	 * @return string
1177
	 */
1178
	public function processWizard(App\Request $request)
1179
	{
1180
		$moduleName = $request->getModule();
1181
		$recordModel = $this->record->getRecord();
1182
		$processWizardModel = Vtiger_ProcessWizard_Model::getInstance($recordModel);
1183
		if ($request->has('step')) {
1184
			$processWizardModel->setStep($request->getInteger('step'));
1185
		}
1186
		$step = $processWizardModel->getStep();
1187
		$viewer = $this->getViewer($request);
1188
		$viewer->assign('PROCESS_WIZARD', $processWizardModel);
1189
		$viewer->assign('STEP', $step);
1190
		$viewer->assign('STEP_URL', "index.php?module={$moduleName}&view=Detail&record={$recordModel->getId()}&mode=processWizard&tab_label=LBL_RECORD_PROCESS_WIZARD&step=");
1191
		$viewer->assign('RECORD', $recordModel);
1192
		if (!$this->recordStructure) {
1193
			$this->recordStructure = Vtiger_RecordStructure_Model::getInstanceFromRecordModel($recordModel, Vtiger_RecordStructure_Model::RECORD_STRUCTURE_MODE_DETAIL);
1194
		}
1195
		$structuredValues = $this->recordStructure->getStructure();
1196
		$viewer->assign('RECORD_STRUCTURE', $structuredValues);
1197
		if (!empty($step['quickEdit'])) {
1198
			$viewer->assign('IS_AJAX_ENABLED', $this->isAjaxEnabled($recordModel));
1199
		} else {
1200
			$viewer->assign('IS_AJAX_ENABLED', false);
1201
		}
1202
		$viewer->assign('BLOCK_LIST', $recordModel->getModule()->getBlocks());
1203
		$viewer->assign('MODULE_MODEL', $recordModel->getModule());
1204
		$viewer->assign('VIEW_MODEL', $this->record);
1205
		$viewer->assign('VIEW', $request->getByType('view', 1));
1206
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
1207
		return $viewer->view('Detail/ProcessWizard.tpl', $moduleName, true);
1208
	}
1209
1210
	/**
1211
	 * Show history by field.
1212
	 *
1213
	 * @param App\Request $request
1214
	 *
1215
	 * @return void
1216
	 */
1217
	public function showModTrackerByField(App\Request $request)
1218
	{
1219
		$moduleName = $request->getModule();
1220
		$recordModel = $this->record->getRecord();
1221
		$fieldName = $request->getByType('field', 'Alnum');
1222
		$value = $recordModel->get($fieldName);
1223
		$fieldModel = $recordModel->getModule()->getFieldByName($fieldName);
1224
		$history = [];
1225
		$dataEnd = null;
1226
		foreach (ModTracker_Record_Model::getFieldHistory($request->getInteger('record'), $fieldName) as $row) {
1227
			$dataStart = $row['changedon'];
1228
			$label = $row['postvalue'];
1229
			if (!isset($history[$label])) {
1230
				$history[$label] = [
1231
					'time' => 0,
1232
					'value' => $fieldModel->getDisplayValue($label, $recordModel->getId(), $recordModel),
1233
				];
1234
			}
1235
			$history[$label]['date'] = $dataStart;
1236
			if (isset($history[$row['prevalue']]) && $row['prevalue'] !== $label) {
1237
				$history[$row['prevalue']]['time'] += \App\Fields\DateTime::getDiff($dataStart, $dataEnd, 'minutes');
1238
			}
1239
			$dataEnd = $dataStart;
1240
		}
1241
		$locks = array_merge_recursive(\App\RecordStatus::getLockStatus($moduleName), $recordModel->getEntity()->getLockFields());
1242
		if (isset($history[$value]) && (!isset($locks[$fieldName]) || !\in_array($value, $locks[$fieldName]))) {
1243
			$history[$value]['time'] += \App\Fields\DateTime::getDiff($history[$value]['date'], date('Y-m-d H:i:s'), 'minutes');
1244
		}
1245
		uasort($history, fn ($a, $b) => strnatcmp($b['date'], $a['date']));
1246
		$viewer = $this->getViewer($request);
1247
		$viewer->assign('VIEW', 'Detail');
1248
		$viewer->assign('FIELD_HISTORY', $history);
1249
		$viewer->assign('FIELD_LABEL', $recordModel->getField($fieldName)->getFieldLabel());
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

1249
		$viewer->assign('FIELD_LABEL', /** @scrutinizer ignore-deprecated */ $recordModel->getField($fieldName)->getFieldLabel());

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...
1250
		$viewer->assign('IS_READ_ONLY', $request->getBoolean('isReadOnly') || $this->record->getRecord()->isReadOnly());
1251
		return $viewer->view('Detail/Widget/UpdatesListContent.tpl', $moduleName, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $viewer->view('De...pl', $moduleName, true) returns the type string which is incompatible with the documented return type void.
Loading history...
1252
	}
1253
1254
	/**
1255
	 * Show Chart.
1256
	 *
1257
	 * @param App\Request $request
1258
	 */
1259
	public function showCharts(App\Request $request)
1260
	{
1261
		$viewer = $this->getViewer($request);
1262
		$moduleName = $request->getModule();
1263
1264
		$widgetId = $request->getByType('widgetId', \App\Purifier::INTEGER);
1265
		$widgetData = (new App\Db\Query())->from('vtiger_widgets')->where(['id' => $widgetId, 'tabid' => \App\Module::getModuleId($moduleName)])->one();
1266
		$data = App\Json::decode($widgetData['data']);
1267
		$data['module'] = \App\Module::getModuleName($data['relatedmodule']);
1268
		$data['recordId'] = $this->record->getRecord()->getId();
1269
		$widgetData['data'] = \App\Json::encode($data);
1270
		$widget = Vtiger_Widget_Model::getInstanceFromValues($widgetData);
1271
		$chartFilterWidgetModel = Vtiger_ChartFilter_Model::getInstance();
1272
		$chartFilterWidgetModel->setWidgetModel($widget);
1273
		$viewer->assign('MODULE_NAME', $moduleName);
1274
		$viewer->assign('CHART_MODEL', $chartFilterWidgetModel);
1275
		$viewer->assign('CHART_DATA', $chartFilterWidgetModel->getChartData());
1276
		$viewer->view('Detail/Widget/ShowChart.tpl', $moduleName);
1277
	}
1278
}
1279