Passed
Push — developer ( 5f736c...9195d5 )
by Mariusz
18:14
created

Vtiger_CalendarExtSource_Model::getByModule()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 10
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
/**
4
 * Calendar extra source model file.
5
 *
6
 * @package Model
7
 *
8
 * @copyright YetiForce S.A.
9
 * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
10
 * @author    Mariusz Krzaczkowski <[email protected]>
11
 */
12
/**
13
 * Calendar extra source model class.
14
 */
15
class Vtiger_CalendarExtSource_Model extends App\Base
16
{
17
	/** @var string[] Extra source details */
18
	const EXTRA_SOURCE_TYPES = [
19
		1 => 'LBL_SOURCE_TYPE_1',
20
		2 => 'LBL_SOURCE_TYPE_2',
21
		3 => 'LBL_SOURCE_TYPE_3',
22
		4 => 'LBL_SOURCE_TYPE_4',
23
	];
24
	/** @var string Extra source table name */
25
	const EXTRA_SOURCE_TABLE = 's_#__calendar_sources';
26
27
	/** @var string Base module name */
28
	protected $baseModuleName;
29
	/** @var string Target module name */
30
	protected $targetModuleName;
31
	/** @var \Vtiger_Module_Model Target module model */
32
	protected $targetModuleModel;
33
	/** @var \App\QueryGenerator Query generator instance */
34
	protected $queryGenerator;
35
	/** @var string[] Name record fields */
36
	protected $nameFields;
37
38
	/**
39
	 * Get calendar extra sources list.
40
	 *
41
	 * @param int $moduleId
42
	 *
43
	 * @return array
44
	 */
45
	public static function getByModule(int $moduleId): array
46
	{
47
		if (\App\Cache::has('Calendar-GetExtraSourcesList', $moduleId)) {
48
			return \App\Cache::get('API-GetExtraSourcesList', $moduleId);
49
		}
50
		$rows = (new \App\Db\Query())->from('s_#__calendar_sources')
51
			->where(['base_module' => $moduleId])
52
			->createCommand(\App\Db::getInstance('admin'))->queryAllByGroup(1);
53
		\App\Cache::save('Calendar-GetExtraSourcesList', $moduleId, $rows, \App\Cache::LONG);
54
		return $rows;
55
	}
56
57
	/**
58
	 * Get calendar extra source instance by id.
59
	 *
60
	 * @param int $id
61
	 *
62
	 * @return $this
63
	 */
64
	public static function getInstanceById(int $id)
65
	{
66
		$source = self::getById($id);
67
		if (!$source) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $source of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
68
			throw new \App\Exceptions\AppException('No calendar extra source found');
69
		}
70
		$moduleName = \App\Module::getModuleName($source['base_module']);
71
		$className = Vtiger_Loader::getComponentClassName('Model', 'CalendarExtSource', $moduleName);
72
		$instance = new $className($source);
73
		$instance->baseModuleName = $moduleName;
74
		$instance->targetModuleName = \App\Module::getModuleName($source['target_module']);
75
		return $instance;
76
	}
77
78
	/**
79
	 * Get calendar extra source clean instance by module name.
80
	 *
81
	 * @param string $moduleName
82
	 *
83
	 * @return $this
84
	 */
85
	public static function getCleanInstance(string $moduleName)
86
	{
87
		$className = Vtiger_Loader::getComponentClassName('Model', 'CalendarExtSource', $moduleName);
88
		$handler = new $className();
89
		$handler->baseModuleName = $moduleName;
90
		return $handler;
91
	}
92
93
	/**
94
	 * Get calendar extra sources data by id.
95
	 *
96
	 * @param int $id
97
	 *
98
	 * @return string[]
99
	 */
100
	public static function getById(int $id): array
101
	{
102
		if (\App\Cache::has('Calendar-GetExtraSourceById', $id)) {
103
			return \App\Cache::get('Calendar-GetExtraSourceById', $id);
104
		}
105
		$row = (new \App\Db\Query())->from(self::EXTRA_SOURCE_TABLE)
106
			->where(['id' => $id])
107
			->one(\App\Db::getInstance('admin'));
108
		\App\Cache::save('Calendar-GetExtraSourceById', $id, $row, \App\Cache::LONG);
109
		return $row;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $row could return the type false which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
110
	}
111
112
	/**
113
	 * Save calendar extra sources.
114
	 *
115
	 * @return int
116
	 */
117
	public function save(): int
118
	{
119
		$dbCommand = \App\Db::getInstance('admin')->createCommand();
120
		if ($id = $this->get('id')) {
121
			$dbCommand->update(self::EXTRA_SOURCE_TABLE, $this->getData(), [
122
				'id' => $id
123
			])->execute();
124
			\App\Cache::save('Calendar-GetExtraSourceById', $id, $this->getData(), \App\Cache::LONG);
125
		} else {
126
			$params = $this->getData();
127
			$params['user_id'] = \App\User::getCurrentUserId();
128
			$dbCommand->insert(self::EXTRA_SOURCE_TABLE, $params)
129
				->execute();
130
			$id = \App\Db::getInstance('admin')->getLastInsertID(self::EXTRA_SOURCE_TABLE . '_id_seq');
131
		}
132
		\App\Cache::delete('Calendar-GetExtraSourcesList', $this->get('base_module'));
133
		return $id;
134
	}
135
136
	/**
137
	 * Delete calendar extra sources.
138
	 *
139
	 * @return bool
140
	 */
141
	public function delete(): bool
142
	{
143
		$dbCommand = \App\Db::getInstance('admin')->createCommand();
144
		$status = $dbCommand->delete(self::EXTRA_SOURCE_TABLE, ['id' => $this->get('id')])->execute();
145
		\App\Cache::delete('Calendar-GetExtraSourceById', $this->get('id'));
146
		\App\Cache::delete('Calendar-GetExtraSourcesList', $this->get('base_module'));
147
		return (bool) $status;
148
	}
149
150
	/**
151
	 * Get module model.
152
	 *
153
	 * @return Vtiger_Module_Model
154
	 */
155
	public function getModule(): Vtiger_Module_Model
156
	{
157
		if (!$this->targetModuleModel) {
158
			$this->targetModuleModel = \Vtiger_Module_Model::getInstance($this->targetModuleName);
159
		}
160
		return $this->targetModuleModel;
161
	}
162
163
	/**
164
	 * Get extra sources query.
165
	 *
166
	 * @return \App\Db\Query
167
	 */
168
	protected function getExtraSourcesQuery(): ?App\Db\Query
169
	{
170
		if (
171
			!\App\Privilege::isPermitted($this->targetModuleName)
172
			|| !\App\CustomView::getCustomViewById($this->get('custom_view'))
173
		) {
174
			return null;
175
		}
176
		$this->queryGenerator = new App\QueryGenerator($this->targetModuleName);
177
		$this->queryGenerator->initForCustomViewById($this->get('custom_view'));
178
		$this->targetModuleModel = $this->queryGenerator->getModuleModel();
179
		if ($this->get('include_filters')) {
180
			$this->loadExtraSourcesQueryFilter();
181
		}
182
		$this->queryGenerator->clearFields();
183
		$this->queryGenerator->setField('assigned_user_id');
184
		$this->nameFields = $this->getModule()->getNameFields();
185
		foreach ($this->nameFields as $field) {
186
			$this->queryGenerator->setField($field);
187
		}
188
		$this->loadExtraSourcesQueryType();
189
		return $this->queryGenerator->createQuery();
190
	}
191
192
	/**
193
	 * Load extra sources query condition by type.
194
	 *
195
	 * @return void
196
	 */
197
	protected function loadExtraSourcesQueryType(): void
198
	{
199
		$fieldA = $this->getModule()->getField($this->get('fieldid_a_date'));
200
		$startDateInstance = DateTimeField::convertToDBTimeZone($this->get('start'));
201
		$startDateTime = $startDateInstance->format('Y-m-d H:i:s');
202
		$startDate = $startDateInstance->format('Y-m-d');
203
		$endDateInstance = DateTimeField::convertToDBTimeZone($this->get('end'));
204
		$endDateTime = $endDateInstance->format('Y-m-d H:i:s');
205
		$endDate = $endDateInstance->format('Y-m-d');
206
		if ('datetime' === $fieldA->getFieldDataType()) {
207
			$startFormatted = $startDateTime;
208
			$endFormatted = $endDateTime;
209
		} else {
210
			$startFormatted = $startDate;
211
			$endFormatted = $endDate;
212
		}
213
		$columnA = $fieldA->getTableName() . '.' . $fieldA->getColumnName();
214
		$this->queryGenerator->setField($fieldA->getName());
215
		switch ($this->get('type')) {
216
			case 1:
217
				$this->queryGenerator->addNativeCondition([
218
					'and', ['>=', $columnA, $startFormatted], ['<=', $columnA,  $endFormatted]
219
				]);
220
				break;
221
			case 3:
222
				$fieldB = $this->getModule()->getField($this->get('fieldid_b_date'));
223
				$columnB = $fieldB->getTableName() . '.' . $fieldB->getColumnName();
224
				$this->queryGenerator->setField($fieldB->getName());
225
				$this->queryGenerator->addNativeCondition([
226
					'or',
227
					['and', ['>=', $columnA, $startFormatted], ['<=', $columnA,  $endFormatted]],
228
					['and', ['>=', $columnB, $startFormatted], ['<=', $columnB,  $endFormatted]],
229
					['and', ['<', $columnA, $startFormatted], ['>', $columnB,  $endFormatted]],
230
				]);
231
				break;
232
			case 2:
233
				$fieldTimeA = $this->getModule()->getField($this->get('fieldid_a_time'));
234
				$this->queryGenerator->setField($fieldTimeA->getName());
235
				$columnATime = $fieldTimeA->getTableName() . '.' . $fieldTimeA->getColumnName();
236
				$this->queryGenerator->addNativeCondition([
237
					'or',
238
					[
239
						'and',
240
						['>=', new \yii\db\Expression("CONCAT($columnA, ' ', $columnATime)"),  $startDateTime],
241
						['<=', new \yii\db\Expression("CONCAT($columnA, ' ', $columnATime)"),  $endDateTime],
242
					],
243
				]);
244
				break;
245
			case 4:
246
				$fieldTimeA = $this->getModule()->getField($this->get('fieldid_a_time'));
247
				$fieldB = $this->getModule()->getField($this->get('fieldid_b_date'));
248
				$fieldTimeB = $this->getModule()->getField($this->get('fieldid_b_time'));
249
				$columnATime = $fieldTimeA->getTableName() . '.' . $fieldTimeA->getColumnName();
250
				$columnB = $fieldB->getTableName() . '.' . $fieldB->getColumnName();
251
				$columnBTime = $fieldTimeB->getTableName() . '.' . $fieldTimeB->getColumnName();
252
				$this->queryGenerator->setField($fieldTimeA->getName());
253
				$this->queryGenerator->setField($fieldB->getName());
254
				$this->queryGenerator->setField($fieldTimeB->getName());
255
				$this->queryGenerator->addNativeCondition([
256
					'or',
257
					[
258
						'and',
259
						['>=', new \yii\db\Expression("CONCAT($columnA, ' ', $columnATime)"),  $startDateTime],
260
						['<=', new \yii\db\Expression("CONCAT($columnA, ' ', $columnATime)"),  $endDateTime],
261
					],
262
					[
263
						'and',
264
						['>=', new \yii\db\Expression("CONCAT($columnB, ' ', $columnBTime)"),  $startDateTime],
265
						['<=', new \yii\db\Expression("CONCAT($columnB, ' ', $columnBTime)"),  $endDateTime],
266
					],
267
					[
268
						'and', ['<', $columnA, $startDate], ['>', $columnB,  $endDate],
269
					],
270
				]);
271
				break;
272
			default:
273
				break;
274
		}
275
	}
276
277
	/**
278
	 * Load extra sources query condition by type.
279
	 *
280
	 * @return void
281
	 */
282
	protected function loadExtraSourcesQueryFilter(): void
283
	{
284
		$conditions = [];
285
		if (!empty($this->get('user')) && isset($this->get('user')['selectedIds'][0])) {
286
			$selectedUsers = $this->get('user');
287
			$selectedIds = $selectedUsers['selectedIds'];
288
			if ('all' !== $selectedIds[0]) {
289
				$conditions[] = ['vtiger_crmentity.smownerid' => $selectedIds];
290
				$subQuery = (new \App\Db\Query())->select(['crmid'])
291
					->from('u_#__crmentity_showners')
292
					->where(['userid' => $selectedIds]);
293
				$conditions[] = ['vtiger_crmentity.crmid' => $subQuery];
294
			}
295
			if (isset($selectedUsers['excludedIds']) && 'all' === $selectedIds[0]) {
296
				$conditions[] = ['not in', 'vtiger_crmentity.smownerid', $selectedUsers['excludedIds']];
297
			}
298
		}
299
		if ($conditions) {
300
			$this->queryGenerator->addNativeCondition(array_merge(['or'], $conditions));
301
		}
302
	}
303
304
	/**
305
	 * Get calendar extra source counter.
306
	 *
307
	 * @return int
308
	 */
309
	public function getExtraSourcesCount(): int
310
	{
311
		return $this->getExtraSourcesQuery()->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getExtraSourcesQuery()->count() could return the type null|string which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
312
	}
313
314
	/**
315
	 * Get calendar extra source rows.
316
	 *
317
	 * @return array
318
	 */
319
	public function getRows(): array
320
	{
321
		$dataReader = $this->getExtraSourcesQuery()->createCommand()->query();
322
		$result = [];
323
		while ($row = $dataReader->read()) {
324
			$result[] = $this->formatRow($row);
325
		}
326
		$dataReader->close();
327
		return $result;
328
	}
329
330
	/**
331
	 * Format record data.
332
	 *
333
	 * @param array $row
334
	 *
335
	 * @return array
336
	 */
337
	protected function formatRow(array $row): array
338
	{
339
		$item = [
340
			'id' => $row['id'],
341
			'editable' => false,
342
		];
343
		$this->formatDate($row, $item);
344
		$title = '';
345
		foreach ($this->nameFields as $field) {
346
			$title .= ' ' . \App\Purifier::encodeHtml($row[$field]);
347
		}
348
		$item['title'] = trim($title);
349
		$item['backgroundColor'] = $this->get('color');
350
		$item['className'] = 'js-popover-tooltip--record ownerCBr_' . $row['assigned_user_id'];
351
		if ($this->getModule()->isSummaryViewSupported()) {
352
			$item['url'] = 'index.php?module=' . $this->targetModuleName . '&view=Detail&record=' . $row['id'];
353
			$item['className'] .= ' js-show-modal';
354
		} else {
355
			$item['url'] = $this->getModule()->getDetailViewUrl($row['id']);
356
		}
357
		return $item;
358
	}
359
360
	/**
361
	 * Format dates.
362
	 *
363
	 * @param array $row
364
	 * @param array $item
365
	 *
366
	 * @return void
367
	 */
368
	protected function formatDate(array $row, array &$item): void
369
	{
370
		$fieldA = $this->getModule()->getField($this->get('fieldid_a_date'));
371
		$valueA = $row[$fieldA->getColumnName()];
372
		switch ($this->get('type')) {
373
			case 3:
374
				$fieldB = $this->getModule()->getField($this->get('fieldid_b_date'));
375
				$valueB = $row[$fieldB->getColumnName()];
376
				if ($valueB) {
377
					if ('datetime' === $fieldB->getFieldDataType()) {
378
						$date = new DateTimeField($valueB);
379
						$item['end'] = $date->getFullcalenderValue();
380
						$item['end_display'] = $date->getDisplayDateTimeValue();
381
					} else {
382
						$item['end'] = $valueB;
383
						$item['end_display'] = \App\Fields\Date::formatToDisplay($valueB);
384
					}
385
				}
386
				// no break
387
			case 1:
388
				if ($valueA) {
389
					if ('datetime' === $fieldA->getFieldDataType()) {
390
						$date = new DateTimeField($valueA);
391
						$item['start'] = $date->getFullcalenderValue();
392
						$item['start_display'] = $date->getDisplayDateTimeValue();
393
					} else {
394
						$item['start'] = $valueA;
395
						$item['start_display'] = \App\Fields\Date::formatToDisplay($valueA);
396
					}
397
				} else {
398
					$item['start'] = $item['end'];
399
					$item['start_display'] = $item['end_display'];
400
				}
401
				break;
402
			case 4:
403
				$valueB = $row[$this->getModule()->getField($this->get('fieldid_b_date'))->getColumnName()];
404
				$valueTimeB = $row[$this->getModule()->getField($this->get('fieldid_b_time'))->getColumnName()];
405
				if ($valueTimeB) {
406
					$date = new DateTimeField($valueB . ' ' . $valueTimeB);
0 ignored issues
show
Bug introduced by
$valueB . ' ' . $valueTimeB of type string is incompatible with the type type expected by parameter $value of DateTimeField::__construct(). ( Ignorable by Annotation )

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

406
					$date = new DateTimeField(/** @scrutinizer ignore-type */ $valueB . ' ' . $valueTimeB);
Loading history...
407
					$item['end'] = $date->getFullcalenderValue();
408
					$item['end_display'] = $date->getDisplayDateTimeValue();
409
				}
410
				// no break
411
			case 2:
412
				$valueTimeA = $row[$this->getModule()->getField($this->get('fieldid_a_time'))->getColumnName()];
413
				if ($valueA) {
414
					$date = new DateTimeField($valueA . ' ' . $valueTimeA);
415
					$item['start'] = $date->getFullcalenderValue();
416
					$item['start_display'] = $date->getDisplayDateTimeValue();
417
				} else {
418
					$item['start'] = $item['end'];
419
					$item['start_display'] = $item['end_display'];
420
				}
421
				break;
422
			default:
423
				break;
424
		}
425
	}
426
}
427