1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Text parser file. |
4
|
|
|
* |
5
|
|
|
* @package App |
6
|
|
|
* |
7
|
|
|
* @copyright YetiForce S.A. |
8
|
|
|
* @license YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com) |
9
|
|
|
* @author Mariusz Krzaczkowski <[email protected]> |
10
|
|
|
* @author Radosław Skrzypczak <[email protected]> |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace App; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Text parser class. |
17
|
|
|
*/ |
18
|
|
|
class TextParser |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* Examples of supported variables. |
22
|
|
|
* |
23
|
|
|
* @var array |
24
|
|
|
*/ |
25
|
|
|
public static $variableExamples = [ |
26
|
|
|
'LBL_ORGANIZATION_NAME' => '$(organization : company_name)$', |
27
|
|
|
'LBL_ORGANIZATION_LOGO' => '$(organization : logo)$', |
28
|
|
|
'LBL_EMPLOYEE_NAME' => '$(employee : last_name)$', |
29
|
|
|
'LBL_CRM_DETAIL_VIEW_URL' => '$(record : CrmDetailViewURL)$', |
30
|
|
|
'LBL_PORTAL_DETAIL_VIEW_URL' => '$(record : PortalDetailViewURL)$', |
31
|
|
|
'LBL_RECORD_ID' => '$(record : RecordId)$', |
32
|
|
|
'LBL_RECORD_LABEL' => '$(record : RecordLabel)$', |
33
|
|
|
'LBL_LIST_OF_CHANGES_IN_RECORD' => '$(record : ChangesListChanges)$', |
34
|
|
|
'LBL_LIST_OF_NEW_VALUES_IN_RECORD' => '$(record : ChangesListValues)$', |
35
|
|
|
'LBL_RECORD_COMMENT' => '$(record : Comments 5)$, $(record : Comments)$', |
36
|
|
|
'LBL_RELATED_RECORD_LABEL' => '$(relatedRecord : parent_id|email1|Accounts)$, $(relatedRecord : parent_id|email1)$', |
37
|
|
|
'LBL_RELATED_NEXT_LEVEL_RECORD_LABEL' => '$(relatedRecordLevel : projectid|Project|linktoaccountscontacts|email1|Accounts)$', |
38
|
|
|
'LBL_OWNER_EMAIL' => '$(relatedRecord : assigned_user_id|email1|Users)$', |
39
|
|
|
'LBL_SOURCE_RECORD_LABEL' => '$(sourceRecord : RecordLabel)$', |
40
|
|
|
'LBL_CUSTOM_FUNCTION' => '$(custom : ContactsPortalPass)$', |
41
|
|
|
'LBL_RELATED_RECORDS_LIST' => '$(relatedRecordsList : Contacts|firstname,lastname,email|[[["firstname","a","Tom"]]]||5)$', |
42
|
|
|
'LBL_RECORDS_LIST' => '$(recordsList : Contacts|firstname,lastname,email|[[["firstname","a","Tom"]]]||5)$', |
43
|
|
|
'LBL_INVENTORY_TABLE' => '$(inventory : type=table columns=seq,name,qty,unit,price,total,net href=no)$', |
44
|
|
|
'LBL_DYNAMIC_INVENTORY_TABLE' => '$(custom : dynamicInventoryColumnsTable)$', |
45
|
|
|
'LBL_BARCODE' => '$(barcode : type=EAN13 class=DNS1D , value=12345678)$', |
46
|
|
|
]; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Default date list. |
50
|
|
|
* |
51
|
|
|
* @var string[] |
52
|
|
|
*/ |
53
|
|
|
public static $variableDates = [ |
54
|
|
|
'LBL_DATE_TODAY' => '$(date : now)$', |
55
|
|
|
'LBL_DATE_TOMORROW' => '$(date : tomorrow)$', |
56
|
|
|
'LBL_DATE_YESTERDAY' => '$(date : yesterday)$', |
57
|
|
|
'LBL_DATE_FIRST_DAY_OF_THIS_WEEK' => '$(date : monday this week)$', |
58
|
|
|
'LBL_DATE_MONDAY_NEXT_WEEK' => '$(date : monday next week)$', |
59
|
|
|
'LBL_DATE_FIRST_DAY_OF_THIS_MONTH' => '$(date : first day of this month)$', |
60
|
|
|
'LBL_DATE_LAST_DAY_OF_THIS_MONTH' => '$(date : last day of this month)$', |
61
|
|
|
'LBL_DATE_FIRST_DAY_OF_NEXT_MONTH' => '$(date : first day of next month)$', |
62
|
|
|
]; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Variables for entity modules. |
66
|
|
|
* |
67
|
|
|
* @var array |
68
|
|
|
*/ |
69
|
|
|
public static $variableGeneral = [ |
70
|
|
|
'LBL_CURRENT_DATE' => '$(general : CurrentDate)$', |
71
|
|
|
'LBL_CURRENT_TIME' => '$(general : CurrentTime)$', |
72
|
|
|
'LBL_BASE_TIMEZONE' => '$(general : BaseTimeZone)$', |
73
|
|
|
'LBL_USER_TIMEZONE' => '$(general : UserTimeZone)$', |
74
|
|
|
'LBL_SITE_URL' => '$(general : SiteUrl)$', |
75
|
|
|
'LBL_PORTAL_URL' => '$(general : PortalUrl)$', |
76
|
|
|
'LBL_TRANSLATE' => '$(translate : Accounts|LBL_COPY_BILLING_ADDRESS)$, $(translate : LBL_SECONDS)$', |
77
|
|
|
]; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Variables for entity modules. |
81
|
|
|
* |
82
|
|
|
* @var array |
83
|
|
|
*/ |
84
|
|
|
public static $variableEntity = [ |
85
|
|
|
'CrmDetailViewURL' => 'LBL_CRM_DETAIL_VIEW_URL', |
86
|
|
|
'PortalDetailViewURL' => 'LBL_PORTAL_DETAIL_VIEW_URL', |
87
|
|
|
'RecordId' => 'LBL_RECORD_ID', |
88
|
|
|
'RecordLabel' => 'LBL_RECORD_LABEL', |
89
|
|
|
'ChangesListChanges' => 'LBL_LIST_OF_CHANGES_IN_RECORD', |
90
|
|
|
'ChangesListValues' => 'LBL_LIST_OF_NEW_VALUES_IN_RECORD', |
91
|
|
|
'Comments' => 'LBL_RECORD_COMMENT', |
92
|
|
|
'SummaryFields' => 'LBL_SUMMARY_FIELDS', |
93
|
|
|
]; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* List of available functions. |
97
|
|
|
* |
98
|
|
|
* @var string[] |
99
|
|
|
*/ |
100
|
|
|
protected static $baseFunctions = ['general', 'translate', 'record', 'relatedRecord', 'relatedRecordLevel', 'sourceRecord', 'organization', 'employee', 'params', 'custom', 'relatedRecordsList', 'recordsList', 'date', 'inventory', 'userVariable', 'barcode']; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* List of source modules. |
104
|
|
|
* |
105
|
|
|
* @var array |
106
|
|
|
*/ |
107
|
|
|
public static $sourceModules = [ |
108
|
|
|
'Campaigns' => ['Leads', 'Accounts', 'Contacts', 'Vendors', 'Partners', 'Competition'], |
109
|
|
|
]; |
110
|
|
|
/** |
111
|
|
|
* Record variables. |
112
|
|
|
* |
113
|
|
|
* @var array |
114
|
|
|
*/ |
115
|
|
|
protected static $recordVariable = []; |
116
|
|
|
/** |
117
|
|
|
* Related variables. |
118
|
|
|
* |
119
|
|
|
* @var array |
120
|
|
|
*/ |
121
|
|
|
protected static $relatedVariable = []; |
122
|
|
|
/** |
123
|
|
|
* Next level related variables. |
124
|
|
|
* |
125
|
|
|
* @var array |
126
|
|
|
*/ |
127
|
|
|
protected static $relatedVariableLevel = []; |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Record id. |
131
|
|
|
* |
132
|
|
|
* @var int |
133
|
|
|
*/ |
134
|
|
|
public $record; |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Module name. |
138
|
|
|
* |
139
|
|
|
* @var string |
140
|
|
|
*/ |
141
|
|
|
public $moduleName; |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Record model. |
145
|
|
|
* |
146
|
|
|
* @var \Vtiger_Record_Model |
147
|
|
|
*/ |
148
|
|
|
public $recordModel; |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Parser type. |
152
|
|
|
* |
153
|
|
|
* @var string|null |
154
|
|
|
*/ |
155
|
|
|
public $type; |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Source record model. |
159
|
|
|
* |
160
|
|
|
* @var \Vtiger_Record_Model |
161
|
|
|
*/ |
162
|
|
|
protected $sourceRecordModel; |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Content. |
166
|
|
|
* |
167
|
|
|
* @var string |
168
|
|
|
*/ |
169
|
|
|
protected $content = ''; |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Rwa content. |
173
|
|
|
* |
174
|
|
|
* @var string |
175
|
|
|
*/ |
176
|
|
|
protected $rawContent; |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* without translations. |
180
|
|
|
* |
181
|
|
|
* @var bool |
182
|
|
|
*/ |
183
|
|
|
protected $withoutTranslations = false; |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Language content. |
187
|
|
|
* |
188
|
|
|
* @var string |
189
|
|
|
*/ |
190
|
|
|
protected $language; |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Additional params. |
194
|
|
|
* |
195
|
|
|
* @var array |
196
|
|
|
*/ |
197
|
|
|
protected $params; |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Separator to display data when there are several values. |
201
|
|
|
* |
202
|
|
|
* @var string |
203
|
|
|
*/ |
204
|
4 |
|
public $relatedRecordSeparator = ','; |
205
|
|
|
|
206
|
4 |
|
/** |
207
|
4 |
|
* Is the parsing text content html? |
208
|
4 |
|
* |
209
|
4 |
|
* @var bool |
210
|
4 |
|
*/ |
211
|
4 |
|
public $isHtml = true; |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Use extended parsing. |
215
|
|
|
* |
216
|
|
|
* @var bool |
217
|
|
|
*/ |
218
|
|
|
public $useExtension = false; |
219
|
|
|
|
220
|
|
|
/** |
221
|
7 |
|
* Variable parser regex. |
222
|
|
|
* |
223
|
7 |
|
* @var string |
224
|
7 |
|
*/ |
225
|
7 |
|
public const VARIABLE_REGEX = '/\$\((\w+) : ([,"\+\#\%\.\:\;\=\-\[\]\&\w\s\|\)\(\:]+)\)\$/u'; |
226
|
7 |
|
|
227
|
7 |
|
/** @var bool Permissions condition */ |
228
|
7 |
|
protected $permissions = true; |
229
|
|
|
|
230
|
|
|
/** @var string[] Uitypes with large data */ |
231
|
|
|
protected $largeDataUiTypes = ['multiImage', 'image']; |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Get instanace by record id. |
235
|
|
|
* |
236
|
|
|
* @param int $record Record id |
237
|
|
|
* @param string $moduleName Module name |
238
|
8 |
|
* |
239
|
|
|
* @return \self |
|
|
|
|
240
|
8 |
|
*/ |
241
|
8 |
|
public static function getInstanceById(int $record, ?string $moduleName = null) |
242
|
8 |
|
{ |
243
|
4 |
|
$class = static::class; |
244
|
|
|
$instance = new $class(); |
245
|
8 |
|
$instance->record = $record; |
246
|
|
|
$instance->recordModel = \Vtiger_Record_Model::getInstanceById($record, $moduleName); |
247
|
|
|
$instance->moduleName = $instance->recordModel->getModuleName(); |
248
|
|
|
return $instance; |
|
|
|
|
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Get instanace by record model. |
253
|
|
|
* |
254
|
|
|
* @param \Vtiger_Record_Model $recordModel |
255
|
4 |
|
* |
256
|
|
|
* @return \self |
257
|
4 |
|
*/ |
258
|
4 |
|
public static function getInstanceByModel(\Vtiger_Record_Model $recordModel) |
259
|
|
|
{ |
260
|
|
|
$class = static::class; |
261
|
|
|
$instance = new $class(); |
262
|
|
|
$instance->record = $recordModel->getId(); |
263
|
|
|
$instance->moduleName = $recordModel->getModuleName(); |
264
|
|
|
$instance->recordModel = $recordModel; |
265
|
|
|
return $instance; |
|
|
|
|
266
|
|
|
} |
267
|
|
|
|
268
|
2 |
|
/** |
269
|
|
|
* Get clean instanace. |
270
|
2 |
|
* |
271
|
2 |
|
* @param string $moduleName Module name |
272
|
|
|
* |
273
|
|
|
* @return \self |
274
|
|
|
*/ |
275
|
|
|
public static function getInstance($moduleName = '') |
276
|
|
|
{ |
277
|
|
|
$class = static::class; |
278
|
|
|
$instance = new $class(); |
279
|
|
|
if ($moduleName) { |
280
|
|
|
$instance->moduleName = $moduleName; |
281
|
1 |
|
} |
282
|
|
|
return $instance; |
|
|
|
|
283
|
1 |
|
} |
284
|
1 |
|
|
285
|
|
|
/** |
286
|
|
|
* Set the active state of extended parsing functionality. |
287
|
|
|
* |
288
|
|
|
* @param bool $state |
289
|
|
|
* |
290
|
|
|
* @return $this |
291
|
|
|
*/ |
292
|
|
|
public function setExtensionState(bool $state) |
293
|
|
|
{ |
294
|
3 |
|
$this->useExtension = $state; |
295
|
|
|
return $this; |
296
|
3 |
|
} |
297
|
3 |
|
|
298
|
|
|
/** |
299
|
|
|
* Set without translations. |
300
|
|
|
* |
301
|
|
|
* @param string $type |
302
|
|
|
* |
303
|
|
|
* @return $this |
304
|
|
|
*/ |
305
|
|
|
public function withoutTranslations($type = true) |
306
|
|
|
{ |
307
|
1 |
|
$this->withoutTranslations = $type; |
|
|
|
|
308
|
|
|
return $this; |
309
|
1 |
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Set language. |
313
|
|
|
* |
314
|
|
|
* @param string $name |
315
|
|
|
* |
316
|
|
|
* @return $this |
317
|
|
|
*/ |
318
|
|
|
public function setLanguage($name = true) |
319
|
|
|
{ |
320
|
|
|
$this->language = $name; |
|
|
|
|
321
|
2 |
|
return $this; |
322
|
|
|
} |
323
|
2 |
|
|
324
|
2 |
|
/** |
325
|
|
|
* Set parser type. |
326
|
|
|
* |
327
|
|
|
* @param string $type |
328
|
|
|
* |
329
|
|
|
* @return $this |
330
|
|
|
*/ |
331
|
|
|
public function setType($type) |
332
|
|
|
{ |
333
|
|
|
$this->type = $type; |
334
|
19 |
|
return $this; |
335
|
|
|
} |
336
|
19 |
|
|
337
|
19 |
|
/** |
338
|
|
|
* Set additional params. |
339
|
|
|
* |
340
|
|
|
* @param array $params |
341
|
|
|
* |
342
|
|
|
* @return $this |
343
|
|
|
*/ |
344
|
|
|
public function setParams($params) |
345
|
16 |
|
{ |
346
|
|
|
$this->params = $params; |
347
|
16 |
|
return $this; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Set param value. |
352
|
|
|
* |
353
|
|
|
* @param string $key |
354
|
|
|
* @param mixed $value |
355
|
|
|
* |
356
|
|
|
* @return $this |
357
|
22 |
|
*/ |
358
|
|
|
public function setParam(string $key, $value) |
359
|
22 |
|
{ |
360
|
|
|
$this->params[$key] = $value; |
361
|
|
|
return $this; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Get additional params. |
366
|
|
|
* |
367
|
19 |
|
* @param string $key |
368
|
|
|
* |
369
|
19 |
|
* @return mixed |
370
|
2 |
|
*/ |
371
|
|
|
public function getParam(string $key) |
372
|
17 |
|
{ |
373
|
3 |
|
return $this->params[$key] ?? null; |
374
|
|
|
} |
375
|
|
|
|
376
|
15 |
|
/** |
377
|
15 |
|
* Set source record. |
378
|
14 |
|
* |
379
|
|
|
* @param int $record |
380
|
1 |
|
* @param bool|string $moduleName |
381
|
17 |
|
* @param mixed $recordModel |
382
|
17 |
|
* |
383
|
17 |
|
* @return $this |
384
|
|
|
*/ |
385
|
|
|
public function setSourceRecord($record, $moduleName = false, $recordModel = false) |
386
|
|
|
{ |
387
|
|
|
$this->sourceRecordModel = $recordModel ?: \Vtiger_Record_Model::getInstanceById($record, $moduleName ?: Record::getType($record)); |
388
|
|
|
return $this; |
389
|
|
|
} |
390
|
|
|
|
391
|
2 |
|
/** |
392
|
|
|
* Set content. |
393
|
2 |
|
* |
394
|
1 |
|
* @param string $content |
395
|
|
|
* |
396
|
|
|
* @return $this |
397
|
1 |
|
*/ |
398
|
1 |
|
public function setContent($content) |
399
|
2 |
|
{ |
400
|
2 |
|
$this->rawContent = $this->content = str_replace(['%20%3A%20', '%20:%20'], ' : ', $content); |
401
|
2 |
|
return $this; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* Get content. |
406
|
|
|
* |
407
|
|
|
* @param mixed $trim |
408
|
|
|
*/ |
409
|
|
|
public function getContent($trim = false) |
410
|
|
|
{ |
411
|
1 |
|
return $trim ? trim($this->content) : $this->content; |
412
|
|
|
} |
413
|
1 |
|
|
414
|
1 |
|
/** |
415
|
|
|
* Function checks if its TextParser type. |
416
|
|
|
* |
417
|
|
|
* @param string $text |
418
|
|
|
* |
419
|
|
|
* @return int |
420
|
|
|
*/ |
421
|
|
|
public static function isVaribleToParse($text) |
422
|
|
|
{ |
423
|
|
|
return (int) preg_match(static::VARIABLE_REGEX, $text); |
424
|
1 |
|
} |
425
|
|
|
|
426
|
1 |
|
/** |
427
|
1 |
|
* Set permissions condition. |
428
|
|
|
* |
429
|
1 |
|
* @param bool $permitted |
430
|
1 |
|
* |
431
|
|
|
* @return $this |
432
|
1 |
|
*/ |
433
|
1 |
|
public function setGlobalPermissions(bool $permitted) |
434
|
1 |
|
{ |
435
|
|
|
$this->permissions = $permitted; |
436
|
|
|
return $this; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* All text parse function. |
441
|
|
|
* |
442
|
|
|
* @return $this |
443
|
|
|
*/ |
444
|
|
|
public function parse() |
445
|
|
|
{ |
446
|
|
|
if (empty($this->content)) { |
447
|
|
|
return $this; |
448
|
|
|
} |
449
|
|
|
if (isset($this->language)) { |
450
|
|
|
Language::setTemporaryLanguage($this->language); |
451
|
|
|
} |
452
|
|
|
$this->content = $this->parseData($this->content); |
453
|
|
|
Language::clearTemporaryLanguage(); |
454
|
|
|
return $this; |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
/** |
458
|
|
|
* Text parse function. |
459
|
|
|
* |
460
|
|
|
* @param string $content |
461
|
|
|
* |
462
|
|
|
* @return string |
463
|
|
|
*/ |
464
|
|
|
public function parseData(string $content) |
465
|
|
|
{ |
466
|
|
|
if ($this->useExtension) { |
467
|
|
|
$content = preg_replace_callback('/<!--[\s]+({% [\s\S]+? %})[\s]+-->/u', fn ($matches) => $matches[1] ?? '', $content); |
468
|
|
|
$twig = new \Twig\Environment(new \Twig\Loader\ArrayLoader(['index' => $content])); |
469
|
|
|
$sandbox = new \Twig\Extension\SandboxExtension(\App\Extension\Twig\SecurityPolicy::getPolicy(), true); |
470
|
|
|
$twig->addExtension($sandbox); |
471
|
|
|
$twig->addFunction(new \Twig\TwigFunction('YFParser', function ($text) { |
472
|
|
|
$value = ''; |
473
|
|
|
preg_match(static::VARIABLE_REGEX, $text, $matches); |
474
|
2 |
|
if ($matches) { |
475
|
|
|
[, $function, $params] = array_pad($matches, 3, ''); |
476
|
2 |
|
$value = \in_array($function, static::$baseFunctions) ? $this->{$function}($params) : ''; |
477
|
2 |
|
} |
478
|
2 |
|
return $value; |
479
|
|
|
})); |
480
|
2 |
|
$content = $twig->render('index'); |
481
|
|
|
} |
482
|
|
|
return preg_replace_callback(static::VARIABLE_REGEX, function ($matches) { |
483
|
2 |
|
[, $function, $params] = array_pad($matches, 3, ''); |
484
|
2 |
|
return \in_array($function, static::$baseFunctions) ? $this->{$function}($params) : ''; |
485
|
2 |
|
}, $content); |
486
|
|
|
} |
487
|
2 |
|
|
488
|
2 |
|
/** |
489
|
1 |
|
* Text parse function. |
490
|
1 |
|
* |
491
|
1 |
|
* @return $this |
492
|
1 |
|
*/ |
493
|
1 |
|
public function parseTranslations() |
494
|
|
|
{ |
495
|
|
|
if (isset($this->language)) { |
496
|
1 |
|
Language::setTemporaryLanguage($this->language); |
497
|
|
|
} |
498
|
2 |
|
$this->content = preg_replace_callback('/\$\(translate : ([,"\+\%\.\=\-\[\]\&\w\s\|]+)\)\$/u', function ($matches) { |
499
|
2 |
|
[, $params] = array_pad($matches, 2, ''); |
500
|
|
|
return $this->translate($params); |
501
|
|
|
}, $this->content); |
502
|
|
|
Language::clearTemporaryLanguage(); |
503
|
|
|
return $this; |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* Function parse date. |
508
|
|
|
* |
509
|
1 |
|
* @param string $param |
510
|
|
|
* |
511
|
1 |
|
* @return string |
512
|
1 |
|
*/ |
513
|
1 |
|
public function date($param) |
514
|
1 |
|
{ |
515
|
1 |
|
if (isset(\App\Condition::DATE_OPERATORS[$param])) { |
516
|
1 |
|
$date = implode(' - ', array_unique(\DateTimeRange::getDateRangeByType($param))); |
517
|
|
|
} else { |
518
|
1 |
|
$date = date('Y-m-d', strtotime($param)); |
519
|
1 |
|
} |
520
|
1 |
|
return $date; |
521
|
1 |
|
} |
522
|
1 |
|
|
523
|
1 |
|
/** |
524
|
1 |
|
* Parsing translations. |
525
|
1 |
|
* |
526
|
1 |
|
* @param string $params |
527
|
|
|
* |
528
|
1 |
|
* @return string |
529
|
|
|
*/ |
530
|
|
|
protected function translate($params) |
531
|
|
|
{ |
532
|
|
|
if ($this->withoutTranslations) { |
533
|
|
|
return "$(translate : $params)$"; |
534
|
|
|
} |
535
|
|
|
if (false === strpos($params, '|')) { |
536
|
|
|
return Language::translate($params); |
537
|
|
|
} |
538
|
|
|
$splitParams = explode('|', $params); |
539
|
|
|
$module = array_shift($splitParams); |
540
|
5 |
|
$key = array_shift($splitParams); |
541
|
|
|
return Language::translate($key, $module, $splitParams[0] ?? $this->language); |
542
|
5 |
|
} |
543
|
1 |
|
|
544
|
|
|
/** |
545
|
5 |
|
* Parsing organization detail. |
546
|
5 |
|
* |
547
|
5 |
|
* @param string $params |
548
|
5 |
|
* |
549
|
1 |
|
* @return string |
550
|
|
|
*/ |
551
|
5 |
|
protected function organization(string $params): string |
552
|
|
|
{ |
553
|
1 |
|
if (!$params) { |
554
|
1 |
|
return ''; |
555
|
1 |
|
} |
556
|
1 |
|
$returnVal = ''; |
557
|
1 |
|
if (false === strpos($params, '|')) { |
558
|
1 |
|
$id = User::getCurrentUserModel()->get('multiCompanyId'); |
559
|
|
|
$fieldName = $params; |
560
|
1 |
|
$params = false; |
561
|
|
|
} else { |
562
|
1 |
|
[$id, $fieldName, $params] = array_pad(explode('|', $params, 3), 3, false); |
563
|
|
|
} |
564
|
|
|
if (Record::isExists($id, 'MultiCompany')) { |
565
|
1 |
|
$companyRecordModel = \Vtiger_Record_Model::getInstanceById($id, 'MultiCompany'); |
566
|
1 |
|
if ($companyRecordModel->has($fieldName)) { |
567
|
1 |
|
$value = $companyRecordModel->get($fieldName); |
568
|
1 |
|
$fieldModel = $companyRecordModel->getModule()->getFieldByName($fieldName); |
569
|
1 |
|
if ('' === $value || !$fieldModel || !$this->useValue($fieldModel, 'MultiCompany')) { |
570
|
1 |
|
return ''; |
571
|
1 |
|
} |
572
|
1 |
|
if ($this->withoutTranslations) { |
573
|
1 |
|
$returnVal = $this->getDisplayValueByType($value, $companyRecordModel, $fieldModel, $params); |
574
|
1 |
|
} else { |
575
|
1 |
|
$returnVal = $fieldModel->getUITypeModel()->getTextParserDisplayValue($value, $companyRecordModel, $params); |
576
|
1 |
|
} |
577
|
|
|
} |
578
|
|
|
} |
579
|
1 |
|
return $returnVal; |
580
|
1 |
|
} |
581
|
1 |
|
|
582
|
1 |
|
/** |
583
|
|
|
* Parsing employee detail. |
584
|
1 |
|
* |
585
|
1 |
|
* @param string $fieldName |
586
|
|
|
* |
587
|
|
|
* @return mixed |
588
|
1 |
|
*/ |
589
|
1 |
|
protected function employee($fieldName) |
590
|
1 |
|
{ |
591
|
1 |
|
$userId = User::getCurrentUserId(); |
592
|
1 |
|
if (Cache::has('TextParserEmployeeDetail', $userId . $fieldName)) { |
593
|
1 |
|
return Cache::get('TextParserEmployeeDetail', $userId . $fieldName); |
594
|
1 |
|
} |
595
|
|
|
if (Cache::has('TextParserEmployeeDetailRows', $userId)) { |
596
|
1 |
|
$employee = Cache::get('TextParserEmployeeDetailRows', $userId); |
597
|
1 |
|
} else { |
598
|
1 |
|
$employee = (new Db\Query())->select(['crmid'])->from('vtiger_crmentity')->where(['deleted' => 0, 'setype' => 'OSSEmployees', 'smownerid' => $userId]) |
599
|
|
|
->scalar(); |
600
|
|
|
Cache::save('TextParserEmployeeDetailRows', $userId, $employee, Cache::LONG); |
601
|
1 |
|
} |
602
|
1 |
|
$value = ''; |
603
|
1 |
|
if ($employee && Record::isExists($employee, 'OSSEmployees')) { |
604
|
|
|
$relatedRecordModel = \Vtiger_Record_Model::getInstanceById($employee, 'OSSEmployees'); |
605
|
1 |
|
$instance = static::getInstanceByModel($relatedRecordModel); |
606
|
|
|
foreach (['withoutTranslations', 'language', 'emailoptout'] as $key) { |
607
|
|
|
if (isset($this->{$key})) { |
608
|
1 |
|
$instance->{$key} = $this->{$key}; |
609
|
|
|
} |
610
|
1 |
|
} |
611
|
1 |
|
$value = $instance->record($fieldName); |
612
|
|
|
} |
613
|
1 |
|
Cache::save('TextParserEmployeeDetail', $userId . $fieldName, $value, Cache::LONG); |
614
|
1 |
|
return $value; |
615
|
|
|
} |
616
|
1 |
|
|
617
|
|
|
/** |
618
|
1 |
|
* Parsing general data. |
619
|
|
|
* |
620
|
|
|
* @param string $key |
621
|
|
|
* |
622
|
|
|
* @return mixed |
623
|
|
|
*/ |
624
|
|
|
protected function general($key) |
625
|
|
|
{ |
626
|
|
|
switch ($key) { |
627
|
|
|
case 'CurrentDate': |
628
|
1 |
|
return (new \DateTimeField(null))->getDisplayDate(); |
629
|
|
|
case 'CurrentTime': |
630
|
1 |
|
return \Vtiger_Util_Helper::convertTimeIntoUsersDisplayFormat(date('H:i:s')); |
631
|
|
|
case 'CurrentDateTime': |
632
|
1 |
|
return Fields\DateTime::formatToDisplay('now'); |
633
|
1 |
|
case 'SiteUrl': |
634
|
1 |
|
return Config::main('site_URL'); |
635
|
|
|
case 'PortalUrl': |
636
|
|
|
return Config::main('PORTAL_URL'); |
637
|
|
|
case 'BaseTimeZone': |
638
|
1 |
|
return Fields\DateTime::getTimeZone(); |
639
|
1 |
|
case 'UserTimeZone': |
640
|
|
|
$userModel = User::getCurrentUserModel(); |
641
|
|
|
return ($userModel && $userModel->getDetail('time_zone')) ? $userModel->getDetail('time_zone') : Config::main('default_timezone'); |
642
|
1 |
|
default: |
643
|
|
|
return $key; |
644
|
|
|
} |
645
|
1 |
|
} |
646
|
1 |
|
|
647
|
1 |
|
/** |
648
|
1 |
|
* Parsing record data. |
649
|
1 |
|
* |
650
|
1 |
|
* @param string $params |
651
|
1 |
|
* @param mixed $isPermitted |
652
|
1 |
|
* |
653
|
1 |
|
* @return string |
654
|
|
|
*/ |
655
|
|
|
protected function record($params, $isPermitted = true) |
656
|
|
|
{ |
657
|
1 |
|
if (!isset($this->recordModel) || ($isPermitted && !Privilege::isPermitted($this->moduleName, 'DetailView', $this->record))) { |
658
|
|
|
return ''; |
659
|
|
|
} |
660
|
|
|
[$key, $params] = array_pad(explode('|', $params, 2), 2, false); |
661
|
|
|
if ($this->recordModel->has($key)) { |
662
|
|
|
$fieldModel = $this->recordModel->getModule()->getFieldByName($key); |
663
|
|
|
if (!$fieldModel || !$this->useValue($fieldModel, $this->moduleName)) { |
664
|
|
|
return ''; |
665
|
|
|
} |
666
|
|
|
return $this->getDisplayValueByField($fieldModel, false, $params); |
|
|
|
|
667
|
|
|
} |
668
|
|
|
switch ($key) { |
669
|
|
|
case 'CrmDetailViewURL': |
670
|
|
|
return Config::main('site_URL') . 'index.php?module=' . $this->moduleName . '&view=Detail&record=' . $this->record; |
671
|
|
|
case 'PortalDetailViewURL': |
672
|
|
|
$recorIdName = 'id'; |
673
|
|
|
if ('HelpDesk' === $this->moduleName) { |
674
|
|
|
$recorIdName = 'ticketid'; |
675
|
|
|
} elseif ('Faq' === $this->moduleName) { |
676
|
|
|
$recorIdName = 'faqid'; |
677
|
1 |
|
} elseif ('Products' === $this->moduleName) { |
678
|
1 |
|
$recorIdName = 'productid'; |
679
|
|
|
} |
680
|
|
|
return Config::main('PORTAL_URL') . '/index.php?module=' . $this->moduleName . '&action=index&' . $recorIdName . '=' . $this->record; |
681
|
1 |
|
case 'ModuleName': |
682
|
1 |
|
return $this->moduleName; |
683
|
1 |
|
case 'RecordId': |
684
|
1 |
|
return $this->record; |
685
|
1 |
|
case 'RecordLabel': |
686
|
|
|
return $this->recordModel->getName(); |
687
|
|
|
case 'ChangesListChanges': |
688
|
1 |
|
$value = ''; |
689
|
|
|
foreach ($this->recordModel->getPreviousValue() as $fieldName => $oldValue) { |
690
|
|
|
$fieldModel = $this->recordModel->getModule()->getFieldByName($fieldName); |
691
|
|
|
if (!$fieldModel) { |
692
|
|
|
continue; |
693
|
|
|
} |
694
|
|
|
$oldValue = $this->getDisplayValueByField($fieldModel, $oldValue); |
695
|
|
|
$currentValue = $this->getDisplayValueByField($fieldModel); |
696
|
|
|
if ($this->withoutTranslations) { |
697
|
|
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
698
|
1 |
|
$value .= "\$(translate : {$this->moduleName}|{$label})\$ \$(translate : LBL_FROM)\$ $oldValue \$(translate : LBL_TO)\$ " . $currentValue . ($this->isHtml ? '<br>' : PHP_EOL); |
|
|
|
|
699
|
|
|
} else { |
700
|
1 |
|
$value .= Language::translate($fieldModel->getFieldLabel(), $this->moduleName, $this->language) . ' '; |
|
|
|
|
701
|
1 |
|
$value .= Language::translate('LBL_FROM') . " $oldValue " . Language::translate('LBL_TO') . " $currentValue" . ($this->isHtml ? '<br>' : PHP_EOL); |
702
|
|
|
} |
703
|
1 |
|
} |
704
|
1 |
|
return $value; |
705
|
1 |
|
case 'ChangesListValues': |
706
|
1 |
|
$value = ''; |
707
|
|
|
$changes = $this->recordModel->getPreviousValue(); |
708
|
|
|
if (empty($changes)) { |
709
|
1 |
|
$changes = array_filter($this->recordModel->getData()); |
710
|
|
|
unset($changes['createdtime'], $changes['modifiedtime'], $changes['id'], $changes['newRecord'], $changes['modifiedby']); |
711
|
|
|
} |
712
|
|
|
foreach ($changes as $fieldName => $oldValue) { |
713
|
|
|
$fieldModel = $this->recordModel->getModule()->getFieldByName($fieldName); |
714
|
|
|
if (!$fieldModel) { |
715
|
|
|
continue; |
716
|
|
|
} |
717
|
|
|
$currentValue = \in_array($fieldModel->getFieldDataType(), $this->largeDataUiTypes) ? '' : $this->getDisplayValueByField($fieldModel); |
718
|
|
|
if ($this->withoutTranslations) { |
719
|
1 |
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
720
|
|
|
$value .= "\$(translate : {$this->moduleName}|{$label})\$: $currentValue" . ($this->isHtml ? '<br>' : PHP_EOL); |
721
|
1 |
|
} else { |
722
|
1 |
|
$value .= Language::translate($fieldModel->getFieldLabel(), $this->moduleName, $this->language) . ": $currentValue" . ($this->isHtml ? '<br>' : PHP_EOL); |
|
|
|
|
723
|
1 |
|
} |
724
|
1 |
|
} |
725
|
|
|
return $value; |
726
|
1 |
|
case 'SummaryFields': |
727
|
1 |
|
$value = ''; |
728
|
1 |
|
$recordStructure = \Vtiger_RecordStructure_Model::getInstanceFromRecordModel($this->recordModel, \Vtiger_RecordStructure_Model::RECORD_STRUCTURE_MODE_SUMMARY); |
729
|
|
|
$fields = $recordStructure->getStructure()['SUMMARY_FIELDS'] ?? []; |
730
|
1 |
|
foreach ($fields as $fieldName => $fieldModel) { |
731
|
1 |
|
$currentValue = $this->getDisplayValueByField($fieldModel); |
732
|
1 |
|
if ($this->withoutTranslations) { |
733
|
1 |
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
734
|
1 |
|
$value .= "\$(translate : {$this->moduleName}|{$label})\$: $currentValue" . ($this->isHtml ? '<br>' : PHP_EOL); |
735
|
|
|
} else { |
736
|
1 |
|
$value .= Language::translate($fieldModel->getFieldLabel(), $this->moduleName, $this->language) . ": $currentValue" . ($this->isHtml ? '<br>' : PHP_EOL); |
737
|
1 |
|
} |
738
|
|
|
} |
739
|
|
|
return $value; |
740
|
1 |
|
default: |
741
|
1 |
|
if (false !== strpos($key, ' ')) { |
742
|
|
|
[$key, $params] = explode(' ', $key); |
743
|
|
|
} |
744
|
1 |
|
if ('Comments' === $key) { |
745
|
1 |
|
return $this->getComments($params); |
746
|
|
|
} |
747
|
1 |
|
break; |
748
|
1 |
|
} |
749
|
1 |
|
return ''; |
750
|
|
|
} |
751
|
1 |
|
|
752
|
1 |
|
/** |
753
|
1 |
|
* Parsing related record data. |
754
|
1 |
|
* |
755
|
1 |
|
* @param string $params |
756
|
1 |
|
* |
757
|
|
|
* @return mixed |
758
|
|
|
*/ |
759
|
|
|
protected function relatedRecord($params) |
760
|
|
|
{ |
761
|
|
|
$params = explode('|', $params); |
762
|
1 |
|
$fieldName = array_shift($params); |
763
|
1 |
|
$relatedField = array_shift($params); |
764
|
1 |
|
$relatedModule = array_shift($params); |
765
|
1 |
|
$value = $params ? $relatedField . '|' . implode('|', $params) : $relatedField; |
766
|
1 |
|
if ( |
767
|
1 |
|
!isset($this->recordModel) |
768
|
1 |
|
|| ($this->permissions && !Privilege::isPermitted($this->moduleName, 'DetailView', $this->record)) |
769
|
1 |
|
|| $this->recordModel->isEmpty($fieldName) |
770
|
|
|
) { |
771
|
|
|
return ''; |
772
|
1 |
|
} |
773
|
|
|
$relatedId = $this->recordModel->get($fieldName); |
774
|
|
|
if (empty($relatedId)) { |
775
|
1 |
|
return ''; |
776
|
|
|
} |
777
|
1 |
|
if (empty($relatedModule) && \in_array($this->recordModel->getField($fieldName)->getFieldDataType(), ['owner', 'sharedOwner'])) { |
778
|
|
|
$relatedModule = 'Users'; |
779
|
|
|
} |
780
|
|
|
if ('Users' === $relatedModule) { |
781
|
|
|
$return = []; |
782
|
|
|
foreach (explode(',', $relatedId) as $relatedValueId) { |
783
|
|
|
if ('Users' === Fields\Owner::getType($relatedValueId)) { |
|
|
|
|
784
|
|
|
$userRecordModel = \Vtiger_Record_Model::getInstanceById($relatedValueId, $relatedModule); |
|
|
|
|
785
|
|
|
if ('Active' === $userRecordModel->get('status')) { |
786
|
|
|
$instance = static::getInstanceByModel($userRecordModel); |
787
|
1 |
|
foreach (['withoutTranslations', 'language', 'emailoptout'] as $key) { |
788
|
|
|
if (isset($this->{$key})) { |
789
|
1 |
|
$instance->{$key} = $this->{$key}; |
790
|
1 |
|
} |
791
|
1 |
|
} |
792
|
1 |
|
$return[] = $instance->record($value, false); |
793
|
1 |
|
} |
794
|
1 |
|
continue; |
795
|
1 |
|
} |
796
|
|
|
foreach (PrivilegeUtil::getUsersByGroup($relatedValueId) as $userId) { |
|
|
|
|
797
|
1 |
|
$userRecordModel = \Vtiger_Record_Model::getInstanceById($userId, $relatedModule); |
798
|
1 |
|
if ('Active' === $userRecordModel->get('status')) { |
799
|
|
|
$instance = static::getInstanceByModel($userRecordModel); |
800
|
|
|
foreach (['withoutTranslations', 'language', 'emailoptout'] as $key) { |
801
|
1 |
|
if (isset($this->{$key})) { |
802
|
1 |
|
$instance->{$key} = $this->{$key}; |
803
|
|
|
} |
804
|
|
|
} |
805
|
1 |
|
$return[] = $instance->record($value, false); |
806
|
1 |
|
} |
807
|
1 |
|
} |
808
|
1 |
|
} |
809
|
|
|
return implode($this->relatedRecordSeparator, $return); |
810
|
1 |
|
} |
811
|
1 |
|
$module = Record::getType($relatedId); |
812
|
1 |
|
if (!Record::isExists($relatedId) || empty($module) || ($relatedModule && $relatedModule !== $module)) { |
813
|
1 |
|
return ''; |
814
|
1 |
|
} |
815
|
1 |
|
$relatedRecordModel = \Vtiger_Record_Model::getInstanceById($relatedId, $module); |
816
|
|
|
if ($this->permissions && !$relatedRecordModel->isViewable()) { |
817
|
|
|
return ''; |
818
|
1 |
|
} |
819
|
1 |
|
$instance = static::getInstanceByModel($relatedRecordModel); |
820
|
1 |
|
foreach (['withoutTranslations', 'language', 'emailoptout'] as $key) { |
821
|
|
|
if (isset($this->{$key})) { |
822
|
1 |
|
$instance->{$key} = $this->{$key}; |
823
|
1 |
|
} |
824
|
1 |
|
} |
825
|
|
|
return $instance->record($value); |
826
|
1 |
|
} |
827
|
1 |
|
|
828
|
1 |
|
/** |
829
|
1 |
|
* Parsing related record data. |
830
|
1 |
|
* |
831
|
|
|
* @param string $params |
832
|
1 |
|
* |
833
|
|
|
* @return mixed |
834
|
|
|
*/ |
835
|
1 |
|
protected function relatedRecordLevel($params) |
836
|
1 |
|
{ |
837
|
1 |
|
[$fieldName, $relatedModule, $relatedRecord] = array_pad(explode('|', $params, 3), 3, ''); |
838
|
1 |
|
if ( |
839
|
1 |
|
!isset($this->recordModel) |
840
|
1 |
|
|| !Privilege::isPermitted($this->moduleName, 'DetailView', $this->record) |
841
|
1 |
|
|| $this->recordModel->isEmpty($fieldName) |
842
|
1 |
|
) { |
843
|
|
|
return ''; |
844
|
|
|
} |
845
|
1 |
|
$relatedId = $this->recordModel->get($fieldName); |
846
|
|
|
if (empty($relatedId)) { |
847
|
|
|
return ''; |
848
|
1 |
|
} |
849
|
|
|
$moduleName = Record::getType($relatedId); |
850
|
1 |
|
if (!empty($moduleName) && ($relatedModule && $relatedModule !== $moduleName)) { |
851
|
|
|
return ''; |
852
|
|
|
} |
853
|
|
|
if ('Users' === $relatedModule && 'Users' === Fields\Owner::getType($relatedId)) { |
|
|
|
|
854
|
|
|
$relatedRecordModel = \Users_Privileges_Model::getInstanceById($relatedId); |
855
|
|
|
if ('Active' !== $relatedRecordModel->get('status')) { |
856
|
|
|
return ''; |
857
|
|
|
} |
858
|
|
|
} else { |
859
|
|
|
$relatedRecordModel = \Vtiger_Record_Model::getInstanceById($relatedId, $moduleName); |
860
|
|
|
if (!$relatedRecordModel->isViewable()) { |
861
|
|
|
return ''; |
862
|
7 |
|
} |
863
|
|
|
} |
864
|
7 |
|
$instance = static::getInstanceByModel($relatedRecordModel); |
865
|
7 |
|
foreach (['withoutTranslations', 'language', 'emailoptout'] as $key) { |
866
|
5 |
|
if (isset($this->{$key})) { |
867
|
5 |
|
$instance->{$key} = $this->{$key}; |
868
|
|
|
} |
869
|
|
|
} |
870
|
3 |
|
return $instance->relatedRecord($relatedRecord); |
871
|
2 |
|
} |
872
|
2 |
|
|
873
|
2 |
|
/** |
874
|
|
|
* Parsing source record data. |
875
|
|
|
* |
876
|
|
|
* @param string $fieldName |
877
|
7 |
|
* |
878
|
1 |
|
* @return mixed |
879
|
|
|
*/ |
880
|
7 |
|
protected function sourceRecord($fieldName) |
881
|
3 |
|
{ |
882
|
|
|
if (empty($this->sourceRecordModel) || !Privilege::isPermitted($this->sourceRecordModel->getModuleName(), 'DetailView', $this->sourceRecordModel->getId())) { |
883
|
6 |
|
return ''; |
884
|
|
|
} |
885
|
|
|
$instance = static::getInstanceByModel($this->sourceRecordModel); |
886
|
|
|
foreach (['withoutTranslations', 'language', 'emailoptout'] as $key) { |
887
|
|
|
if (isset($this->{$key})) { |
888
|
|
|
$instance->{$key} = $this->{$key}; |
889
|
|
|
} |
890
|
|
|
} |
891
|
|
|
return $instance->record($fieldName); |
892
|
|
|
} |
893
|
|
|
|
894
|
|
|
/** |
895
|
|
|
* Parsing related records list. |
896
|
3 |
|
* |
897
|
|
|
* @param string $params Parameter construction: RelatedModuleNameOrRelationId|Columns|Conditions|CustomViewIdOrName|Limit, Example: Contacts|firstname,lastname,modifiedtime|[[["firstname","a","Tom"]]]||2 |
898
|
3 |
|
* |
899
|
3 |
|
* @return string |
900
|
1 |
|
*/ |
901
|
1 |
|
protected function relatedRecordsList($params) |
902
|
1 |
|
{ |
903
|
3 |
|
[$relatedModuleName, $columns, $conditions, $viewIdOrName, $limit, $maxLength] = array_pad(explode('|', $params), 6, ''); |
904
|
1 |
|
if (is_numeric($relatedModuleName)) { |
905
|
1 |
|
if ($relationListView = \Vtiger_RelationListView_Model::getInstance($this->recordModel, '', $relatedModuleName)) { |
906
|
1 |
|
$relatedModuleName = $relationListView->getRelatedModuleModel()->getName(); |
907
|
1 |
|
} |
908
|
1 |
|
} else { |
909
|
|
|
$relationListView = \Vtiger_RelationListView_Model::getInstance($this->recordModel, $relatedModuleName); |
910
|
1 |
|
} |
911
|
1 |
|
if (!$relationListView || !Privilege::isPermitted($relatedModuleName)) { |
|
|
|
|
912
|
|
|
return ''; |
913
|
1 |
|
} |
914
|
1 |
|
$pagingModel = new \Vtiger_Paging_Model(); |
915
|
3 |
|
$pagingModel->set('limit', (int) $limit); |
916
|
1 |
|
if ($viewIdOrName) { |
917
|
1 |
|
if (!is_numeric($viewIdOrName)) { |
918
|
3 |
|
$customView = CustomView::getInstance($relatedModuleName); |
919
|
|
|
if ($cvId = $customView->getViewIdByName($viewIdOrName)) { |
920
|
|
|
$viewIdOrName = $cvId; |
921
|
|
|
} else { |
922
|
|
|
$viewIdOrName = false; |
923
|
|
|
Log::warning("No view found. Module: $relatedModuleName, view name: $viewIdOrName", 'TextParser'); |
924
|
|
|
} |
925
|
|
|
} |
926
|
|
|
if ($viewIdOrName) { |
927
|
|
|
$relationListView->getQueryGenerator()->initForCustomViewById($viewIdOrName); |
928
|
|
|
} |
929
|
|
|
if ($cvId && ($customViewModel = \CustomView_Record_Model::getInstanceById($cvId)) && ($orderBy = $customViewModel->getSortOrderBy())) { |
|
|
|
|
930
|
|
|
$relationListView->set('orderby', $orderBy); |
931
|
|
|
} |
932
|
|
|
} |
933
|
|
|
if ($columns) { |
934
|
|
|
$relationListView->setFields($columns); |
935
|
|
|
} else { |
936
|
|
|
$fields = array_filter($relationListView->getHeaders(), fn ($fieldModel) => !$fieldModel->get('fromOutsideList')); |
937
|
|
|
$relationListView->setFields(array_keys($fields)); |
938
|
|
|
} |
939
|
|
|
if ($conditions) { |
940
|
|
|
$transformedSearchParams = $relationListView->getQueryGenerator()->parseBaseSearchParamsToCondition(Json::decode($conditions)); |
941
|
3 |
|
$relationListView->set('search_params', $transformedSearchParams); |
942
|
|
|
} |
943
|
|
|
return $this->relatedRecordsListPrinter($relationListView, $pagingModel, (int) $maxLength); |
944
|
|
|
} |
945
|
|
|
|
946
|
|
|
/** |
947
|
|
|
* Printer related records list. |
948
|
|
|
* |
949
|
|
|
* @param \Vtiger_RelationListView_Model $relationListView |
950
|
|
|
* @param \Vtiger_Paging_Model $pagingModel |
951
|
|
|
* @param int $maxLength |
952
|
|
|
* |
953
|
|
|
* @return string |
954
|
|
|
*/ |
955
|
|
|
protected function relatedRecordsListPrinter(\Vtiger_RelationListView_Model $relationListView, \Vtiger_Paging_Model $pagingModel, int $maxLength): string |
956
|
|
|
{ |
957
|
|
|
$relatedModuleName = $relationListView->getRelationModel()->getRelationModuleName(); |
958
|
3 |
|
$rows = $headers = ''; |
959
|
|
|
$fields = $relationListView->getRelationModel()->getQueryFields(); |
960
|
1 |
|
foreach ($fields as $fieldModel) { |
961
|
|
|
if ($fieldModel->isViewable() || $fieldModel->get('fromOutsideList')) { |
962
|
|
|
if ($this->withoutTranslations) { |
963
|
|
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
964
|
|
|
$headers .= "<th class=\"col-type-{$fieldModel->getFieldType()}\">$(translate : {$label}|$relatedModuleName)$</th>"; |
965
|
|
|
} else { |
966
|
|
|
$headers .= "<th class=\"col-type-{$fieldModel->getFieldType()}\">" . Language::translate($fieldModel->getFieldLabel(), $relatedModuleName) . '</th>'; |
|
|
|
|
967
|
|
|
} |
968
|
|
|
} |
969
|
|
|
} |
970
|
1 |
|
$counter = 0; |
971
|
|
|
foreach ($relationListView->getEntries($pagingModel) as $relatedRecordModel) { |
972
|
1 |
|
++$counter; |
973
|
1 |
|
$rows .= '<tr class="row-' . $counter . '">'; |
974
|
1 |
|
foreach ($fields as $fieldModel) { |
975
|
1 |
|
$value = $this->getDisplayValueByField($fieldModel, $relatedRecordModel); |
976
|
|
|
if (false !== $value) { |
977
|
1 |
|
if ($maxLength) { |
978
|
1 |
|
$value = TextUtils::textTruncate($value, $maxLength); |
979
|
1 |
|
} |
980
|
1 |
|
$rows .= "<td class=\"col-type-{$fieldModel->getFieldType()}\">{$value}</td>"; |
981
|
1 |
|
} |
982
|
|
|
} |
983
|
|
|
$rows .= '</tr>'; |
984
|
1 |
|
} |
985
|
|
|
return empty($rows) ? '' : "<table style=\"border-collapse:collapse;width:100%\" class=\"related-records-list\"><thead><tr>{$headers}</tr></thead><tbody>{$rows}</tbody></table>"; |
986
|
|
|
} |
987
|
1 |
|
|
988
|
|
|
/** |
989
|
|
|
* Parsing records list. |
990
|
|
|
* |
991
|
|
|
* @param string $params Parameter construction: ModuleName|Columns|Conditions|CustomViewIdOrName|Limit, Example: Contacts|firstname,lastname,modifiedtime|[[["firstname","a","Tom"]]]||2 |
992
|
|
|
* |
993
|
|
|
* @return string |
994
|
|
|
*/ |
995
|
|
|
protected function recordsList($params) |
996
|
|
|
{ |
997
|
|
|
[$moduleName, $columns, $conditions, $viewIdOrName, $limit, $maxLength, $params] = array_pad(explode('|', $params, 7), 7, ''); |
998
|
4 |
|
$paramsArray = $params ? self::parseFieldParam($params) : []; |
999
|
|
|
$cvId = 0; |
1000
|
4 |
|
if ($viewIdOrName) { |
1001
|
|
|
if (!is_numeric($viewIdOrName)) { |
1002
|
|
|
$customView = CustomView::getInstance($moduleName); |
1003
|
|
|
if ($cvIdByName = $customView->getViewIdByName($viewIdOrName)) { |
1004
|
|
|
$viewIdOrName = $cvIdByName; |
1005
|
|
|
} else { |
1006
|
|
|
$viewIdOrName = false; |
1007
|
|
|
Log::warning("No view found. Module: $moduleName, view name: $viewIdOrName", 'TextParser'); |
1008
|
|
|
} |
1009
|
|
|
} |
1010
|
2 |
|
if ($viewIdOrName) { |
1011
|
|
|
$cvId = $viewIdOrName; |
1012
|
2 |
|
} |
1013
|
2 |
|
} |
1014
|
|
|
$listView = \Vtiger_ListView_Model::getInstance($moduleName, $cvId); |
1015
|
1 |
|
if ($cvId && ($customViewModel = \CustomView_Record_Model::getInstanceById($cvId)) && ($orderBy = $customViewModel->getSortOrderBy())) { |
1016
|
|
|
$listView->set('orderby', $orderBy); |
1017
|
|
|
} |
1018
|
|
|
$limit = (int) $limit; |
1019
|
|
|
$listView->getQueryGenerator()->setLimit((int) ($limit ?: \App\Config::main('list_max_entries_per_page', 20))); |
1020
|
|
|
if ($columns) { |
1021
|
|
|
$headerFields = []; |
1022
|
|
|
foreach (explode(',', $columns) as $fieldName) { |
1023
|
|
|
$headerFields[] = [ |
1024
|
|
|
'field_name' => $fieldName, |
1025
|
1 |
|
'module_name' => $moduleName, |
1026
|
|
|
]; |
1027
|
1 |
|
} |
1028
|
|
|
$listView->set('header_fields', $headerFields); |
1029
|
|
|
$listView->getQueryGenerator()->setFields(explode(',', $columns)); |
1030
|
|
|
$listView->getQueryGenerator()->setField('id'); |
1031
|
|
|
} |
1032
|
|
|
if ($conditions) { |
1033
|
1 |
|
$transformedSearchParams = $listView->getQueryGenerator()->parseBaseSearchParamsToCondition(Json::decode($conditions)); |
1034
|
1 |
|
$listView->set('search_params', $transformedSearchParams); |
1035
|
1 |
|
} |
1036
|
|
|
if (($pdf = $this->getParam('pdf')) && $pdf->get('module_name') === $moduleName && ($ids = $pdf->getVariable('recordsId'))) { |
1037
|
1 |
|
$listView->getQueryGenerator()->addCondition('id', $ids, 'e', 1); |
1038
|
1 |
|
} |
1039
|
1 |
|
$rows = $headers = $headerStyle = $borderStyle = ''; |
1040
|
1 |
|
$fields = $listView->getListViewHeaders(); |
1041
|
1 |
|
if (isset($paramsArray['headerStyle']) && 'background' === $paramsArray['headerStyle']) { |
1042
|
|
|
$headerStyle = 'background-color:#ddd;'; |
1043
|
|
|
} |
1044
|
1 |
|
if (isset($paramsArray['table']) && 'border' === $paramsArray['table']) { |
1045
|
1 |
|
$borderStyle = 'border:1px solid #ddd;'; |
1046
|
1 |
|
} |
1047
|
1 |
|
foreach ($fields as $fieldModel) { |
1048
|
1 |
|
if ($this->withoutTranslations) { |
1049
|
|
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
1050
|
|
|
$headers .= "<th class=\"col-type-{$fieldModel->getFieldType()}\" style=\"{$headerStyle}\">$(translate : {$label}|$moduleName)$</th>"; |
1051
|
|
|
} else { |
1052
|
1 |
|
$headers .= "<th class=\"col-type-{$fieldModel->getFieldType()}\" style=\"{$headerStyle}\">" . Language::translate($fieldModel->getFieldLabel(), $moduleName) . '</th>'; |
|
|
|
|
1053
|
1 |
|
} |
1054
|
1 |
|
} |
1055
|
1 |
|
$counter = 0; |
1056
|
|
|
foreach ($listView->getAllEntries() as $relatedRecordModel) { |
1057
|
1 |
|
++$counter; |
1058
|
|
|
$rows .= '<tr class="row-' . $counter . '">'; |
1059
|
1 |
|
foreach ($fields as $fieldModel) { |
1060
|
1 |
|
$value = $this->getDisplayValueByField($fieldModel, $relatedRecordModel, $params); |
1061
|
|
|
if (false !== $value) { |
1062
|
|
|
if ((int) $maxLength) { |
1063
|
|
|
$value = TextUtils::textTruncate($value, (int) $maxLength); |
1064
|
|
|
} |
1065
|
|
|
$rows .= "<td class=\"col-type-{$fieldModel->getFieldType()}\" style=\"{$borderStyle}\">{$value}</td>"; |
1066
|
|
|
} |
1067
|
|
|
} |
1068
|
|
|
$rows .= '</tr>'; |
1069
|
|
|
} |
1070
|
|
|
if (empty($rows)) { |
1071
|
|
|
return ''; |
1072
|
1 |
|
} |
1073
|
|
|
$headers = "<tr>{$headers}</tr>"; |
1074
|
1 |
|
$table = "class=\"records-list\" style=\"border-collapse:collapse;width:100%;{$borderStyle}\""; |
1075
|
1 |
|
if (isset($paramsArray['addCounter']) && '1' === $paramsArray['addCounter']) { |
1076
|
1 |
|
$headers = '<tr><th colspan="' . \count($fields) . '">' . Language::translate('LBL_NUMBER_OF_ALL_ENTRIES') . ": $counter</th></th></tr>$headers"; |
1077
|
|
|
} |
1078
|
1 |
|
return "<table {$table}><thead>{$headers}</thead><tbody>{$rows}</tbody></table>"; |
1079
|
1 |
|
} |
1080
|
1 |
|
|
1081
|
1 |
|
/** |
1082
|
1 |
|
* Get record display value. |
1083
|
1 |
|
* |
1084
|
1 |
|
* @param \Vtiger_Field_Model $fieldModel |
1085
|
|
|
* @param bool|mixed|\Vtiger_Record_Model $value |
1086
|
|
|
* @param string $params |
1087
|
|
|
* |
1088
|
1 |
|
* @return array|bool|mixed|string |
1089
|
1 |
|
*/ |
1090
|
1 |
|
protected function getDisplayValueByField(\Vtiger_Field_Model $fieldModel, $value = false, $params = null) |
1091
|
1 |
|
{ |
1092
|
1 |
|
$model = $this->recordModel; |
1093
|
1 |
|
if (false === $value) { |
1094
|
1 |
|
$value = \App\Utils\Completions::decode($this->recordModel->get($fieldModel->getName()), \App\Utils\Completions::FORMAT_TEXT); |
1095
|
1 |
|
if (!$fieldModel->isViewEnabled() && !$fieldModel->get('fromOutsideList')) { |
1096
|
|
|
return ''; |
1097
|
|
|
} |
1098
|
|
|
} elseif (\is_object($value)) { |
1099
|
|
|
$model = $value; |
1100
|
1 |
|
$value = $value->get($fieldModel->getName()); |
1101
|
1 |
|
if (!$fieldModel->isViewEnabled() && !$fieldModel->get('fromOutsideList')) { |
1102
|
|
|
return false; |
1103
|
|
|
} |
1104
|
|
|
} |
1105
|
|
|
if ('' === $value) { |
1106
|
|
|
return ''; |
1107
|
|
|
} |
1108
|
|
|
if ($this->withoutTranslations) { |
1109
|
1 |
|
return $this->getDisplayValueByType($value, $model, $fieldModel, $params); |
1110
|
|
|
} |
1111
|
1 |
|
return $fieldModel->getUITypeModel()->getTextParserDisplayValue($value, $model, $params); |
1112
|
1 |
|
} |
1113
|
|
|
|
1114
|
1 |
|
/** |
1115
|
1 |
|
* Get record display value by type. |
1116
|
1 |
|
* |
1117
|
1 |
|
* @param mixed $value |
1118
|
1 |
|
* @param \Vtiger_Record_Model $recordModel |
1119
|
1 |
|
* @param \Vtiger_Field_Model $fieldModel |
1120
|
|
|
* @param string $params |
1121
|
|
|
* |
1122
|
1 |
|
* @return array|mixed|string |
1123
|
1 |
|
*/ |
1124
|
1 |
|
protected function getDisplayValueByType($value, \Vtiger_Record_Model $recordModel, \Vtiger_Field_Model $fieldModel, $params) |
1125
|
1 |
|
{ |
1126
|
1 |
|
switch ($fieldModel->getFieldDataType()) { |
1127
|
1 |
|
case 'boolean': |
1128
|
1 |
|
$value = (1 === $value) ? 'LBL_YES' : 'LBL_NO'; |
1129
|
1 |
|
$value = "$(translate : $value)$"; |
1130
|
1 |
|
break; |
1131
|
|
|
case 'multipicklist': |
1132
|
|
|
$value = explode(' |##| ', $value); |
1133
|
|
|
$trValue = []; |
1134
|
|
|
$countValue = \count($value); |
1135
|
|
|
for ($i = 0; $i < $countValue; ++$i) { |
1136
|
1 |
|
$trValue[] = "$(translate : {$recordModel->getModuleName()}|{$value[$i]})$"; |
1137
|
|
|
} |
1138
|
|
|
if (\is_array($trValue)) { |
|
|
|
|
1139
|
|
|
$trValue = implode(' |##| ', $trValue); |
1140
|
|
|
} |
1141
|
|
|
$value = str_ireplace(' |##| ', ', ', $trValue); |
1142
|
|
|
break; |
1143
|
|
|
case 'picklist': |
1144
|
|
|
$value = "$(translate : {$recordModel->getModuleName()}|$value)$"; |
1145
|
|
|
break; |
1146
|
1 |
|
case 'time': |
1147
|
|
|
$userModel = \Users_Privileges_Model::getCurrentUserModel(); |
1148
|
1 |
|
$value = \DateTimeField::convertToUserTimeZone(date('Y-m-d') . ' ' . $value)->format('H:i:s'); |
1149
|
1 |
|
if (12 === (int) $userModel->get('hour_format')) { |
1150
|
1 |
|
if ($value) { |
1151
|
|
|
[$hours, $minutes] = array_pad(explode(':', $value), 2, ''); |
1152
|
1 |
|
$format = '$(translate : PM)$'; |
1153
|
1 |
|
if ($hours > 12) { |
1154
|
1 |
|
$hours = (int) $hours - 12; |
1155
|
1 |
|
} elseif ($hours < 12) { |
1156
|
1 |
|
$format = '$(translate : AM)$'; |
1157
|
1 |
|
} |
1158
|
|
|
//If hours zero then we need to make it as 12 AM |
1159
|
1 |
|
if ('00' == $hours) { |
1160
|
|
|
$hours = '12'; |
1161
|
1 |
|
$format = '$(translate : AM)$'; |
1162
|
1 |
|
} |
1163
|
1 |
|
$value = "$hours:$minutes $format"; |
1164
|
1 |
|
} else { |
1165
|
1 |
|
$value = ''; |
1166
|
1 |
|
} |
1167
|
1 |
|
} |
1168
|
|
|
break; |
1169
|
|
|
case 'tree': |
1170
|
|
|
$template = $fieldModel->getFieldParams(); |
1171
|
1 |
|
$value = $parentName = ''; |
1172
|
1 |
|
if ($row = Fields\Tree::getValueByTreeId($template, $value)) { |
|
|
|
|
1173
|
1 |
|
if ($row['depth'] > 0) { |
1174
|
1 |
|
$pieces = explode('::', $row['parentTree']); |
1175
|
1 |
|
end($pieces); |
1176
|
1 |
|
$parent = prev($pieces); |
1177
|
1 |
|
$parentRow = Fields\Tree::getValueByTreeId($template, $parent); |
1178
|
1 |
|
$parentName = "($(translate : {$recordModel->getModuleName()}|{$parentRow['name']})$) "; |
1179
|
1 |
|
} |
1180
|
1 |
|
$value = $parentName . "$(translate : {$recordModel->getModuleName()}|{$row['name']})$"; |
1181
|
1 |
|
} |
1182
|
|
|
break; |
1183
|
|
|
default: |
1184
|
|
|
return $fieldModel->getUITypeModel()->getTextParserDisplayValue($value, $recordModel, $params); |
1185
|
|
|
} |
1186
|
|
|
return $value; |
1187
|
|
|
} |
1188
|
1 |
|
|
1189
|
|
|
/** |
1190
|
1 |
|
* Get last comments. |
1191
|
|
|
* |
1192
|
|
|
* @param mixed $params |
1193
|
|
|
* |
1194
|
|
|
* @return string |
1195
|
|
|
*/ |
1196
|
|
|
protected function getComments($params = false) |
1197
|
|
|
{ |
1198
|
1 |
|
[$limit, $showAuthor] = array_pad(explode('|', $params, 2), 2, false); |
1199
|
|
|
$query = (new \App\Db\Query())->select(['commentcontent', 'userid'])->from('vtiger_modcomments')->where(['related_to' => $this->record])->orderBy(['modcommentsid' => SORT_DESC]); |
1200
|
|
|
if ($limit) { |
1201
|
|
|
$query->limit($limit); |
1202
|
1 |
|
} |
1203
|
1 |
|
$commentsList = ''; |
1204
|
|
|
foreach ($query->all() as $comment) { |
1205
|
1 |
|
if ('' != $comment['commentcontent']) { |
1206
|
1 |
|
$commentsList .= '<br><br>'; |
1207
|
|
|
if ('true' === $showAuthor) { |
1208
|
|
|
$commentsList .= Purifier::encodeHtml(\App\Fields\Owner::getUserLabel($comment['userid'])) . ': '; |
1209
|
|
|
} |
1210
|
|
|
$commentsList .= nl2br($comment['commentcontent']); |
1211
|
|
|
} |
1212
|
|
|
} |
1213
|
|
|
return ltrim($commentsList, '<br><br>'); |
1214
|
1 |
|
} |
1215
|
|
|
|
1216
|
1 |
|
/** |
1217
|
1 |
|
* Check if this content can be used. |
1218
|
1 |
|
* |
1219
|
1 |
|
* @param \Vtiger_Field_Model $fieldModel |
1220
|
1 |
|
* @param string $moduleName |
1221
|
1 |
|
* |
1222
|
|
|
* @return bool |
1223
|
|
|
*/ |
1224
|
|
|
protected function useValue($fieldModel, $moduleName) |
1225
|
1 |
|
{ |
1226
|
1 |
|
return true; |
1227
|
|
|
} |
1228
|
|
|
|
1229
|
1 |
|
/** |
1230
|
|
|
* Parsing params. |
1231
|
|
|
* |
1232
|
1 |
|
* @param string $key |
1233
|
|
|
* |
1234
|
|
|
* @return string |
1235
|
|
|
*/ |
1236
|
|
|
protected function params(string $key) |
1237
|
|
|
{ |
1238
|
|
|
return isset($this->params[$key]) ? \App\Purifier::purifyHtml($this->params[$key]) : ''; |
1239
|
|
|
} |
1240
|
1 |
|
|
1241
|
|
|
/** |
1242
|
1 |
|
* Parsing custom. |
1243
|
1 |
|
* |
1244
|
1 |
|
* @param string $params |
1245
|
1 |
|
* |
1246
|
1 |
|
* @return string |
1247
|
1 |
|
*/ |
1248
|
1 |
|
protected function custom($params) |
1249
|
1 |
|
{ |
1250
|
|
|
$instance = null; |
1251
|
|
|
if (false !== strpos($params, '||')) { |
1252
|
1 |
|
$params = explode('||', $params); |
1253
|
|
|
$parserName = array_shift($params); |
1254
|
|
|
$baseParams = $params; |
1255
|
|
|
$params = []; |
1256
|
1 |
|
} else { |
1257
|
|
|
$params = explode('|', $params); |
1258
|
|
|
$parserName = array_shift($params); |
1259
|
|
|
$baseParams = $params; |
1260
|
|
|
} |
1261
|
|
|
$module = false; |
1262
|
|
|
if (!empty($params)) { |
1263
|
|
|
$module = array_shift($params); |
1264
|
1 |
|
if (!Module::getModuleId($module)) { |
1265
|
|
|
$module = $this->moduleName; |
1266
|
1 |
|
} |
1267
|
1 |
|
} |
1268
|
1 |
|
$className = "\\App\\TextParser\\$parserName"; |
1269
|
1 |
|
if ($module && $handlerClass = \Vtiger_Loader::getComponentClassName('TextParser', $parserName, $module, false)) { |
1270
|
1 |
|
$className = $handlerClass; |
1271
|
1 |
|
} |
1272
|
1 |
|
if (!class_exists($className)) { |
1273
|
|
|
Log::error("Not found custom class: $parserName|{$module}"); |
1274
|
|
|
} else { |
1275
|
1 |
|
$instance = new $className($this, $baseParams); |
1276
|
|
|
} |
1277
|
|
|
return $instance && $instance->isActive() ? $instance->process() : ''; |
1278
|
|
|
} |
1279
|
|
|
|
1280
|
|
|
/** |
1281
|
|
|
* Get record variables. |
1282
|
|
|
* |
1283
|
1 |
|
* @param bool|string $fieldType |
1284
|
|
|
* |
1285
|
1 |
|
* @return array |
1286
|
1 |
|
*/ |
1287
|
1 |
|
public function getRecordVariable($fieldType = false) |
1288
|
1 |
|
{ |
1289
|
1 |
|
$cacheKey = "{$this->moduleName}|$fieldType"; |
1290
|
|
|
if (isset(static::$recordVariable[$cacheKey])) { |
1291
|
|
|
return static::$recordVariable[$cacheKey]; |
1292
|
1 |
|
} |
1293
|
|
|
$variables = []; |
1294
|
|
|
if (!$fieldType) { |
1295
|
|
|
foreach (static::$variableEntity as $key => $name) { |
1296
|
|
|
$variables[Language::translate('LBL_ENTITY_VARIABLES', 'Other.TextParser')][] = [ |
1297
|
|
|
'var_value' => "$(record : $key)$", |
1298
|
|
|
'var_label' => "$(translate : Other.TextParser|$name)$", |
1299
|
|
|
'label' => Language::translate($name, 'Other.TextParser'), |
1300
|
|
|
]; |
1301
|
|
|
} |
1302
|
|
|
} |
1303
|
|
|
$moduleModel = \Vtiger_Module_Model::getInstance($this->moduleName); |
1304
|
|
|
foreach ($moduleModel->getBlocks() as $blockModel) { |
1305
|
|
|
foreach ($blockModel->getFields() as $fieldModel) { |
1306
|
|
|
if ($fieldModel->isViewable() && !($fieldType && $fieldModel->getFieldDataType() !== $fieldType)) { |
1307
|
2 |
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
1308
|
|
|
$variables[Language::translate($blockModel->get('label'), $this->moduleName)][] = [ |
1309
|
2 |
|
'var_value' => "$(record : {$fieldModel->getName()})$", |
1310
|
1 |
|
'var_label' => "$(translate : {$this->moduleName}|{$label})$", |
1311
|
|
|
'label' => Language::translate($fieldModel->getFieldLabel(), $this->moduleName), |
|
|
|
|
1312
|
2 |
|
]; |
1313
|
2 |
|
} |
1314
|
2 |
|
} |
1315
|
2 |
|
} |
1316
|
2 |
|
static::$recordVariable[$cacheKey] = $variables; |
1317
|
2 |
|
return $variables; |
1318
|
2 |
|
} |
1319
|
2 |
|
|
1320
|
2 |
|
/** |
1321
|
2 |
|
* Get source variables. |
1322
|
2 |
|
* |
1323
|
2 |
|
* @return array |
1324
|
2 |
|
*/ |
1325
|
2 |
|
public function getSourceVariable() |
1326
|
|
|
{ |
1327
|
|
|
if (empty(self::$sourceModules[$this->moduleName])) { |
1328
|
|
|
return false; |
|
|
|
|
1329
|
|
|
} |
1330
|
2 |
|
$variables = []; |
1331
|
1 |
|
foreach (static::$variableEntity as $key => $name) { |
1332
|
|
|
$variables['LBL_ENTITY_VARIABLES'][] = [ |
1333
|
2 |
|
'var_value' => "$(sourceRecord : $key)$", |
1334
|
|
|
'var_label' => "$(translate : Other.TextParser|$name)$", |
1335
|
2 |
|
'label' => Language::translate($name, 'Other.TextParser'), |
1336
|
2 |
|
]; |
1337
|
2 |
|
} |
1338
|
2 |
|
foreach (self::$sourceModules[$this->moduleName] as $moduleName) { |
1339
|
2 |
|
$moduleModel = \Vtiger_Module_Model::getInstance($moduleName); |
1340
|
2 |
|
foreach ($moduleModel->getBlocks() as $blockModel) { |
1341
|
2 |
|
foreach ($blockModel->getFields() as $fieldModel) { |
1342
|
|
|
if ($fieldModel->isViewable()) { |
1343
|
2 |
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
1344
|
2 |
|
$variables[$moduleName][$blockModel->get('label')][] = [ |
1345
|
|
|
'var_value' => "$(sourceRecord : {$fieldModel->getName()})$", |
1346
|
2 |
|
'var_label' => "$(translate : $moduleName|{$label})$", |
1347
|
1 |
|
'label' => Language::translate($fieldModel->getFieldLabel(), $moduleName), |
|
|
|
|
1348
|
1 |
|
]; |
1349
|
1 |
|
} |
1350
|
1 |
|
} |
1351
|
|
|
} |
1352
|
|
|
} |
1353
|
1 |
|
return $variables; |
1354
|
|
|
} |
1355
|
|
|
|
1356
|
2 |
|
/** |
1357
|
|
|
* Get related variables. |
1358
|
2 |
|
* |
1359
|
1 |
|
* @param bool|string $fieldType |
1360
|
|
|
* @param bool $skipEmpty |
1361
|
2 |
|
* |
1362
|
2 |
|
* @return array |
1363
|
2 |
|
*/ |
1364
|
2 |
|
public function getRelatedVariable($fieldType = false, $skipEmpty = false) |
1365
|
|
|
{ |
1366
|
|
|
$cacheKey = "{$this->moduleName}|$fieldType|{$skipEmpty}"; |
1367
|
|
|
if (isset(static::$relatedVariable[$cacheKey])) { |
1368
|
|
|
return static::$relatedVariable[$cacheKey]; |
1369
|
|
|
} |
1370
|
|
|
$moduleModel = \Vtiger_Module_Model::getInstance($this->moduleName); |
1371
|
|
|
$variables = []; |
1372
|
|
|
$entityVariables = Language::translate('LBL_ENTITY_VARIABLES', 'Other.TextParser'); |
1373
|
|
|
foreach ($moduleModel->getFieldsByType(array_merge(\Vtiger_Field_Model::$referenceTypes, ['userCreator', 'owner', 'sharedOwner'])) as $parentFieldName => $field) { |
1374
|
|
|
if ('owner' === $field->getFieldDataType() || 'sharedOwner' === $field->getFieldDataType()) { |
1375
|
|
|
$relatedModules = ['Users']; |
1376
|
27 |
|
} else { |
1377
|
|
|
$relatedModules = $field->getReferenceList(); |
1378
|
27 |
|
} |
1379
|
3 |
|
$parentFieldNameLabel = Language::translate($field->getFieldLabel(), $this->moduleName); |
1380
|
|
|
if (!$fieldType) { |
1381
|
27 |
|
foreach (static::$variableEntity as $key => $name) { |
1382
|
27 |
|
$variables[$parentFieldName]["$parentFieldNameLabel - $entityVariables"][] = [ |
1383
|
1 |
|
'var_value' => "$(relatedRecord : $parentFieldName|$key)$", |
1384
|
1 |
|
'var_label' => "$(translate : Other.TextParser|$key)$", |
1385
|
1 |
|
'label' => $parentFieldNameLabel . ': ' . Language::translate($name, 'Other.TextParser'), |
1386
|
|
|
]; |
1387
|
|
|
} |
1388
|
|
|
} |
1389
|
|
|
$relRecord = false; |
1390
|
|
|
if ($skipEmpty && $this->recordModel && !(($relId = $this->recordModel->get($field->getName())) |
1391
|
|
|
&& ( |
1392
|
|
|
\in_array($field->getFieldDataType(), ['userCreator', 'owner', 'sharedOwner']) |
1393
|
|
|
|| ((Record::isExists($relId)) && ($relRecord = \Vtiger_Record_Model::getInstanceById($relId))->isViewable() && ($relatedModules = [Record::getType($relId)])) |
1394
|
27 |
|
) |
1395
|
|
|
)) { |
1396
|
|
|
continue; |
1397
|
|
|
} |
1398
|
|
|
|
1399
|
|
|
foreach ($relatedModules as $relatedModule) { |
1400
|
|
|
$relatedModuleLang = Language::translate($relatedModule, $relatedModule); |
1401
|
|
|
foreach (\Vtiger_Module_Model::getInstance($relatedModule)->getBlocks() as $blockModel) { |
1402
|
|
|
foreach ($blockModel->getFields() as $fieldName => $fieldModel) { |
1403
|
|
|
if ( |
1404
|
5783 |
|
$fieldModel->isViewable() |
1405
|
|
|
&& !($fieldType && $fieldModel->getFieldDataType() !== $fieldType) |
1406
|
5783 |
|
&& (!$relRecord || ($relRecord && !$relRecord->isEmpty($fieldModel->getName()))) |
1407
|
5783 |
|
) { |
1408
|
|
|
$labelGroup = "$parentFieldNameLabel: ($relatedModuleLang) " . Language::translate($blockModel->get('label'), $relatedModule); |
1409
|
|
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
1410
|
|
|
$variables[$parentFieldName][$labelGroup][] = [ |
1411
|
|
|
'var_value' => "$(relatedRecord : $parentFieldName|$fieldName|$relatedModule)$", |
1412
|
|
|
'var_label' => "$(translate : $relatedModule|{$label})$", |
1413
|
|
|
'label' => "$parentFieldNameLabel: ($relatedModuleLang) " . Language::translate($fieldModel->getFieldLabel(), $relatedModule), |
|
|
|
|
1414
|
|
|
]; |
1415
|
|
|
} |
1416
|
|
|
} |
1417
|
|
|
} |
1418
|
|
|
} |
1419
|
|
|
} |
1420
|
|
|
static::$relatedVariable[$cacheKey] = $variables; |
1421
|
|
|
return $variables; |
1422
|
|
|
} |
1423
|
|
|
|
1424
|
|
|
/** |
1425
|
|
|
* Get related variables. |
1426
|
|
|
* |
1427
|
|
|
* @param bool|string $fieldType |
1428
|
|
|
* |
1429
|
|
|
* @return array |
1430
|
|
|
*/ |
1431
|
|
|
public function getRelatedLevelVariable($fieldType = false) |
1432
|
|
|
{ |
1433
|
|
|
$cacheKey = "{$this->moduleName}|$fieldType"; |
1434
|
|
|
if (isset(static::$relatedVariableLevel[$cacheKey])) { |
1435
|
|
|
return static::$relatedVariableLevel[$cacheKey]; |
1436
|
|
|
} |
1437
|
|
|
$moduleModel = \Vtiger_Module_Model::getInstance($this->moduleName); |
1438
|
|
|
$variables = []; |
1439
|
|
|
foreach ($moduleModel->getFieldsByType(array_merge(\Vtiger_Field_Model::$referenceTypes, ['userCreator', 'owner'])) as $parentFieldName => $fieldModel) { |
1440
|
|
|
if ('owner' === $fieldModel->getFieldDataType()) { |
1441
|
|
|
$relatedModules = ['Users']; |
1442
|
|
|
} else { |
1443
|
|
|
$relatedModules = $fieldModel->getReferenceList(); |
1444
|
|
|
} |
1445
|
|
|
$parentFieldNameLabel = Language::translate($fieldModel->getFieldLabel(), $this->moduleName); |
1446
|
|
|
foreach ($relatedModules as $relatedModule) { |
1447
|
|
|
$relatedModuleLang = Language::translate($relatedModule, $relatedModule); |
1448
|
|
|
foreach (\Vtiger_Module_Model::getInstance($relatedModule)->getFieldsByType(array_merge(\Vtiger_Field_Model::$referenceTypes, ['userCreator', 'owner', 'sharedOwner'])) as $parentFieldNameNextLevel => $fieldModelNextLevel) { |
1449
|
|
|
if ('owner' === $fieldModelNextLevel->getFieldDataType() || 'sharedOwner' === $fieldModelNextLevel->getFieldDataType()) { |
1450
|
|
|
$relatedModulesNextLevel = ['Users']; |
1451
|
|
|
} else { |
1452
|
|
|
$relatedModulesNextLevel = $fieldModelNextLevel->getReferenceList(); |
1453
|
|
|
} |
1454
|
|
|
$parentFieldNameLabelNextLevel = Language::translate($fieldModelNextLevel->getFieldLabel(), $relatedModule); |
1455
|
|
|
foreach ($relatedModulesNextLevel as $relatedModuleNextLevel) { |
1456
|
|
|
$relatedModuleLangNextLevel = Language::translate($relatedModuleNextLevel, $relatedModuleNextLevel); |
1457
|
|
|
foreach (\Vtiger_Module_Model::getInstance($relatedModuleNextLevel)->getBlocks() as $blockModel) { |
1458
|
|
|
foreach ($blockModel->getFields() as $fieldName => $fieldModel) { |
|
|
|
|
1459
|
|
|
if ($fieldModel->isViewable() && !($fieldType && $fieldModel->getFieldDataType() !== $fieldType)) { |
1460
|
|
|
$labelGroup = "{$parentFieldNameLabel}($relatedModuleLang) -> {$parentFieldNameLabelNextLevel}($relatedModuleLangNextLevel) " . Language::translate($blockModel->get('label'), $relatedModuleNextLevel); |
1461
|
|
|
$label = \App\Purifier::encodeHtml($fieldModel->getFieldLabel()); |
|
|
|
|
1462
|
|
|
$variables[$labelGroup][] = [ |
1463
|
|
|
'var_value' => "$(relatedRecordLevel : $parentFieldName|$relatedModule|$parentFieldNameNextLevel|$fieldName|$relatedModuleNextLevel)$", |
1464
|
|
|
'var_label' => "$(translate : $relatedModuleNextLevel|{$label})$", |
1465
|
|
|
'label' => "{$parentFieldNameLabel}($relatedModuleLang) -> {$parentFieldNameLabelNextLevel}($relatedModuleLangNextLevel) " . Language::translate($fieldModel->getFieldLabel(), $relatedModuleNextLevel), |
|
|
|
|
1466
|
|
|
]; |
1467
|
|
|
} |
1468
|
|
|
} |
1469
|
|
|
} |
1470
|
|
|
} |
1471
|
|
|
} |
1472
|
|
|
} |
1473
|
|
|
} |
1474
|
|
|
static::$relatedVariableLevel[$cacheKey] = $variables; |
1475
|
|
|
return $variables; |
1476
|
|
|
} |
1477
|
|
|
|
1478
|
|
|
/** |
1479
|
|
|
* Get general variables. |
1480
|
|
|
* |
1481
|
|
|
* @return array |
1482
|
|
|
*/ |
1483
|
|
|
public function getGeneralVariable() |
1484
|
|
|
{ |
1485
|
|
|
$variables = [ |
1486
|
|
|
'LBL_ENTITY_VARIABLES' => array_map(fn ($value) => Language::translate($value, 'Other.TextParser'), array_flip(static::$variableGeneral)), |
1487
|
|
|
]; |
1488
|
|
|
$variables['LBL_CUSTOM_VARIABLES'] = array_merge($this->getBaseGeneralVariable(), $this->getModuleGeneralVariable()); |
1489
|
|
|
return $variables; |
1490
|
|
|
} |
1491
|
|
|
|
1492
|
|
|
/** |
1493
|
|
|
* Get general variables base function. |
1494
|
|
|
* |
1495
|
|
|
* @return array |
1496
|
|
|
*/ |
1497
|
|
|
protected function getBaseGeneralVariable() |
1498
|
|
|
{ |
1499
|
|
|
$variables = []; |
1500
|
|
|
foreach ((new \DirectoryIterator(__DIR__ . \DIRECTORY_SEPARATOR . 'TextParser')) as $fileInfo) { |
1501
|
|
|
$fileName = $fileInfo->getBasename('.php'); |
1502
|
|
|
if ('dir' !== $fileInfo->getType() && 'Base' !== $fileName && 'php' === $fileInfo->getExtension()) { |
1503
|
|
|
$className = '\App\TextParser\\' . $fileName; |
1504
|
|
|
if (!class_exists($className)) { |
1505
|
|
|
Log::warning('Not found custom class'); |
1506
|
|
|
continue; |
1507
|
|
|
} |
1508
|
|
|
$instance = new $className($this); |
1509
|
|
|
if (isset($this->type) && $this->type !== $instance->type) { |
1510
|
|
|
continue; |
1511
|
|
|
} |
1512
|
|
|
$key = $instance->default ?? "$(custom : $fileName)$"; |
1513
|
|
|
$variables[$key] = Language::translate($instance->name, 'Other.TextParser'); |
1514
|
|
|
} |
1515
|
|
|
} |
1516
|
|
|
return $variables; |
1517
|
|
|
} |
1518
|
|
|
|
1519
|
|
|
/** |
1520
|
|
|
* Get general variables module function. |
1521
|
|
|
* |
1522
|
|
|
* @return array |
1523
|
|
|
*/ |
1524
|
|
|
protected function getModuleGeneralVariable() |
1525
|
|
|
{ |
1526
|
|
|
$variables = []; |
1527
|
|
|
if ($this->moduleName && is_dir(("modules/{$this->moduleName}/textparsers/"))) { |
1528
|
|
|
foreach ((new \DirectoryIterator("modules/{$this->moduleName}/textparsers/")) as $fileInfo) { |
1529
|
|
|
$fileName = $fileInfo->getBasename('.php'); |
1530
|
|
|
if ('dir' !== $fileInfo->getType() && 'php' === $fileInfo->getExtension()) { |
1531
|
|
|
$handlerClass = \Vtiger_Loader::getComponentClassName('TextParser', $fileName, $this->moduleName); |
1532
|
|
|
$instanceClass = new $handlerClass($this); |
1533
|
|
|
if (isset($this->type) && $this->type !== $instanceClass->type) { |
1534
|
|
|
continue; |
1535
|
|
|
} |
1536
|
|
|
$variables["$(custom : $fileName|{$this->moduleName})$"] = Language::translate($instanceClass->name, $this->moduleName); |
1537
|
|
|
} |
1538
|
|
|
} |
1539
|
|
|
} |
1540
|
|
|
return $variables; |
1541
|
|
|
} |
1542
|
|
|
|
1543
|
|
|
/** |
1544
|
|
|
* Get related modules list. |
1545
|
|
|
* |
1546
|
|
|
* @return array |
1547
|
|
|
*/ |
1548
|
|
|
public function getRelatedListVariable() |
1549
|
|
|
{ |
1550
|
|
|
$moduleModel = \Vtiger_Module_Model::getInstance($this->moduleName); |
1551
|
|
|
$variables = []; |
1552
|
|
|
foreach ($moduleModel->getRelations() as $relation) { |
1553
|
|
|
$var = $relation->get('relatedModuleName'); |
1554
|
|
|
if ($relation->get('field_name')) { |
1555
|
|
|
$var = $relation->get('relation_id'); |
1556
|
|
|
} |
1557
|
|
|
$variables[] = [ |
1558
|
|
|
'key' => "$(relatedRecordsList : $var|__FIELDS_NAME__|__CONDITIONS__|__VIEW_ID_OR_NAME__|__LIMIT__|__MAX_LENGTH__)$", |
1559
|
|
|
'label' => Language::translate($relation->get('label'), $relation->get('relatedModuleName')), |
1560
|
|
|
]; |
1561
|
|
|
} |
1562
|
|
|
return $variables; |
1563
|
|
|
} |
1564
|
|
|
|
1565
|
|
|
/** |
1566
|
|
|
* Get base modules list. |
1567
|
|
|
* |
1568
|
|
|
* @return array |
1569
|
|
|
*/ |
1570
|
|
|
public function getBaseListVariable() |
1571
|
|
|
{ |
1572
|
|
|
$variables = []; |
1573
|
|
|
foreach (\vtlib\Functions::getAllModules() as $module) { |
1574
|
|
|
$variables[] = [ |
1575
|
|
|
'key' => "$(recordsList : {$module['name']})$", |
1576
|
|
|
'label' => Language::translate($module['name'], $module['name']), |
1577
|
|
|
]; |
1578
|
|
|
} |
1579
|
|
|
return $variables; |
1580
|
|
|
} |
1581
|
|
|
|
1582
|
|
|
/** |
1583
|
|
|
* Gets user variables. |
1584
|
|
|
* |
1585
|
|
|
* @param string $text |
1586
|
|
|
* @param bool $useRegex |
1587
|
|
|
* |
1588
|
|
|
* @return array |
1589
|
|
|
*/ |
1590
|
|
|
public function getUserVariables(string $text, bool $useRegex = true) |
1591
|
|
|
{ |
1592
|
|
|
$data = []; |
1593
|
|
|
if ($useRegex) { |
1594
|
|
|
preg_match_all('/\$\(userVariable : ([,"\+\%\.\=\-\[\]\&\w\s\|\)\(\:]+)\)\$/u', str_replace(['%20%3A%20', '%20:%20'], ' : ', $text), $matches); |
1595
|
|
|
$matches = $matches[1] ?? []; |
1596
|
|
|
} else { |
1597
|
|
|
$matches = [$text]; |
1598
|
|
|
} |
1599
|
|
|
foreach ($matches as $param) { |
1600
|
|
|
$part = self::parseFieldParam($param); |
1601
|
|
|
if (!empty($part['name']) && !(isset($data[$part['name']]))) { |
1602
|
|
|
$data[$part['name']] = $part; |
1603
|
|
|
} |
1604
|
|
|
} |
1605
|
|
|
return $data; |
1606
|
|
|
} |
1607
|
|
|
|
1608
|
|
|
/** |
1609
|
|
|
* Parsing user variable. |
1610
|
|
|
* |
1611
|
|
|
* @param string $params |
1612
|
|
|
* |
1613
|
|
|
* @return string |
1614
|
|
|
*/ |
1615
|
|
|
protected function userVariable($params) |
1616
|
|
|
{ |
1617
|
|
|
$instance = null; |
1618
|
|
|
$className = '\\App\\TextParser\\' . ucfirst(__FUNCTION__); |
1619
|
|
|
if (!class_exists($className)) { |
1620
|
|
|
Log::error("Not found custom class: $className"); |
1621
|
|
|
} else { |
1622
|
|
|
$instance = new $className($this, $params); |
1623
|
|
|
} |
1624
|
|
|
return $instance && $instance->isActive() ? $instance->process() : ''; |
1625
|
|
|
} |
1626
|
|
|
|
1627
|
|
|
/** |
1628
|
|
|
* Parsing inventory. |
1629
|
|
|
* |
1630
|
|
|
* @param string $params |
1631
|
|
|
* |
1632
|
|
|
* @return string |
1633
|
|
|
*/ |
1634
|
|
|
protected function inventory($params) |
1635
|
|
|
{ |
1636
|
|
|
if (!$this->recordModel->getModule()->isInventory()) { |
1637
|
|
|
return ''; |
1638
|
|
|
} |
1639
|
|
|
$config = $this->parseParams($params); |
1640
|
|
|
if ('table' === $config['type']) { |
1641
|
|
|
return $this->getInventoryTable($config); |
1642
|
|
|
} |
1643
|
|
|
return ''; |
1644
|
|
|
} |
1645
|
|
|
|
1646
|
|
|
/** |
1647
|
|
|
* Get an instance of barcode text parser. |
1648
|
|
|
* |
1649
|
|
|
* @param string $params |
1650
|
|
|
* |
1651
|
|
|
* @return string |
1652
|
|
|
*/ |
1653
|
|
|
protected function barcode($params): string |
1654
|
|
|
{ |
1655
|
|
|
$params = $this->parseParams($params); |
1656
|
|
|
if (isset($params['value'])) { |
1657
|
|
|
$valueForParse = $params['value']; |
1658
|
|
|
} |
1659
|
|
|
if (isset($params['fieldName'])) { |
1660
|
|
|
$valueForParse = $this->recordModel->get($params['fieldName']); |
1661
|
|
|
} |
1662
|
|
|
if ($valueForParse) { |
|
|
|
|
1663
|
|
|
$className = '\Milon\Barcode\\' . $params['class']; |
1664
|
|
|
if (!class_exists($className)) { |
1665
|
|
|
throw new \App\Exceptions\AppException('ERR_CLASS_NOT_FOUND||' . $className); |
1666
|
|
|
} |
1667
|
|
|
$qrCodeGenerator = new $className(); |
1668
|
|
|
$qrCodeGenerator->setStorPath(__DIR__ . Config::main('tmp_dir')); |
1669
|
|
|
$barcodeHeight = $this->params['height'] ?? 2; |
1670
|
|
|
$barcodeWidth = $this->params['width'] ?? 30; |
1671
|
|
|
$barcodeType = $this->params['type'] ?? 'EAN13'; |
1672
|
|
|
$showText = $this->params['showText'] ?? true; |
1673
|
|
|
$png = $qrCodeGenerator->getBarcodePNG($valueForParse, $barcodeType, $barcodeHeight, $barcodeWidth, [0, 0, 0], $showText); |
1674
|
|
|
return '<img src="data:image/png;base64,' . $png . '"/>'; |
1675
|
|
|
} |
1676
|
|
|
return ''; |
1677
|
|
|
} |
1678
|
|
|
|
1679
|
|
|
/** |
1680
|
|
|
* Get inventory param. |
1681
|
|
|
* |
1682
|
|
|
* @param string $params |
1683
|
|
|
* |
1684
|
|
|
* @return array |
1685
|
|
|
*/ |
1686
|
|
|
protected function parseParams(string $params): array |
1687
|
|
|
{ |
1688
|
|
|
preg_match('/type=(\w+)/', $params, $matches); |
1689
|
|
|
$config = [ |
1690
|
|
|
'type' => ($matches[1] ?? false), |
1691
|
|
|
]; |
1692
|
|
|
$params = ltrim($params, $matches[0] . ' '); |
1693
|
|
|
foreach (explode(' , ', $params) as $value) { |
1694
|
|
|
parse_str($value, $row); |
1695
|
|
|
$config += $row; |
1696
|
|
|
} |
1697
|
|
|
if (isset($config['columns'])) { |
1698
|
|
|
$config['columns'] = explode(',', $config['columns']); |
1699
|
|
|
} |
1700
|
|
|
return $config; |
1701
|
|
|
} |
1702
|
|
|
|
1703
|
|
|
/** |
1704
|
|
|
* Parsing inventory table. |
1705
|
|
|
* |
1706
|
|
|
* @param array $config |
1707
|
|
|
* |
1708
|
|
|
* @return string |
1709
|
|
|
*/ |
1710
|
|
|
public function getInventoryTable(array $config): string |
1711
|
|
|
{ |
1712
|
|
|
$rawText = empty($config['href']) || 'yes' !== $config['href']; |
1713
|
|
|
$inventory = \Vtiger_Inventory_Model::getInstance($this->moduleName); |
1714
|
|
|
$fields = $inventory->getFieldsByBlocks(); |
1715
|
|
|
$inventoryRows = $this->recordModel->getInventoryData(); |
1716
|
|
|
$baseCurrency = \Vtiger_Util_Helper::getBaseCurrency(); |
1717
|
|
|
$firstRow = current($inventoryRows); |
1718
|
|
|
if ($inventory->isField('currency')) { |
1719
|
|
|
if (!empty($firstRow) && null !== $firstRow['currency']) { |
1720
|
|
|
$currency = $firstRow['currency']; |
1721
|
|
|
} else { |
1722
|
|
|
$currency = $baseCurrency['id']; |
1723
|
|
|
} |
1724
|
|
|
$currencyData = \App\Fields\Currency::getById($currency); |
1725
|
|
|
$currencySymbol = $currencyData['currency_symbol']; |
1726
|
|
|
} |
1727
|
|
|
$html = ''; |
1728
|
|
|
if (!empty($fields[1])) { |
1729
|
|
|
$fieldsTextAlignRight = ['Unit', 'TotalPrice', 'Tax', 'MarginP', 'Margin', 'Purchase', 'Discount', 'NetPrice', 'GrossPrice', 'UnitPrice', 'Quantity', 'TaxPercent']; |
1730
|
|
|
$fieldsWithCurrency = ['TotalPrice', 'Purchase', 'NetPrice', 'GrossPrice', 'UnitPrice', 'Discount', 'Margin', 'Tax']; |
1731
|
|
|
$html .= '<table class="inventory-table" style="border-collapse:collapse;width:100%"><thead><tr>'; |
1732
|
|
|
$columns = []; |
1733
|
|
|
$customFieldClassSeq = 0; |
1734
|
|
|
$labels = isset($config['labels']) ? explode(',', $config['labels']) : []; |
1735
|
|
|
$width = isset($config['width']) ? preg_replace('/[^[:alnum:]]/', '', explode(',', $config['width'])) : []; |
1736
|
|
|
foreach ($config['columns'] as $key => $name) { |
1737
|
|
|
if (false !== strpos($name, '||')) { |
1738
|
|
|
[$title,$value] = explode('||', $name, 2); |
1739
|
|
|
if ('(' === $title[0] && ')' === substr($title, -1)) { |
1740
|
|
|
$title = $this->parseVariable("\${$title}\$"); |
1741
|
|
|
} |
1742
|
|
|
++$customFieldClassSeq; |
1743
|
|
|
$html .= '<th class="col-type-customField' . $customFieldClassSeq . '" style="border:1px solid #ddd">' . $title . '</th>'; |
1744
|
|
|
$columns[$title] = $value; |
1745
|
|
|
continue; |
1746
|
|
|
} |
1747
|
|
|
if ('seq' === $name) { |
1748
|
|
|
$html .= '<th class="col-type-ItemNumber" style="border:1px solid #ddd">' . Language::translate('LBL_ITEM_NUMBER', $this->moduleName) . '</th>'; |
1749
|
|
|
$columns[$name] = false; |
1750
|
|
|
continue; |
1751
|
|
|
} |
1752
|
|
|
if (empty($fields[1][$name]) && empty($fields[2][$name])) { |
1753
|
|
|
continue; |
1754
|
|
|
} |
1755
|
|
|
$field = $fields[1][$name] ?? $fields[2][$name]; |
1756
|
|
|
if (!$field->isVisible()) { |
1757
|
|
|
continue; |
1758
|
|
|
} |
1759
|
|
|
$html .= '<th class="col-type-' . $field->getType() . '" style="border:1px solid #ddd ' . (empty($width[$key]) ? '' : ";width: {$width[$key]}") . '">' . (empty($labels[$key]) ? Language::translate($field->get('label'), $this->moduleName) : Purifier::encodeHtml($labels[$key])) . '</th>'; |
1760
|
|
|
$columns[$field->getColumnName()] = $field; |
1761
|
|
|
} |
1762
|
|
|
$html .= '</tr></thead><tbody>'; |
1763
|
|
|
$counter = 0; |
1764
|
|
|
foreach ($inventoryRows as $inventoryRow) { |
1765
|
|
|
++$counter; |
1766
|
|
|
$html .= '<tr class="row-' . $counter . '">'; |
1767
|
|
|
$customFieldClassSeq = 0; |
1768
|
|
|
foreach ($columns as $name => $field) { |
1769
|
|
|
if ('seq' === $name) { |
1770
|
|
|
$html .= '<td class="col-type-ItemNumber" style="border:1px solid #ddd;font-weight:bold;">' . $counter . '</td>'; |
1771
|
|
|
} elseif (!\is_object($field)) { |
1772
|
|
|
if ('(' === $field[0] && ')' === substr($field, -1)) { |
1773
|
|
|
$field = $this->parseVariable("\${$field}\$", $inventoryRow['name'] ?? 0); |
1774
|
|
|
} |
1775
|
|
|
++$customFieldClassSeq; |
1776
|
|
|
$html .= '<td class="col-type-customField' . $customFieldClassSeq . '" style="border:1px solid #ddd;font-weight:bold;">' . $field . '</td>'; |
1777
|
|
|
} elseif ('ItemNumber' === $field->getType()) { |
1778
|
|
|
$html .= '<td class="col-type-ItemNumber" style="border:1px solid #ddd;font-weight:bold;">' . $counter . '</td>'; |
1779
|
|
|
} elseif ('ean' === $name) { |
1780
|
|
|
$itemValue = $inventoryRow[$name]; |
1781
|
|
|
$html .= '<td class="col-type-barcode" style="border:1px solid #ddd;padding:0px 4px;' . (\in_array($field->getType(), $fieldsTextAlignRight) ? 'text-align:right;' : '') . '"><div data-barcode="EAN13" data-code="' . $itemValue . '" data-size="1" data-height="16"></div></td>'; |
1782
|
|
|
} else { |
1783
|
|
|
$itemValue = $inventoryRow[$name]; |
1784
|
|
|
$html .= '<td class="col-type-' . $field->getType() . '" style="border:1px solid #ddd;padding:0px 4px;' . (\in_array($field->getType(), $fieldsTextAlignRight) ? 'text-align:right;' : '') . '">'; |
1785
|
|
|
if ('Name' === $field->getType()) { |
1786
|
|
|
$html .= '<strong>' . $field->getDisplayValue($itemValue, $inventoryRow, $rawText) . '</strong>'; |
1787
|
|
|
foreach ($inventory->getFieldsByType('Comment') as $commentField) { |
1788
|
|
|
if ($commentField->isVisible() && ($value = $inventoryRow[$commentField->getColumnName()])) { |
1789
|
|
|
$comment = $commentField->getDisplayValue($value, $inventoryRow, $rawText); |
1790
|
|
|
if ($comment) { |
1791
|
|
|
$html .= '<br>' . $comment; |
1792
|
|
|
} |
1793
|
|
|
} |
1794
|
|
|
} |
1795
|
|
|
} elseif (\in_array($field->getType(), $fieldsWithCurrency, true)) { |
1796
|
|
|
$html .= \CurrencyField::appendCurrencySymbol($field->getDisplayValue($itemValue, $inventoryRow, $rawText), $currencySymbol); |
|
|
|
|
1797
|
|
|
} else { |
1798
|
|
|
$html .= $field->getDisplayValue($itemValue, $inventoryRow, $rawText); |
1799
|
|
|
} |
1800
|
|
|
$html .= '</td>'; |
1801
|
|
|
} |
1802
|
|
|
} |
1803
|
|
|
$html .= '</tr>'; |
1804
|
|
|
} |
1805
|
|
|
|
1806
|
|
|
$html .= '</tbody><tfoot><tr>'; |
1807
|
|
|
foreach ($columns as $name => $field) { |
1808
|
|
|
$tb = $style = ''; |
1809
|
|
|
if (\is_object($field) && $field->isSummary()) { |
1810
|
|
|
$style = 'border:1px solid #ddd;'; |
1811
|
|
|
$sum = 0; |
1812
|
|
|
foreach ($inventoryRows as $inventoryRow) { |
1813
|
|
|
$sum += $inventoryRow[$name]; |
1814
|
|
|
} |
1815
|
|
|
$tb = \CurrencyField::appendCurrencySymbol(\CurrencyField::convertToUserFormat($sum, null, true), $currencySymbol); |
|
|
|
|
1816
|
|
|
} |
1817
|
|
|
$html .= '<th class="col-type-' . (\is_object($field) ? $field->getType() : $name) . '" style="padding:0px 4px;text-align:right;' . $style . '">' . $tb . '</th>'; |
1818
|
|
|
} |
1819
|
|
|
$html .= '</tr></tfoot></table>'; |
1820
|
|
|
} |
1821
|
|
|
return $html; |
1822
|
|
|
} |
1823
|
|
|
|
1824
|
|
|
/** |
1825
|
|
|
* Parse variable. |
1826
|
|
|
* |
1827
|
|
|
* @param string $variable |
1828
|
|
|
* @param int $id |
1829
|
|
|
* |
1830
|
|
|
* @return string |
1831
|
|
|
*/ |
1832
|
|
|
protected function parseVariable(string $variable, int $id = 0): string |
1833
|
|
|
{ |
1834
|
|
|
if ($id && Record::isExists($id)) { |
1835
|
|
|
$recordModel = \Vtiger_Record_Model::getInstanceById($id); |
1836
|
|
|
if (!$recordModel->isViewable()) { |
1837
|
|
|
return ''; |
1838
|
|
|
} |
1839
|
|
|
$instance = static::getInstanceByModel($recordModel); |
1840
|
|
|
} else { |
1841
|
|
|
$instance = static::getInstance(); |
1842
|
|
|
} |
1843
|
|
|
foreach (['withoutTranslations', 'language', 'emailoptout'] as $key) { |
1844
|
|
|
if (isset($this->{$key})) { |
1845
|
|
|
$instance->{$key} = $this->{$key}; |
1846
|
|
|
} |
1847
|
|
|
} |
1848
|
|
|
$instance->setContent($variable)->parse(); |
1849
|
|
|
return $instance->getContent(); |
1850
|
|
|
} |
1851
|
|
|
|
1852
|
|
|
/** |
1853
|
|
|
* Parse custom params. |
1854
|
|
|
* |
1855
|
|
|
* @param string $param |
1856
|
|
|
* |
1857
|
|
|
* @return array |
1858
|
|
|
*/ |
1859
|
|
|
public static function parseFieldParam(string $param): array |
1860
|
|
|
{ |
1861
|
|
|
$part = []; |
1862
|
|
|
if ($param) { |
1863
|
|
|
foreach (explode('|', $param) as $type) { |
1864
|
|
|
[$name, $value] = array_pad(explode('=', $type, 2), 2, ''); |
1865
|
|
|
$part[$name] = $value; |
1866
|
|
|
} |
1867
|
|
|
} |
1868
|
|
|
return $part; |
1869
|
|
|
} |
1870
|
|
|
} |
1871
|
|
|
|
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths