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

1,016 lines 38.7 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)) {
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 if (
566 $displayOutside
567 && (
568 in_array($field->id, [1, 2])
569 || in_array($field->type, ['text', 'textarea', 'single_dropdown', 'multiple_dropdown', 'custom_text', 'file', 'language'])
570 )
571 ) {
572 echo '<label class="cell margin-top-1"><span class="acym__users__creation__fields__title">'.esc_html($field->name).'</span>';
573 }
574 if ($displayOutside && in_array($field->type, ['date', 'radio', 'checkbox'])) {
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 $labelTypes = ['text', 'textarea', 'single_dropdown', 'multiple_dropdown', 'custom_text', 'language'];
827 if ($displayOutside && (in_array($field->id, [1, 2]) || in_array($field->type, $labelTypes))) {
828 echo '</label>';
829 }
830 if ($displayOutside && in_array($field->type, ['date', 'radio', 'checkbox'])) {
831 echo '</div>';
832 }
833
834 echo '<div class="acym__field__error__block" data-acym-field-id="'.intval($field->id).'"></div>';
835 if ($displayFront && $extraErrors && !acym_isAdmin()) {
836 echo '<div class="acym__message__invalid__field acym__color__error" style="display: none;">';
837 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>';
838 }
839 }
840
841 /**
842 * @param mixed $defaultValue
843 *
844 * @return mixed
845 */
846 private function prepareDefaultValue($defaultValue, string $fieldType, ?object $user)
847 {
848 if ($fieldType === 'language') {
849 if (empty($defaultValue) && !empty($user->language)) {
850 $defaultValue = $user->language;
851 }
852
853 return empty($defaultValue) ? acym_getLanguageTag() : $defaultValue;
854 }
855
856 if ($fieldType === 'phone') {
857 return !empty($defaultValue) ? explode(',', $defaultValue) : '';
858 }
859
860 if ($fieldType === 'file') {
861 return is_array($defaultValue) ? $defaultValue[0] : $defaultValue;
862 }
863
864 if (in_array($fieldType, ['single_dropdown', 'textarea'])) {
865 return empty($defaultValue) ? '' : $defaultValue;
866 }
867
868 if ($fieldType === 'radio') {
869 return strlen($defaultValue) === 0 ? null : $defaultValue;
870 }
871
872 if (in_array($fieldType, ['checkbox', 'multiple_dropdown'])) {
873 if (empty($defaultValue)) {
874 return [];
875 }
876
877 if (is_string($defaultValue)) {
878 return explode(',', $defaultValue);
879 }
880
881 if (is_object($defaultValue)) {
882 $defaultValues = [];
883 foreach ($defaultValue as $oneKey => $oneValue) {
884 if (!empty($oneValue)) {
885 $defaultValues[] = $oneKey;
886 }
887 }
888
889 return $defaultValues;
890 }
891
892 return is_array($defaultValue) ? $defaultValue : [];
893 }
894
895 return $defaultValue;
896 }
897
898 public function getFieldTypeById(int $id): string
899 {
900 return acym_loadResult('SELECT type FROM #__acym_field WHERE id = '.intval($id));
901 }
902
903 public function setInactive(array $elements): void
904 {
905 if (empty($elements)) {
906 return;
907 }
908
909 acym_arrayToInteger($elements);
910
911 // Core fields can't be disabled
912 $excludedFields = acym_loadResultArray('SELECT `id` FROM #__acym_field WHERE `core` = 1');
913 acym_arrayToInteger($excludedFields);
914
915 $elements = array_diff($elements, $excludedFields);
916
917 parent::setInactive($elements);
918 }
919
920 public function insertLanguageField(): void
921 {
922 if (!empty($this->getLanguageFieldId())) {
923 return;
924 }
925
926 $field = new \stdClass();
927 $field->name = 'ACYM_LANGUAGE';
928 $field->type = 'language';
929 $field->active = 1;
930 $field->required = 1;
931 $field->ordering = $this->getOrdering() + 1;
932 $field->core = 1;
933 $field->backend_edition = 1;
934 $field->backend_listing = 0;
935 $field->frontend_edition = 1;
936 $field->frontend_listing = 0;
937 $field->namekey = 'acym_language';
938
939 $rowId = acym_insertObject('#__acym_field', $field);
940
941 if (empty($rowId)) {
942 return;
943 }
944
945 $this->config->saveConfig([self::LANGUAGE_FIELD_ID_KEY => $rowId]);
946 }
947
948 public function getLanguageFieldId(): int
949 {
950 $languageFieldId = acym_loadResult('SELECT `id` FROM `#__acym_field` WHERE `namekey` = "acym_language"');
951
952 return empty($languageFieldId) ? 0 : $languageFieldId;
953 }
954
955 public function getLanguagesForDropdown(): array
956 {
957 $languages = acym_getLanguages(false, true);
958 $dataLanguages = [];
959
960 foreach ($languages as $langCode => $language) {
961 if ($langCode == "xx-XX") continue;
962
963 $oneLanguage = new \stdClass();
964 $oneLanguage->value = $langCode;
965 $oneLanguage->text = $language->name;
966
967 $dataLanguages[] = $oneLanguage;
968 }
969
970 usort($dataLanguages, function ($a, $b) {
971 return strtolower($a->text) > strtolower($b->text) ? 1 : -1;
972 });
973
974 return $dataLanguages;
975 }
976
977 public function setEmailConfirmationField(bool $displayOutside, object $field, string $container = 'div', bool $displayInline = false): void
978 {
979 $uniqueId = 'email_confirmation_field_'.acym_rand(100, 900);
980
981 echo '<'.esc_attr($container).' class="onefield acym_email_confirmation_field acyfield_text">';
982 if ($displayOutside) {
983 echo '<label class="cell margin-top-1">';
984 echo '<span class="acym__users__creation__fields__title">'.esc_html(acym_translation('ACYM_EMAIL_CONFIRMATION')).'</span>';
985 }
986
987 $placeholder = !$displayOutside ? acym_translation('ACYM_EMAIL_CONFIRMATION') : '';
988 echo '<input id="'.esc_attr($uniqueId).'"
989 '.(empty($field->option->size) ? '' : ' style="width:'.intval($field->option->size).'px"').'
990 required
991 type="email"
992 class="cell acym__user__edit__email"
993 name="user[email_confirmation]"
994 placeholder="'.esc_attr($placeholder).'" />';
995 echo '<span class="acym__field__error__block"></span>';
996
997 $extraErrors = $this->config->get('extra_errors', '0');
998 if ($extraErrors && !acym_isAdmin()) {
999 echo '<span class="acym__message__invalid__field acym__color__error" style="display: none;">';
1000 echo '<i class="acymicon-times-circle acym__cross__invalid acym__color__error"></i>';
1001 echo esc_html(acym_translation('ACYM_THANKS_TO_FILL_IN_THIS_FIELD'));
1002 echo '</span>';
1003 }
1004
1005 if ($displayOutside) {
1006 echo '</label>';
1007 }
1008 echo '</'.esc_attr($container).'>';
1009
1010 if ($container === 'td' && !$displayInline) {
1011 //TODO either open tr here, or close it where setEmailConfirmationField is called
1012 echo '</tr>';
1013 }
1014 }
1015 }
1016