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

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