Passed
Push — developer ( 0a5a9d...dbf062 )
by Radosław
37:42 queued 18:16
created

Vtiger_PDF_Model::attachAsDocument()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 24
ccs 0
cts 8
cp 0
rs 9.568
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 12
1
<?php
2
3
/**
4
 * Basic PDF Model Class.
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    Maciej Stencel <[email protected]>
11
 * @author    Mariusz Krzaczkowski <[email protected]>
12
 * @author    Radoslaw Skrzypczak <[email protected]>
13
 * @author    Rafal Pospiech <[email protected]>
14
 */
15
class Vtiger_PDF_Model extends \App\Base
16
{
17
	/**
18
	 * Template type standard.
19
	 *
20
	 * @var int
21
	 */
22
	public const TEMPLATE_TYPE_STANDARD = 0;
23
	/**
24
	 * Template type summary.
25
	 *
26
	 * @var int
27
	 */
28
	public const TEMPLATE_TYPE_SUMMARY = 1;
29
	/**
30
	 * Template type dynamic.
31
	 *
32
	 * @var int
33
	 */
34
	public const TEMPLATE_TYPE_DYNAMIC = 2;
35
36
	/**
37
	 * Table name.
38
	 *
39
	 * @var string
40
	 */
41
	public static $baseTable = 'a_yf_pdf';
42
43
	/**
44
	 * Table index.
45
	 *
46
	 * @var string
47
	 */
48
	public static $baseIndex = 'pdfid';
49
50
	/**
51
	 * Variables.
52
	 *
53
	 * @var array
54
	 */
55
	protected $variables = [];
56
57
	/**
58
	 * View to picklist assignment array.
59
	 *
60
	 * @var array
61
	 */
62
	protected $viewToPicklistValue = ['Detail' => 'PLL_DETAILVIEW', 'List' => 'PLL_LISTVIEW', 'RelatedList' => 'PLL_RELATEDLISTVIEW'];
63
64
	/**
65
	 * Custom columns.
66
	 *
67
	 * @var bool
68
	 */
69
	public static $customColumns = false;
70
71
	/**
72
	 * Function to get watermark type.
73
	 *
74
	 * @return array
75
	 */
76
	public function getWatermarkType()
77
	{
78
		return [0 => 'PLL_TEXT', 1 => 'PLL_IMAGE'];
79
	}
80
81
	/**
82
	 * Function to get the id of the record.
83
	 *
84 1
	 * @return <Number> - Record Id
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Number> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Number>.
Loading history...
85
	 */
86 1
	public function getId()
87
	{
88
		return $this->get('pdfid');
89
	}
90
91
	/**
92
	 * Fuction to get the Name of the record.
93
	 *
94
	 * @return string - Entity Name of the record
95
	 */
96
	public function getName()
97
	{
98
		$displayName = $this->get('primary_name');
99
100
		return \App\Purifier::encodeHtml(App\Purifier::decodeHtml($displayName));
101
	}
102
103
	/**
104
	 *  Return key value.
105
	 *
106
	 * @param string $key
107
	 *
108 3
	 * @return mixed
109
	 */
110 3
	public function get($key)
111 1
	{
112
		if ('conditions' === $key && ($value = parent::get($key)) && !\is_array($value)) {
113 3
			return json_decode($value, true);
114
		}
115
		return parent::get($key);
116
	}
117
118
	/**
119
	 * Return raw key value.
120
	 *
121
	 * @param string $key
122
	 *
123
	 * @return mixed
124
	 */
125
	public function getRaw($key)
126
	{
127
		return parent::get($key);
128
	}
129
130
	/**
131
	 * Sets custom variable.
132
	 *
133
	 * @param string $key
134
	 * @param mixed  $value
135
	 *
136 1
	 * @return $this
137
	 */
138 1
	public function setVariable(string $key, $value)
139 1
	{
140
		$this->variables[$key] = $value;
141
		return $this;
142
	}
143
144
	/**
145
	 * Gets custom variable.
146
	 *
147
	 * @param string $key
148
	 *
149
	 * @return mixed
150
	 */
151
	public function getVariable(string $key)
152
	{
153
		return $this->variables[$key] ?? null;
154
	}
155
156
	/**
157
	 * Return module instance or false.
158
	 *
159
	 * @return false|object
160
	 */
161
	public function getModule()
162
	{
163
		return Vtiger_Module_Model::getInstance($this->get('module_name'));
164
	}
165
166
	/**
167
	 * Check if pdf templates are avauble for this record, user and view.
168
	 *
169
	 * @param int    $recordId   - id of a record
170
	 * @param string $moduleName - name of the module
171
	 * @param string $view       - modules view - Detail or List
172
	 *
173
	 * @return bool true or false
174
	 */
175
	public function checkActiveTemplates($recordId, $moduleName, $view)
176
	{
177
		$templates = $this->getActiveTemplatesForRecord($recordId, $view, $moduleName);
178
179
		if (\count($templates) > 0) {
180
			return true;
181
		}
182
		return false;
183
	}
184
185
	/**
186
	 * Return available templates for record.
187
	 *
188
	 * @param int    $recordId
189
	 * @param string $view
190
	 * @param string $moduleName
191
	 *
192
	 * @return array
193
	 */
194
	public function getActiveTemplatesForRecord($recordId, $view, $moduleName = false)
195
	{
196
		if (!\App\Record::isExists($recordId)) {
197
			return [];
198
		}
199
		if (!$moduleName) {
200
			$moduleName = \App\Record::getType($recordId);
201
		}
202
203
		$templates = $this->getTemplatesByModule($moduleName);
204
		foreach ($templates as $id => &$template) {
205
			if (!$template->isVisible($view) || !$template->checkFiltersForRecord($recordId) || !$template->checkUserPermissions()) {
206
				unset($templates[$id]);
207
			}
208
		}
209
		return $templates;
210
	}
211
212
	/**
213
	 * Return available templates for module.
214
	 *
215
	 * @param string $moduleName
216
	 * @param string $view
217
	 *
218
	 * @return array
219
	 */
220
	public function getActiveTemplatesForModule($moduleName, $view)
221
	{
222
		$templates = $this->getTemplatesByModule($moduleName);
223
		foreach ($templates as $id => $template) {
224
			if (!$template->isVisible($view) || !$template->checkUserPermissions()) {
225
				unset($templates[$id]);
226
			}
227
		}
228
		return $templates;
229
	}
230
231
	/**
232
	 * Returns template records by module name.
233
	 *
234
	 * @param string $moduleName - module name for which template was created
235
	 *
236
	 * @return array of template record models
237
	 */
238
	public static function getTemplatesByModule($moduleName)
239
	{
240
		$dataReader = (new \App\Db\Query())->from(self::$baseTable)
241
			->where(['module_name' => $moduleName, 'status' => 1])
242
			->createCommand()->query();
243
		$templates = [];
244
		while ($row = $dataReader->read()) {
245
			$handlerClass = Vtiger_Loader::getComponentClassName('Model', 'PDF', $moduleName);
246
			$pdf = new $handlerClass();
247
			$pdf->setData($row);
248
			$templates[$pdf->getId()] = $pdf;
249
		}
250
		return $templates;
251
	}
252
253
	/**
254
	 * Get PDF instance by id.
255
	 *
256
	 * @param int $recordId
257
	 *
258 1
	 * @return bool|Vtiger_PDF_Model
259
	 */
260 1
	public static function getInstanceById(int $recordId)
261 1
	{
262 1
		$pdf = false;
263
		$cache = __CLASS__;
264
		if (\App\Cache::has($cache, $recordId)) {
265 1
			$pdf = \App\Cache::get($cache, $recordId);
266 1
		} else {
267 1
			$row = (new \App\Db\Query())->from(self::$baseTable)->where([self::$baseIndex => $recordId])->one();
268 1
			if ($row) {
269 1
				$handlerClass = Vtiger_Loader::getComponentClassName('Model', 'PDF', $row['module_name']);
270
				$pdf = new $handlerClass();
271 1
				$pdf->setData($row);
272
			}
273 1
			\App\Cache::save($cache, $recordId, $pdf);
274
		}
275
		return $pdf ? clone $pdf : $pdf;
276
	}
277
278
	/**
279
	 * Function returns valuetype of the field filter.
280
	 *
281
	 * @param mixed $fieldname
282
	 *
283
	 * @return string
284
	 */
285
	public function getFieldFilterValueType($fieldname)
286
	{
287
		$conditions = $this->get('conditions');
288
		if (!empty($conditions) && \is_array($conditions)) {
289
			foreach ($conditions as $filter) {
290
				if ($fieldname == $filter['fieldname']) {
291
					return $filter['valuetype'];
292
				}
293
			}
294
		}
295
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
296
	}
297
298
	/**
299
	 * Remove conditions for current record.
300
	 */
301
	public function deleteConditions()
302
	{
303
		\App\Db::getInstance()->createCommand()
304
			->update(self::$baseTable, [
305
				'conditions' => '',
306
			], [self::$baseIndex => $this->getId()])
307
			->execute();
308
	}
309
310
	/**
311
	 * Check if is visible for provided view.
312
	 *
313
	 * @param string $view
314
	 *
315
	 * @return bool
316
	 */
317
	public function isVisible($view)
318
	{
319
		$visibility = explode(',', $this->get('visibility'));
320
		return \in_array($this->viewToPicklistValue[$view], $visibility);
321
	}
322
323
	/**
324
	 * Function to check filters for record.
325
	 *
326
	 * @param int $recordId
327
	 *
328
	 * @return bool
329
	 */
330
	public function checkFiltersForRecord($recordId)
331
	{
332
		$key = $this->getId() . '_' . $recordId;
333
		if (\App\Cache::staticHas(__METHOD__, $key)) {
334
			return \App\Cache::staticGet(__METHOD__, $key);
335
		}
336
		$test = \App\Json::isEmpty($this->getRaw('conditions'));
337
		if (!$test) {
338
			Vtiger_Loader::includeOnce('~/modules/com_vtiger_workflow/VTJsonCondition.php');
339
			$conditionStrategy = new VTJsonCondition();
340
			$recordModel = Vtiger_Record_Model::getInstanceById($recordId);
341
			$conditions = htmlspecialchars_decode($this->getRaw('conditions'));
342
			$test = $conditionStrategy->evaluate($conditions, $recordModel);
0 ignored issues
show
Bug introduced by
$conditions of type string is incompatible with the type array expected by parameter $condition of VTJsonCondition::evaluate(). ( Ignorable by Annotation )

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

342
			$test = $conditionStrategy->evaluate(/** @scrutinizer ignore-type */ $conditions, $recordModel);
Loading history...
343
		}
344
		\App\Cache::staticSave(__METHOD__, $key, $test);
345
346
		return $test;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $test also could return the type string which is incompatible with the documented return type boolean.
Loading history...
347
	}
348
349
	/**
350
	 * Check if user has permissions to record.
351
	 *
352
	 * @return bool
353
	 */
354
	public function checkUserPermissions()
355
	{
356
		$permissions = $this->get('template_members');
357
		if (empty($permissions)) {
358
			return true;
359
		}
360
		$currentUser = Users_Record_Model::getCurrentUserModel();
361
		$permissions = explode(',', $permissions);
362
		$getTypes = [];
363
		foreach ($permissions as $name) {
364
			$valueType = explode(':', $name);
365
			$getTypes[$valueType[0]][] = $valueType[1];
366
		}
367
		if (\in_array('Users:' . $currentUser->getId(), $permissions)) { // check user id
368
			return true;
369
		}
370
		if (\in_array('Roles:' . $currentUser->getRole(), $permissions)) {
371
			return true;
372
		}
373
		if (\array_key_exists('Groups', $getTypes)) {
374
			$accessibleGroups = array_keys(\App\Fields\Owner::getInstance($this->get('module_name'), $currentUser)->getAccessibleGroupForModule());
375
			$groups = array_intersect($getTypes['Groups'], $currentUser->getGroups());
376
			if (array_intersect($groups, $accessibleGroups)) {
377
				return true;
378
			}
379
		}
380
		if (\array_key_exists('RoleAndSubordinates', $getTypes)) {
381
			$roles = $currentUser->getParentRoles();
382
			$roles[] = $currentUser->getRole();
383
			if (array_intersect($getTypes['RoleAndSubordinates'], array_filter($roles))) {
384
				return true;
385
			}
386
		}
387
		return false;
388
	}
389
390
	/**
391
	 * Returns array of template parameters understood by the pdf engine.
392
	 *
393 1
	 * @return array
394
	 */
395 1
	public function getParameters()
396 1
	{
397 1
		$parameters = [];
398 1
		$parameters['page_format'] = $this->get('page_format');
399 1
		$parameters['page_orientation'] = $this->getOrientation();
400
		$parameters['header_height'] = $this->get('header_height');
401
		$parameters['footer_height'] = $this->get('footer_height');
402 1
403 1
		// margins
404 1
		if (0 == $this->get('margin_chkbox')) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->get('margin_chkbox') of type mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
405 1
			$parameters['margin-top'] = $this->get('margin_top');
406 1
			$parameters['margin-right'] = $this->get('margin_right');
407
			$parameters['margin-bottom'] = $this->get('margin_bottom');
408
			$parameters['margin-left'] = $this->get('margin_left');
409
		} else {
410
			$parameters['margin-top'] = '';
411
			$parameters['margin-right'] = '';
412
			$parameters['margin-bottom'] = '';
413
			$parameters['margin-left'] = '';
414
		}
415 1
416 1
		// metadata
417 1
		$parameters['creator'] = 'YetiForce CRM';
418 1
		if (1 === (int) $this->get('metatags_status')) {
419 1
			$parameters['title'] = $this->parseVariables($this->get('meta_title'));
420 1
			$parameters['author'] = $this->parseVariables($this->get('meta_author'));
421
			$parameters['subject'] = $this->parseVariables($this->get('meta_subject'));
422 1
			$parameters['keywords'] = $this->parseVariables($this->get('meta_keywords'));
423
		}
424
		return $parameters;
425
	}
426
427
	/**
428
	 * Returns page format.
429
	 *
430 1
	 * @return string page format
431
	 */
432 1
	public function getFormat()
433
	{
434
		return $this->get('page_format');
435
	}
436
437
	/**
438
	 * Get page orientation.
439
	 *
440 1
	 * @return string
441
	 */
442 1
	public function getOrientation()
443 1
	{
444
		$orientation = $this->get('page_orientation');
445
		if ('PLL_LANDSCAPE' === $orientation) {
446 1
			return 'L';
447
		}
448
		return 'P';
449
	}
450
451
	/**
452
	 * Get header content.
453
	 *
454 1
	 * @return string - header content
455
	 */
456 1
	public function getHeader()
457
	{
458
		return $this->get('header_content');
459
	}
460
461
	/**
462
	 * Get body content.
463
	 *
464 1
	 * @return string - body content
465
	 */
466 1
	public function getFooter()
467
	{
468
		return $this->get('footer_content');
469
	}
470
471
	/**
472
	 * Get body content.
473
	 *
474 1
	 * @return string - body content
475
	 */
476 1
	public function getBody()
477
	{
478
		return $this->get('body_content');
479
	}
480
481
	/**
482
	 * Gets TextParser.
483
	 *
484
	 * @return \App\TextParser
485
	 */
486
	public function getParser()
487 1
	{
488
		if (!isset($this->textParser)) {
489 1
			if (isset($this->variables['recordId'])) {
490 1
				$this->textParser = \App\TextParser::getInstanceById($this->variables['recordId'], $this->get('module_name'));
0 ignored issues
show
Bug Best Practice introduced by
The property textParser does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
491
			} else {
492 1
				$this->textParser = \App\TextParser::getInstance($this->get('module_name'));
493 1
			}
494 1
			if ($this->get('language')) {
495 1
				$this->textParser->setLanguage($this->get('language'));
496 1
			}
497 1
			$this->textParser->setType('pdf');
498 1
			$this->textParser->useExtension = true;
499 1
			$this->textParser->setParams(['pdf' => $this]);
500
		} elseif (($this->variables['recordId'] ?? null) !== $this->textParser->record) {
501 1
			$this->textParser = null;
502 1
			$this->textParser = $this->getParser();
503
		}
504
		return $this->textParser;
505
	}
506
507
	/**
508
	 * Parse variables.
509 1
	 *
510
	 * @param string $str
511 1
	 *
512 1
	 * @return string
513 1
	 */
514
	public function parseVariables(string $str)
515
	{
516
		return $str ? $this->getParser()->setContent($str)->parse()->getContent() : '';
517 1
	}
518 1
519
	/**
520 1
	 * Attach current record to email.
521 1
	 *
522 1
	 * @param string $salt
523
	 */
524
	public static function attachToEmail($salt)
525
	{
526 1
		header('location: index.php?module=OSSMail&view=Compose&pdf_path=' . $salt);
527
	}
528
529
	/**
530
	 * Compress files and send to browser.
531
	 *
532
	 * @param array $fileNames
533
	 *
534
	 * @throws \App\Exceptions\NoPermitted
535
	 */
536 1
	public static function zipAndDownload(array $fileNames)
537
	{
538 1
		$zipPath = 'cache' . DIRECTORY_SEPARATOR . 'pdf' . DIRECTORY_SEPARATOR;
539
		$tmpFileName = tempnam($zipPath, 'PDFZIP' . time());
540
		$zipPath .= basename($tmpFileName);
541
542
		$zip = \App\Zip::createFile($zipPath);
543
		foreach ($fileNames as $file) {
544
			$zip->addFile($file['path'], $file['name']);
545
		}
546
547
		$zip->download('PdfZipFile_' . time());
548
	}
549
550
	/**
551
	 * Gets path.
552
	 *
553
	 * @param string $prefix
554
	 *
555
	 * @return string
556
	 */
557
	public function getPath(string $prefix = '')
558
	{
559
		return \App\Fields\File::createTempFile($prefix, 'pdf');
560
	}
561
562
	/**
563
	 * Attach pdf as document.
564
	 *
565
	 * @param array $pdfFiles
566
	 *
567
	 * @return void
568
	 */
569
	public static function attachAsDocument(array $pdfFiles): void
570
	{
571
		$documentsModuleModel = Vtiger_Module_Model::getInstance('Documents');
572
		foreach ($pdfFiles as $pdfFile) {
573
			$recordId = $pdfFile['recordId'];
574
			if (App\Record::isExists($recordId)) {
575
				$sourceRecordModel = Vtiger_Record_Model::getInstanceById($recordId, $pdfFile['moduleName']);
576
				$filePath = \App\Fields\File::loadFromPath($pdfFile['path']);
577
				$fileName = $pdfFile['name'];
578
				$file = ['name' => $fileName, 'type' => $filePath->getMimeType(), 'tmp_name' => $filePath->getPath(), 'error' => 0, 'size' => $filePath->getSize()];
579
				$documentRecordModel = Vtiger_Record_Model::getCleanInstance('Documents');
580
				$documentRecordModel->set('assigned_user_id', $sourceRecordModel->get('assigned_user_id'));
581
				$documentRecordModel->set('filename', $fileName);
582
				$documentRecordModel->set('filelocationtype', 'I');
583
				$documentRecordModel->set('filetype', $file['type']);
584
				$documentRecordModel->set('filesize', $file['size']);
585
				$documentRecordModel->set('notes_title', $file['name']);
586
				$documentRecordModel->set('filestatus', true);
587
				$documentRecordModel->set('filelocationtype', 'I');
588
				$documentRecordModel->file = $file;
589
				$documentRecordModel->ext['attachedFromPdf'] = true;
590
				$documentRecordModel->save();
591
				$relationModel = Vtiger_Relation_Model::getInstance($sourceRecordModel->getModule(), $documentsModuleModel);
592
				$relationModel->addRelation($recordId, $documentRecordModel->getId());
593
			}
594
		}
595
	}
596
}
597