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 +329 -91 10.1.4 → 11.1.0 View file →
@@ -7,8 +7,9 @@
7 7 use AcyMailing\Core\AcymObject;
8 8
9 9 class ImportHelper extends AcymObject
10 10 {
11 + //Handle messages to say that X users have been subscribed to list Y
11 12 private array $subscribedUsers = [];
12 13 private array $importUserInLists = [];
13 14 private array $allUserIds = [];
14 15 private array $columns = [];
@@ -20,20 +21,25 @@
20 21 private bool $overwrite = false;
21 22 private string $header = '';
22 23 private string $separator = ',';
23 24
25 + //Variables used to handle the import on filter
24 26 public string $tableName = '';
25 27 public array $dbWhere = [];
26 28 public array $fieldsMap = [];
29 + public array $subscriptionHistory = [];
30 + public array $importedUsersByEmail = [];
27 31
28 32 public function __construct()
29 33 {
30 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!
31 36 acym_increasePerf();
32 37 }
33 38
34 39 public function file(): bool
35 40 {
41 + //Step 1 : we copy the file in the correct directory
36 42 $importFile = acym_getVar('array', 'import_file', [], 'files');
37 43
38 44 if (empty($importFile['name'])) {
39 45 acym_enqueueMessage(acym_translation('ACYM_PLEASE_BROWSE_FILE_IMPORT'), 'error');
@@ -42,14 +48,15 @@
42 48 }
43 49
44 50 $extension = strtolower(acym_fileGetExt($importFile['name']));
45 51
46 - if (!preg_match('#^(csv)$#Ui', $extension) || preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)$#Ui', $importFile['name'])) {
47 - acym_enqueueMessage(acym_translationSprintf('ACYM_ACCEPTED_TYPE', acym_escape($extension), $this->config->get('allowed_files')), 'error');
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');
48 54
49 55 return false;
50 56 }
51 57
58 + // Checking error upload
52 59 $fileError = $importFile['error'];
53 60 if ($fileError > 0) {
54 61 switch ($fileError) {
55 62 case 1:
@@ -73,25 +80,20 @@
73 80 }
74 81
75 82 $uploadPath = $this->createUploadFolder();
76 83
77 - $attachment = new \stdClass();
78 - $attachment->filename = uniqid('import_').'.csv';
79 - acym_setVar('acym_import_filename', $attachment->filename);
84 + $filename = uniqid('import_').'.csv';
85 + acym_setVar('acym_import_filename', $filename);
80 86
81 - $attachment->size = $importFile['size'];
82 -
83 - if (!acym_uploadFile($importFile['tmp_name'], $uploadPath.$attachment->filename)) {
84 - if (!move_uploaded_file($importFile['tmp_name'], $uploadPath.$attachment->filename)) {
85 - acym_enqueueMessage(
86 - acym_translationSprintf(
87 - 'ACYM_FAIL_UPLOAD',
88 - '<b><i>'.acym_escape($importFile['tmp_name']).'</i></b>',
89 - '<b><i>'.acym_escape($uploadPath.$attachment->filename).'</i></b>'
90 - ),
91 - 'error'
92 - );
93 - }
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 + );
94 96 }
95 97
96 98 return true;
97 99 }
@@ -111,11 +113,14 @@
111 113 public function cms(): bool
112 114 {
113 115 global $acymCmsUserVars;
114 116
115 - $query = 'UPDATE IGNORE '.$acymCmsUserVars->table.' as b, #__acym_user as a SET a.email = 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';
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';
116 119 $nbUpdated = acym_query($query);
117 120
121 + //Step 1 : update the existing ones.
122 + //Update the users to give them a userid
118 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;
119 124 $affected = acym_query($query);
120 125 $nbUpdated += intval($affected);
121 126
@@ -120,8 +125,9 @@
120 125 $nbUpdated += intval($affected);
121 126
122 127 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_UPDATE', $nbUpdated), 'success');
123 128
129 + //Step 2 : delete the ones which don't exist any more
124 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';
125 131 $deletedSubid = acym_loadResultArray($query);
126 132
127 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';
@@ -129,30 +135,47 @@
129 135
130 136 if (!empty($deletedSubid)) {
131 137 $userClass = new UserClass();
132 138 $deletedUsers = $userClass->delete($deletedSubid);
133 - acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_DELETE', $deletedUsers), 'success');
139 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_DELETE', $deletedUsers));
134 140 }
135 141
142 + //Step 3 : insert the new ones
136 143 $time = time();
137 144 $formattedTime = acym_date($time, 'Y-m-d H:i:s');
138 145 $sourceImport = 'Import on '.$formattedTime;
139 - $query = 'INSERT IGNORE INTO #__acym_user (`name`,`email`,`creation_date`,`active`,`cms_id`, `source`) SELECT `user`.`'.$acymCmsUserVars->name.'`,`user`.`'.$acymCmsUserVars->email.'`,`user`.`'.$acymCmsUserVars->registered.'`,1 - `user`.'.$acymCmsUserVars->blocked.',`user`.`'.$acymCmsUserVars->id.'`,\''.$sourceImport.'\' FROM '.$acymCmsUserVars->table.' AS `user` ';
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 + }
140 154 $groups = acym_getVar('array', 'groups', []);
141 - $this->config->save(['import_groups' => implode(',', $groups)]);
155 + $this->config->saveConfig(['import_groups' => implode(',', $groups)]);
142 156 if (!empty($groups)) {
143 157 if (ACYM_CMS === 'joomla') {
144 158 acym_arrayToInteger($groups);
145 - $query .= ' JOIN #__user_usergroup_map AS `map` ON map.user_id = `user`.`'.$acymCmsUserVars->id.'` WHERE `map`.`group_id` IN ('.implode(', ', $groups).')';
159 + $queryJoin[] = ' JOIN #__user_usergroup_map AS `map` ON map.user_id = `user`.`'.$acymCmsUserVars->id.'`';
160 + $queryWhere[] = '`map`.`group_id` IN ('.implode(', ', $groups).')';
146 161 } else {
147 - $query .= ' JOIN #__usermeta AS `meta` ON meta.user_id = `user`.`'.$acymCmsUserVars->id.'` AND `meta`.`meta_key` = "#__capabilities"';
162 + if (!is_multisite()) {
163 + $queryJoin[] = 'JOIN #__usermeta AS `meta` ON meta.user_id = `user`.`'.$acymCmsUserVars->id.'` AND `meta`.`meta_key` = "#__capabilities"';
164 + }
165 +
148 166 foreach ($groups as $i => $oneGroup) {
149 167 $groups[$i] = acym_escapeDB('%'.strlen($oneGroup).':"'.$oneGroup.'"%');
150 168 }
151 - $query .= ' WHERE `meta`.`meta_value` LIKE '.implode(' OR `meta`.`meta_value` LIKE ', $groups);
169 + $queryWhere[] = '`meta`.`meta_value` LIKE '.implode(' OR `meta`.`meta_value` LIKE ', $groups);
152 170 }
153 171 }
154 172
173 + $query .= implode(' ', $queryJoin);
174 + if (!empty($queryWhere)) {
175 + $query .= ' WHERE ('.implode(') AND (', $queryWhere).')';
176 + }
177 +
155 178 $insertedUsers = acym_query($query);
156 179
157 180 acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = \'last_import\'');
158 181
@@ -159,8 +182,9 @@
159 182 acym_trigger('onAcymAfterCMSUserImport', [$sourceImport]);
160 183
161 184 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $insertedUsers), 'info');
162 185
186 + //Step 4 : subscribe all the registered users to one or several lists
163 187 $lists = $this->getImportedLists();
164 188 $listsSubscribe = [];
165 189 if (!empty($lists)) {
166 190 foreach ($lists as $listid => $val) {
@@ -174,9 +198,9 @@
174 198 return true;
175 199 }
176 200
177 201 $query = 'INSERT IGNORE INTO #__acym_user_has_list (`user_id`,`list_id`,`status`,`subscription_date`) ';
178 - $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(date('Y-m-d H:i:s', time() - date('Z'))).'
202 + $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(gmdate('Y-m-d H:i:s', time())).'
179 203 FROM #__acym_list AS list, #__acym_user AS user ';
180 204 $conditions = [];
181 205 $conditions[] = 'list.`id` IN ('.implode(',', $listsSubscribe).')';
182 206 $conditions[] = 'user.`cms_id` > 0';
@@ -186,9 +210,9 @@
186 210 $query .= ' JOIN #__user_usergroup_map AS `map` ON map.user_id = `user`.`cms_id`';
187 211 $conditions[] = '`map`.`group_id` IN ('.implode(', ', $groups).')';
188 212 } else {
189 213 $query .= ' JOIN #__usermeta AS `meta` ON meta.user_id = `user`.`cms_id` AND `meta`.`meta_key` = "#__capabilities"';
190 - $conditions[] = '`meta`.`meta_value` LIKE ('.implode(' OR `meta`.`meta_value` LIKE ', $groups).')';
214 + $conditions[] = '(`meta`.`meta_value` LIKE '.implode(' OR `meta`.`meta_value` LIKE ', $groups).')';
191 215 }
192 216 }
193 217
194 218 $query .= ' WHERE '.implode(' AND ', $conditions);
@@ -212,15 +236,18 @@
212 236
213 237 return false;
214 238 }
215 239
240 + //We got a table... let's load the fields from this table
216 241 $fields = acym_getColumns($table, false, false);
217 242 if (empty($fields)) {
243 + //there is no field... so we consider it was the wrong table
218 244 acym_enqueueMessage(acym_translation('ACYM_SPECIFYTABLE'), 'warning');
219 245
220 246 return false;
221 247 }
222 248
249 + //Ok now we have some fields and now we have a table selected.
223 250 $equivalentFields = empty($this->fieldsMap) ? acym_getVar('array', 'fields', []) : $this->fieldsMap;
224 251
225 252 if (empty($equivalentFields['email'])) {
226 253 acym_enqueueMessage(acym_translation('ACYM_SPECIFYFIELDEMAIL'), 'warning');
@@ -228,8 +255,9 @@
228 255 return false;
229 256 }
230 257
231 258 $select = [];
259 + //We check the data and create the query
232 260 foreach ($equivalentFields as $acyField => $tableField) {
233 261 $tableField = trim($tableField);
234 262 if (empty($tableField)) {
235 263 continue;
@@ -252,10 +280,13 @@
252 280
253 281 $sourceTxt = 'Import on '.$formattedTime;
254 282 $select['`source`'] = acym_escapeDB($sourceTxt);
255 283
284 + $emailColumn = acym_secureDBColumn($select['`email`']);
285 + $select['`email`'] = 'LOWER('.$emailColumn.')';
286 +
256 287 $query = 'INSERT IGNORE INTO #__acym_user ('.implode(' , ', array_keys($select)).') SELECT '.implode(' , ', $select);
257 - $query .= ' FROM '.acym_secureDBColumn($table).' WHERE '.acym_secureDBColumn($select['`email`']).' LIKE "%@%"';
288 + $query .= ' FROM '.acym_secureDBColumn($table).' WHERE '.$emailColumn.' LIKE "%@%"';
258 289 if (!empty($this->dbWhere)) {
259 290 $query .= ' AND ( '.implode(' ) AND (', $this->dbWhere).' )';
260 291 }
261 292
@@ -271,8 +302,9 @@
271 302
272 303 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $affectedRows), 'info');
273 304
274 305
306 + //Step 4 : subscribe all the registered users to one or several lists
275 307 $lists = $this->getImportedLists();
276 308 $listsSubscribe = [];
277 309 if (!empty($lists)) {
278 310 foreach ($lists as $listid => $val) {
@@ -286,12 +318,12 @@
286 318 return true;
287 319 }
288 320
289 321 $query = 'INSERT IGNORE INTO #__acym_user_has_list (`user_id`,`list_id`,`status`,`subscription_date`) ';
290 - $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(date('Y-m-d H:i:s', time() - date('Z'))).'
322 + $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(gmdate('Y-m-d H:i:s', time())).'
291 323 FROM #__acym_list AS list, #__acym_user AS user
292 324 WHERE list.`id` IN ('.implode(',', $listsSubscribe).')
293 - AND user.`email` IN (SELECT '.acym_secureDBColumn($select['`email`']).' FROM '.acym_secureDBColumn($table).')';
325 + AND user.`email` IN (SELECT LOWER('.$emailColumn.') FROM '.acym_secureDBColumn($table).')';
294 326
295 327 $nbsubscribed = acym_query($query);
296 328 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_SUBSCRIPTION', $nbsubscribed));
297 329
@@ -299,8 +331,9 @@
299 331 }
300 332
301 333 public function mailpoet(): bool
302 334 {
335 + // Step 1 : import subscriber from mailpoet
303 336 $time = time();
304 337 $formattedTime = acym_date($time, 'Y-m-d H:i:s');
305 338 $sourceImport = 'import_on_'.$formattedTime.'_mailpoet';
306 339
@@ -305,9 +338,9 @@
305 338 $sourceImport = 'import_on_'.$formattedTime.'_mailpoet';
306 339
307 340 $query = 'INSERT IGNORE INTO #__acym_user (`name`,`email`,`creation_date`,`active`, `confirmed`,`cms_id`, `source`)
308 341 SELECT CONCAT(`subscriber`.`first_name`, `subscriber`.`last_name`),
309 - `subscriber`.`email`,
342 + LOWER(`subscriber`.`email`),
310 343 `subscriber`.`created_at`,
311 344 IF(`subscriber`.`status` = "inactive", 0, 1),
312 345 IF(`subscriber`.`status` = "subscribed", 1, 0),
313 346 `subscriber`.`wp_user_id`,
@@ -328,8 +361,9 @@
328 361 acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = \'last_import\'');
329 362
330 363 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $insertedUsers), 'info');
331 364
365 + // Step 2 : subscribe all the registered users to one or several lists
332 366 $lists = $this->getImportedLists();
333 367 $listsSubscribe = [];
334 368 if (!empty($lists)) {
335 369 foreach ($lists as $listid => $val) {
@@ -343,9 +377,9 @@
343 377 return true;
344 378 }
345 379
346 380 $query = 'INSERT IGNORE INTO #__acym_user_has_list (`user_id`,`list_id`,`status`,`subscription_date`) ';
347 - $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(date('Y-m-d H:i:s', time() - date('Z'))).'
381 + $query .= 'SELECT user.`id`, list.`id`, 1, '.acym_escapeDB(gmdate('Y-m-d H:i:s', time())).'
348 382 FROM #__acym_list AS list, #__acym_user AS user ';
349 383 $conditions = [];
350 384 $conditions[] = 'list.`id` IN ('.implode(',', $listsSubscribe).')';
351 385 $conditions[] = 'user.`source` = '.acym_escapeDB($sourceImport);
@@ -372,13 +406,16 @@
372 406 $this->forceConfirm = acym_getVar('bool', 'import_confirmed_generic', true);
373 407 $this->generateName = acym_getVar('bool', 'import_generate_generic', true);
374 408 $this->overwrite = acym_getVar('bool', 'import_overwrite_generic', true);
375 409
376 - $newConfig = new \stdClass();
377 - $newConfig->import_confirmed = $this->forceConfirm;
378 - $newConfig->import_generate = $this->generateName;
379 - $newConfig->import_overwrite = $this->overwrite;
380 - $this->config->save($newConfig);
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 + );
381 418
382 419 $filename = str_replace(['.', ' '], '_', substr($filename, 0, strpos($filename, $extension))).$extension;
383 420 $uploadPath = ACYM_MEDIA.'import'.DS.$filename;
384 421
@@ -396,8 +433,9 @@
396 433 }
397 434
398 435 $contentFile = file_get_contents($uploadPath);
399 436
437 + //We convert into the correct charset
400 438 if (acym_getVar('cmd', 'acyencoding', '') != '') {
401 439 $encodingHelper = new EncodingHelper();
402 440 $contentFile = $encodingHelper->change($contentFile, acym_getVar('cmd', 'acyencoding'), 'UTF-8');
403 441 }
@@ -428,9 +466,10 @@
428 466 }
429 467
430 468 $this->handleContent($contentFile);
431 469
432 - unlink($uploadPath);
470 + //We can now delete the file
471 + acym_deleteFile($uploadPath);
433 472 $this->cleanImportFolder();
434 473
435 474 return true;
436 475 }
@@ -437,9 +476,9 @@
437 476
438 477 public function getImportedLists(): array
439 478 {
440 479 $listClass = new ListClass();
441 - $listsId = json_decode(acym_getVar('string', 'acym__entity_select__selected'));
480 + $listsId = json_decode(acym_getVar('string', 'acym__entity_select__selected'), true);
442 481 $newListName = acym_getVar('string', 'new_list');
443 482
444 483 if (empty($listsId) && empty($newListName)) {
445 484 return [];
@@ -465,34 +504,41 @@
465 504
466 505 return $lists;
467 506 }
468 507
469 - public function additionalDataUsersImport(bool $isGeneric): string
508 + public function displayAdditionalDataUsersImport(bool $isGeneric): void
470 509 {
471 510 $buttonAddListId = $isGeneric ? 'acym__users__generic__import__create-list__button' : 'acym__users__import__create-list__button';
472 511 $buttonImportClass = $isGeneric ? 'acym__users__import__generic__import__button' : 'acym__users__import__button';
473 512 $buttonSkipId = $isGeneric ? 'acym__users__generic__import__skip__button' : 'acym__users__import__skip__button';
474 513 $buttonImportDataTask = $isGeneric ? 'listing' : '';
475 -
476 - return '<div class="cell align-right grid-x margin-bottom-2">
477 - <div id="acym__users__import__create-list" class="grid-x" style="display: none;">
478 - <label for="acym__users__import__create-list__field" class="margin-right-1 acym_vcenter">'.acym_translation('ACYM_LIST_NAME').' : </label>
479 - <div>
480 - <input id="acym__users__import__create-list__field" type="text">
481 - </div>
482 - </div>
483 - <button type="button" class="button button-secondary margin-left-1 acym_vcenter margin-right-2" id="'.$buttonAddListId.'">'.acym_translation(
484 - 'ACYM_CREATE_NEW_LIST'
485 - ).'</button>
486 - <i style="display: none;" class="acym_vcenter acymicon-circle-o-notch acymicon-spin" id="acym__users__import__create-list__loading-logo"></i>
487 - </div>
488 - <div class="cell align-right grid-x">
489 - <button type="button" class="button-secondary button cell shrink margin-right-1" id="'.$buttonSkipId.'">'.acym_translation('ACYM_SKIP').'</button>
490 - <button
491 - type="button"
492 - class="button-primary button acy_button_submit cell shrink margin-right-2 '.$buttonImportClass.'"
493 - id="acym__entity_select__button__submit" data-task="'.$buttonImportDataTask.'">'.acym_translation('ACYM_SUBSCRIBE_USERS_TO_THESE_LISTS').'</button>
494 - </div>';
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
495 541 }
496 542
497 543 private function createUploadFolder(): string
498 544 {
@@ -503,11 +549,11 @@
503 549 if (!is_dir($folderPath)) {
504 550 acym_createDir($folderPath, true, true);
505 551 }
506 552
507 - if (!is_writable($folderPath)) {
508 - @chmod($folderPath, '0755');
509 - if (!is_writable($folderPath)) {
553 + if (!acym_isWritable($folderPath)) {
554 + acym_chmod($folderPath, 0755);
555 + if (!acym_isWritable($folderPath)) {
510 556 acym_enqueueMessage(acym_translationSprintf('ACYM_WRITABLE_FOLDER', $folderPath), 'warning');
511 557 }
512 558 }
513 559
@@ -517,11 +563,13 @@
517 563 private function handleContent(string $contentFile): void
518 564 {
519 565 $timestamp = time();
520 566
567 + //We convert the file into something valid so that mac or not will be Ok!
521 568 $contentFile = str_replace(["\r\n", "\r"], "\n", $contentFile);
522 569 $importLines = explode("\n", $contentFile);
523 570
571 + //A little trick to take the second line if the first line is empty...
524 572 $i = 0;
525 573 $this->header = '';
526 574 $this->allUserIds = [];
527 575 while (empty($this->header) && $i < 10) {
@@ -529,14 +577,17 @@
529 577 $i++;
530 578 }
531 579
532 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
533 583 $this->header = 'email';
534 584 $i--;
535 585 }
536 586
587 + //Step 3 : we make sure the header of the file is correct (so it does correspond to columns from the database)
537 588 if (!$this->autoDetectHeader()) {
538 - acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_HEADER', acym_escape($this->header)), 'error');
589 + acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_HEADER', esc_html($this->header)), 'error');
539 590 acym_enqueueMessage(acym_translation('ACYM_IMPORT_EMAIL'), 'error');
540 591
541 592 return;
542 593 }
@@ -550,28 +601,37 @@
550 601 $countUsersBeforeImport = $userClass->getCountTotalUsers();
551 602 $importUsers = [];
552 603 $errorLines = [];
553 604
605 + //Step 4 : we start importing the file as everything is OK
554 606 while (isset($importLines[$i])) {
607 + // If there are quotes in the line, check if it is a broken line or not
555 608 if (strpos($importLines[$i], '"') !== false) {
556 609 $data = [];
557 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
558 612 $position = -1;
559 613
614 + // Concatenate 30 lines max
560 615 while ($j < ($i + 30)) {
561 -
616 + // Test if the value is encapsulated by quotes
562 617 $quoteOpened = substr($importLines[$i], $position + 1, 1) == '"';
563 618
619 + // If encapsulated, search the end of the value
564 620 if ($quoteOpened) {
565 621 $nextQuotePosition = strpos($importLines[$i], '"', $position + 2);
566 - while ($nextQuotePosition !== false && $nextQuotePosition + 1 != strlen($importLines[$i]) && substr(
567 - $importLines[$i],
568 - $nextQuotePosition + 1,
569 - 1
570 - ) != $this->separator) {
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 + ) {
571 628 $nextQuotePosition = strpos($importLines[$i], '"', $nextQuotePosition + 1);
572 629 }
630 +
631 + // If we didn't find the whole value, then concatenate the current line with the next one
573 632 if ($nextQuotePosition === false) {
633 + // If end of import file, error...
574 634 if (!isset($importLines[$j])) {
575 635 break;
576 636 }
577 637
@@ -579,18 +639,26 @@
579 639 $importLines[$i] = rtrim($importLines[$i], $this->separator);
580 640 unset($importLines[$j]);
581 641 $j++;
582 642 } else {
643 + // Found the entire value, add it to data and move the position to the next separator
583 644
645 + // If the quote is at the end of the line and the line is completed...
584 646 if (strlen($importLines[$i]) - 1 == $nextQuotePosition) {
585 647 $data[] = substr($importLines[$i], $position + 1);
586 648 break;
587 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".
588 654 $data[] = substr($importLines[$i], $position + 1, $nextQuotePosition + 1 - ($position + 1));
589 655 $position = $nextQuotePosition + 1;
590 656 }
591 657 } else {
658 + // If not encapsulated by quotes, search the next separator
592 659 $nextSeparatorPosition = strpos($importLines[$i], $this->separator, $position + 1);
660 + // If not found, the line is completed
593 661 if ($nextSeparatorPosition === false) {
594 662 $data[] = substr($importLines[$i], $position + 1);
595 663 break;
596 664 } else { // If found the next separator, add the value in $data and change the position
@@ -599,15 +667,18 @@
599 667 }
600 668 }
601 669 }
602 670
671 + // We unset some lines, don't forget to change the remaining lines keys
603 672 $importLines = array_merge($importLines);
604 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.
605 675 $data = explode($this->separator, rtrim(trim($importLines[$i]), $this->separator));
606 676 }
607 677
678 + //We clean it... maybe there are other arguments at the end we should remove
608 679 if (!empty($this->separatorsToRemove)) {
609 - for ($b = $numberColumns + $this->separatorsToRemove - 1 ; $b >= $numberColumns ; $b--) {
680 + for ($b = $numberColumns + $this->separatorsToRemove - 1; $b >= $numberColumns; $b--) {
610 681 if (isset($data[$b]) && (strlen($data[$b]) == 0 || $data[$b] == ' ')) {
611 682 unset($data[$b]);
612 683 }
613 684 }
@@ -613,28 +684,38 @@
613 684 }
614 685 }
615 686
616 687 $i++;
688 + //We don't handle empty lines...
617 689 if (empty($importLines[$i - 1])) {
618 690 continue;
619 691 }
620 692
621 693 $this->totalTry++;
694 + //Lets try to fix it first...
622 695 if (count($data) > $numberColumns) {
623 696 $copy = $data;
624 697 foreach ($copy as $oneelem => $oneval) {
625 - if (!empty($oneval[0]) && $oneval[0] == '"' && $oneval[strlen($oneval) - 1] != '"' && isset($copy[$oneelem + 1]) && $copy[$oneelem + 1][strlen(
626 - $copy[$oneelem + 1]
627 - ) - 1] == '"') {
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
628 706 $data[$oneelem] = $copy[$oneelem].$this->separator.$copy[$oneelem + 1];
629 707 unset($data[$oneelem + 1]);
630 708 }
631 709 }
710 +
632 711 $data = array_values($data);
633 712 }
634 713
714 + // Not enough columns found...
635 715 if (count($data) < $numberColumns) {
636 - for ($a = count($data) ; $a < $numberColumns ; $a++) {
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++) {
637 718 $data[$a] = '';
638 719 }
639 720 }
640 721
@@ -644,8 +725,9 @@
644 725 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_ARGUMENTS', $numberColumns), 'warning');
645 726 }
646 727 $errorcount++;
647 728
729 + //If it's for the first line, we return so we don't check the rest and let the user fix it
648 730 if ($this->totalTry == 1) {
649 731 return;
650 732 }
651 733 if (empty($errorLines)) {
@@ -657,19 +739,23 @@
657 739
658 740 $newUser = new \stdClass();
659 741 $newUser->customfields = [];
660 742
743 + // Handle email column first to be able to use it with listids and listname
661 744 $emailKey = array_search('email', $this->columns);
662 - $newUser->email = trim(strip_tags($data[$emailKey]), '\'" ');
745 + $newUser->email = trim(acym_stripTags($data[$emailKey]), '\'" ');
746 + // Remove all whitespace type
663 747 $newUser->email = preg_replace("/\s+/u", '', $newUser->email);
664 748 if (!empty($newUser->email)) {
665 749 $newUser->email = acym_punycode($newUser->email);
666 750 }
667 751 $newUser->email = trim(str_replace([' ', "\t"], '', $encodingHelper->change($newUser->email, 'UTF-8', 'ISO-8859-1')));
752 + $newUser->email = strtolower($newUser->email);
668 753
669 754
670 755 if (!acym_isValidEmail($newUser->email)) {
671 756 static $errorcountfail = 0;
757 + //We limit to 10 errors otherwise it may break the Joomla messaging system in terms of memory usage
672 758 if ($errorcountfail == 0) {
673 759 acym_enqueueMessage(acym_translation('ACYM_ADDRESSES_INVALID'), 'warning');
674 760 }
675 761 $errorcountfail++;
@@ -680,15 +766,18 @@
680 766 continue;
681 767 }
682 768
683 769 foreach ($data as $num => $value) {
770 + // Already handled the email address
684 771 if ($num == $emailKey) continue;
685 772
686 773 $field = $this->columns[$num];
687 774
775 + // Ignored
688 776 if ($field == 1) continue;
689 777
690 778 if ($field === 'listids') {
779 + //We explode the listids separated by "-" and forach of them we add the user in the importUserInLists
691 780 $liststosub = explode('-', trim($value, '\'" '));
692 781 foreach ($liststosub as $onelistid) {
693 782 $this->importUserInLists[intval(trim($onelistid))][] = acym_escapeDB($newUser->email);
694 783 }
@@ -706,9 +795,9 @@
706 795 $newList = new \stdClass();
707 796 $newList->name = $onelistName;
708 797 $newList->active = 1;
709 798 $colors = ['#3366ff', '#7240A4', '#7A157D', '#157D69', '#ECE649'];
710 - $newList->color = $colors[rand(0, count($colors) - 1)];
799 + $newList->color = $colors[acym_rand(0, count($colors) - 1)];
711 800 $listid = $listClass->save($newList);
712 801 $newList->id = $listid;
713 802 $allLists[$onelistName] = $newList;
714 803 }
@@ -716,10 +805,48 @@
716 805 }
717 806 continue;
718 807 }
719 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
720 847 if (strpos($field, 'cf_') === 0) {
721 - $newUser->customfields[substr($field, 3)] = trim(strip_tags($value), '\'" ');
848 + $newUser->customfields[substr($field, 3)] = trim(acym_stripTags($value), '\'" ');
722 849 continue;
723 850 }
724 851
725 852 if ($value === 'null') {
@@ -724,9 +851,10 @@
724 851
725 852 if ($value === 'null') {
726 853 $newUser->$field = '';
727 854 } else {
728 - $newUser->$field = trim(strip_tags($value), '\'" ');
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), '\'" ');
729 857 }
730 858 }
731 859
732 860 if (acym_isMultilingual() && empty($newUser->language)) {
@@ -732,11 +860,13 @@
732 860 if (acym_isMultilingual() && empty($newUser->language)) {
733 861 $newUser->language = $this->config->get('multilingual_default', '');
734 862 }
735 863
864 + //Everything is Ok... we can add the line
736 865 $importUsers[] = $newUser;
737 866 $this->totalValid++;
738 867
868 + //Every 50 users, we handle it
739 869 if ($this->totalValid % 50 == 0) {
740 870 $this->insertUsers($importUsers, $timestamp);
741 871 $importUsers = [];
742 872 }
@@ -763,8 +893,9 @@
763 893 $this->insertUsers($importUsers, $timestamp);
764 894
765 895 acym_query('UPDATE #__acym_configuration SET `value` = '.intval($timestamp).' WHERE `name` = \'last_import\'');
766 896
897 + // We could have imported empty values
767 898 acym_query('DELETE FROM #__acym_user_has_field WHERE `value` = ""');
768 899
769 900 $countUsersAfterImport = $userClass->getCountTotalUsers();
770 901 $totalInserted = $countUsersAfterImport - $countUsersBeforeImport;
@@ -782,8 +913,11 @@
782 913 $maybeUpdated
783 914 );
784 915 acym_enqueueMessage($reportMsg, 'info');
785 916
917 + $this->insertSubscriptionHistory();
918 +
919 + // All users have been added properly into the database... we will now subscribe the users
786 920 $this->subscribeUsers();
787 921 }
788 922
789 923 private function insertUsers(array $users, int $timestamp): void
@@ -809,8 +943,10 @@
809 943 foreach ($colNames as $oneColumn) {
810 944 acym_secureDBColumn($oneColumn);
811 945 }
812 946
947 + $dateColumns = $this->getUserDateColumns();
948 +
813 949 $queryInsertUsers = 'INSERT'.($this->overwrite ? '' : ' IGNORE').' INTO #__acym_user (`'.implode('`,`', $colNames).'`) VALUES (';
814 950 $values = [];
815 951 $customFieldsvalues = [];
816 952 $allemails = [];
@@ -822,15 +958,17 @@
822 958 if ($map === 'customfields') continue;
823 959
824 960 $oneValue = htmlspecialchars_decode($oneValue, ENT_QUOTES);
825 961
826 - if ($map !== 'id') {
962 + if ($map === 'id') {
963 + $oneValue = intval($oneValue);
964 + } elseif (isset($dateColumns[$map]) && strlen(trim((string)$oneValue)) === 0) {
965 + $oneValue = 'NULL';
966 + } else {
827 967 $oneValue = acym_escapeDB($oneValue);
828 968 if ($map === 'email') {
829 969 $allemails[] = $oneValue;
830 970 }
831 - } else {
832 - $oneValue = intval($oneValue);
833 971 }
834 972
835 973 $value[] = $oneValue;
836 974 }
@@ -837,8 +975,9 @@
837 975
838 976 if (!isset($oneUser->key)) $value[] = acym_escapeDB(acym_generateKey(14));
839 977 $values[] = implode(',', $value);
840 978
979 + // Prepare import custom fields
841 980 if (!empty($oneUser->customfields)) $customFieldsvalues[$oneUser->email] = $oneUser->customfields;
842 981 }
843 982 acym_trigger('onAcymUserImport', [&$users]);
844 983
@@ -860,8 +999,10 @@
860 999 acym_query($queryInsertUsers);
861 1000
862 1001 $importedUsers = acym_loadObjectList('SELECT id, email FROM #__acym_user WHERE email IN ('.implode(',', $allemails).')', 'id');
863 1002
1003 + $this->importedUsersByEmail = array_combine(array_column($importedUsers, 'email'), array_column($importedUsers, 'id'));
1004 +
864 1005 if (!empty($customFieldsvalues)) {
865 1006 $insertValues = [];
866 1007 foreach ($importedUsers as $one) {
867 1008 if (empty($customFieldsvalues[$one->email])) continue;
@@ -887,16 +1028,33 @@
887 1028
888 1029 $this->allUserIds = array_merge($this->allUserIds, array_keys($importedUsers));
889 1030 }
890 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 +
891 1048 private function checkData(object &$user, int $timestamp)
892 1049 {
1050 + //Created field
893 1051 if (empty($user->creation_date)) {
894 1052 $user->creation_date = time();
895 1053 }
896 1054
897 1055 if (is_numeric($user->creation_date)) {
898 - $user->creation_date = date('Y-m-d H:i:s', $user->creation_date);
1056 + $user->creation_date = gmdate('Y-m-d H:i:s', $user->creation_date);
899 1057 }
900 1058
901 1059 if (!isset($user->active) || strlen($user->active) == 0) {
902 1060 $user->active = 1;
@@ -909,8 +1067,9 @@
909 1067 if (empty($user->source)) {
910 1068 $user->source = 'Import on '.acym_date($timestamp, 'Y-m-d H:i:s');
911 1069 }
912 1070
1071 + //name field
913 1072 if (empty($user->name) && $this->generateName) {
914 1073 $user->name = ucwords(trim(str_replace(['.', '_', '-', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0], ' ', substr($user->email, 0, strpos($user->email, '@')))));
915 1074 }
916 1075 }
@@ -918,10 +1077,12 @@
918 1077 private function autoDetectHeader(): bool
919 1078 {
920 1079 $this->separator = ',';
921 1080
1081 + // Remove BOM characters if there is one
922 1082 $this->header = str_replace("\xEF\xBB\xBF", '', $this->header);
923 1083
1084 + // Detect the separator
924 1085 $listSeparators = ["\t", ';', ','];
925 1086 foreach ($listSeparators as $sep) {
926 1087 if (strpos($this->header, $sep) !== false) {
927 1088 $this->separator = $sep;
@@ -930,9 +1091,10 @@
930 1091 }
931 1092
932 1093 $this->columns = explode($this->separator, $this->header);
933 1094
934 - for ($i = count($this->columns) - 1 ; $i >= 0 ; $i--) {
1095 + // Clean the headers
1096 + for ($i = count($this->columns) - 1; $i >= 0; $i--) {
935 1097 if (strlen($this->columns[$i]) == 0) {
936 1098 unset($this->columns[$i]);
937 1099 $this->separatorsToRemove++;
938 1100 }
@@ -946,14 +1108,16 @@
946 1108 foreach ($this->columns as $i => $oneColumn) {
947 1109 $this->columns[$i] = strtolower(trim($oneColumn, '\'" '));
948 1110 if (in_array($this->columns[$i], ['listids', 'listname'])) continue;
949 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;
950 1114
951 1115 if (!in_array($this->columns[$i], $columns) && $this->columns[$i] != 1) {
952 1116 acym_enqueueMessage(
953 1117 acym_translationSprintf(
954 1118 'ACYM_IMPORT_ERROR_FIELD',
955 - '<b>'.acym_escape($this->columns[$i]).'</b>',
1119 + '<b>'.esc_html($this->columns[$i]).'</b>',
956 1120 '<b>'.implode('</b> | <b>', array_diff($columns, ['id', 'cms_id'])).'</b>'
957 1121 ),
958 1122 'error'
959 1123 );
@@ -976,9 +1140,9 @@
976 1140 if (acym_fileGetExt($oneFile) != 'csv') {
977 1141 continue;
978 1142 }
979 1143 if (filectime($oneFile) < time() - 86400) {
980 - unlink($oneFile);
1144 + acym_deleteFile($oneFile);
981 1145 }
982 1146 }
983 1147 }
984 1148
@@ -983,13 +1147,14 @@
983 1147 }
984 1148
985 1149 private function subscribeUsers(): void
986 1150 {
1151 + //All users are known as : $this->allUserIds
987 1152 if (empty($this->allUserIds)) {
988 1153 return;
989 1154 }
990 1155
991 - $subdate = date('Y-m-d H:i:s', time() - date('Z'));
1156 + $subdate = gmdate('Y-m-d H:i:s', time());
992 1157
993 1158 $listClass = new ListClass();
994 1159 $lists = $this->getImportedLists();
995 1160
@@ -1007,8 +1172,9 @@
1007 1172 }
1008 1173 }
1009 1174
1010 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...
1011 1177 foreach ($this->importUserInLists as $listid => $arrayEmails) {
1012 1178 if (empty($listid)) continue;
1013 1179
1014 1180 $listid = (int)$listid;
@@ -1028,9 +1194,9 @@
1028 1194 }
1029 1195 }
1030 1196
1031 1197 if (!empty($lists)) {
1032 -
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
1033 1199 foreach ($lists as $listid => $val) {
1034 1200 if (empty($val)) {
1035 1201 continue;
1036 1202 }
@@ -1036,9 +1202,9 @@
1036 1202 }
1037 1203
1038 1204 if ($val == -1) {
1039 1205 $dateColumn = 'unsubscribe_date';
1040 - $status = -1;
1206 + $status = 0;
1041 1207 } else {
1042 1208 $dateColumn = 'subscription_date';
1043 1209 $status = 1;
1044 1210 }
@@ -1055,9 +1221,9 @@
1055 1221
1056 1222 if ($b > 200) {
1057 1223 $query = rtrim($query, ',');
1058 1224 if ($val == -1) {
1059 - $query .= ' ON DUPLICATE KEY UPDATE status = -1';
1225 + $query .= ' ON DUPLICATE KEY UPDATE status = 0';
1060 1226 $nbsubscribed = -acym_loadResult(
1061 1227 'SELECT COUNT(*) FROM #__acym_listsub WHERE `list_id` = '.intval($listid).' AND status != -1 AND `user_id` IN ('.implode(',', $currentSubids).')'
1062 1228 );
1063 1229 }
@@ -1071,9 +1237,10 @@
1071 1237 $query .= '('.intval($listid).','.intval($subid).','.acym_escapeDB($subdate).','.$status.'),';
1072 1238 }
1073 1239 $query = rtrim($query, ',');
1074 1240 if ($val == -1) {
1075 - $query .= ' ON DUPLICATE KEY UPDATE status = -1';
1241 + $query .= ' ON DUPLICATE KEY UPDATE status = 0';
1242 + // It could be empty if we imported exactly 200, 400, 600... users
1076 1243 if (!empty($currentSubids)) {
1077 1244 $nbsubscribed = -acym_loadResult(
1078 1245 'SELECT COUNT(*) FROM #__acym_listsub WHERE `list_id` = '.intval($listid).' AND status != -1 AND `user_id` IN ('.implode(',', $currentSubids).')'
1079 1246 );
@@ -1089,8 +1256,79 @@
1089 1256 $myList = $listClass->getOneById($listid);
1090 1257 $myList->status = $val;
1091 1258 $this->subscribedUsers[$listid] = $myList;
1092 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);
1093 1331 }
1094 1332 }
1095 1333 }
1096 1334 }