PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.0
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Helpers / ImportHelper.php

ImportHelper.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.1.0, at back/Helpers/ImportHelper.php

1,336 lines 54.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\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 }
1336