PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.5
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.5
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.5, at back/Classes/FieldClass.php

1,048 lines 40.3 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">'.wp_kses(
573 $field->name,
574 SecurityHelper::ALLOWED_HTML_FIELD_NAME
575 ).'</span>';
576 }
577 if ($useTitle) {
578 if (in_array($field->type, ['date', 'radio', 'checkbox'])) {
579 $groupLabelId = 'acym__field__label__'.intval($field->id).'_'.acym_rand(1000, 9999);
580 echo '<div class="cell margin-top-1" role="group" aria-labelledby="'.esc_attr($groupLabelId).'"'.($field->required ? ' aria-required="true"' : '').'>';
581 echo '<div id="'.esc_attr($groupLabelId).'" class="acym__users__creation__fields__title">'.wp_kses(
582 $field->name,
583 SecurityHelper::ALLOWED_HTML_FIELD_NAME
584 ).'</div>';
585 } else {
586 echo '<div class="cell margin-top-1"><div class="acym__users__creation__fields__title">'.wp_kses(
587 $field->name,
588 SecurityHelper::ALLOWED_HTML_FIELD_NAME
589 ).'</div>';
590 }
591 }
592
593 $messageRequired = empty($field->option->error_message) ? '' : acym_translation($field->option->error_message);
594 $requiredJson = json_encode(['type' => $field->type, 'message' => $messageRequired]);
595 $required = '';
596 if ($field->required) {
597 $data['data-required'] = $requiredJson;
598 $data['aria-required'] = 'true';
599 $required = ' data-required="'.esc_attr($requiredJson).'"';
600 $attributesSelectField['data-required'] = $requiredJson;
601 $attributesSelectField['aria-required'] = 'true';
602 }
603
604 $readonly = '';
605 if (!$showField && acym_isAdmin() && acym_level(ACYM_ENTERPRISE) && $isCoreField) {
606 $attributesSelectField['disabled'] = true;
607 unset($attributesSelectField['data-required']);
608 $readonly = ' disabled ';
609 $required = '';
610 $data['disabled'] = true;
611 unset($data['data-required']);
612 }
613
614 $defaultValue = $this->prepareDefaultValue($defaultValue, $field->type, $user);
615
616 if ($field->id == 1) {
617 $data['type'] = 'text';
618 $data['name'] = 'user[name]';
619 $data['autocomplete'] = 'name';
620 $data['class'] = 'cell';
621
622 if (empty($readonly)) {
623 include acym_getPartial('fields', 'input');
624 } else {
625 ob_start();
626 include acym_getPartial('fields', 'input');
627 $input = ob_get_clean();
628
629 acym_tooltip(
630 [
631 'hoveredText' => $input,
632 'textShownInTooltip' => acym_translation('ACYM_CF_EDITION_BLOCKED'),
633 ]
634 );
635 }
636 } elseif ($field->id == 2) {
637 $uniqueId = 'email_field_'.acym_rand(100, 900);
638
639 unset($data['data-required']);
640 $data['type'] = 'email';
641 $data['name'] = 'user[email]';
642 $data['autocomplete'] = 'email';
643 $data['id'] = $uniqueId;
644 $data['class'] = 'cell acym__user__edit__email';
645 $data['required'] = true;
646 $data['readonly'] = $cmsUser;
647
648 if (empty($readonly)) {
649 include acym_getPartial('fields', 'input');
650 } else {
651 ob_start();
652 include acym_getPartial('fields', 'input');
653 $input = ob_get_clean();
654
655 acym_tooltip(
656 [
657 'hoveredText' => $input,
658 'textShownInTooltip' => acym_translation('ACYM_CF_EDITION_BLOCKED'),
659 ]
660 );
661 }
662
663 if ($displayFront && !$cmsUser && !empty($this->config->get('email_spellcheck'))) {
664 echo '<ul acym-data-field="'.esc_attr($uniqueId).'" class="acym_email_suggestions" style="display: none;"></ul>';
665 }
666 } elseif ($field->type === 'language') {
667 $attributesSelectField['class'] = 'acym__select';
668 if (empty($readonly)) {
669 acym_select(
670 $this->getLanguagesForDropdown(),
671 'user[language]',
672 $defaultValue,
673 $attributesSelectField,
674 'value',
675 'text',
676 null,
677 false,
678 true
679 );
680 } else {
681 acym_tooltip(
682 [
683 'hoveredText' => acym_select(
684 $this->getLanguagesForDropdown(),
685 'user[language]',
686 $defaultValue,
687 $attributesSelectField
688 ),
689 'textShownInTooltip' => acym_translation('ACYM_CF_EDITION_BLOCKED'),
690 ]
691 );
692 }
693 } elseif ($field->type === 'text') {
694 $data['type'] = 'text';
695 $data['name'] = $name;
696 include acym_getPartial('fields', 'input');
697 } elseif ($field->type === 'textarea') {
698 echo '<textarea name="'.esc_attr($name).'"';
699 if (!empty($data['data-required'])) {
700 echo ' data-required="'.esc_attr($data['data-required']).'"';
701 }
702 if (!empty($data['aria-required'])) {
703 echo ' aria-required="true"';
704 }
705 if (isset($data['maxCharacters'])) {
706 echo ' maxlength="'.esc_attr($data['maxCharacters']).'"';
707 }
708 echo ' rows="'.intval($field->option->rows).'" cols="'.intval($field->option->columns).'">';
709 echo esc_html(is_scalar($defaultValue) ? $defaultValue : '');
710 echo '</textarea>';
711 } elseif ($field->type === 'radio') {
712 if ($displayFront) {
713 $data['name'] = $name;
714 $data['values'] = $valuesArray;
715 $data['value'] = $defaultValue;
716 include acym_getPartial('fields', 'radio');
717 } else {
718 acym_radio(
719 $valuesArray,
720 $name.'[]',
721 $defaultValue,
722 $field->required ? ['data-required' => $requiredJson] : []
723 );
724 }
725 } elseif ($field->type === 'checkbox') {
726 $data['name'] = $name;
727 $data['values'] = $valuesArray;
728 $data['value'] = $defaultValue;
729 $data['displayFront'] = $displayFront;
730 include acym_getPartial('fields', 'checkbox');
731 } elseif ($field->type === 'single_dropdown') {
732 $attributesSelectField['class'] = 'acym__custom__fields__select__form acym__select';
733 acym_select(
734 $valuesArray,
735 $name,
736 $defaultValue,
737 $attributesSelectField,
738 'value',
739 'text',
740 null,
741 false,
742 true
743 );
744 } elseif ($field->type === 'multiple_dropdown') {
745 $attributes = [
746 'class' => 'acym__custom__fields__select__multiple__form acym__select',
747 'style' => empty($field->option->size) ? '' : 'width:'.$field->option->size.'px',
748 ];
749 if ($field->required) {
750 $attributes['data-required'] = $displayFront ? esc_attr($requiredJson) : $requiredJson;
751 $attributes['aria-required'] = 'true';
752 }
753
754 acym_selectMultiple(
755 $valuesArray,
756 $name,
757 $defaultValue,
758 $attributes,
759 'value',
760 'text',
761 true
762 );
763 } elseif ($field->type === 'date') {
764 $attributes = [
765 'class' => 'acym__custom__fields__select__form acym__select',
766 'acym-field-type' => 'date',
767 ];
768 if (!empty($required)) {
769 $attributes['data-required'] = $requiredJson;
770 }
771
772 echo wp_kses(
773 acym_displayDateFormat(
774 $field->option->format,
775 $name.'[]',
776 $defaultValue,
777 $attributes
778 ),
779 array_merge_recursive(
780 SecurityHelper::ALLOWED_HTML_SELECT,
781 [
782 'select' => [
783 'data-required' => true,
784 'acym-field-type' => true,
785 'aria-label' => true,
786 ],
787 'div' => [
788 'class' => true,
789 ],
790 ]
791 )
792 );
793 } elseif ($field->type === 'file') {
794 if ($displayFront) {
795 unset($data['placeholder'], $data['value'], $data['authorizedContent'], $data['style'], $data['maxCharacters']);
796 $data['type'] = 'file';
797 $data['name'] = $name;
798 include acym_getPartial('fields', 'input');
799 } else {
800 $downloadUrl = '';
801 if (!empty($defaultValue) && !empty($user->id)) {
802 $fileName = is_array($defaultValue) ? $defaultValue[0] : $defaultValue;
803 if (!empty($fileName)) {
804 $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
805 $userFilePath = $uploadFolder.'userfiles'.DS.$user->id.DS.$fileName;
806 $legacyFilePath = $uploadFolder.'userfiles'.DS.$fileName;
807
808 if (file_exists(acym_cleanPath(ACYM_ROOT.$userFilePath))) {
809 $downloadUrl = ACYM_LIVE.str_replace(DS, '/', $userFilePath);
810 } elseif (file_exists(acym_cleanPath(ACYM_ROOT.$legacyFilePath))) {
811 $downloadUrl = ACYM_LIVE.str_replace(DS, '/', $legacyFilePath);
812 }
813 }
814 }
815 $attributes = [];
816 if (!empty($required)) {
817 $attributes['data-required'] = $requiredJson;
818 }
819 acym_inputFile($name.'[]', $defaultValue, '', $attributes, $downloadUrl);
820 }
821 } elseif ($field->type === 'phone') {
822 $indicator = !empty($defaultValue[0]) ? $defaultValue[0] : '';
823 $number = !empty($defaultValue[1]) ? $defaultValue[1] : '';
824
825 if ($displayOutside) {
826 $phoneLabelId = 'acym__field__label__'.intval($field->id).'_'.acym_rand(1000, 9999);
827 echo '<div class="cell margin-top-1 grid-x" role="group" aria-labelledby="'.esc_attr($phoneLabelId).'"'.($field->required ? ' aria-required="true"' : '').'>';
828 echo '<div id="'.esc_attr($phoneLabelId).'" class="acym__users__creation__fields__title cell">'.wp_kses(
829 $field->name,
830 SecurityHelper::ALLOWED_HTML_FIELD_NAME
831 ).'</div>';
832 }
833 echo '<div class="cell large-5 medium-4 padding-next-1">';
834 acym_generateCountryNumber($name.'[code]', $indicator);
835 echo '</div>';
836
837 unset($data['authorizedContent']);
838 $data['type'] = 'tel';
839 $data['name'] = $name.'[phone]';
840 $data['autocomplete'] = 'tel-national';
841 $data['class'] = 'cell large-7 medium-8';
842 $data['value'] = $number;
843 if ($displayOutside) {
844 $data['aria-label'] = acym_translation('ACYM_PHONE_NUMBER');
845 }
846 include acym_getPartial('fields', 'input');
847
848 if ($displayOutside) {
849 echo '</div>';
850 }
851 } elseif ($field->type === 'custom_text') {
852 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Admin-defined custom HTML content, intentionally rendered as-is.
853 echo $field->option->custom_text;
854 }
855
856 if ($useLabel) {
857 echo '</label>';
858 }
859 if ($useTitle) {
860 echo '</div>';
861 }
862
863 echo '<div class="acym__field__error__block" data-acym-field-id="'.intval($field->id).'" aria-live="assertive"></div>';
864 if ($displayFront && $extraErrors && !acym_isAdmin()) {
865 echo '<div class="acym__message__invalid__field acym__color__error" style="display: none;">';
866 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>';
867 }
868 }
869
870 /**
871 * @param mixed $defaultValue
872 *
873 * @return mixed
874 */
875 private function prepareDefaultValue($defaultValue, string $fieldType, ?object $user)
876 {
877 if ($fieldType === 'language') {
878 if (empty($defaultValue) && !empty($user->language)) {
879 $defaultValue = $user->language;
880 }
881
882 return empty($defaultValue) ? acym_getLanguageTag() : $defaultValue;
883 }
884
885 if ($fieldType === 'phone') {
886 return !empty($defaultValue) ? explode(',', $defaultValue) : '';
887 }
888
889 if ($fieldType === 'file') {
890 return is_array($defaultValue) ? $defaultValue[0] : $defaultValue;
891 }
892
893 if (in_array($fieldType, ['single_dropdown', 'textarea'])) {
894 return empty($defaultValue) ? '' : $defaultValue;
895 }
896
897 if ($fieldType === 'radio') {
898 return strlen($defaultValue) === 0 ? null : $defaultValue;
899 }
900
901 if (in_array($fieldType, ['checkbox', 'multiple_dropdown'])) {
902 if (empty($defaultValue)) {
903 return [];
904 }
905
906 if (is_string($defaultValue)) {
907 return explode(',', $defaultValue);
908 }
909
910 if (is_object($defaultValue)) {
911 $defaultValues = [];
912 foreach ($defaultValue as $oneKey => $oneValue) {
913 if (!empty($oneValue)) {
914 $defaultValues[] = $oneKey;
915 }
916 }
917
918 return $defaultValues;
919 }
920
921 return is_array($defaultValue) ? $defaultValue : [];
922 }
923
924 return $defaultValue;
925 }
926
927 public function getFieldTypeById(int $id): string
928 {
929 return acym_loadResult('SELECT type FROM #__acym_field WHERE id = '.intval($id));
930 }
931
932 public function setInactive(array $elements): void
933 {
934 if (empty($elements)) {
935 return;
936 }
937
938 acym_arrayToInteger($elements);
939
940 // Core fields can't be disabled
941 $excludedFields = acym_loadResultArray('SELECT `id` FROM #__acym_field WHERE `core` = 1');
942 acym_arrayToInteger($excludedFields);
943
944 $elements = array_diff($elements, $excludedFields);
945
946 parent::setInactive($elements);
947 }
948
949 public function insertLanguageField(): void
950 {
951 if (!empty($this->getLanguageFieldId())) {
952 return;
953 }
954
955 $field = new \stdClass();
956 $field->name = 'ACYM_LANGUAGE';
957 $field->type = 'language';
958 $field->active = 1;
959 $field->required = 1;
960 $field->ordering = $this->getOrdering() + 1;
961 $field->core = 1;
962 $field->backend_edition = 1;
963 $field->backend_listing = 0;
964 $field->frontend_edition = 1;
965 $field->frontend_listing = 0;
966 $field->namekey = 'acym_language';
967
968 $rowId = acym_insertObject('#__acym_field', $field);
969
970 if (empty($rowId)) {
971 return;
972 }
973
974 $this->config->saveConfig([self::LANGUAGE_FIELD_ID_KEY => $rowId]);
975 }
976
977 public function getLanguageFieldId(): int
978 {
979 $languageFieldId = acym_loadResult('SELECT `id` FROM `#__acym_field` WHERE `namekey` = "acym_language"');
980
981 return empty($languageFieldId) ? 0 : $languageFieldId;
982 }
983
984 public function getLanguagesForDropdown(): array
985 {
986 $languages = acym_getLanguages(false, true);
987 $dataLanguages = [];
988
989 foreach ($languages as $langCode => $language) {
990 if ($langCode == "xx-XX") continue;
991
992 $oneLanguage = new \stdClass();
993 $oneLanguage->value = $langCode;
994 $oneLanguage->text = $language->name;
995
996 $dataLanguages[] = $oneLanguage;
997 }
998
999 usort($dataLanguages, function ($a, $b) {
1000 return strtolower($a->text) > strtolower($b->text) ? 1 : -1;
1001 });
1002
1003 return $dataLanguages;
1004 }
1005
1006 public function setEmailConfirmationField(bool $displayOutside, object $field, string $container = 'div', bool $displayInline = false): void
1007 {
1008 $uniqueId = 'email_confirmation_field_'.acym_rand(100, 900);
1009
1010 echo '<'.esc_attr($container).' class="onefield acym_email_confirmation_field acyfield_text">';
1011 if ($displayOutside) {
1012 echo '<label class="cell margin-top-1">';
1013 echo '<span class="acym__users__creation__fields__title">'.esc_html(acym_translation('ACYM_EMAIL_CONFIRMATION')).'</span>';
1014 }
1015
1016 $placeholder = !$displayOutside ? acym_translation('ACYM_EMAIL_CONFIRMATION') : '';
1017 echo '<input id="'.esc_attr($uniqueId).'"
1018 '.(empty($field->option->size) ? '' : ' style="width:'.intval($field->option->size).'px"').'
1019 required
1020 type="email"
1021 class="cell acym__user__edit__email"
1022 name="user[email_confirmation]"';
1023 if (!$displayOutside) {
1024 echo ' aria-label="'.esc_attr(acym_translation('ACYM_EMAIL_CONFIRMATION')).'"';
1025 }
1026 echo ' placeholder="'.esc_attr($placeholder).'" />';
1027 echo '<span class="acym__field__error__block" aria-live="assertive"></span>';
1028
1029 $extraErrors = $this->config->get('extra_errors', '0');
1030 if ($extraErrors && !acym_isAdmin()) {
1031 echo '<span class="acym__message__invalid__field acym__color__error" style="display: none;">';
1032 echo '<i class="acymicon-times-circle acym__cross__invalid acym__color__error"></i>';
1033 echo esc_html(acym_translation('ACYM_THANKS_TO_FILL_IN_THIS_FIELD'));
1034 echo '</span>';
1035 }
1036
1037 if ($displayOutside) {
1038 echo '</label>';
1039 }
1040 echo '</'.esc_attr($container).'>';
1041
1042 if ($container === 'td' && !$displayInline) {
1043 //TODO either open tr here, or close it where setEmailConfirmationField is called
1044 echo '</tr>';
1045 }
1046 }
1047 }
1048