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

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