PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.3
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.3
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Classes / FieldClass.php

FieldClass.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.3, at back/Classes/FieldClass.php

1,015 lines 38.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Classes;
4
5 use AcyMailing\Core\AcymClass;
6 use AcyMailing\Helpers\SecurityHelper;
7
8 class FieldClass extends AcymClass
9 {
10 const LANGUAGE_FIELD_ID_KEY = 'language_field_id';
11
12 public function __construct()
13 {
14 parent::__construct();
15
16 $this->table = 'field';
17 $this->pkey = 'id';
18 }
19
20 public function getMatchingElements(array $settings = []): array
21 {
22 $query = 'SELECT * FROM #__acym_field';
23 $queryCount = 'SELECT COUNT(*) FROM #__acym_field';
24
25 $where = [];
26 if (!empty($settings['types'])) {
27 foreach ($settings['types'] as $i => $oneType) {
28 $settings['types'][$i] = acym_escapeDB($oneType);
29 }
30 $where[] = ' type IN ('.implode(',', $settings['types']).')';
31 }
32
33 if (!empty($where)) {
34 $query .= ' WHERE '.implode(' AND ', $where);
35 $queryCount .= ' WHERE '.implode(' AND ', $where);
36 }
37
38 $query .= ' ORDER BY `ordering` ASC';
39
40 return [
41 'elements' => acym_loadObjectList($query, 'id'),
42 'total' => acym_loadResult($queryCount),
43 ];
44 }
45
46 public function getFieldsByID(array $ids): array
47 {
48 if (empty($ids)) {
49 return [];
50 }
51
52 acym_arrayToInteger($ids);
53
54 return acym_loadObjectList('SELECT * FROM #__acym_field WHERE `id` IN('.implode(',', $ids).') ORDER BY `ordering` ASC');
55 }
56
57 public function getFieldsByNameKey(array $nameKeys): array
58 {
59 if (empty($nameKeys)) {
60 return [];
61 }
62
63 $nameKeys = array_map('acym_escapeDB', $nameKeys);
64
65 return acym_loadObjectList('SELECT * FROM #__acym_field WHERE `namekey` IN('.implode(',', $nameKeys).') ORDER BY `ordering` ASC', 'id');
66 }
67
68 public function getFieldsByType(array $types): array
69 {
70 if (empty($types)) {
71 return [];
72 }
73
74 $types = array_map('acym_escapeDB', $types);
75 $query = 'SELECT * FROM #__acym_field WHERE `type` IN('.implode(',', $types).') ORDER BY `ordering` ASC';
76
77 return acym_loadObjectList($query);
78 }
79
80 public function getOrdering(): int
81 {
82 return (int)acym_loadResult('SELECT COUNT(*) FROM #__acym_field');
83 }
84
85 public function getAllFieldsForUser(): array
86 {
87 return acym_loadObjectList('SELECT * FROM #__acym_field WHERE id NOT IN (1, 2) ORDER BY `ordering` ASC', 'id');
88 }
89
90 public function getAllFieldsForModuleFront(): array
91 {
92 return acym_loadObjectList(
93 'SELECT *
94 FROM #__acym_field
95 WHERE id != 2
96 AND active = 1
97 ORDER BY `ordering` ASC',
98 'id'
99 );
100 }
101
102 public function getFieldsValueByUserId(int $userId): array
103 {
104 return acym_loadObjectList('SELECT * FROM #__acym_user_has_field WHERE user_id = '.intval($userId), 'field_id');
105 }
106
107 public function getFieldsValueByFieldId(int $fieldId): array
108 {
109 return acym_loadObjectList('SELECT * FROM #__acym_user_has_field WHERE field_id = '.intval($fieldId));
110 }
111
112 public function generateNamekey(string $name, string $namekey = ''): string
113 {
114 $fieldsNamekey = acym_loadResultArray('SELECT namekey FROM #__acym_field');
115 $columnsUser = acym_getColumns('user');
116 $namekeyForbidden = array_merge($fieldsNamekey, $columnsUser);
117
118 $namekey = empty($namekey) ? substr(preg_replace('#[^a-z0-9_]#i', '', strtolower($name)), 0, 50) : $namekey;
119 $baseNamekey = $namekey;
120 $baseCount = count($namekeyForbidden);
121
122 while (in_array($namekey, $namekeyForbidden)) {
123 $namekey = $baseNamekey.'_'.$baseCount;
124 $baseCount++;
125 }
126
127 return $namekey;
128 }
129
130 public function getValueFromDB(object $fieldDB): array
131 {
132 $query = 'SELECT '.acym_secureDBColumn($fieldDB->value).' AS value, '.acym_secureDBColumn($fieldDB->title).' AS title
133 FROM `'.acym_secureDBColumn($fieldDB->database).'`.`'.acym_secureDBColumn($fieldDB->table).'`';
134 if (isset($fieldDB->where_value) && strlen($fieldDB->where_value) > 0) {
135 $allowedSigns = ['=', '!=', '>', '<', '>=', '<=', 'BEGINS', 'END', 'CONTAINS', 'NOTCONTAINS', 'LIKE', 'NOT LIKE', 'REGEXP', 'NOT REGEXP', 'IS NULL', 'IS NOT NULL'];
136 $whereSign = in_array($fieldDB->where_sign ?? '', $allowedSigns, true) ? $fieldDB->where_sign : '=';
137 $query .= ' WHERE `'.acym_secureDBColumn($fieldDB->where).'` '.$whereSign.' '.acym_escapeDB($fieldDB->where_value);
138 }
139 if (!empty($fieldDB->order_by)) $query .= ' ORDER BY '.acym_secureDBColumn($fieldDB->order_by).' '.acym_secureDBColumn($fieldDB->sort_order);
140
141 return acym_loadObjectList($query);
142 }
143
144 public function store(int $userID, array $fields, bool $ajax = false): void
145 {
146 $customField = acym_getVar('array', 'customField', [], 'FILES');
147 if (!empty($customField['tmp_name'])) {
148 $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
149 $uploadPath = acym_cleanPath(ACYM_ROOT.$uploadFolder.'userfiles'.DS.$userID.DS);
150 if (!file_exists($uploadPath)) {
151 acym_createDir($uploadPath);
152 }
153 $allowedExtensions = explode(',', $this->config->get('allowed_files'));
154
155 foreach ($customField['tmp_name'] as $key => $value) {
156 if (is_array($value) && isset($value[0])) $value = $value[0];
157 if (empty($value)) continue;
158
159 // Get the uploaded file name
160 $fileName = $customField['name'][$key];
161 while (is_array($fileName) && isset($fileName[0])) {
162 $fileName = $fileName[0];
163 }
164
165 if (!preg_match('#\.('.implode('|', $allowedExtensions).')$#Ui', $fileName)) {
166 $ext = substr($fileName, strrpos($fileName, '.') + 1);
167 if ($ajax) {
168 $this->errors[] = acym_translationSprintf(
169 'ACYM_ACCEPTED_TYPE',
170 esc_html($ext),
171 implode(', ', $allowedExtensions)
172 );
173 } else {
174 acym_enqueueMessage(
175 acym_translationSprintf(
176 'ACYM_ACCEPTED_TYPE',
177 esc_html($ext),
178 implode(', ', $allowedExtensions)
179 ),
180 'error',
181 5000
182 );
183 }
184
185 continue;
186 }
187
188 if (!acym_uploadFile($value, $uploadPath.$fileName)) {
189 if ($ajax) {
190 $this->errors[] = acym_translation('ACYM_ERROR_SAVING');
191 } else {
192 acym_enqueueMessage(acym_translation('ACYM_ERROR_SAVING'), 'error');
193 }
194
195 continue;
196 }
197 $fields[$key] = $customField['name'][$key];
198 }
199 }
200
201 if (empty($fields)) return;
202
203 foreach ($fields as $id => $value) {
204 $field = $this->getOneById($id);
205 if (empty($field)) {
206 acym_enqueueMessage(acym_translationSprintf('ACYM_WRONG_FIELD_ID', $id), 'error');
207 continue;
208 }
209 $fieldOptions = @json_decode(empty($field->option) ? '{}' : $field->option);
210
211 if (is_array($value)) {
212 if (in_array($field->type, ['multiple_dropdown', 'radio', 'phone'])) {
213 if ($field->type === 'phone' && !empty($fieldOptions->max_characters) && !empty($value['phone'])) {
214 $value['phone'] = substr($value['phone'], 0, $fieldOptions->max_characters);
215 }
216
217 $value = implode(',', $value);
218 if ($value === ',') $value = '';
219 } elseif ($field->type == 'checkbox') {
220 $value = implode(',', array_keys($value));
221 } elseif ($field->type == 'date') {
222 $formatToDisplay = explode('%', $fieldOptions->format);
223 unset($formatToDisplay[0]);
224
225 $year = '0000';
226 $month = '00';
227 $day = '00';
228 $i = 0;
229 foreach ($formatToDisplay as $one) {
230 if ($one === 'd') {
231 $day = $value[$i];
232 }
233 if ($one === 'm') {
234 $month = $value[$i];
235 }
236 if ($one === 'y') {
237 $year = $value[$i];
238 }
239 $i++;
240 }
241
242 $value = $year.'-'.$month.'-'.$day;
243 } else {
244 $value = json_encode($value);
245 }
246 }
247
248 if (in_array($field->type, ['text', 'textarea']) && !empty($fieldOptions->max_characters)) {
249 $value = substr($value, 0, $fieldOptions->max_characters);
250 }
251
252 // If deleting a file field, also delete the physical file
253 if ($field->type === 'file' && strlen($value) === 0) {
254 $oldValue = acym_loadResult(
255 'SELECT `value` FROM #__acym_user_has_field WHERE `user_id` = '.intval($userID).' AND `field_id` = '.intval($id)
256 );
257 if (!empty($oldValue)) {
258 $fileName = @json_decode($oldValue, true);
259 if (is_array($fileName)) $fileName = $fileName[0];
260 if (empty($fileName)) $fileName = $oldValue;
261
262 $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
263 // Try new path (with user folder) first, then legacy path (without user folder)
264 $filePath = acym_cleanPath(ACYM_ROOT.$uploadFolder.'userfiles'.DS.$userID.DS.$fileName);
265 if (!file_exists($filePath)) {
266 $filePath = acym_cleanPath(ACYM_ROOT.$uploadFolder.'userfiles'.DS.$fileName);
267 }
268 acym_deleteFile($filePath);
269 }
270 }
271
272 // The user removed a value, don't add an empty line and remove any previous value from the bd
273 if (strlen($value) === 0) {
274 $query = 'DELETE FROM `#__acym_user_has_field`
275 WHERE `user_id` = '.intval($userID).'
276 AND `field_id` = '.intval($id);
277 } else {
278 $query = 'INSERT INTO #__acym_user_has_field (`user_id`, `field_id`, `value`) VALUES ('.intval($userID).', '.intval($id).', '.acym_escapeDB($value).')';
279 $query .= ' ON DUPLICATE KEY UPDATE `value`= VALUES(`value`)';
280 }
281
282 acym_query($query);
283 }
284 }
285
286 public function getAllFieldsByUserIds(array $userIds): array
287 {
288 acym_arrayToInteger($userIds);
289 if (empty($userIds)) return [];
290
291 $query = 'SELECT user_field.user_id, user_field.value, field.name FROM #__acym_user_has_field AS user_field
292 JOIN #__acym_field AS field ON user_field.field_id = field.id
293 WHERE user_field.user_id IN ('.implode(',', $userIds).')';
294
295 return acym_loadObjectList($query);
296 }
297
298 public function getAllFieldsListingByUserIds(array $userIds, array $fieldIds, string $listing = ''): array
299 {
300 acym_arrayToInteger($userIds);
301 if (empty($userIds)) return [];
302
303 acym_arrayToInteger($fieldIds);
304 if (empty($fieldIds)) return [];
305
306 $query = 'SELECT field.type AS `type`, field.value AS `value`, field.name AS field_name, user_field.user_id AS user_id, user_field.field_id AS field_id, user_field.value AS field_value, field.active, field.option
307 FROM #__acym_user_has_field AS user_field
308 JOIN #__acym_field AS field ON user_field.field_id = field.id';
309
310 $conditions = [];
311
312 if (!empty($listing)) {
313 $conditions[] = $listing;
314 }
315
316 $conditions[] = 'user_field.user_id IN ('.implode(',', $userIds).')';
317 $conditions[] = 'user_field.field_id IN ('.implode(',', $fieldIds).')';
318
319 if (!empty($conditions)) {
320 $query .= ' WHERE ('.implode(') AND (', $conditions).')';
321 }
322
323 $fieldValues = [];
324 $values = acym_loadObjectList($query);
325
326 $fieldsTypeWithInField = ['radio', 'checkbox', 'single_dropdown', 'multiple_dropdown'];
327 foreach ($values as $one) {
328 if (intval($one->active) === 0) {
329 continue;
330 }
331
332 $key = $one->field_id.'-'.$one->user_id;
333
334 if (!in_array($one->type, $fieldsTypeWithInField)) {
335 if ($one->type === 'phone') {
336 if (empty($one->field_value)) {
337 $fieldValues[$key] = '';
338 } else {
339 if (substr($one->field_value, 0, 1) === ',') {
340 $fieldValues[$key] = str_replace(',', '', $one->field_value);
341 } else {
342 $fieldValues[$key] = '+'.str_replace(',', ' ', $one->field_value);
343 }
344 }
345 } elseif ($one->type === 'date') {
346 if (empty($one->field_value)) {
347 $fieldValues[$key] = '';
348 } else {
349 $one->option = json_decode($one->option);
350 $fieldValues[$key] = acym_displayDateFormat($one->option->format, '', $one->field_value, [], false);
351 }
352 } else {
353 $decoded = json_decode($one->field_value);
354 $fieldValues[$key] = is_array($decoded) ? implode(', ', $decoded) : $one->field_value;
355 }
356 } else {
357 $defaultValues = json_decode($one->value, true);
358 $defaultValues = array_filter($defaultValues, function ($fieldOption) {
359 return !empty($fieldOption['value']) || !empty($fieldOption['title']);
360 });
361
362 if (empty($defaultValues)) {
363 // Options from database
364 if (!empty($one->field_value)) {
365 $fieldOptions = json_decode($one->option, true);
366 if (!empty($fieldOptions['fieldDB'])) {
367 $dbOptions = json_decode($fieldOptions['fieldDB'], true);
368 if (!empty($dbOptions['database']) && !empty($dbOptions['table']) && !empty($dbOptions['value']) && !empty($dbOptions['title'])) {
369 $query = 'SELECT '.acym_secureDBColumn($dbOptions['title']).'
370 FROM '.acym_secureDBColumn($dbOptions['database']).'.'.acym_secureDBColumn($dbOptions['table']).'
371 WHERE '.acym_secureDBColumn($dbOptions['value']).' = '.acym_escapeDB($one->field_value);
372 $fieldValues[$key][] = acym_loadResult($query);
373 }
374 }
375 }
376 } else {
377 // Classic options
378 foreach ($defaultValues as $oneValue) {
379 if (!in_array($oneValue['value'], explode(',', $one->field_value))) continue;
380 $fieldValues[$key][] = $oneValue['title'];
381 }
382 }
383
384 if (!empty($fieldValues[$key])) {
385 $fieldValues[$key] = implode(', ', $fieldValues[$key]);
386 }
387 }
388 }
389
390 return $fieldValues;
391 }
392
393 public function getAllFieldsBackendListing(): array
394 {
395 $whereLanguage = '(backend_listing = 1 OR `type` = "language")';
396 $query = 'SELECT id, name FROM #__acym_field WHERE '.$whereLanguage.' AND active = 1 AND id NOT IN (1, 2) ORDER BY ordering';
397
398 $return = [
399 'names' => [],
400 'ids' => [],
401 ];
402
403 foreach (acym_loadObjectList($query) as $one) {
404 $return['names'][] = $one->name;
405 $return['ids'][] = $one->id;
406 }
407
408 return $return;
409 }
410
411 public function getAllFieldsFrontendListing(): array
412 {
413 $fields = acym_loadObjectList('SELECT id, name FROM #__acym_field WHERE frontend_listing = 1 AND active = 1 AND id NOT IN (1, 2)');
414
415 $return = [
416 'names' => [],
417 'ids' => [],
418 ];
419
420 foreach ($fields as $oneField) {
421 $return['names'][] = $oneField->name;
422 $return['ids'][] = $oneField->id;
423 }
424
425 return $return;
426 }
427
428 public function delete(array $elements): int
429 {
430 if (empty($elements)) return 0;
431
432 acym_arrayToInteger($elements);
433
434 acym_trigger('specialActionOnDelete', ['field', $elements]);
435
436 acym_query('DELETE FROM #__acym_user_has_field WHERE field_id IN ('.implode(',', $elements).')');
437
438 return parent::delete($elements);
439 }
440
441 /**
442 * @param mixed $defaultValue
443 */
444 public function displayField(
445 object $field,
446 $defaultValue,
447 array $valuesArray,
448 bool $displayOutside = true,
449 bool $displayFront = false,
450 ?object $user = null,
451 bool $showField = true
452 ): void {
453 if (intval($field->active) === 0 && intval($field->core) !== 1) {
454 return;
455 }
456
457 $extraErrors = $this->config->get('extra_errors', '0');
458 $isCoreField = $field->core == 1;
459
460 if (!$isCoreField && !acym_level(ACYM_ENTERPRISE)) {
461 return;
462 }
463
464 if (!$showField) {
465 if (!acym_isAdmin()) {
466 return;
467 }
468
469 if (acym_isAdmin() && acym_level(ACYM_ENTERPRISE) && !$isCoreField) {
470 return;
471 }
472 }
473
474 $data = [];
475
476 $cmsUser = false;
477 if ($displayFront && !empty($user->id)) {
478 $cmsUser = !empty($user->cms_id);
479 if ($field->id == 1) {
480 $defaultValue = $user->name;
481 } elseif ($field->id == 2) {
482 $defaultValue = $user->email;
483 } else {
484 $allValues = [];
485 $defaultUserValue = $this->getFieldsValueByUserId($user->id);
486 if (!empty($defaultUserValue)) {
487 foreach ($defaultUserValue as $one) {
488 $allValues[$one->field_id] = $one->value;
489 }
490 }
491
492 if (isset($allValues[$field->id])) {
493 $defaultValue = is_null(json_decode($allValues[$field->id])) ? $allValues[$field->id] : json_decode($allValues[$field->id]);
494 }
495 }
496 }
497
498 if (in_array($field->type, ['radio', 'checkbox'])) {
499 $valuesArrayTmp = [];
500 foreach ($valuesArray as $oneValue) {
501 if (!is_object($oneValue)) {
502 $valuesArrayTmp = $valuesArray;
503 break;
504 }
505
506 if (!empty($oneValue->disable)) continue;
507 $valuesArrayTmp[$oneValue->value] = $oneValue->text;
508 }
509 $valuesArray = $valuesArrayTmp;
510 }
511
512 // Translate text of the options
513 if (is_array($valuesArray)) {
514 foreach ($valuesArray as $key => $oneValue) {
515 if (is_object($oneValue) && !empty($valuesArray[$key]->text)) {
516 $valuesArray[$key]->text = acym_translation($valuesArray[$key]->text);
517 } elseif (is_string($oneValue)) {
518 $valuesArray[$key] = acym_translation($valuesArray[$key]);
519 }
520 }
521 }
522
523 if (acym_isMultilingual() && $displayFront && !empty($field->translation)) {
524 $field->translation = json_decode($field->translation, true);
525 if (!empty($field->translation[acym_getLanguageTag()]) && !empty($field->translation[acym_getLanguageTag()]['name'])) {
526 $field->name = $field->translation[acym_getLanguageTag()]['name'];
527 }
528 }
529
530 $field->name = acym_translation($field->name);
531
532 if (!empty((array)$field->option) && isset($field->option->authorized_content)) {
533 if (empty($field->option->error_message_invalid)) {
534 $field->option->authorized_content->message = acym_translationSprintf('ACYM_INCORRECT_FIELD_VALUE', $field->name);
535 } else {
536 $field->option->authorized_content->message = $field->option->error_message_invalid;
537 }
538 $data['authorizedContent'] = json_encode($field->option->authorized_content);
539 }
540
541 $attributesSelectField = [];
542 if (!empty($field->option->max_characters)) {
543 $data['maxCharacters'] = $field->option->max_characters;
544 }
545 if (!empty($field->option->size)) {
546 $attributesSelectField['style'] = 'width:'.$field->option->size.'px';
547 $data['style'] = 'width:'.$field->option->size.'px';
548 }
549
550 if (!$displayOutside) {
551 $data['placeholder'] = $field->name;
552 }
553
554 $name = 'customField['.intval($field->id).']';
555 if ($field->type === 'text') {
556 $data['value'] = empty($defaultValue)
557 ? ''
558 : (
559 is_scalar($defaultValue)
560 ? $defaultValue
561 : json_encode($defaultValue)
562 );
563 }
564
565 $labelTypes = ['text', 'textarea', 'single_dropdown', 'multiple_dropdown', 'custom_text', 'language'];
566 $titleTypes = ['date', 'radio', 'checkbox', 'file'];
567
568 $useLabel = $displayOutside && (in_array($field->id, [1, 2]) || in_array($field->type, $labelTypes));
569 $useTitle = $displayOutside && in_array($field->type, $titleTypes);
570
571 if ($useLabel) {
572 echo '<label class="cell margin-top-1"><span class="acym__users__creation__fields__title">'.esc_html($field->name).'</span>';
573 }
574 if ($useTitle) {
575 echo '<div class="cell margin-top-1"><div class="acym__users__creation__fields__title">'.esc_html($field->name).'</div>';
576 }
577
578 $messageRequired = empty($field->option->error_message) ? '' : acym_translation($field->option->error_message);
579 $requiredJson = json_encode(['type' => $field->type, 'message' => $messageRequired]);
580 $required = '';
581 if ($field->required) {
582 $data['data-required'] = $requiredJson;
583 $required = ' data-required="'.esc_attr($requiredJson).'"';
584 $attributesSelectField['data-required'] = $requiredJson;
585 }
586
587 $readonly = '';
588 if (!$showField && acym_isAdmin() && acym_level(ACYM_ENTERPRISE) && $isCoreField) {
589 $attributesSelectField['disabled'] = true;
590 unset($attributesSelectField['data-required']);
591 $readonly = ' disabled ';
592 $required = '';
593 $data['disabled'] = true;
594 unset($data['data-required']);
595 }
596
597 $defaultValue = $this->prepareDefaultValue($defaultValue, $field->type, $user);
598
599 if ($field->id == 1) {
600 $data['type'] = 'text';
601 $data['name'] = 'user[name]';
602 $data['autocomplete'] = 'name';
603 $data['class'] = 'cell';
604
605 if (empty($readonly)) {
606 include acym_getPartial('fields', 'input');
607 } else {
608 ob_start();
609 include acym_getPartial('fields', 'input');
610 $input = ob_get_clean();
611
612 acym_tooltip(
613 [
614 'hoveredText' => $input,
615 'textShownInTooltip' => acym_translation('ACYM_CF_EDITION_BLOCKED'),
616 ]
617 );
618 }
619 } elseif ($field->id == 2) {
620 $uniqueId = 'email_field_'.acym_rand(100, 900);
621
622 unset($data['data-required']);
623 $data['type'] = 'email';
624 $data['name'] = 'user[email]';
625 $data['autocomplete'] = 'email';
626 $data['id'] = $uniqueId;
627 $data['class'] = 'cell acym__user__edit__email';
628 $data['required'] = true;
629 $data['readonly'] = $cmsUser;
630
631 if (empty($readonly)) {
632 include acym_getPartial('fields', 'input');
633 } else {
634 ob_start();
635 include acym_getPartial('fields', 'input');
636 $input = ob_get_clean();
637
638 acym_tooltip(
639 [
640 'hoveredText' => $input,
641 'textShownInTooltip' => acym_translation('ACYM_CF_EDITION_BLOCKED'),
642 ]
643 );
644 }
645
646 if ($displayFront && !$cmsUser && !empty($this->config->get('email_spellcheck'))) {
647 echo '<ul acym-data-field="'.esc_attr($uniqueId).'" class="acym_email_suggestions" style="display: none;"></ul>';
648 }
649 } elseif ($field->type === 'language') {
650 $attributesSelectField['class'] = 'acym__select';
651 if (empty($readonly)) {
652 acym_select(
653 $this->getLanguagesForDropdown(),
654 'user[language]',
655 $defaultValue,
656 $attributesSelectField,
657 'value',
658 'text',
659 null,
660 false,
661 true
662 );
663 } else {
664 acym_tooltip(
665 [
666 'hoveredText' => acym_select(
667 $this->getLanguagesForDropdown(),
668 'user[language]',
669 $defaultValue,
670 $attributesSelectField
671 ),
672 'textShownInTooltip' => acym_translation('ACYM_CF_EDITION_BLOCKED'),
673 ]
674 );
675 }
676 } elseif ($field->type === 'text') {
677 $data['type'] = 'text';
678 $data['name'] = $name;
679 include acym_getPartial('fields', 'input');
680 } elseif ($field->type === 'textarea') {
681 echo '<textarea name="'.esc_attr($name).'"';
682 if (!empty($data['data-required'])) {
683 echo ' data-required="'.esc_attr($data['data-required']).'"';
684 }
685 if (isset($data['maxCharacters'])) {
686 echo ' maxlength="'.esc_attr($data['maxCharacters']).'"';
687 }
688 echo ' rows="'.intval($field->option->rows).'" cols="'.intval($field->option->columns).'">';
689 echo esc_html(is_scalar($defaultValue) ? $defaultValue : '');
690 echo '</textarea>';
691 } elseif ($field->type === 'radio') {
692 if ($displayFront) {
693 $data['name'] = $name;
694 $data['values'] = $valuesArray;
695 $data['value'] = $defaultValue;
696 include acym_getPartial('fields', 'radio');
697 } else {
698 acym_radio(
699 $valuesArray,
700 $name.'[]',
701 $defaultValue,
702 $field->required ? ['data-required' => $requiredJson] : []
703 );
704 }
705 } elseif ($field->type === 'checkbox') {
706 $data['name'] = $name;
707 $data['values'] = $valuesArray;
708 $data['value'] = $defaultValue;
709 $data['displayFront'] = $displayFront;
710 include acym_getPartial('fields', 'checkbox');
711 } elseif ($field->type === 'single_dropdown') {
712 $attributesSelectField['class'] = 'acym__custom__fields__select__form acym__select';
713 acym_select(
714 $valuesArray,
715 $name,
716 $defaultValue,
717 $attributesSelectField,
718 'value',
719 'text',
720 null,
721 false,
722 true
723 );
724 } elseif ($field->type === 'multiple_dropdown') {
725 $attributes = [
726 'class' => 'acym__custom__fields__select__multiple__form acym__select',
727 'style' => empty($field->option->size) ? '' : 'width:'.$field->option->size.'px',
728 ];
729 if ($field->required) {
730 $attributes['data-required'] = $displayFront ? esc_attr($requiredJson) : $requiredJson;
731 }
732
733 acym_selectMultiple(
734 $valuesArray,
735 $name,
736 $defaultValue,
737 $attributes,
738 'value',
739 'text',
740 true
741 );
742 } elseif ($field->type === 'date') {
743 $attributes = [
744 'class' => 'acym__custom__fields__select__form acym__select',
745 'acym-field-type' => 'date',
746 ];
747 if (!empty($required)) {
748 $attributes['data-required'] = $requiredJson;
749 }
750
751 echo wp_kses(
752 acym_displayDateFormat(
753 $field->option->format,
754 $name.'[]',
755 $defaultValue,
756 $attributes
757 ),
758 array_merge_recursive(
759 SecurityHelper::ALLOWED_HTML_SELECT,
760 [
761 'select' => [
762 'data-required' => true,
763 'acym-field-type' => true,
764 ],
765 'div' => [
766 'class' => true,
767 ],
768 ]
769 )
770 );
771 } elseif ($field->type === 'file') {
772 if ($displayFront) {
773 unset($data['placeholder'], $data['value'], $data['authorizedContent'], $data['style'], $data['maxCharacters']);
774 $data['type'] = 'file';
775 $data['name'] = $name;
776 include acym_getPartial('fields', 'input');
777 } else {
778 $downloadUrl = '';
779 if (!empty($defaultValue) && !empty($user->id)) {
780 $fileName = is_array($defaultValue) ? $defaultValue[0] : $defaultValue;
781 if (!empty($fileName)) {
782 $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
783 $userFilePath = $uploadFolder.'userfiles'.DS.$user->id.DS.$fileName;
784 $legacyFilePath = $uploadFolder.'userfiles'.DS.$fileName;
785
786 if (file_exists(acym_cleanPath(ACYM_ROOT.$userFilePath))) {
787 $downloadUrl = ACYM_LIVE.str_replace(DS, '/', $userFilePath);
788 } elseif (file_exists(acym_cleanPath(ACYM_ROOT.$legacyFilePath))) {
789 $downloadUrl = ACYM_LIVE.str_replace(DS, '/', $legacyFilePath);
790 }
791 }
792 }
793 $attributes = [];
794 if (!empty($required)) {
795 $attributes['data-required'] = $requiredJson;
796 }
797 acym_inputFile($name.'[]', $defaultValue, '', $attributes, $downloadUrl);
798 }
799 } elseif ($field->type === 'phone') {
800 $indicator = !empty($defaultValue[0]) ? $defaultValue[0] : '';
801 $number = !empty($defaultValue[1]) ? $defaultValue[1] : '';
802
803 if ($displayOutside) {
804 echo '<div class="cell margin-top-1 grid-x"><div class="acym__users__creation__fields__title cell">'.esc_html($field->name).'</div>';
805 }
806 echo '<div class="cell large-5 medium-4 padding-next-1">';
807 acym_generateCountryNumber($name.'[code]', $indicator);
808 echo '</div>';
809
810 unset($data['authorizedContent']);
811 $data['type'] = 'tel';
812 $data['name'] = $name.'[phone]';
813 $data['autocomplete'] = 'tel-national';
814 $data['class'] = 'cell large-7 medium-8';
815 $data['value'] = $number;
816 include acym_getPartial('fields', 'input');
817
818 if ($displayOutside) {
819 echo '</div>';
820 }
821 } elseif ($field->type === 'custom_text') {
822 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Admin-defined custom HTML content, intentionally rendered as-is.
823 echo $field->option->custom_text;
824 }
825
826 if ($useLabel) {
827 echo '</label>';
828 }
829 if ($useTitle) {
830 echo '</div>';
831 }
832
833 echo '<div class="acym__field__error__block" data-acym-field-id="'.intval($field->id).'"></div>';
834 if ($displayFront && $extraErrors && !acym_isAdmin()) {
835 echo '<div class="acym__message__invalid__field acym__color__error" style="display: none;">';
836 echo '<i class="acymicon-times-circle acym__cross__invalid acym__color__error"></i>'.esc_html(acym_translation('ACYM_THANKS_TO_FILL_IN_THIS_FIELD')).'</div>';
837 }
838 }
839
840 /**
841 * @param mixed $defaultValue
842 *
843 * @return mixed
844 */
845 private function prepareDefaultValue($defaultValue, string $fieldType, ?object $user)
846 {
847 if ($fieldType === 'language') {
848 if (empty($defaultValue) && !empty($user->language)) {
849 $defaultValue = $user->language;
850 }
851
852 return empty($defaultValue) ? acym_getLanguageTag() : $defaultValue;
853 }
854
855 if ($fieldType === 'phone') {
856 return !empty($defaultValue) ? explode(',', $defaultValue) : '';
857 }
858
859 if ($fieldType === 'file') {
860 return is_array($defaultValue) ? $defaultValue[0] : $defaultValue;
861 }
862
863 if (in_array($fieldType, ['single_dropdown', 'textarea'])) {
864 return empty($defaultValue) ? '' : $defaultValue;
865 }
866
867 if ($fieldType === 'radio') {
868 return strlen($defaultValue) === 0 ? null : $defaultValue;
869 }
870
871 if (in_array($fieldType, ['checkbox', 'multiple_dropdown'])) {
872 if (empty($defaultValue)) {
873 return [];
874 }
875
876 if (is_string($defaultValue)) {
877 return explode(',', $defaultValue);
878 }
879
880 if (is_object($defaultValue)) {
881 $defaultValues = [];
882 foreach ($defaultValue as $oneKey => $oneValue) {
883 if (!empty($oneValue)) {
884 $defaultValues[] = $oneKey;
885 }
886 }
887
888 return $defaultValues;
889 }
890
891 return is_array($defaultValue) ? $defaultValue : [];
892 }
893
894 return $defaultValue;
895 }
896
897 public function getFieldTypeById(int $id): string
898 {
899 return acym_loadResult('SELECT type FROM #__acym_field WHERE id = '.intval($id));
900 }
901
902 public function setInactive(array $elements): void
903 {
904 if (empty($elements)) {
905 return;
906 }
907
908 acym_arrayToInteger($elements);
909
910 // Core fields can't be disabled
911 $excludedFields = acym_loadResultArray('SELECT `id` FROM #__acym_field WHERE `core` = 1');
912 acym_arrayToInteger($excludedFields);
913
914 $elements = array_diff($elements, $excludedFields);
915
916 parent::setInactive($elements);
917 }
918
919 public function insertLanguageField(): void
920 {
921 if (!empty($this->getLanguageFieldId())) {
922 return;
923 }
924
925 $field = new \stdClass();
926 $field->name = 'ACYM_LANGUAGE';
927 $field->type = 'language';
928 $field->active = 1;
929 $field->required = 1;
930 $field->ordering = $this->getOrdering() + 1;
931 $field->core = 1;
932 $field->backend_edition = 1;
933 $field->backend_listing = 0;
934 $field->frontend_edition = 1;
935 $field->frontend_listing = 0;
936 $field->namekey = 'acym_language';
937
938 $rowId = acym_insertObject('#__acym_field', $field);
939
940 if (empty($rowId)) {
941 return;
942 }
943
944 $this->config->saveConfig([self::LANGUAGE_FIELD_ID_KEY => $rowId]);
945 }
946
947 public function getLanguageFieldId(): int
948 {
949 $languageFieldId = acym_loadResult('SELECT `id` FROM `#__acym_field` WHERE `namekey` = "acym_language"');
950
951 return empty($languageFieldId) ? 0 : $languageFieldId;
952 }
953
954 public function getLanguagesForDropdown(): array
955 {
956 $languages = acym_getLanguages(false, true);
957 $dataLanguages = [];
958
959 foreach ($languages as $langCode => $language) {
960 if ($langCode == "xx-XX") continue;
961
962 $oneLanguage = new \stdClass();
963 $oneLanguage->value = $langCode;
964 $oneLanguage->text = $language->name;
965
966 $dataLanguages[] = $oneLanguage;
967 }
968
969 usort($dataLanguages, function ($a, $b) {
970 return strtolower($a->text) > strtolower($b->text) ? 1 : -1;
971 });
972
973 return $dataLanguages;
974 }
975
976 public function setEmailConfirmationField(bool $displayOutside, object $field, string $container = 'div', bool $displayInline = false): void
977 {
978 $uniqueId = 'email_confirmation_field_'.acym_rand(100, 900);
979
980 echo '<'.esc_attr($container).' class="onefield acym_email_confirmation_field acyfield_text">';
981 if ($displayOutside) {
982 echo '<label class="cell margin-top-1">';
983 echo '<span class="acym__users__creation__fields__title">'.esc_html(acym_translation('ACYM_EMAIL_CONFIRMATION')).'</span>';
984 }
985
986 $placeholder = !$displayOutside ? acym_translation('ACYM_EMAIL_CONFIRMATION') : '';
987 echo '<input id="'.esc_attr($uniqueId).'"
988 '.(empty($field->option->size) ? '' : ' style="width:'.intval($field->option->size).'px"').'
989 required
990 type="email"
991 class="cell acym__user__edit__email"
992 name="user[email_confirmation]"
993 placeholder="'.esc_attr($placeholder).'" />';
994 echo '<span class="acym__field__error__block"></span>';
995
996 $extraErrors = $this->config->get('extra_errors', '0');
997 if ($extraErrors && !acym_isAdmin()) {
998 echo '<span class="acym__message__invalid__field acym__color__error" style="display: none;">';
999 echo '<i class="acymicon-times-circle acym__cross__invalid acym__color__error"></i>';
1000 echo esc_html(acym_translation('ACYM_THANKS_TO_FILL_IN_THIS_FIELD'));
1001 echo '</span>';
1002 }
1003
1004 if ($displayOutside) {
1005 echo '</label>';
1006 }
1007 echo '</'.esc_attr($container).'>';
1008
1009 if ($container === 'td' && !$displayInline) {
1010 //TODO either open tr here, or close it where setEmailConfirmationField is called
1011 echo '</tr>';
1012 }
1013 }
1014 }
1015