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
← All changes | back/Classes/FieldClass.php +1073 -0 11.1.1 → 11.1.0 View file →
@@ -1,0 +1,1073 @@
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 +}