Passed
Push — developer ( 5daf12...5389df )
by Radosław
35:30 queued 17:27
created

Calendar_Module_Model::getConfig()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 9
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 3
nc 4
nop 2
crap 12
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
/**
14
 * Calendar Module Model Class.
15
 */
16
class Calendar_Module_Model extends Vtiger_Module_Model
17
{
18
	/** {@inheritdoc} */
19
	public $allowTypeChange = false;
20
21
	/** @var string Config table name the calendar */
22 2
	public const TABLE_NAME_CONFIG = 'vtiger_calendar_config';
23
24 2
	/**
25
	 * Function returns the default view for the Calendar module.
26
	 *
27
	 * @return string
28
	 */
29
	public function getDefaultViewName()
30
	{
31
		return 'Calendar';
32 2
	}
33
34 2
	/**
35 2
	 *  Function returns the url for Calendar view.
36 2
	 *
37
	 * @return string
38 2
	 */
39
	public function getCalendarViewUrl()
40
	{
41
		return 'index.php?module=' . $this->get('name') . '&view=' . $this->getDefaultViewName();
42
	}
43
44
	/**
45
	 * Get calendar configuration by type.
46
	 *
47
	 * @param string $type
48
	 * @param string $name
49
	 *
50
	 * @return string|array
51
	 */
52
	public static function getConfig(string $type, string $name = '')
53
	{
54
		if (\App\Cache::has('CalendarConfiguration', $type)) {
55
			$config = \App\Cache::get('CalendarConfiguration', $type);
56
		} else {
57
			$config = (new \App\Db\Query())->from(self::TABLE_NAME_CONFIG)->indexBy('name')->where(['type' => $type])->all();
58
			\App\Cache::save('CalendarConfiguration', $type, $config);
59
		}
60
		return $name ? $config[$name]['value'] ?? '' : $config;
61
	}
62
63
	/**
64
	 * Function to check whether the module is summary view supported.
65
	 *
66
	 * @return bool
67
	 */
68
	public function isSummaryViewSupported()
69
	{
70
		return false;
71
	}
72
73
	/** {@inheritdoc} */
74
	public function getSideBarLinks($linkParams)
75
	{
76
		$links = Vtiger_Link_Model::getAllByType($this->getId(), ['SIDEBARLINK', 'SIDEBARWIDGET'], $linkParams);
77
		$links['SIDEBARLINK'][] = Vtiger_Link_Model::getInstanceFromValues([
78
			'linktype' => 'SIDEBARLINK',
79
			'linklabel' => 'LBL_CALENDAR_VIEW',
80
			'linkurl' => $this->getCalendarViewUrl(),
81
			'linkicon' => 'fas fa-calendar-alt',
82
		]);
83
		$links['SIDEBARLINK'][] = Vtiger_Link_Model::getInstanceFromValues([
84
			'linktype' => 'SIDEBARLINK',
85
			'linklabel' => 'LBL_RECORDS_LIST',
86
			'linkurl' => $this->getListViewUrl(),
87
			'linkicon' => 'fas fa-list',
88
		]);
89
		if (isset($linkParams['ACTION']) && 'Calendar' === $linkParams['ACTION'] && App\Config::module('Calendar', 'SHOW_LIST_BUTTON')) {
90
			$links['SIDEBARLINK'][] = Vtiger_Link_Model::getInstanceFromValues([
91
				'linktype' => 'SIDEBARLINK',
92
				'linklabel' => 'LBL_CALENDAR_LIST',
93
				'linkurl' => 'javascript:Calendar_Calendar_Js.goToRecordsList("' . $this->getListViewUrl() . '");',
94
				'linkicon' => 'far fa-calendar-minus',
95
			]);
96
		}
97
		if ($this->isPermitted('Kanban') && \App\Utils\Kanban::getBoards($this->getName(), true)) {
0 ignored issues
show
Bug introduced by
$this->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Utils\Kanban::getBoards(). ( Ignorable by Annotation )

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

97
		if ($this->isPermitted('Kanban') && \App\Utils\Kanban::getBoards(/** @scrutinizer ignore-type */ $this->getName(), true)) {
Loading history...
98
			$links['SIDEBARLINK'][] = Vtiger_Link_Model::getInstanceFromValues([
99
				'linktype' => 'SIDEBARLINK',
100
				'linklabel' => 'LBL_VIEW_KANBAN',
101
				'linkurl' => 'index.php?module=' . $this->getName() . '&view=Kanban',
102
				'linkicon' => 'yfi yfi-kanban',
103
			]);
104
		}
105
		return $links;
106
	}
107
108
	/**
109
	 * Function returns the url that shows Calendar Import result.
110
	 *
111
	 * @return string url
112
	 */
113
	public function getImportResultUrl()
114
	{
115
		return 'index.php?module=' . $this->getName() . '&view=ImportResult';
116
	}
117
118
	/**
119
	 * Function to get export query.
120
	 *
121
	 * @param mixed $focus
122
	 * @param mixed $where
123
	 *
124
	 * @return string query;
125
	 */
126
	public function getExportQuery($focus = '', $where = '')
0 ignored issues
show
Unused Code introduced by
The parameter $focus 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

126
	public function getExportQuery(/** @scrutinizer ignore-unused */ $focus = '', $where = '')

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...
Unused Code introduced by
The parameter $where 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

126
	public function getExportQuery($focus = '', /** @scrutinizer ignore-unused */ $where = '')

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...
127
	{
128
		return (new App\Db\Query())->select(['vtiger_activity.*', 'vtiger_crmentity.description', 'assigned_user_id' => 'vtiger_crmentity.smownerid', 'vtiger_activity_reminder.reminder_time'])
129
			->from('vtiger_activity')
130
			->innerJoin('vtiger_crmentity', 'vtiger_activity.activityid = vtiger_crmentity.crmid')
131
			->leftJoin('vtiger_activity_reminder', 'vtiger_activity_reminder.activity_id = vtiger_activity.activityid')
132
			->where(['vtiger_crmentity.deleted' => 0, 'vtiger_crmentity.smownerid' => App\User::getCurrentUserId()]);
133
	}
134
135
	/**
136
	 * Function to set event fields for export.
137
	 */
138
	public function setEventFieldsForExport()
139
	{
140
		$keysToReplace = ['taskpriority'];
141
		$keysValuesToReplace = ['taskpriority' => 'priority'];
142
		foreach ($this->getFields() as $fieldName => $fieldModel) {
143
			if ($fieldModel->getPermissions()) {
144
				if (!\in_array($fieldName, $keysToReplace)) {
145
					$eventFields[$fieldName] = 'yes';
146
				} else {
147
					$eventFields[$keysValuesToReplace[$fieldName]] = 'yes';
148
				}
149
			}
150
		}
151
		$this->set('eventFields', $eventFields);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eventFields does not seem to be defined for all execution paths leading up to this point.
Loading history...
152
	}
153
154
	/**
155
	 * Function to set todo fields for export.
156
	 */
157
	public function setTodoFieldsForExport()
158
	{
159
		$keysToReplace = ['taskpriority', 'activitystatus'];
160
		$keysValuesToReplace = ['taskpriority' => 'priority', 'activitystatus' => 'status'];
161
		foreach ($this->getFields() as $fieldName => $fieldModel) {
162
			if ($fieldModel->getPermissions()) {
163
				if (!\in_array($fieldName, $keysToReplace)) {
164
					$todoFields[$fieldName] = 'yes';
165
				} else {
166
					$todoFields[$keysValuesToReplace[$fieldName]] = 'yes';
167
				}
168
			}
169
		}
170
		$this->set('todoFields', $todoFields);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $todoFields does not seem to be defined for all execution paths leading up to this point.
Loading history...
171
	}
172
173
	/**
174
	 * Function to get the url to view Details for the module.
175
	 *
176
	 * @param mixed $id
177
	 *
178
	 * @return string - url
179
	 */
180
	public function getDetailViewUrl($id)
181
	{
182
		return 'index.php?module=Calendar&view=' . $this->getDetailViewName() . '&record=' . $id;
183
	}
184
185
	/**
186
	 * Function to get Alphabet Search Field.
187
	 */
188
	public function getAlphabetSearchField()
189
	{
190
		return 'subject';
191
	}
192
193
	/**
194
	 * Function returns Calendar Reminder record models.
195
	 *
196
	 * @return \Calendar_Record_Model[]
197
	 */
198
	public static function getCalendarReminder()
199
	{
200
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
201
		$activityReminder = $currentUserModel->getCurrentUserActivityReminderInSeconds();
202
		$recordModels = [];
203
		if (!empty($activityReminder)) {
204
			$time = date('Y-m-d H:i:s', strtotime("+$activityReminder seconds"));
205
			$queryGenerator = new \App\QueryGenerator('Calendar');
206
			$queryGenerator->setFields(['id']);
207
			$queryGenerator->addJoin(['INNER JOIN', 'vtiger_activity_reminder_popup', 'vtiger_activity_reminder_popup.recordid = vtiger_activity.activityid']);
208
			$queryGenerator->addJoin(['INNER JOIN', 'vtiger_activity_reminder_popup', 'vtiger_activity_reminder_popup.recordid = vtiger_crmentity.crmid']);
209
			$queryGenerator->addCondition('assigned_user_id', $currentUserModel->getId(), 'e', false);
210
			if (self::getConfig('reminder', 'shared_persons')) {
211
				$queryGenerator->addCondition('shownerid', $currentUserModel->getId(), 'e', false);
212
			}
213
			$queryGenerator->addCondition('activitystatus', self::getComponentActivityStateLabel('current'), 'e');
214
			$queryGenerator->addNativeCondition(['or', ['and', ['vtiger_activity_reminder_popup.status' => Calendar_Record_Model::REMNDER_POPUP_ACTIVE], ['<=', 'vtiger_activity_reminder_popup.datetime', $time]], ['and', ['vtiger_activity_reminder_popup.status' => Calendar_Record_Model::REMNDER_POPUP_WAIT], ['<=', 'vtiger_activity_reminder_popup.datetime', date('Y-m-d H:i:s')]]]);
215
			$queryGenerator->setLimit(\App\Config::module('Calendar', 'maxNumberCalendarNotifications', 20));
216
			$dataReader = $queryGenerator->createQuery()->orderBy(['vtiger_activity_reminder_popup.datetime' => SORT_DESC])->distinct()->createCommand()->query();
217
218
			while ($row = $dataReader->read()) {
219
				$recordModels[] = Vtiger_Record_Model::getInstanceById($row['id'], 'Calendar');
220
			}
221
			$dataReader->close();
222
		}
223
		return $recordModels;
224
	}
225
226
	/** {@inheritdoc} */
227
	public function getFieldsByType($type, bool $active = false): array
228
	{
229
		$restrictedField = ['picklist' => ['activitystatus', 'visibility', 'duration_minutes']];
230
		if (!\is_array($type)) {
231
			$type = [$type];
232
		}
233
		$fields = $this->getFields();
234
		$fieldList = [];
235
		foreach ($fields as $field) {
236
			$fieldType = $field->getFieldDataType();
237
			if (\in_array($fieldType, $type)) {
238
				$fieldName = $field->getName();
239
				if ('picklist' == $fieldType && \in_array($fieldName, $restrictedField[$fieldType])) {
240
				} else {
241
					$fieldList[$fieldName] = $field;
242
				}
243
			}
244
		}
245
		return $fieldList;
246
	}
247
248
	/** {@inheritdoc} */
249
	public function getSettingLinks(): array
250
	{
251
		$currentUserModel = Users_Record_Model::getCurrentUserModel();
252
		$settingLinks = [];
253
		if ($currentUserModel->isAdminUser()) {
254
			$settingLinks[] = [
255
				'linktype' => 'LISTVIEWSETTING',
256
				'linklabel' => 'LBL_EDIT_FIELDS',
257
				'linkurl' => 'index.php?parent=Settings&module=LayoutEditor&sourceModule=' . $this->getName(),
258
				'linkicon' => 'adminIcon-modules-fields',
259
			];
260
			$settingLinks[] = [
261
				'linktype' => 'LISTVIEWSETTING',
262
				'linklabel' => 'LBL_EDIT_PICKLIST_VALUES',
263
				'linkurl' => 'index.php?parent=Settings&module=Picklist&view=Index&source_module=' . $this->getName(),
264
				'linkicon' => 'adminIcon-fields-picklists',
265
			];
266
		}
267
		return $settingLinks;
0 ignored issues
show
introduced by
The expression return $settingLinks returns an array which contains values of type array<string,string> which are incompatible with the return type string mandated by Vtiger_Module_Model::getSettingLinks().
Loading history...
268
	}
269
270
	/**
271
	 * Function to get orderby sql from orderby field.
272
	 *
273
	 * @param mixed $orderBy
274
	 */
275
	public function getOrderBySql($orderBy)
276
	{
277
		if ('status' == $orderBy) {
278
			return $orderBy;
279
		}
280
		return parent::getOrderBySql($orderBy);
281
	}
282
283
	public static function getCalendarTypes()
284
	{
285
		return App\Fields\Picklist::getValuesName('activitytype');
286
	}
287
288
	/** {@inheritdoc} */
289
	public function getValuesFromSource(App\Request $request, $moduleName = false): array
290
	{
291
		$data = parent::getValuesFromSource($request);
292
293
		if (($postponeTime = $request->getInteger('postponeTime')) && $sourceRecordId = $request->getInteger('sourceRecord')) {
294
			$data = array_merge($data, $this->postponeTimeValue($sourceRecordId, $postponeTime));
295
		}
296
		return $data;
297
	}
298
299
	/**
300
	 * Load field  postpone values.
301
	 *
302
	 * @param int $sourceRecordId
303
	 * @param int $postponeTime
304
	 *
305
	 * @return array
306
	 */
307
	public function postponeTimeValue(int $sourceRecordId, int $postponeTime): array
308
	{
309
		$sourceRecordModel = Vtiger_Record_Model::getInstanceById($sourceRecordId);
310 1
		$dateStart = $sourceRecordModel->get('date_start') . ' ' . $sourceRecordModel->get('time_start');
311
		$dateEnd = $sourceRecordModel->get('due_date') . ' ' . $sourceRecordModel->get('time_end');
312 1
		$data['date_start'] = (new DateTime($dateStart))->modify("+ {$postponeTime} minutes")->format('Y-m-d H:i:s');
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
313 1
		$data['due_date'] = (new DateTime($dateEnd))->modify("+ {$postponeTime} minutes")->format('Y-m-d H:i:s');
314 1
		return $data;
315 1
	}
316
317 1
	public static function getCalendarState($data = [])
318 1
	{
319 1
		if ($data) {
320 1
			$activityStatus = $data['activitystatus'];
321 1
			if (\in_array($activityStatus, self::getComponentActivityStateLabel('history'))) {
322 1
				return false;
323 1
			}
324 1
			$dueDateTime = $data['due_date'] . ' ' . $data['time_end'];
325 1
			$startDateTime = $data['date_start'] . ' ' . $data['time_start'];
326
			$dates = ['start' => $startDateTime, 'end' => $dueDateTime, 'current' => null];
327 1
			foreach ($dates as $key => $date) {
328 1
				$date = new DateTimeField($date);
329
				$userFormatedString = $date->getDisplayDate();
330
				$timeFormatedString = $date->getDisplayTime();
331 1
				$dBFomatedDate = DateTimeField::convertToDBFormat($userFormatedString);
332 1
				$dates[$key] = strtotime($dBFomatedDate . ' ' . $timeFormatedString);
333
			}
334 1
			$activityStatusLabels = self::getComponentActivityStateLabel();
335 1
			if (!empty($data['activitystatus']) && isset($activityStatusLabels[$data['activitystatus']])) {
336
				$state = $activityStatusLabels[$data['activitystatus']];
337
			} else {
338
				$state = $activityStatusLabels['not_started'];
339
				if ($dates['end'] > $dates['current'] && $dates['start'] < $dates['current']) {
340 1
					$state = $activityStatusLabels['in_realization'];
341
				} elseif ($dates['end'] > $dates['current']) {
342
					$state = $activityStatusLabels['not_started'];
343
				} elseif ($dates['end'] < $dates['current']) {
344
					$state = $activityStatusLabels['overdue'];
345
				}
346
			}
347
			return $state;
348
		}
349
		return false;
350
	}
351
352 1
	/**
353
	 * The function gets the labels for a given status field.
354 1
	 *
355 1
	 * @param string $key
356
	 *
357
	 * @return <Array>
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Array> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Array>.
Loading history...
358 1
	 */
359 1
	public static function getComponentActivityStateLabel($key = '')
360 1
	{
361 1
		$pickListValues = App\Fields\Picklist::getValuesName('activitystatus');
362 1
		if (!\is_array($pickListValues)) {
0 ignored issues
show
introduced by
The condition is_array($pickListValues) is always true.
Loading history...
363 1
			return [];
364 1
		}
365 1
		$componentsActivityState = [];
366 1
		foreach ($pickListValues as $value) {
367 1
			switch ($value) {
368 1
				case 'PLL_PLANNED':
369 1
					$componentsActivityState['not_started'] = $value;
370 1
					break;
371 1
				case 'PLL_IN_REALIZATION':
372 1
					$componentsActivityState['in_realization'] = $value;
373 1
					break;
374 1
				case 'PLL_COMPLETED':
375 1
					$componentsActivityState['completed'] = $value;
376 1
					break;
377 1
				case 'PLL_POSTPONED':
378 1
					$componentsActivityState['postponed'] = $value;
379
					break;
380
				case 'PLL_OVERDUE':
381
					$componentsActivityState['overdue'] = $value;
382
					break;
383 1
				case 'PLL_CANCELLED':
384 1
					$componentsActivityState['cancelled'] = $value;
385 1
					break;
386 1
				default:
387 1
					break;
388
			}
389
		}
390 1
		if ('current' == $key) {
391
			$componentsActivityState = ['PLL_PLANNED', 'PLL_IN_REALIZATION', 'PLL_OVERDUE'];
392
		} elseif ('history' == $key) {
393
			$componentsActivityState = ['PLL_COMPLETED', 'PLL_POSTPONED', 'PLL_CANCELLED'];
394
		} elseif ($key) {
395
			return $componentsActivityState[$key];
396
		}
397
		return $componentsActivityState;
398
	}
399
400
	/**
401
	 * Import calendar rekords from ICS.
402
	 *
403
	 * @param string $filePath
404
	 *
405
	 * @throws \Exception
406
	 *
407
	 * @return array
408
	 */
409
	public function importICS(string $filePath)
410
	{
411
		$userId = \App\User::getCurrentUserRealId();
412
		$lastImport = new ICalLastImport();
413
		$lastImport->clearRecords($userId);
414
		$eventModule = 'Events';
415
		$todoModule = 'Calendar';
416
		$totalCount = $skipCount = [$eventModule => 0, $todoModule => 0];
417
		$calendar = \App\Integrations\Dav\Calendar::loadFromContent(file_get_contents($filePath));
418
		foreach ($calendar->getRecordInstance() as $recordModel) {
419
			$recordModel->set('assigned_user_id', $userId);
420
			$recordModel->save();
421
			$calendar->recordSaveAttendee($recordModel);
422
			if ('VEVENT' === (string) $calendar->getComponent()->name) {
423
				$module = $eventModule;
424
			} else {
425
				$module = $todoModule;
426
			}
427
			if ($recordModel->getId()) {
428
				++$totalCount[$module];
429
				$lastImport = new ICalLastImport();
430
				$lastImport->setFields(['userid' => $userId, 'entitytype' => $this->getName(), 'crmid' => $recordModel->getId()]);
431
				$lastImport->save();
432
			} else {
433
				++$skipCount[$module];
434
			}
435
		}
436
		return ['events' => $totalCount[$eventModule] - $skipCount[$eventModule], 'skipped_events' => $skipCount[$eventModule], 'task' => $totalCount[$todoModule] - $skipCount[$todoModule], 'skipped_task' => $skipCount[$todoModule]];
437
	}
438
439
	/** {@inheritdoc} */
440
	public function getLayoutTypeForQuickCreate(): string
441
	{
442
		return 'standard';
443
	}
444
}
445