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/Helpers/ImportHelper.php +1335 -0 10.10.1 → 11.1.0 View file →
@@ -1,0 +1,1335 @@
1 +<?php
2 +
3 +namespace AcyMailing\Helpers;
4 +
5 +use AcyMailing\Classes\ListClass;
6 +use AcyMailing\Classes\UserClass;
7 +use AcyMailing\Core\AcymObject;
8 +
9 +class ImportHelper extends AcymObject
10 +{
11 + //Handle messages to say that X users have been subscribed to list Y
12 + private array $subscribedUsers = [];
13 + private array $importUserInLists = [];
14 + private array $allUserIds = [];
15 + private array $columns = [];
16 + private int $totalTry = 0;
17 + private int $totalValid = 0;
18 + private int $separatorsToRemove = 0;
19 + private bool $forceConfirm = false;
20 + private bool $generateName = true;
21 + private bool $overwrite = false;
22 + private string $header = '';
23 + private string $separator = ',';
24 +
25 + //Variables used to handle the import on filter
26 + public string $tableName = '';
27 + public array $dbWhere = [];
28 + public array $fieldsMap = [];
29 + public array $subscriptionHistory = [];
30 + public array $importedUsersByEmail = [];
31 +
32 + public function __construct()
33 + {
34 + parent::__construct();
35 + //We do an import... so it might take a lot of times and it also might take some memory... so we increase those limits if we can!
36 + acym_increasePerf();
37 + }
38 +
39 + public function file(): bool
40 + {
41 + //Step 1 : we copy the file in the correct directory
42 + $importFile = acym_getVar('array', 'import_file', [], 'files');
43 +
44 + if (empty($importFile['name'])) {
45 + acym_enqueueMessage(acym_translation('ACYM_PLEASE_BROWSE_FILE_IMPORT'), 'error');
46 +
47 + return false;
48 + }
49 +
50 + $extension = strtolower(acym_fileGetExt($importFile['name']));
51 +
52 + if (!preg_match('#^(csv)$#Ui', $extension) || acym_isForbiddenFileExtension($importFile['name'])) {
53 + acym_enqueueMessage(acym_translationSprintf('ACYM_ACCEPTED_TYPE', esc_html($extension), $this->config->get('allowed_files')), 'error');
54 +
55 + return false;
56 + }
57 +
58 + // Checking error upload
59 + $fileError = $importFile['error'];
60 + if ($fileError > 0) {
61 + switch ($fileError) {
62 + case 1:
63 + case 2:
64 + acym_enqueueMessage(acym_translation('ACYM_UPLOADED_FILE_EXCEED_MAX_FILESIZE_PHP'), 'error');
65 +
66 + return false;
67 + case 3:
68 + acym_enqueueMessage(acym_translation('ACYM_FILE_UPLOADED_PARTIALLY'), 'error');
69 +
70 + return false;
71 + case 4:
72 + acym_enqueueMessage(acym_translation('ACYM_NO_FILE_WAS_UPLOADED'), 'error');
73 +
74 + return false;
75 + default:
76 + acym_enqueueMessage(acym_translationSprintf('ACYM_UNKNOWN_ERROR_UPLOADING_FILE', $fileError), 'error');
77 +
78 + return false;
79 + }
80 + }
81 +
82 + $uploadPath = $this->createUploadFolder();
83 +
84 + $filename = uniqid('import_').'.csv';
85 + acym_setVar('acym_import_filename', $filename);
86 +
87 + if (!acym_uploadFile($importFile['tmp_name'], $uploadPath.$filename)) {
88 + acym_enqueueMessage(
89 + acym_translationSprintf(
90 + 'ACYM_FAIL_UPLOAD',
91 + '<b><i>'.esc_html($importFile['tmp_name']).'</i></b>',
92 + '<b><i>'.esc_html($uploadPath.$filename).'</i></b>'
93 + ),
94 + 'error'
95 + );
96 + }
97 +
98 + return true;
99 + }
100 +
101 + public function textarea(): bool
102 + {
103 + $content = acym_getVar('string', 'acym__users__import__from_text__textarea');
104 + $path = $this->createUploadFolder();
105 + $filename = uniqid('import_').'.csv';
106 +
107 + acym_writeFile($path.$filename, $content);
108 + acym_setVar('acym_import_filename', $filename);
109 +
110 + return true;
111 + }
112 +
113 + public function cms(): bool
114 + {
115 + global $acymCmsUserVars;
116 +
117 + //Update the users which already have a userid
118 + $query = 'UPDATE IGNORE '.$acymCmsUserVars->table.' as b, #__acym_user as a SET a.email = LOWER(b.'.$acymCmsUserVars->email.'), a.name = b.'.$acymCmsUserVars->name.', a.active = 1 - b.'.$acymCmsUserVars->blocked.' WHERE a.cms_id = b.'.$acymCmsUserVars->id.' AND a.cms_id IS NOT NULL';
119 + $nbUpdated = acym_query($query);
120 +
121 + //Step 1 : update the existing ones.
122 + //Update the users to give them a userid
123 + $query = 'UPDATE IGNORE '.$acymCmsUserVars->table.' as b, #__acym_user as a SET a.cms_id = b.'.$acymCmsUserVars->id.' WHERE a.email = b.'.$acymCmsUserVars->email;
124 + $affected = acym_query($query);
125 + $nbUpdated += intval($affected);
126 +
127 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_UPDATE', $nbUpdated), 'success');
128 +
129 + //Step 2 : delete the ones which don't exist any more
130 + $query = 'SELECT a.id FROM #__acym_user as a LEFT JOIN '.$acymCmsUserVars->table.' as b on a.cms_id = b.'.$acymCmsUserVars->id.' WHERE b.'.$acymCmsUserVars->id.' IS NULL AND a.cms_id > 0';
131 + $deletedSubid = acym_loadResultArray($query);
132 +
133 + $query = 'SELECT a.id FROM #__acym_user as a LEFT JOIN '.$acymCmsUserVars->table.' as b on a.email = b.'.$acymCmsUserVars->email.' WHERE b.'.$acymCmsUserVars->id.' IS NULL AND a.cms_id > 0';
134 + $deletedSubid = array_merge(acym_loadResultArray($query), $deletedSubid);
135 +
136 + if (!empty($deletedSubid)) {
137 + $userClass = new UserClass();
138 + $deletedUsers = $userClass->delete($deletedSubid);
139 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_DELETE', $deletedUsers));
140 + }
141 +
142 + //Step 3 : insert the new ones
143 + $time = time();
144 + $formattedTime = acym_date($time, 'Y-m-d H:i:s');
145 + $sourceImport = 'Import on '.$formattedTime;
146 + $query = 'INSERT IGNORE INTO #__acym_user (`name`,`email`,`creation_date`,`active`,`cms_id`, `source`) SELECT `user`.`'.$acymCmsUserVars->name.'`,LOWER(`user`.`'.$acymCmsUserVars->email.'`),`user`.`'.$acymCmsUserVars->registered.'`,1 - `user`.'.$acymCmsUserVars->blocked.',`user`.`'.$acymCmsUserVars->id.'`,\''.$sourceImport.'\' FROM '.$acymCmsUserVars->table.' AS `user` ';
147 +
148 + $queryJoin = [];
149 + $queryWhere = [];
150 + if (ACYM_CMS === 'wordpress' && is_multisite()) {
151 + $queryJoin[] = 'JOIN #__usermeta AS `meta` ON `user`.'.$acymCmsUserVars->id.'=`meta`.`user_id`';
152 + $queryWhere[] = '`meta`.`meta_key`=\'#__capabilities\'';
153 + }
154 + $groups = acym_getVar('array', 'groups', []);
155 + $this->config->saveConfig(['import_groups' => implode(',', $groups)]);
156 + if (!empty($groups)) {
157 + if (ACYM_CMS === 'joomla') {
158 + acym_arrayToInteger($groups);
159 + $queryJoin[] = ' JOIN #__user_usergroup_map AS `map` ON map.user_id = `user`.`'.$acymCmsUserVars->id.'`';
160 + $queryWhere[] = '`map`.`group_id` IN ('.implode(', ', $groups).')';
161 + } else {
162 + if (!is_multisite()) {
163 + $queryJoin[] = 'JOIN #__usermeta AS `meta` ON meta.user_id = `user`.`'.$acymCmsUserVars->id.'` AND `meta`.`meta_key` = "#__capabilities"';
164 + }
165 +
166 + foreach ($groups as $i => $oneGroup) {
167 + $groups[$i] = acym_escapeDB('%'.strlen($oneGroup).':"'.$oneGroup.'"%');
168 + }
169 + $queryWhere[] = '`meta`.`meta_value` LIKE '.implode(' OR `meta`.`meta_value` LIKE ', $groups);
170 + }
171 + }
172 +
173 + $query .= implode(' ', $queryJoin);
174 + if (!empty($queryWhere)) {
175 + $query .= ' WHERE ('.implode(') AND (', $queryWhere).')';
176 + }
177 +
178 + $insertedUsers = acym_query($query);
179 +
180 + acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = \'last_import\'');
181 +
182 + acym_trigger('onAcymAfterCMSUserImport', [$sourceImport]);
183 +
184 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $insertedUsers), 'info');
185 +
186 + //Step 4 : subscribe all the registered users to one or several lists
187 + $lists = $this->getImportedLists();
188 + $listsSubscribe = [];
189 + if (!empty($lists)) {
190 + foreach ($lists as $listid => $val) {
191 + if (!empty($val)) {
192 + $listsSubscribe[] = intval($listid);
193 + }
194 + }
195 + }
196 +
197 + if (empty($listsSubscribe)) {
198 + return true;
199 + }
200 +
201 + $query = 'INSERT IGNORE INTO #__acym_user_has_list (`user_id`,`list_id`,`status`,`subscription_date`) ';
202 + $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(gmdate('Y-m-d H:i:s', time())).'
203 + FROM #__acym_list AS list, #__acym_user AS user ';
204 + $conditions = [];
205 + $conditions[] = 'list.`id` IN ('.implode(',', $listsSubscribe).')';
206 + $conditions[] = 'user.`cms_id` > 0';
207 +
208 + if (!empty($groups)) {
209 + if (ACYM_CMS === 'joomla') {
210 + $query .= ' JOIN #__user_usergroup_map AS `map` ON map.user_id = `user`.`cms_id`';
211 + $conditions[] = '`map`.`group_id` IN ('.implode(', ', $groups).')';
212 + } else {
213 + $query .= ' JOIN #__usermeta AS `meta` ON meta.user_id = `user`.`cms_id` AND `meta`.`meta_key` = "#__capabilities"';
214 + $conditions[] = '(`meta`.`meta_value` LIKE '.implode(' OR `meta`.`meta_value` LIKE ', $groups).')';
215 + }
216 + }
217 +
218 + $query .= ' WHERE '.implode(' AND ', $conditions);
219 + $nbsubscribed = acym_query($query);
220 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_SUBSCRIPTION', $nbsubscribed));
221 +
222 + return true;
223 + }
224 +
225 +
226 + public function database(bool $onlyImport = false): bool
227 + {
228 + $this->forceConfirm = acym_getVar('bool', 'import_confirmed_database', true);
229 +
230 + $table = empty($this->tableName) ? trim(acym_getVar('string', 'tablename')) : $this->tableName;
231 + $time = time();
232 + $formattedTime = acym_date($time, 'Y-m-d H:i:s');
233 +
234 + if (empty($table)) {
235 + acym_enqueueMessage(acym_translation('ACYM_SPECIFYTABLE'), 'warning');
236 +
237 + return false;
238 + }
239 +
240 + //We got a table... let's load the fields from this table
241 + $fields = acym_getColumns($table, false, false);
242 + if (empty($fields)) {
243 + //there is no field... so we consider it was the wrong table
244 + acym_enqueueMessage(acym_translation('ACYM_SPECIFYTABLE'), 'warning');
245 +
246 + return false;
247 + }
248 +
249 + //Ok now we have some fields and now we have a table selected.
250 + $equivalentFields = empty($this->fieldsMap) ? acym_getVar('array', 'fields', []) : $this->fieldsMap;
251 +
252 + if (empty($equivalentFields['email'])) {
253 + acym_enqueueMessage(acym_translation('ACYM_SPECIFYFIELDEMAIL'), 'warning');
254 +
255 + return false;
256 + }
257 +
258 + $select = [];
259 + //We check the data and create the query
260 + foreach ($equivalentFields as $acyField => $tableField) {
261 + $tableField = trim($tableField);
262 + if (empty($tableField)) {
263 + continue;
264 + }
265 + if (!in_array($tableField, $fields)) {
266 + acym_enqueueMessage(acym_translationSprintf('ACYM_SPECIFYFIELD', $tableField, implode(' <br> ', $fields)), 'warning');
267 +
268 + return false;
269 + }
270 + $select['`'.acym_secureDBColumn($acyField).'`'] = acym_secureDBColumn($tableField);
271 + }
272 +
273 + if (empty($select['`creation_date`'])) {
274 + $select['`creation_date`'] = acym_escapeDB(acym_date('now', 'Y-m-d H:i:s'));
275 + }
276 +
277 + if ($this->forceConfirm && empty($select['`confirmed`'])) {
278 + $select['`confirmed`'] = 1;
279 + }
280 +
281 + $sourceTxt = 'Import on '.$formattedTime;
282 + $select['`source`'] = acym_escapeDB($sourceTxt);
283 +
284 + $emailColumn = acym_secureDBColumn($select['`email`']);
285 + $select['`email`'] = 'LOWER('.$emailColumn.')';
286 +
287 + $query = 'INSERT IGNORE INTO #__acym_user ('.implode(' , ', array_keys($select)).') SELECT '.implode(' , ', $select);
288 + $query .= ' FROM '.acym_secureDBColumn($table).' WHERE '.$emailColumn.' LIKE "%@%"';
289 + if (!empty($this->dbWhere)) {
290 + $query .= ' AND ( '.implode(' ) AND (', $this->dbWhere).' )';
291 + }
292 +
293 + $affectedRows = acym_query($query);
294 +
295 + acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = "last_import"');
296 +
297 + acym_trigger('onAcymAfterDatabaseUserImport', [$sourceTxt]);
298 +
299 + if ($onlyImport) {
300 + return true;
301 + }
302 +
303 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $affectedRows), 'info');
304 +
305 +
306 + //Step 4 : subscribe all the registered users to one or several lists
307 + $lists = $this->getImportedLists();
308 + $listsSubscribe = [];
309 + if (!empty($lists)) {
310 + foreach ($lists as $listid => $val) {
311 + if (!empty($val)) {
312 + $listsSubscribe[] = intval($listid);
313 + }
314 + }
315 + }
316 +
317 + if (empty($listsSubscribe)) {
318 + return true;
319 + }
320 +
321 + $query = 'INSERT IGNORE INTO #__acym_user_has_list (`user_id`,`list_id`,`status`,`subscription_date`) ';
322 + $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(gmdate('Y-m-d H:i:s', time())).'
323 + FROM #__acym_list AS list, #__acym_user AS user
324 + WHERE list.`id` IN ('.implode(',', $listsSubscribe).')
325 + AND user.`email` IN (SELECT LOWER('.$emailColumn.') FROM '.acym_secureDBColumn($table).')';
326 +
327 + $nbsubscribed = acym_query($query);
328 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_SUBSCRIPTION', $nbsubscribed));
329 +
330 + return true;
331 + }
332 +
333 + public function mailpoet(): bool
334 + {
335 + // Step 1 : import subscriber from mailpoet
336 + $time = time();
337 + $formattedTime = acym_date($time, 'Y-m-d H:i:s');
338 + $sourceImport = 'import_on_'.$formattedTime.'_mailpoet';
339 +
340 + $query = 'INSERT IGNORE INTO #__acym_user (`name`,`email`,`creation_date`,`active`, `confirmed`,`cms_id`, `source`)
341 + SELECT CONCAT(`subscriber`.`first_name`, `subscriber`.`last_name`),
342 + LOWER(`subscriber`.`email`),
343 + `subscriber`.`created_at`,
344 + IF(`subscriber`.`status` = "inactive", 0, 1),
345 + IF(`subscriber`.`status` = "subscribed", 1, 0),
346 + `subscriber`.`wp_user_id`,
347 + \''.$sourceImport.'\'
348 + FROM #__mailpoet_subscribers AS `subscriber` ';
349 + $listsMailpoet = acym_getVar('array', 'mailpoet_lists', []);
350 + if (!empty($listsMailpoet)) {
351 + acym_arrayToInteger($listsMailpoet);
352 + $query .= ' JOIN #__mailpoet_subscriber_segment AS sub_segment ON `subscriber`.`id` = `sub_segment`.`subscriber_id` AND `sub_segment`.`segment_id` IN ('.implode(
353 + ',',
354 + $listsMailpoet
355 + ).')';
356 + }
357 + $query .= ' WHERE `subscriber`.`deleted_at` IS NULL ON DUPLICATE KEY UPDATE `source`='.acym_escapeDB($sourceImport);
358 +
359 + $insertedUsers = acym_query($query);
360 +
361 + acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = \'last_import\'');
362 +
363 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $insertedUsers), 'info');
364 +
365 + // Step 2 : subscribe all the registered users to one or several lists
366 + $lists = $this->getImportedLists();
367 + $listsSubscribe = [];
368 + if (!empty($lists)) {
369 + foreach ($lists as $listid => $val) {
370 + if (!empty($val)) {
371 + $listsSubscribe[] = intval($listid);
372 + }
373 + }
374 + }
375 +
376 + if (empty($listsSubscribe)) {
377 + return true;
378 + }
379 +
380 + $query = 'INSERT IGNORE INTO #__acym_user_has_list (`user_id`,`list_id`,`status`,`subscription_date`) ';
381 + $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(gmdate('Y-m-d H:i:s', time())).'
382 + FROM #__acym_list AS list, #__acym_user AS user ';
383 + $conditions = [];
384 + $conditions[] = 'list.`id` IN ('.implode(',', $listsSubscribe).')';
385 + $conditions[] = 'user.`source` = '.acym_escapeDB($sourceImport);
386 +
387 + if (!empty($listsMailpoet)) {
388 + $query .= ' JOIN #__mailpoet_subscribers AS subscriber ON subscriber.email = user.email';
389 + $query .= ' JOIN #__mailpoet_subscriber_segment AS subscriber_segment ON subscriber.id = subscriber_segment.subscriber_id AND subscriber_segment.segment_id IN ('.implode(
390 + ',',
391 + $listsMailpoet
392 + ).')';
393 + }
394 +
395 + $query .= ' WHERE '.implode(' AND ', $conditions);
396 + $nbsubscribed = acym_query($query);
397 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_SUBSCRIPTION', $nbsubscribed));
398 +
399 + return true;
400 + }
401 +
402 + public function finalizeImport(): bool
403 + {
404 + $filename = strtolower(acym_getVar('cmd', 'acym_import_filename'));
405 + $extension = '.'.acym_fileGetExt($filename);
406 + $this->forceConfirm = acym_getVar('bool', 'import_confirmed_generic', true);
407 + $this->generateName = acym_getVar('bool', 'import_generate_generic', true);
408 + $this->overwrite = acym_getVar('bool', 'import_overwrite_generic', true);
409 +
410 + // Remember user's choices
411 + $this->config->saveConfig(
412 + [
413 + 'import_confirmed' => $this->forceConfirm,
414 + 'import_generate' => $this->generateName,
415 + 'import_overwrite' => $this->overwrite,
416 + ]
417 + );
418 +
419 + $filename = str_replace(['.', ' '], '_', substr($filename, 0, strpos($filename, $extension))).$extension;
420 + $uploadPath = ACYM_MEDIA.'import'.DS.$filename;
421 +
422 + if (!file_exists($uploadPath) || !is_file($uploadPath)) {
423 + acym_enqueueMessage(acym_translation('ACYM_UPLOADED_FILE_NOT_FOUND').' '.$uploadPath, 'error');
424 +
425 + return false;
426 + }
427 +
428 + $importColumns = acym_getVar('string', 'import_columns');
429 + if (empty($importColumns)) {
430 + acym_enqueueMessage(acym_translation('ACYM_COLUMNS_NOT_FOUND'), 'error');
431 +
432 + return false;
433 + }
434 +
435 + $contentFile = file_get_contents($uploadPath);
436 +
437 + //We convert into the correct charset
438 + if (acym_getVar('cmd', 'acyencoding', '') != '') {
439 + $encodingHelper = new EncodingHelper();
440 + $contentFile = $encodingHelper->change($contentFile, acym_getVar('cmd', 'acyencoding'), 'UTF-8');
441 + }
442 +
443 + $cutContent = str_replace(["\r\n", "\r"], "\n", $contentFile);
444 + $allLines = explode("\n", $cutContent);
445 +
446 + $listSeparators = ["\t", ';', ','];
447 + $separator = ',';
448 + foreach ($listSeparators as $sep) {
449 + if (strpos($allLines[0], $sep) !== false) {
450 + $separator = $sep;
451 + break;
452 + }
453 + }
454 +
455 + if (!empty($listsId)) {
456 + $allLines[0] .= $sep;
457 + }
458 +
459 + $importColumns = str_replace(',', $separator, $importColumns);
460 +
461 + if (strpos($allLines[0], '@')) {
462 + $contentFile = $importColumns."\n".$contentFile;
463 + } else {
464 + $allLines[0] = $importColumns;
465 + $contentFile = implode("\n", $allLines);
466 + }
467 +
468 + $this->handleContent($contentFile);
469 +
470 + //We can now delete the file
471 + acym_deleteFile($uploadPath);
472 + $this->cleanImportFolder();
473 +
474 + return true;
475 + }
476 +
477 + public function getImportedLists(): array
478 + {
479 + $listClass = new ListClass();
480 + $listsId = json_decode(acym_getVar('string', 'acym__entity_select__selected'), true);
481 + $newListName = acym_getVar('string', 'new_list');
482 +
483 + if (empty($listsId) && empty($newListName)) {
484 + return [];
485 + }
486 +
487 + $lists = [];
488 +
489 + if (!empty($newListName)) {
490 + $newList = new \stdClass();
491 + $newList->name = $newListName;
492 + $newList->active = 1;
493 + $colors = '#'.substr(str_shuffle('ABCDEF0123456789'), 0, 6);
494 + $newList->color = $colors;
495 + $listid = $listClass->save($newList);
496 + $lists[$listid] = 1;
497 + }
498 +
499 + if (!empty($listsId)) {
500 + foreach ($listsId as $id) {
501 + $lists[$id] = 1;
502 + }
503 + }
504 +
505 + return $lists;
506 + }
507 +
508 + public function displayAdditionalDataUsersImport(bool $isGeneric): void
509 + {
510 + $buttonAddListId = $isGeneric ? 'acym__users__generic__import__create-list__button' : 'acym__users__import__create-list__button';
511 + $buttonImportClass = $isGeneric ? 'acym__users__import__generic__import__button' : 'acym__users__import__button';
512 + $buttonSkipId = $isGeneric ? 'acym__users__generic__import__skip__button' : 'acym__users__import__skip__button';
513 + $buttonImportDataTask = $isGeneric ? 'listing' : '';
514 + ?>
515 + <div class="cell align-right grid-x margin-bottom-2">
516 + <div id="acym__users__import__create-list" class="grid-x" style="display: none;">
517 + <label for="acym__users__import__create-list__field" class="margin-right-1 acym_vcenter">
518 + <?php echo esc_html(acym_translation('ACYM_LIST_NAME')); ?> :
519 + </label>
520 + <div>
521 + <input id="acym__users__import__create-list__field" type="text">
522 + </div>
523 + </div>
524 + <button type="button" class="button button-secondary margin-left-1 acym_vcenter margin-right-2" id="<?php echo esc_attr($buttonAddListId); ?>">
525 + <?php echo esc_html(acym_translation('ACYM_CREATE_NEW_LIST')); ?>
526 + </button>
527 + <i style="display: none;" class="acym_vcenter acymicon-circle-o-notch acymicon-spin" id="acym__users__import__create-list__loading-logo"></i>
528 + </div>
529 + <div class="cell align-right grid-x">
530 + <button type="button" class="button-secondary button cell shrink margin-right-1" id="<?php echo esc_attr($buttonSkipId); ?>">
531 + <?php echo esc_html(acym_translation('ACYM_SKIP')); ?>
532 + </button>
533 + <button
534 + type="button"
535 + class="button-primary button acy_button_submit cell shrink margin-right-2 <?php echo esc_attr($buttonImportClass); ?>"
536 + id="acym__entity_select__button__submit" data-task="<?php echo esc_attr($buttonImportDataTask); ?>">
537 + <?php echo esc_html(acym_translation('ACYM_SUBSCRIBE_USERS_TO_THESE_LISTS')); ?>
538 + </button>
539 + </div>
540 + <?php
541 + }
542 +
543 + private function createUploadFolder(): string
544 + {
545 + $folderPath = acym_cleanPath(ACYM_ROOT.trim(html_entity_decode(ACYM_MEDIA_FOLDER.'import'))).DS;
546 + if ('wordpress' === ACYM_CMS) {
547 + $folderPath = acym_cleanPath(WP_PLUGIN_DIR.DS.ACYM_COMPONENT.DS.'media'.DS.'import').DS;
548 + }
549 + if (!is_dir($folderPath)) {
550 + acym_createDir($folderPath, true, true);
551 + }
552 +
553 + if (!acym_isWritable($folderPath)) {
554 + acym_chmod($folderPath, 0755);
555 + if (!acym_isWritable($folderPath)) {
556 + acym_enqueueMessage(acym_translationSprintf('ACYM_WRITABLE_FOLDER', $folderPath), 'warning');
557 + }
558 + }
559 +
560 + return $folderPath;
561 + }
562 +
563 + private function handleContent(string $contentFile): void
564 + {
565 + $timestamp = time();
566 +
567 + //We convert the file into something valid so that mac or not will be Ok!
568 + $contentFile = str_replace(["\r\n", "\r"], "\n", $contentFile);
569 + $importLines = explode("\n", $contentFile);
570 +
571 + //A little trick to take the second line if the first line is empty...
572 + $i = 0;
573 + $this->header = '';
574 + $this->allUserIds = [];
575 + while (empty($this->header) && $i < 10) {
576 + $this->header = trim($importLines[$i]);
577 + $i++;
578 + }
579 +
580 + if (strpos($this->header, '@') && !strpos($this->header, ',') && !strpos($this->header, ';') && !strpos($this->header, "\t")) {
581 + //We have an @ in the first line so there is definitely an issue there and the first line is not the right one!
582 + //And we don't have separators so we know the format
583 + $this->header = 'email';
584 + $i--;
585 + }
586 +
587 + //Step 3 : we make sure the header of the file is correct (so it does correspond to columns from the database)
588 + if (!$this->autoDetectHeader()) {
589 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_HEADER', esc_html($this->header)), 'error');
590 + acym_enqueueMessage(acym_translation('ACYM_IMPORT_EMAIL'), 'error');
591 +
592 + return;
593 + }
594 +
595 + $encodingHelper = new EncodingHelper();
596 + $userClass = new UserClass();
597 + $listClass = new ListClass();
598 +
599 + $allLists = $listClass->getAll('name');
600 + $numberColumns = count($this->columns);
601 + $countUsersBeforeImport = $userClass->getCountTotalUsers();
602 + $importUsers = [];
603 + $errorLines = [];
604 +
605 + //Step 4 : we start importing the file as everything is OK
606 + while (isset($importLines[$i])) {
607 + // If there are quotes in the line, check if it is a broken line or not
608 + if (strpos($importLines[$i], '"') !== false) {
609 + $data = [];
610 + $j = $i + 1;
611 + // The position is the separator's one, -1 for the beginning as the line does not start by a separator
612 + $position = -1;
613 +
614 + // Concatenate 30 lines max
615 + while ($j < ($i + 30)) {
616 + // Test if the value is encapsulated by quotes
617 + $quoteOpened = substr($importLines[$i], $position + 1, 1) == '"';
618 +
619 + // If encapsulated, search the end of the value
620 + if ($quoteOpened) {
621 + $nextQuotePosition = strpos($importLines[$i], '"', $position + 2);
622 + // If quotes in the value encapsulated by quotes... find the real closing quote...
623 + while (
624 + $nextQuotePosition !== false
625 + && $nextQuotePosition + 1 != strlen($importLines[$i])
626 + && substr($importLines[$i], $nextQuotePosition + 1, 1) != $this->separator
627 + ) {
628 + $nextQuotePosition = strpos($importLines[$i], '"', $nextQuotePosition + 1);
629 + }
630 +
631 + // If we didn't find the whole value, then concatenate the current line with the next one
632 + if ($nextQuotePosition === false) {
633 + // If end of import file, error...
634 + if (!isset($importLines[$j])) {
635 + break;
636 + }
637 +
638 + $importLines[$i] .= "\n".$importLines[$j];
639 + $importLines[$i] = rtrim($importLines[$i], $this->separator);
640 + unset($importLines[$j]);
641 + $j++;
642 + } else {
643 + // Found the entire value, add it to data and move the position to the next separator
644 +
645 + // If the quote is at the end of the line and the line is completed...
646 + if (strlen($importLines[$i]) - 1 == $nextQuotePosition) {
647 + $data[] = substr($importLines[$i], $position + 1);
648 + break;
649 + }
650 + // ,"toto",value...
651 + // 3456789
652 + // Start in 3+1 so 4: the first quote
653 + // Length = 9+1-(3+1) = 6, the length of "toto".
654 + $data[] = substr($importLines[$i], $position + 1, $nextQuotePosition + 1 - ($position + 1));
655 + $position = $nextQuotePosition + 1;
656 + }
657 + } else {
658 + // If not encapsulated by quotes, search the next separator
659 + $nextSeparatorPosition = strpos($importLines[$i], $this->separator, $position + 1);
660 + // If not found, the line is completed
661 + if ($nextSeparatorPosition === false) {
662 + $data[] = substr($importLines[$i], $position + 1);
663 + break;
664 + } else { // If found the next separator, add the value in $data and change the position
665 + $data[] = substr($importLines[$i], $position + 1, $nextSeparatorPosition - ($position + 1));
666 + $position = $nextSeparatorPosition;
667 + }
668 + }
669 + }
670 +
671 + // We unset some lines, don't forget to change the remaining lines keys
672 + $importLines = array_merge($importLines);
673 + } else {
674 + //We remove the separators from the end of the string as we don't need them... they will be added as empty string anyway during the import process.
675 + $data = explode($this->separator, rtrim(trim($importLines[$i]), $this->separator));
676 + }
677 +
678 + //We clean it... maybe there are other arguments at the end we should remove
679 + if (!empty($this->separatorsToRemove)) {
680 + for ($b = $numberColumns + $this->separatorsToRemove - 1; $b >= $numberColumns; $b--) {
681 + if (isset($data[$b]) && (strlen($data[$b]) == 0 || $data[$b] == ' ')) {
682 + unset($data[$b]);
683 + }
684 + }
685 + }
686 +
687 + $i++;
688 + //We don't handle empty lines...
689 + if (empty($importLines[$i - 1])) {
690 + continue;
691 + }
692 +
693 + $this->totalTry++;
694 + //Lets try to fix it first...
695 + if (count($data) > $numberColumns) {
696 + $copy = $data;
697 + foreach ($copy as $oneelem => $oneval) {
698 + if (
699 + !empty($oneval[0])
700 + && $oneval[0] === '"'
701 + && $oneval[strlen($oneval) - 1] !== '"'
702 + && isset($copy[$oneelem + 1])
703 + && $copy[$oneelem + 1][strlen($copy[$oneelem + 1]) - 1] == '"'
704 + ) {
705 + //We concat both with the separator
706 + $data[$oneelem] = $copy[$oneelem].$this->separator.$copy[$oneelem + 1];
707 + unset($data[$oneelem + 1]);
708 + }
709 + }
710 +
711 + $data = array_values($data);
712 + }
713 +
714 + // Not enough columns found...
715 + if (count($data) < $numberColumns) {
716 + // If not enough info compared to the header... lets add them as empty! We don't care if the user does not specify everything...
717 + for ($a = count($data); $a < $numberColumns; $a++) {
718 + $data[$a] = '';
719 + }
720 + }
721 +
722 + if (count($data) != $numberColumns) {
723 + static $errorcount = 0;
724 + if (empty($errorcount)) {
725 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_ARGUMENTS', $numberColumns), 'warning');
726 + }
727 + $errorcount++;
728 +
729 + //If it's for the first line, we return so we don't check the rest and let the user fix it
730 + if ($this->totalTry == 1) {
731 + return;
732 + }
733 + if (empty($errorLines)) {
734 + $errorLines[] = 'error,'.$importLines[0];
735 + }
736 + $errorLines[] = acym_translation('ACYM_IMPORT_ERROR_WRONG_NUMBER_ARGUMENTS').','.$importLines[$i - 1];
737 + continue;
738 + }
739 +
740 + $newUser = new \stdClass();
741 + $newUser->customfields = [];
742 +
743 + // Handle email column first to be able to use it with listids and listname
744 + $emailKey = array_search('email', $this->columns);
745 + $newUser->email = trim(acym_stripTags($data[$emailKey]), '\'" ');
746 + // Remove all whitespace type
747 + $newUser->email = preg_replace("/\s+/u", '', $newUser->email);
748 + if (!empty($newUser->email)) {
749 + $newUser->email = acym_punycode($newUser->email);
750 + }
751 + $newUser->email = trim(str_replace([' ', "\t"], '', $encodingHelper->change($newUser->email, 'UTF-8', 'ISO-8859-1')));
752 + $newUser->email = strtolower($newUser->email);
753 +
754 +
755 + if (!acym_isValidEmail($newUser->email)) {
756 + static $errorcountfail = 0;
757 + //We limit to 10 errors otherwise it may break the Joomla messaging system in terms of memory usage
758 + if ($errorcountfail == 0) {
759 + acym_enqueueMessage(acym_translation('ACYM_ADDRESSES_INVALID'), 'warning');
760 + }
761 + $errorcountfail++;
762 + if (empty($errorLines)) {
763 + $errorLines[] = 'error,'.$importLines[0];
764 + }
765 + $errorLines[] = acym_translation('ACYM_INVALID_EMAIL_ADDRESS').','.$importLines[$i - 1];
766 + continue;
767 + }
768 +
769 + foreach ($data as $num => $value) {
770 + // Already handled the email address
771 + if ($num == $emailKey) continue;
772 +
773 + $field = $this->columns[$num];
774 +
775 + // Ignored
776 + if ($field == 1) continue;
777 +
778 + if ($field === 'listids') {
779 + //We explode the listids separated by "-" and forach of them we add the user in the importUserInLists
780 + $liststosub = explode('-', trim($value, '\'" '));
781 + foreach ($liststosub as $onelistid) {
782 + $this->importUserInLists[intval(trim($onelistid))][] = acym_escapeDB($newUser->email);
783 + }
784 + continue;
785 + }
786 +
787 + if ($field === 'listname') {
788 + $liststosub = explode('-', trim($value, '\'" '));
789 + foreach ($liststosub as $onelistName) {
790 + if (empty($onelistName)) {
791 + continue;
792 + }
793 + $onelistName = trim($onelistName);
794 + if (empty($allLists[$onelistName])) {
795 + $newList = new \stdClass();
796 + $newList->name = $onelistName;
797 + $newList->active = 1;
798 + $colors = ['#3366ff', '#7240A4', '#7A157D', '#157D69', '#ECE649'];
799 + $newList->color = $colors[acym_rand(0, count($colors) - 1)];
800 + $listid = $listClass->save($newList);
801 + $newList->id = $listid;
802 + $allLists[$onelistName] = $newList;
803 + }
804 + $this->importUserInLists[intval($allLists[$onelistName]->id)][] = acym_escapeDB($newUser->email);
805 + }
806 + continue;
807 + }
808 +
809 + if (strpos($field, 'subscription_date_') === 0 || strpos($field, 'unsubscription_date_') === 0) {
810 + $value = trim($value, '"');
811 + if (strpos($field, 'subscription_date_') === 0) {
812 + $listId = (int)str_replace('subscription_date_', '', $field);
813 + $dateType = 'subscribed_date';
814 + } else {
815 + $listId = (int)str_replace('unsubscription_date_', '', $field);
816 + $dateType = 'unsubscribed_date';
817 + }
818 +
819 + if (!isset($this->subscriptionHistory[$listId])) {
820 + $this->subscriptionHistory[$listId] = [];
821 + }
822 +
823 + $email = $newUser->email;
824 + $userFound = false;
825 +
826 + foreach ($this->subscriptionHistory[$listId] as &$entry) {
827 + if ($entry['email'] === $email) {
828 + $entry[$dateType] = $value;
829 + $userFound = true;
830 + break;
831 + }
832 + }
833 + unset($entry);
834 +
835 + if (!$userFound) {
836 + $this->subscriptionHistory[$listId][] = [
837 + 'email' => $email,
838 + $dateType => $value,
839 + ];
840 + }
841 +
842 + continue;
843 + }
844 +
845 +
846 + // If we assigned the data to an Acy custom field
847 + if (strpos($field, 'cf_') === 0) {
848 + $newUser->customfields[substr($field, 3)] = trim(acym_stripTags($value), '\'" ');
849 + continue;
850 + }
851 +
852 + if ($value === 'null') {
853 + $newUser->$field = '';
854 + } else {
855 + //We remove anything which should not be there (quotes or spaces or return char or html tags)
856 + $newUser->$field = trim(acym_stripTags($value), '\'" ');
857 + }
858 + }
859 +
860 + if (acym_isMultilingual() && empty($newUser->language)) {
861 + $newUser->language = $this->config->get('multilingual_default', '');
862 + }
863 +
864 + //Everything is Ok... we can add the line
865 + $importUsers[] = $newUser;
866 + $this->totalValid++;
867 +
868 + //Every 50 users, we handle it
869 + if ($this->totalValid % 50 == 0) {
870 + $this->insertUsers($importUsers, $timestamp);
871 + $importUsers = [];
872 + }
873 + }
874 +
875 + if (!empty($errorLines)) {
876 + $filename = strtolower(acym_getVar('cmd', 'acym_import_filename', ''));
877 + if (!empty($filename)) {
878 + $extension = '.'.acym_fileGetExt($filename);
879 + $filename = str_replace(['.', ' '], '_', substr($filename, 0, strpos($filename, $extension))).$extension;
880 + $errorFile = implode("\n", $errorLines);
881 + acym_writeFile(ACYM_MEDIA.'import'.DS.'error_'.$filename, $errorFile);
882 + acym_enqueueMessage(
883 + '<a target="_blank" href="'.acym_prepareAjaxURL((acym_isAdmin() ? '' : 'front').'users&task=downloadImport').'&filename=error_'.preg_replace(
884 + '#\.[^.]*$#',
885 + '',
886 + $filename
887 + ).'&'.acym_noTemplate().'" >'.acym_translation('ACYM_DOWNLOAD_IMPORT_ERRORS').'</a>',
888 + 'notice'
889 + );
890 + }
891 + }
892 +
893 + $this->insertUsers($importUsers, $timestamp);
894 +
895 + acym_query('UPDATE #__acym_configuration SET `value` = '.intval($timestamp).' WHERE `name` = \'last_import\'');
896 +
897 + // We could have imported empty values
898 + acym_query('DELETE FROM #__acym_user_has_field WHERE `value` = ""');
899 +
900 + $countUsersAfterImport = $userClass->getCountTotalUsers();
901 + $totalInserted = $countUsersAfterImport - $countUsersBeforeImport;
902 +
903 + $maybeUpdated = acym_translation('ACYM_NOT_UPDATED');
904 + if ($this->overwrite) {
905 + $maybeUpdated = acym_translation('ACYM_UPDATED');
906 + }
907 + $reportMsg = acym_translationSprintf(
908 + 'ACYM_IMPORT_REPORTING_UPDATED',
909 + $this->totalTry,
910 + $totalInserted,
911 + $this->totalTry - $this->totalValid,
912 + $this->totalValid - $totalInserted,
913 + $maybeUpdated
914 + );
915 + acym_enqueueMessage($reportMsg, 'info');
916 +
917 + $this->insertSubscriptionHistory();
918 +
919 + // All users have been added properly into the database... we will now subscribe the users
920 + $this->subscribeUsers();
921 + }
922 +
923 + private function insertUsers(array $users, int $timestamp): void
924 + {
925 + if (empty($users)) {
926 + return;
927 + }
928 +
929 + $importedCols = array_keys(get_object_vars($users[0]));
930 + unset($importedCols[array_search('customfields', $importedCols)]);
931 + if ($this->forceConfirm) $importedCols[] = 'confirmed';
932 +
933 + foreach ($users as $a => $oneUser) {
934 + $this->checkData($users[$a], $timestamp);
935 + }
936 +
937 + $columns = reset($users);
938 + $colNames = array_keys(get_object_vars($columns));
939 + unset($colNames[array_search('customfields', $colNames)]);
940 +
941 + if (!in_array('key', $colNames)) $colNames[] = 'key';
942 +
943 + foreach ($colNames as $oneColumn) {
944 + acym_secureDBColumn($oneColumn);
945 + }
946 +
947 + $dateColumns = $this->getUserDateColumns();
948 +
949 + $queryInsertUsers = 'INSERT'.($this->overwrite ? '' : ' IGNORE').' INTO #__acym_user (`'.implode('`,`', $colNames).'`) VALUES (';
950 + $values = [];
951 + $customFieldsvalues = [];
952 + $allemails = [];
953 + foreach ($users as $oneUser) {
954 + $value = [];
955 + acym_trigger('onAcymBeforeUserImport', [&$oneUser]);
956 +
957 + foreach ($oneUser as $map => $oneValue) {
958 + if ($map === 'customfields') continue;
959 +
960 + $oneValue = htmlspecialchars_decode($oneValue, ENT_QUOTES);
961 +
962 + if ($map === 'id') {
963 + $oneValue = intval($oneValue);
964 + } elseif (isset($dateColumns[$map]) && strlen(trim((string)$oneValue)) === 0) {
965 + $oneValue = 'NULL';
966 + } else {
967 + $oneValue = acym_escapeDB($oneValue);
968 + if ($map === 'email') {
969 + $allemails[] = $oneValue;
970 + }
971 + }
972 +
973 + $value[] = $oneValue;
974 + }
975 +
976 + if (!isset($oneUser->key)) $value[] = acym_escapeDB(acym_generateKey(14));
977 + $values[] = implode(',', $value);
978 +
979 + // Prepare import custom fields
980 + if (!empty($oneUser->customfields)) $customFieldsvalues[$oneUser->email] = $oneUser->customfields;
981 + }
982 + acym_trigger('onAcymUserImport', [&$users]);
983 +
984 + $queryInsertUsers .= implode('),(', $values).')';
985 +
986 + if ($this->overwrite) {
987 + $queryInsertUsers .= ' ON DUPLICATE KEY UPDATE ';
988 + foreach ($importedCols as &$oneColumn) {
989 + acym_secureDBColumn($oneColumn);
990 + if ($oneColumn == 'key') {
991 + $oneColumn = '`'.$oneColumn.'` = `'.$oneColumn.'`';
992 + } else {
993 + $oneColumn = '`'.$oneColumn.'` = VALUES(`'.$oneColumn.'`)';
994 + }
995 + }
996 + $queryInsertUsers .= implode(',', $importedCols);
997 + }
998 +
999 + acym_query($queryInsertUsers);
1000 +
1001 + $importedUsers = acym_loadObjectList('SELECT id, email FROM #__acym_user WHERE email IN ('.implode(',', $allemails).')', 'id');
1002 +
1003 + $this->importedUsersByEmail = array_combine(array_column($importedUsers, 'email'), array_column($importedUsers, 'id'));
1004 +
1005 + if (!empty($customFieldsvalues)) {
1006 + $insertValues = [];
1007 + foreach ($importedUsers as $one) {
1008 + if (empty($customFieldsvalues[$one->email])) continue;
1009 +
1010 + foreach ($customFieldsvalues[$one->email] as $fieldId => $value) {
1011 + $value = htmlspecialchars_decode($value, ENT_QUOTES);
1012 + $insertValues[] = '('.intval($one->id).','.intval($fieldId).','.acym_escapeDB($value).')';
1013 + }
1014 + }
1015 +
1016 + if (!empty($insertValues)) {
1017 + $queryInsertCustomFields = 'INSERT'.($this->overwrite ? '' : ' IGNORE').' INTO #__acym_user_has_field (`user_id`, `field_id`, `value`) VALUES '.implode(
1018 + ',',
1019 + $insertValues
1020 + );
1021 + if ($this->overwrite) {
1022 + $queryInsertCustomFields .= ' ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)';
1023 + }
1024 + acym_query($queryInsertCustomFields);
1025 + }
1026 + }
1027 +
1028 +
1029 + $this->allUserIds = array_merge($this->allUserIds, array_keys($importedUsers));
1030 + }
1031 +
1032 + private function getUserDateColumns(): array
1033 + {
1034 + static $dateColumns = null;
1035 + if ($dateColumns === null) {
1036 + $dateColumns = [];
1037 + foreach (acym_loadObjectList('SHOW COLUMNS FROM #__acym_user') as $column) {
1038 + $isDateColumn = stripos($column->Type, 'date') !== false || stripos($column->Type, 'time') !== false;
1039 + if ($isDateColumn && strtoupper($column->Null) === 'YES') {
1040 + $dateColumns[strtolower($column->Field)] = true;
1041 + }
1042 + }
1043 + }
1044 +
1045 + return $dateColumns;
1046 + }
1047 +
1048 + private function checkData(object &$user, int $timestamp)
1049 + {
1050 + //Created field
1051 + if (empty($user->creation_date)) {
1052 + $user->creation_date = time();
1053 + }
1054 +
1055 + if (is_numeric($user->creation_date)) {
1056 + $user->creation_date = gmdate('Y-m-d H:i:s', $user->creation_date);
1057 + }
1058 +
1059 + if (!isset($user->active) || strlen($user->active) == 0) {
1060 + $user->active = 1;
1061 + }
1062 +
1063 + if ((!isset($user->confirmed) || strlen($user->confirmed) == 0) && $this->forceConfirm) {
1064 + $user->confirmed = 1;
1065 + }
1066 +
1067 + if (empty($user->source)) {
1068 + $user->source = 'Import on '.acym_date($timestamp, 'Y-m-d H:i:s');
1069 + }
1070 +
1071 + //name field
1072 + if (empty($user->name) && $this->generateName) {
1073 + $user->name = ucwords(trim(str_replace(['.', '_', '-', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0], ' ', substr($user->email, 0, strpos($user->email, '@')))));
1074 + }
1075 + }
1076 +
1077 + private function autoDetectHeader(): bool
1078 + {
1079 + $this->separator = ',';
1080 +
1081 + // Remove BOM characters if there is one
1082 + $this->header = str_replace("\xEF\xBB\xBF", '', $this->header);
1083 +
1084 + // Detect the separator
1085 + $listSeparators = ["\t", ';', ','];
1086 + foreach ($listSeparators as $sep) {
1087 + if (strpos($this->header, $sep) !== false) {
1088 + $this->separator = $sep;
1089 + break;
1090 + }
1091 + }
1092 +
1093 + $this->columns = explode($this->separator, $this->header);
1094 +
1095 + // Clean the headers
1096 + for ($i = count($this->columns) - 1; $i >= 0; $i--) {
1097 + if (strlen($this->columns[$i]) == 0) {
1098 + unset($this->columns[$i]);
1099 + $this->separatorsToRemove++;
1100 + }
1101 + }
1102 +
1103 + $columns = acym_getColumns('user');
1104 + foreach ($columns as $i => $oneColumn) {
1105 + $columns[$i] = strtolower($oneColumn);
1106 + }
1107 +
1108 + foreach ($this->columns as $i => $oneColumn) {
1109 + $this->columns[$i] = strtolower(trim($oneColumn, '\'" '));
1110 + if (in_array($this->columns[$i], ['listids', 'listname'])) continue;
1111 + if (strpos($this->columns[$i], 'cf_') === 0) continue;
1112 + if (strpos($this->columns[$i], 'subscription_date_') === 0) continue;
1113 + if (strpos($this->columns[$i], 'unsubscription_date_') === 0) continue;
1114 +
1115 + if (!in_array($this->columns[$i], $columns) && $this->columns[$i] != 1) {
1116 + acym_enqueueMessage(
1117 + acym_translationSprintf(
1118 + 'ACYM_IMPORT_ERROR_FIELD',
1119 + '<b>'.esc_html($this->columns[$i]).'</b>',
1120 + '<b>'.implode('</b> | <b>', array_diff($columns, ['id', 'cms_id'])).'</b>'
1121 + ),
1122 + 'error'
1123 + );
1124 +
1125 + return false;
1126 + }
1127 + }
1128 +
1129 + if (!in_array('email', $this->columns)) {
1130 + return false;
1131 + }
1132 +
1133 + return true;
1134 + }
1135 +
1136 + private function cleanImportFolder(): void
1137 + {
1138 + $files = acym_getFiles(ACYM_MEDIA.'import', '.', false, true, []);
1139 + foreach ($files as $oneFile) {
1140 + if (acym_fileGetExt($oneFile) != 'csv') {
1141 + continue;
1142 + }
1143 + if (filectime($oneFile) < time() - 86400) {
1144 + acym_deleteFile($oneFile);
1145 + }
1146 + }
1147 + }
1148 +
1149 + private function subscribeUsers(): void
1150 + {
1151 + //All users are known as : $this->allUserIds
1152 + if (empty($this->allUserIds)) {
1153 + return;
1154 + }
1155 +
1156 + $subdate = gmdate('Y-m-d H:i:s', time());
1157 +
1158 + $listClass = new ListClass();
1159 + $lists = $this->getImportedLists();
1160 +
1161 + if (!acym_isAdmin() && 'joomla' === ACYM_CMS) {
1162 + $listClass = new ListClass();
1163 + $listManagementId = $listClass->getfrontManagementList();
1164 + if (empty($listManagementId)) {
1165 + acym_redirect(acym_rootURI(), 'ACYM_UNABLE_TO_CREATE_MANAGEMENT_LIST', 'error');
1166 + }
1167 +
1168 + if (empty($lists)) {
1169 + $lists = [$listManagementId => 1];
1170 + } else {
1171 + $lists[$listManagementId] = 1;
1172 + }
1173 + }
1174 +
1175 + if (!empty($this->importUserInLists)) {
1176 + //We import the users based on the subscription of each of them, specified in the listids or listname columns...
1177 + foreach ($this->importUserInLists as $listid => $arrayEmails) {
1178 + if (empty($listid)) continue;
1179 +
1180 + $listid = (int)$listid;
1181 + $query = 'INSERT IGNORE INTO #__acym_user_has_list (`list_id`,`user_id`,`subscription_date`,`status`) ';
1182 + $query .= 'SELECT '.intval($listid).',`id`,'.acym_escapeDB($subdate).',1 FROM #__acym_user WHERE `email` IN (';
1183 + $query .= implode(',', $arrayEmails).')';
1184 + $nbsubscribed = acym_query($query);
1185 + $nbsubscribed = intval($nbsubscribed);
1186 +
1187 + if (isset($this->subscribedUsers[$listid])) {
1188 + $this->subscribedUsers[$listid]->nbusers += $nbsubscribed;
1189 + } else {
1190 + $myList = $listClass->getOneById($listid);
1191 + $this->subscribedUsers[$listid] = $myList;
1192 + $this->subscribedUsers[$listid]->nbusers = $nbsubscribed;
1193 + }
1194 + }
1195 + }
1196 +
1197 + if (!empty($lists)) {
1198 + //We do one query per list to avoid problem with huge queries and so in the mean time we can display info for each list
1199 + foreach ($lists as $listid => $val) {
1200 + if (empty($val)) {
1201 + continue;
1202 + }
1203 +
1204 + if ($val == -1) {
1205 + $dateColumn = 'unsubscribe_date';
1206 + $status = 0;
1207 + } else {
1208 + $dateColumn = 'subscription_date';
1209 + $status = 1;
1210 + }
1211 +
1212 + $nbsubscribed = 0;
1213 + $listid = intval($listid);
1214 + $query = 'INSERT IGNORE INTO #__acym_user_has_list (`list_id`,`user_id`,`'.$dateColumn.'`,`status`) VALUES ';
1215 + $b = 0;
1216 + $currentSubids = [];
1217 + foreach ($this->allUserIds as $subid) {
1218 + $subid = intval($subid);
1219 + $currentSubids[] = $subid;
1220 + $b++;
1221 +
1222 + if ($b > 200) {
1223 + $query = rtrim($query, ',');
1224 + if ($val == -1) {
1225 + $query .= ' ON DUPLICATE KEY UPDATE status = 0';
1226 + $nbsubscribed = -acym_loadResult(
1227 + 'SELECT COUNT(*) FROM #__acym_listsub WHERE `list_id` = '.intval($listid).' AND status != -1 AND `user_id` IN ('.implode(',', $currentSubids).')'
1228 + );
1229 + }
1230 + $affected = acym_query($query);
1231 + $nbsubscribed += intval($affected);
1232 + $b = 0;
1233 + $currentSubids = [];
1234 + $query = 'INSERT IGNORE INTO #__acym_user_has_list (`list_id`,`user_id`,`'.$dateColumn.'`,`status`) VALUES ';
1235 + }
1236 +
1237 + $query .= '('.intval($listid).','.intval($subid).','.acym_escapeDB($subdate).','.$status.'),';
1238 + }
1239 + $query = rtrim($query, ',');
1240 + if ($val == -1) {
1241 + $query .= ' ON DUPLICATE KEY UPDATE status = 0';
1242 + // It could be empty if we imported exactly 200, 400, 600... users
1243 + if (!empty($currentSubids)) {
1244 + $nbsubscribed = -acym_loadResult(
1245 + 'SELECT COUNT(*) FROM #__acym_listsub WHERE `list_id` = '.intval($listid).' AND status != -1 AND `user_id` IN ('.implode(',', $currentSubids).')'
1246 + );
1247 + }
1248 + }
1249 +
1250 + $affected = acym_query($query);
1251 + $nbsubscribed += intval($affected);
1252 +
1253 + if (isset($this->subscribedUsers[$listid])) {
1254 + $this->subscribedUsers[$listid]->nbusers += $nbsubscribed;
1255 + } else {
1256 + $myList = $listClass->getOneById($listid);
1257 + $myList->status = $val;
1258 + $this->subscribedUsers[$listid] = $myList;
1259 + $this->subscribedUsers[$listid]->nbusers = $nbsubscribed;
1260 + }
1261 + }
1262 + }
1263 + }
1264 +
1265 + public function insertSubscriptionHistory(): void
1266 + {
1267 + if (empty($this->subscriptionHistory)) {
1268 + return;
1269 + }
1270 +
1271 + $listClass = new ListClass();
1272 +
1273 + $userIds = array_values($this->importedUsersByEmail);
1274 + $listIds = array_keys($this->subscriptionHistory);
1275 +
1276 + $existingEntries = $listClass->getUserListEntries($userIds, $listIds);
1277 +
1278 + foreach ($this->subscriptionHistory as $listId => $entries) {
1279 + foreach ($entries as $entry) {
1280 + if (empty($entry['subscribed_date']) && empty($entry['unsubscribed_date'])) {
1281 + continue;
1282 + }
1283 +
1284 + $userId = $this->importedUsersByEmail[$entry['email']] ?? null;
1285 + if (empty($userId)) {
1286 + continue;
1287 + }
1288 +
1289 + $key = $userId.'_'.$listId;
1290 + $existing = $existingEntries[$key] ?? null;
1291 +
1292 + $subscriptionDate = !empty($entry['subscribed_date']) ? acym_escapeDB($entry['subscribed_date']) : 'NULL';
1293 + $unsubscribeDate = !empty($entry['unsubscribed_date']) ? acym_escapeDB($entry['unsubscribed_date']) : 'NULL';
1294 +
1295 + if (!empty($existing)) {
1296 + if ($existing->status === 0) {
1297 + continue;
1298 + }
1299 +
1300 + $fieldsToUpdate = [];
1301 +
1302 + if (empty($existing->subscription_date)) {
1303 + if ($unsubscribeDate != 'NULL' && ($subscriptionDate != 'NULL' && $unsubscribeDate > $subscriptionDate)) {
1304 + $fieldsToUpdate[] = 'status ='. 0;
1305 + $fieldsToUpdate[] = 'unsubscribe_date ='.$unsubscribeDate;
1306 + }
1307 +
1308 + $fieldsToUpdate[] = 'subscription_date ='.$subscriptionDate;
1309 + } else {
1310 + if ($unsubscribeDate != 'NULL' && ($existing->subscription_date != 'NULL' && $unsubscribeDate > $existing->subscription_date)) {
1311 + $fieldsToUpdate[] = 'status ='. 0;
1312 + $fieldsToUpdate[] = 'unsubscribe_date ='.$unsubscribeDate;
1313 + }
1314 + }
1315 +
1316 + if (!empty($fieldsToUpdate)) {
1317 + $query = 'UPDATE #__acym_user_has_list SET '.implode(', ', $fieldsToUpdate)
1318 + .' WHERE user_id = '.$userId.' AND list_id = '.$listId;
1319 + acym_query($query);
1320 + }
1321 + } else {
1322 + if ($subscriptionDate === 'NULL' && $unsubscribeDate !== 'NULL') {
1323 + $subscriptionDate = $unsubscribeDate;
1324 + }
1325 +
1326 + $status = ($unsubscribeDate !== 'NULL') ? 0 : 1;
1327 +
1328 + $insertQuery = 'INSERT INTO #__acym_user_has_list (`list_id`, `user_id`, `subscription_date`, `status`, `unsubscribe_date`) VALUES '
1329 + .'('.$listId.','.$userId.','.$subscriptionDate.','.$status.','.$unsubscribeDate.')';
1330 + acym_query($insertQuery);
1331 + }
1332 + }
1333 + }
1334 + }
1335 +}