PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.2.2
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.2.2
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 10.2.2, at back/Helpers/ImportHelper.php

1,098 lines 43.4 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 private array $subscribedUsers = [];
12 private array $importUserInLists = [];
13 private array $allUserIds = [];
14 private array $columns = [];
15 private int $totalTry = 0;
16 private int $totalValid = 0;
17 private int $separatorsToRemove = 0;
18 private bool $forceConfirm = false;
19 private bool $generateName = true;
20 private bool $overwrite = false;
21 private string $header = '';
22 private string $separator = ',';
23
24 public string $tableName = '';
25 public array $dbWhere = [];
26 public array $fieldsMap = [];
27
28 public function __construct()
29 {
30 parent::__construct();
31 acym_increasePerf();
32 }
33
34 public function file(): bool
35 {
36 $importFile = acym_getVar('array', 'import_file', [], 'files');
37
38 if (empty($importFile['name'])) {
39 acym_enqueueMessage(acym_translation('ACYM_PLEASE_BROWSE_FILE_IMPORT'), 'error');
40
41 return false;
42 }
43
44 $extension = strtolower(acym_fileGetExt($importFile['name']));
45
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');
48
49 return false;
50 }
51
52 $fileError = $importFile['error'];
53 if ($fileError > 0) {
54 switch ($fileError) {
55 case 1:
56 case 2:
57 acym_enqueueMessage(acym_translation('ACYM_UPLOADED_FILE_EXCEED_MAX_FILESIZE_PHP'), 'error');
58
59 return false;
60 case 3:
61 acym_enqueueMessage(acym_translation('ACYM_FILE_UPLOADED_PARTIALLY'), 'error');
62
63 return false;
64 case 4:
65 acym_enqueueMessage(acym_translation('ACYM_NO_FILE_WAS_UPLOADED'), 'error');
66
67 return false;
68 default:
69 acym_enqueueMessage(acym_translationSprintf('ACYM_UNKNOWN_ERROR_UPLOADING_FILE', $fileError), 'error');
70
71 return false;
72 }
73 }
74
75 $uploadPath = $this->createUploadFolder();
76
77 $attachment = new \stdClass();
78 $attachment->filename = uniqid('import_').'.csv';
79 acym_setVar('acym_import_filename', $attachment->filename);
80
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 }
94 }
95
96 return true;
97 }
98
99 public function textarea(): bool
100 {
101 $content = acym_getVar('string', 'acym__users__import__from_text__textarea');
102 $path = $this->createUploadFolder();
103 $filename = uniqid('import_').'.csv';
104
105 acym_writeFile($path.$filename, $content);
106 acym_setVar('acym_import_filename', $filename);
107
108 return true;
109 }
110
111 public function cms(): bool
112 {
113 global $acymCmsUserVars;
114
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';
116 $nbUpdated = acym_query($query);
117
118 $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 $affected = acym_query($query);
120 $nbUpdated += intval($affected);
121
122 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_UPDATE', $nbUpdated), 'success');
123
124 $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 $deletedSubid = acym_loadResultArray($query);
126
127 $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';
128 $deletedSubid = array_merge(acym_loadResultArray($query), $deletedSubid);
129
130 if (!empty($deletedSubid)) {
131 $userClass = new UserClass();
132 $deletedUsers = $userClass->delete($deletedSubid);
133 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_DELETE', $deletedUsers), 'success');
134 }
135
136 $time = time();
137 $formattedTime = acym_date($time, 'Y-m-d H:i:s');
138 $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` ';
140 $groups = acym_getVar('array', 'groups', []);
141 $this->config->save(['import_groups' => implode(',', $groups)]);
142 if (!empty($groups)) {
143 if (ACYM_CMS === 'joomla') {
144 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).')';
146 } else {
147 $query .= ' JOIN #__usermeta AS `meta` ON meta.user_id = `user`.`'.$acymCmsUserVars->id.'` AND `meta`.`meta_key` = "#__capabilities"';
148 foreach ($groups as $i => $oneGroup) {
149 $groups[$i] = acym_escapeDB('%'.strlen($oneGroup).':"'.$oneGroup.'"%');
150 }
151 $query .= ' WHERE `meta`.`meta_value` LIKE '.implode(' OR `meta`.`meta_value` LIKE ', $groups);
152 }
153 }
154
155 $insertedUsers = acym_query($query);
156
157 acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = \'last_import\'');
158
159 acym_trigger('onAcymAfterCMSUserImport', [$sourceImport]);
160
161 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $insertedUsers), 'info');
162
163 $lists = $this->getImportedLists();
164 $listsSubscribe = [];
165 if (!empty($lists)) {
166 foreach ($lists as $listid => $val) {
167 if (!empty($val)) {
168 $listsSubscribe[] = intval($listid);
169 }
170 }
171 }
172
173 if (empty($listsSubscribe)) {
174 return true;
175 }
176
177 $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'))).'
179 FROM #__acym_list AS list, #__acym_user AS user ';
180 $conditions = [];
181 $conditions[] = 'list.`id` IN ('.implode(',', $listsSubscribe).')';
182 $conditions[] = 'user.`cms_id` > 0';
183
184 if (!empty($groups)) {
185 if (ACYM_CMS === 'joomla') {
186 $query .= ' JOIN #__user_usergroup_map AS `map` ON map.user_id = `user`.`cms_id`';
187 $conditions[] = '`map`.`group_id` IN ('.implode(', ', $groups).')';
188 } else {
189 $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).')';
191 }
192 }
193
194 $query .= ' WHERE '.implode(' AND ', $conditions);
195 $nbsubscribed = acym_query($query);
196 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_SUBSCRIPTION', $nbsubscribed));
197
198 return true;
199 }
200
201
202 public function database(bool $onlyImport = false): bool
203 {
204 $this->forceConfirm = acym_getVar('bool', 'import_confirmed_database', true);
205
206 $table = empty($this->tableName) ? trim(acym_getVar('string', 'tablename')) : $this->tableName;
207 $time = time();
208 $formattedTime = acym_date($time, 'Y-m-d H:i:s');
209
210 if (empty($table)) {
211 acym_enqueueMessage(acym_translation('ACYM_SPECIFYTABLE'), 'warning');
212
213 return false;
214 }
215
216 $fields = acym_getColumns($table, false, false);
217 if (empty($fields)) {
218 acym_enqueueMessage(acym_translation('ACYM_SPECIFYTABLE'), 'warning');
219
220 return false;
221 }
222
223 $equivalentFields = empty($this->fieldsMap) ? acym_getVar('array', 'fields', []) : $this->fieldsMap;
224
225 if (empty($equivalentFields['email'])) {
226 acym_enqueueMessage(acym_translation('ACYM_SPECIFYFIELDEMAIL'), 'warning');
227
228 return false;
229 }
230
231 $select = [];
232 foreach ($equivalentFields as $acyField => $tableField) {
233 $tableField = trim($tableField);
234 if (empty($tableField)) {
235 continue;
236 }
237 if (!in_array($tableField, $fields)) {
238 acym_enqueueMessage(acym_translationSprintf('ACYM_SPECIFYFIELD', $tableField, implode(' <br> ', $fields)), 'warning');
239
240 return false;
241 }
242 $select['`'.acym_secureDBColumn($acyField).'`'] = acym_secureDBColumn($tableField);
243 }
244
245 if (empty($select['`creation_date`'])) {
246 $select['`creation_date`'] = acym_escapeDB(acym_date('now', 'Y-m-d H:i:s'));
247 }
248
249 if ($this->forceConfirm && empty($select['`confirmed`'])) {
250 $select['`confirmed`'] = 1;
251 }
252
253 $sourceTxt = 'Import on '.$formattedTime;
254 $select['`source`'] = acym_escapeDB($sourceTxt);
255
256 $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 "%@%"';
258 if (!empty($this->dbWhere)) {
259 $query .= ' AND ( '.implode(' ) AND (', $this->dbWhere).' )';
260 }
261
262 $affectedRows = acym_query($query);
263
264 acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = "last_import"');
265
266 acym_trigger('onAcymAfterDatabaseUserImport', [$sourceTxt]);
267
268 if ($onlyImport) {
269 return true;
270 }
271
272 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $affectedRows), 'info');
273
274
275 $lists = $this->getImportedLists();
276 $listsSubscribe = [];
277 if (!empty($lists)) {
278 foreach ($lists as $listid => $val) {
279 if (!empty($val)) {
280 $listsSubscribe[] = intval($listid);
281 }
282 }
283 }
284
285 if (empty($listsSubscribe)) {
286 return true;
287 }
288
289 $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'))).'
291 FROM #__acym_list AS list, #__acym_user AS user
292 WHERE list.`id` IN ('.implode(',', $listsSubscribe).')
293 AND user.`email` IN (SELECT '.acym_secureDBColumn($select['`email`']).' FROM '.acym_secureDBColumn($table).')';
294
295 $nbsubscribed = acym_query($query);
296 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_SUBSCRIPTION', $nbsubscribed));
297
298 return true;
299 }
300
301 public function mailpoet(): bool
302 {
303 $time = time();
304 $formattedTime = acym_date($time, 'Y-m-d H:i:s');
305 $sourceImport = 'import_on_'.$formattedTime.'_mailpoet';
306
307 $query = 'INSERT IGNORE INTO #__acym_user (`name`,`email`,`creation_date`,`active`, `confirmed`,`cms_id`, `source`)
308 SELECT CONCAT(`subscriber`.`first_name`, `subscriber`.`last_name`),
309 `subscriber`.`email`,
310 `subscriber`.`created_at`,
311 IF(`subscriber`.`status` = "inactive", 0, 1),
312 IF(`subscriber`.`status` = "subscribed", 1, 0),
313 `subscriber`.`wp_user_id`,
314 \''.$sourceImport.'\'
315 FROM #__mailpoet_subscribers AS `subscriber` ';
316 $listsMailpoet = acym_getVar('array', 'mailpoet_lists', []);
317 if (!empty($listsMailpoet)) {
318 acym_arrayToInteger($listsMailpoet);
319 $query .= ' JOIN #__mailpoet_subscriber_segment AS sub_segment ON `subscriber`.`id` = `sub_segment`.`subscriber_id` AND `sub_segment`.`segment_id` IN ('.implode(
320 ',',
321 $listsMailpoet
322 ).')';
323 }
324 $query .= ' WHERE `subscriber`.`deleted_at` IS NULL ON DUPLICATE KEY UPDATE `source`='.acym_escapeDB($sourceImport);
325
326 $insertedUsers = acym_query($query);
327
328 acym_query('UPDATE #__acym_configuration SET `value` = '.intval($time).' WHERE `name` = \'last_import\'');
329
330 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_NEW_SUBS', $insertedUsers), 'info');
331
332 $lists = $this->getImportedLists();
333 $listsSubscribe = [];
334 if (!empty($lists)) {
335 foreach ($lists as $listid => $val) {
336 if (!empty($val)) {
337 $listsSubscribe[] = intval($listid);
338 }
339 }
340 }
341
342 if (empty($listsSubscribe)) {
343 return true;
344 }
345
346 $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'))).'
348 FROM #__acym_list AS list, #__acym_user AS user ';
349 $conditions = [];
350 $conditions[] = 'list.`id` IN ('.implode(',', $listsSubscribe).')';
351 $conditions[] = 'user.`source` = '.acym_escapeDB($sourceImport);
352
353 if (!empty($listsMailpoet)) {
354 $query .= ' JOIN #__mailpoet_subscribers AS subscriber ON subscriber.email = user.email';
355 $query .= ' JOIN #__mailpoet_subscriber_segment AS subscriber_segment ON subscriber.id = subscriber_segment.subscriber_id AND subscriber_segment.segment_id IN ('.implode(
356 ',',
357 $listsMailpoet
358 ).')';
359 }
360
361 $query .= ' WHERE '.implode(' AND ', $conditions);
362 $nbsubscribed = acym_query($query);
363 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_SUBSCRIPTION', $nbsubscribed));
364
365 return true;
366 }
367
368 public function finalizeImport(): bool
369 {
370 $filename = strtolower(acym_getVar('cmd', 'acym_import_filename'));
371 $extension = '.'.acym_fileGetExt($filename);
372 $this->forceConfirm = acym_getVar('bool', 'import_confirmed_generic', true);
373 $this->generateName = acym_getVar('bool', 'import_generate_generic', true);
374 $this->overwrite = acym_getVar('bool', 'import_overwrite_generic', true);
375
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);
381
382 $filename = str_replace(['.', ' '], '_', substr($filename, 0, strpos($filename, $extension))).$extension;
383 $uploadPath = ACYM_MEDIA.'import'.DS.$filename;
384
385 if (!file_exists($uploadPath) || !is_file($uploadPath)) {
386 acym_enqueueMessage(acym_translation('ACYM_UPLOADED_FILE_NOT_FOUND').' '.$uploadPath, 'error');
387
388 return false;
389 }
390
391 $importColumns = acym_getVar('string', 'import_columns');
392 if (empty($importColumns)) {
393 acym_enqueueMessage(acym_translation('ACYM_COLUMNS_NOT_FOUND'), 'error');
394
395 return false;
396 }
397
398 $contentFile = file_get_contents($uploadPath);
399
400 if (acym_getVar('cmd', 'acyencoding', '') != '') {
401 $encodingHelper = new EncodingHelper();
402 $contentFile = $encodingHelper->change($contentFile, acym_getVar('cmd', 'acyencoding'), 'UTF-8');
403 }
404
405 $cutContent = str_replace(["\r\n", "\r"], "\n", $contentFile);
406 $allLines = explode("\n", $cutContent);
407
408 $listSeparators = ["\t", ';', ','];
409 $separator = ',';
410 foreach ($listSeparators as $sep) {
411 if (strpos($allLines[0], $sep) !== false) {
412 $separator = $sep;
413 break;
414 }
415 }
416
417 if (!empty($listsId)) {
418 $allLines[0] .= $sep;
419 }
420
421 $importColumns = str_replace(',', $separator, $importColumns);
422
423 if (strpos($allLines[0], '@')) {
424 $contentFile = $importColumns."\n".$contentFile;
425 } else {
426 $allLines[0] = $importColumns;
427 $contentFile = implode("\n", $allLines);
428 }
429
430 $this->handleContent($contentFile);
431
432 unlink($uploadPath);
433 $this->cleanImportFolder();
434
435 return true;
436 }
437
438 public function getImportedLists(): array
439 {
440 $listClass = new ListClass();
441 $listsId = json_decode(acym_getVar('string', 'acym__entity_select__selected'));
442 $newListName = acym_getVar('string', 'new_list');
443
444 if (empty($listsId) && empty($newListName)) {
445 return [];
446 }
447
448 $lists = [];
449
450 if (!empty($newListName)) {
451 $newList = new \stdClass();
452 $newList->name = $newListName;
453 $newList->active = 1;
454 $colors = '#'.substr(str_shuffle('ABCDEF0123456789'), 0, 6);
455 $newList->color = $colors;
456 $listid = $listClass->save($newList);
457 $lists[$listid] = 1;
458 }
459
460 if (!empty($listsId)) {
461 foreach ($listsId as $id) {
462 $lists[$id] = 1;
463 }
464 }
465
466 return $lists;
467 }
468
469 public function additionalDataUsersImport(bool $isGeneric): string
470 {
471 $buttonAddListId = $isGeneric ? 'acym__users__generic__import__create-list__button' : 'acym__users__import__create-list__button';
472 $buttonImportClass = $isGeneric ? 'acym__users__import__generic__import__button' : 'acym__users__import__button';
473 $buttonSkipId = $isGeneric ? 'acym__users__generic__import__skip__button' : 'acym__users__import__skip__button';
474 $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>';
495 }
496
497 private function createUploadFolder(): string
498 {
499 $folderPath = acym_cleanPath(ACYM_ROOT.trim(html_entity_decode(ACYM_MEDIA_FOLDER.'import'))).DS;
500 if ('wordpress' === ACYM_CMS) {
501 $folderPath = acym_cleanPath(WP_PLUGIN_DIR.DS.ACYM_COMPONENT.DS.'media'.DS.'import').DS;
502 }
503 if (!is_dir($folderPath)) {
504 acym_createDir($folderPath, true, true);
505 }
506
507 if (!is_writable($folderPath)) {
508 @chmod($folderPath, '0755');
509 if (!is_writable($folderPath)) {
510 acym_enqueueMessage(acym_translationSprintf('ACYM_WRITABLE_FOLDER', $folderPath), 'warning');
511 }
512 }
513
514 return $folderPath;
515 }
516
517 private function handleContent(string $contentFile): void
518 {
519 $timestamp = time();
520
521 $contentFile = str_replace(["\r\n", "\r"], "\n", $contentFile);
522 $importLines = explode("\n", $contentFile);
523
524 $i = 0;
525 $this->header = '';
526 $this->allUserIds = [];
527 while (empty($this->header) && $i < 10) {
528 $this->header = trim($importLines[$i]);
529 $i++;
530 }
531
532 if (strpos($this->header, '@') && !strpos($this->header, ',') && !strpos($this->header, ';') && !strpos($this->header, "\t")) {
533 $this->header = 'email';
534 $i--;
535 }
536
537 if (!$this->autoDetectHeader()) {
538 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_HEADER', acym_escape($this->header)), 'error');
539 acym_enqueueMessage(acym_translation('ACYM_IMPORT_EMAIL'), 'error');
540
541 return;
542 }
543
544 $encodingHelper = new EncodingHelper();
545 $userClass = new UserClass();
546 $listClass = new ListClass();
547
548 $allLists = $listClass->getAll('name');
549 $numberColumns = count($this->columns);
550 $countUsersBeforeImport = $userClass->getCountTotalUsers();
551 $importUsers = [];
552 $errorLines = [];
553
554 while (isset($importLines[$i])) {
555 if (strpos($importLines[$i], '"') !== false) {
556 $data = [];
557 $j = $i + 1;
558 $position = -1;
559
560 while ($j < ($i + 30)) {
561
562 $quoteOpened = substr($importLines[$i], $position + 1, 1) == '"';
563
564 if ($quoteOpened) {
565 $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) {
571 $nextQuotePosition = strpos($importLines[$i], '"', $nextQuotePosition + 1);
572 }
573 if ($nextQuotePosition === false) {
574 if (!isset($importLines[$j])) {
575 break;
576 }
577
578 $importLines[$i] .= "\n".$importLines[$j];
579 $importLines[$i] = rtrim($importLines[$i], $this->separator);
580 unset($importLines[$j]);
581 $j++;
582 } else {
583
584 if (strlen($importLines[$i]) - 1 == $nextQuotePosition) {
585 $data[] = substr($importLines[$i], $position + 1);
586 break;
587 }
588 $data[] = substr($importLines[$i], $position + 1, $nextQuotePosition + 1 - ($position + 1));
589 $position = $nextQuotePosition + 1;
590 }
591 } else {
592 $nextSeparatorPosition = strpos($importLines[$i], $this->separator, $position + 1);
593 if ($nextSeparatorPosition === false) {
594 $data[] = substr($importLines[$i], $position + 1);
595 break;
596 } else { // If found the next separator, add the value in $data and change the position
597 $data[] = substr($importLines[$i], $position + 1, $nextSeparatorPosition - ($position + 1));
598 $position = $nextSeparatorPosition;
599 }
600 }
601 }
602
603 $importLines = array_merge($importLines);
604 } else {
605 $data = explode($this->separator, rtrim(trim($importLines[$i]), $this->separator));
606 }
607
608 if (!empty($this->separatorsToRemove)) {
609 for ($b = $numberColumns + $this->separatorsToRemove - 1 ; $b >= $numberColumns ; $b--) {
610 if (isset($data[$b]) && (strlen($data[$b]) == 0 || $data[$b] == ' ')) {
611 unset($data[$b]);
612 }
613 }
614 }
615
616 $i++;
617 if (empty($importLines[$i - 1])) {
618 continue;
619 }
620
621 $this->totalTry++;
622 if (count($data) > $numberColumns) {
623 $copy = $data;
624 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] == '"') {
628 $data[$oneelem] = $copy[$oneelem].$this->separator.$copy[$oneelem + 1];
629 unset($data[$oneelem + 1]);
630 }
631 }
632 $data = array_values($data);
633 }
634
635 if (count($data) < $numberColumns) {
636 for ($a = count($data) ; $a < $numberColumns ; $a++) {
637 $data[$a] = '';
638 }
639 }
640
641 if (count($data) != $numberColumns) {
642 static $errorcount = 0;
643 if (empty($errorcount)) {
644 acym_enqueueMessage(acym_translationSprintf('ACYM_IMPORT_ARGUMENTS', $numberColumns), 'warning');
645 }
646 $errorcount++;
647
648 if ($this->totalTry == 1) {
649 return;
650 }
651 if (empty($errorLines)) {
652 $errorLines[] = 'error,'.$importLines[0];
653 }
654 $errorLines[] = acym_translation('ACYM_IMPORT_ERROR_WRONG_NUMBER_ARGUMENTS').','.$importLines[$i - 1];
655 continue;
656 }
657
658 $newUser = new \stdClass();
659 $newUser->customfields = [];
660
661 $emailKey = array_search('email', $this->columns);
662 $newUser->email = trim(strip_tags($data[$emailKey]), '\'" ');
663 $newUser->email = preg_replace("/\s+/u", '', $newUser->email);
664 if (!empty($newUser->email)) {
665 $newUser->email = acym_punycode($newUser->email);
666 }
667 $newUser->email = trim(str_replace([' ', "\t"], '', $encodingHelper->change($newUser->email, 'UTF-8', 'ISO-8859-1')));
668
669
670 if (!acym_isValidEmail($newUser->email)) {
671 static $errorcountfail = 0;
672 if ($errorcountfail == 0) {
673 acym_enqueueMessage(acym_translation('ACYM_ADDRESSES_INVALID'), 'warning');
674 }
675 $errorcountfail++;
676 if (empty($errorLines)) {
677 $errorLines[] = 'error,'.$importLines[0];
678 }
679 $errorLines[] = acym_translation('ACYM_INVALID_EMAIL_ADDRESS').','.$importLines[$i - 1];
680 continue;
681 }
682
683 foreach ($data as $num => $value) {
684 if ($num == $emailKey) continue;
685
686 $field = $this->columns[$num];
687
688 if ($field == 1) continue;
689
690 if ($field === 'listids') {
691 $liststosub = explode('-', trim($value, '\'" '));
692 foreach ($liststosub as $onelistid) {
693 $this->importUserInLists[intval(trim($onelistid))][] = acym_escapeDB($newUser->email);
694 }
695 continue;
696 }
697
698 if ($field === 'listname') {
699 $liststosub = explode('-', trim($value, '\'" '));
700 foreach ($liststosub as $onelistName) {
701 if (empty($onelistName)) {
702 continue;
703 }
704 $onelistName = trim($onelistName);
705 if (empty($allLists[$onelistName])) {
706 $newList = new \stdClass();
707 $newList->name = $onelistName;
708 $newList->active = 1;
709 $colors = ['#3366ff', '#7240A4', '#7A157D', '#157D69', '#ECE649'];
710 $newList->color = $colors[rand(0, count($colors) - 1)];
711 $listid = $listClass->save($newList);
712 $newList->id = $listid;
713 $allLists[$onelistName] = $newList;
714 }
715 $this->importUserInLists[intval($allLists[$onelistName]->id)][] = acym_escapeDB($newUser->email);
716 }
717 continue;
718 }
719
720 if (strpos($field, 'cf_') === 0) {
721 $newUser->customfields[substr($field, 3)] = trim(strip_tags($value), '\'" ');
722 continue;
723 }
724
725 if ($value === 'null') {
726 $newUser->$field = '';
727 } else {
728 $newUser->$field = trim(strip_tags($value), '\'" ');
729 }
730 }
731
732 if (acym_isMultilingual() && empty($newUser->language)) {
733 $newUser->language = $this->config->get('multilingual_default', '');
734 }
735
736 $importUsers[] = $newUser;
737 $this->totalValid++;
738
739 if ($this->totalValid % 50 == 0) {
740 $this->insertUsers($importUsers, $timestamp);
741 $importUsers = [];
742 }
743 }
744
745 if (!empty($errorLines)) {
746 $filename = strtolower(acym_getVar('cmd', 'acym_import_filename', ''));
747 if (!empty($filename)) {
748 $extension = '.'.acym_fileGetExt($filename);
749 $filename = str_replace(['.', ' '], '_', substr($filename, 0, strpos($filename, $extension))).$extension;
750 $errorFile = implode("\n", $errorLines);
751 acym_writeFile(ACYM_MEDIA.'import'.DS.'error_'.$filename, $errorFile);
752 acym_enqueueMessage(
753 '<a target="_blank" href="'.acym_prepareAjaxURL((acym_isAdmin() ? '' : 'front').'users&task=downloadImport').'&filename=error_'.preg_replace(
754 '#\.[^.]*$#',
755 '',
756 $filename
757 ).'&'.acym_noTemplate().'" >'.acym_translation('ACYM_DOWNLOAD_IMPORT_ERRORS').'</a>',
758 'notice'
759 );
760 }
761 }
762
763 $this->insertUsers($importUsers, $timestamp);
764
765 acym_query('UPDATE #__acym_configuration SET `value` = '.intval($timestamp).' WHERE `name` = \'last_import\'');
766
767 acym_query('DELETE FROM #__acym_user_has_field WHERE `value` = ""');
768
769 $countUsersAfterImport = $userClass->getCountTotalUsers();
770 $totalInserted = $countUsersAfterImport - $countUsersBeforeImport;
771
772 $maybeUpdated = acym_translation('ACYM_NOT_UPDATED');
773 if ($this->overwrite) {
774 $maybeUpdated = acym_translation('ACYM_UPDATED');
775 }
776 $reportMsg = acym_translationSprintf(
777 'ACYM_IMPORT_REPORTING_UPDATED',
778 $this->totalTry,
779 $totalInserted,
780 $this->totalTry - $this->totalValid,
781 $this->totalValid - $totalInserted,
782 $maybeUpdated
783 );
784 acym_enqueueMessage($reportMsg, 'info');
785
786 $this->subscribeUsers();
787 }
788
789 private function insertUsers(array $users, int $timestamp): void
790 {
791 if (empty($users)) {
792 return;
793 }
794
795 $importedCols = array_keys(get_object_vars($users[0]));
796 unset($importedCols[array_search('customfields', $importedCols)]);
797 if ($this->forceConfirm) $importedCols[] = 'confirmed';
798
799 foreach ($users as $a => $oneUser) {
800 $this->checkData($users[$a], $timestamp);
801 }
802
803 $columns = reset($users);
804 $colNames = array_keys(get_object_vars($columns));
805 unset($colNames[array_search('customfields', $colNames)]);
806
807 if (!in_array('key', $colNames)) $colNames[] = 'key';
808
809 foreach ($colNames as $oneColumn) {
810 acym_secureDBColumn($oneColumn);
811 }
812
813 $queryInsertUsers = 'INSERT'.($this->overwrite ? '' : ' IGNORE').' INTO #__acym_user (`'.implode('`,`', $colNames).'`) VALUES (';
814 $values = [];
815 $customFieldsvalues = [];
816 $allemails = [];
817 foreach ($users as $oneUser) {
818 $value = [];
819 acym_trigger('onAcymBeforeUserImport', [&$oneUser]);
820
821 foreach ($oneUser as $map => $oneValue) {
822 if ($map === 'customfields') continue;
823
824 $oneValue = htmlspecialchars_decode($oneValue, ENT_QUOTES);
825
826 if ($map !== 'id') {
827 $oneValue = acym_escapeDB($oneValue);
828 if ($map === 'email') {
829 $allemails[] = $oneValue;
830 }
831 } else {
832 $oneValue = intval($oneValue);
833 }
834
835 $value[] = $oneValue;
836 }
837
838 if (!isset($oneUser->key)) $value[] = acym_escapeDB(acym_generateKey(14));
839 $values[] = implode(',', $value);
840
841 if (!empty($oneUser->customfields)) $customFieldsvalues[$oneUser->email] = $oneUser->customfields;
842 }
843 acym_trigger('onAcymUserImport', [&$users]);
844
845 $queryInsertUsers .= implode('),(', $values).')';
846
847 if ($this->overwrite) {
848 $queryInsertUsers .= ' ON DUPLICATE KEY UPDATE ';
849 foreach ($importedCols as &$oneColumn) {
850 acym_secureDBColumn($oneColumn);
851 if ($oneColumn == 'key') {
852 $oneColumn = '`'.$oneColumn.'` = `'.$oneColumn.'`';
853 } else {
854 $oneColumn = '`'.$oneColumn.'` = VALUES(`'.$oneColumn.'`)';
855 }
856 }
857 $queryInsertUsers .= implode(',', $importedCols);
858 }
859
860 acym_query($queryInsertUsers);
861
862 $importedUsers = acym_loadObjectList('SELECT id, email FROM #__acym_user WHERE email IN ('.implode(',', $allemails).')', 'id');
863
864 if (!empty($customFieldsvalues)) {
865 $insertValues = [];
866 foreach ($importedUsers as $one) {
867 if (empty($customFieldsvalues[$one->email])) continue;
868
869 foreach ($customFieldsvalues[$one->email] as $fieldId => $value) {
870 $value = htmlspecialchars_decode($value, ENT_QUOTES);
871 $insertValues[] = '('.intval($one->id).','.intval($fieldId).','.acym_escapeDB($value).')';
872 }
873 }
874
875 if (!empty($insertValues)) {
876 $queryInsertCustomFields = 'INSERT'.($this->overwrite ? '' : ' IGNORE').' INTO #__acym_user_has_field (`user_id`, `field_id`, `value`) VALUES '.implode(
877 ',',
878 $insertValues
879 );
880 if ($this->overwrite) {
881 $queryInsertCustomFields .= ' ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)';
882 }
883 acym_query($queryInsertCustomFields);
884 }
885 }
886
887
888 $this->allUserIds = array_merge($this->allUserIds, array_keys($importedUsers));
889 }
890
891 private function checkData(object &$user, int $timestamp)
892 {
893 if (empty($user->creation_date)) {
894 $user->creation_date = time();
895 }
896
897 if (is_numeric($user->creation_date)) {
898 $user->creation_date = date('Y-m-d H:i:s', $user->creation_date);
899 }
900
901 if (!isset($user->active) || strlen($user->active) == 0) {
902 $user->active = 1;
903 }
904
905 if ((!isset($user->confirmed) || strlen($user->confirmed) == 0) && $this->forceConfirm) {
906 $user->confirmed = 1;
907 }
908
909 if (empty($user->source)) {
910 $user->source = 'Import on '.acym_date($timestamp, 'Y-m-d H:i:s');
911 }
912
913 if (empty($user->name) && $this->generateName) {
914 $user->name = ucwords(trim(str_replace(['.', '_', '-', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0], ' ', substr($user->email, 0, strpos($user->email, '@')))));
915 }
916 }
917
918 private function autoDetectHeader(): bool
919 {
920 $this->separator = ',';
921
922 $this->header = str_replace("\xEF\xBB\xBF", '', $this->header);
923
924 $listSeparators = ["\t", ';', ','];
925 foreach ($listSeparators as $sep) {
926 if (strpos($this->header, $sep) !== false) {
927 $this->separator = $sep;
928 break;
929 }
930 }
931
932 $this->columns = explode($this->separator, $this->header);
933
934 for ($i = count($this->columns) - 1 ; $i >= 0 ; $i--) {
935 if (strlen($this->columns[$i]) == 0) {
936 unset($this->columns[$i]);
937 $this->separatorsToRemove++;
938 }
939 }
940
941 $columns = acym_getColumns('user');
942 foreach ($columns as $i => $oneColumn) {
943 $columns[$i] = strtolower($oneColumn);
944 }
945
946 foreach ($this->columns as $i => $oneColumn) {
947 $this->columns[$i] = strtolower(trim($oneColumn, '\'" '));
948 if (in_array($this->columns[$i], ['listids', 'listname'])) continue;
949 if (strpos($this->columns[$i], 'cf_') === 0) continue;
950
951 if (!in_array($this->columns[$i], $columns) && $this->columns[$i] != 1) {
952 acym_enqueueMessage(
953 acym_translationSprintf(
954 'ACYM_IMPORT_ERROR_FIELD',
955 '<b>'.acym_escape($this->columns[$i]).'</b>',
956 '<b>'.implode('</b> | <b>', array_diff($columns, ['id', 'cms_id'])).'</b>'
957 ),
958 'error'
959 );
960
961 return false;
962 }
963 }
964
965 if (!in_array('email', $this->columns)) {
966 return false;
967 }
968
969 return true;
970 }
971
972 private function cleanImportFolder(): void
973 {
974 $files = acym_getFiles(ACYM_MEDIA.'import', '.', false, true, []);
975 foreach ($files as $oneFile) {
976 if (acym_fileGetExt($oneFile) != 'csv') {
977 continue;
978 }
979 if (filectime($oneFile) < time() - 86400) {
980 unlink($oneFile);
981 }
982 }
983 }
984
985 private function subscribeUsers(): void
986 {
987 if (empty($this->allUserIds)) {
988 return;
989 }
990
991 $subdate = date('Y-m-d H:i:s', time() - date('Z'));
992
993 $listClass = new ListClass();
994 $lists = $this->getImportedLists();
995
996 if (!acym_isAdmin() && 'joomla' === ACYM_CMS) {
997 $listClass = new ListClass();
998 $listManagementId = $listClass->getfrontManagementList();
999 if (empty($listManagementId)) {
1000 acym_redirect(acym_rootURI(), 'ACYM_UNABLE_TO_CREATE_MANAGEMENT_LIST', 'error');
1001 }
1002
1003 if (empty($lists)) {
1004 $lists = [$listManagementId => 1];
1005 } else {
1006 $lists[$listManagementId] = 1;
1007 }
1008 }
1009
1010 if (!empty($this->importUserInLists)) {
1011 foreach ($this->importUserInLists as $listid => $arrayEmails) {
1012 if (empty($listid)) continue;
1013
1014 $listid = (int)$listid;
1015 $query = 'INSERT IGNORE INTO #__acym_user_has_list (`list_id`,`user_id`,`subscription_date`,`status`) ';
1016 $query .= 'SELECT '.intval($listid).',`id`,'.acym_escapeDB($subdate).',1 FROM #__acym_user WHERE `email` IN (';
1017 $query .= implode(',', $arrayEmails).')';
1018 $nbsubscribed = acym_query($query);
1019 $nbsubscribed = intval($nbsubscribed);
1020
1021 if (isset($this->subscribedUsers[$listid])) {
1022 $this->subscribedUsers[$listid]->nbusers += $nbsubscribed;
1023 } else {
1024 $myList = $listClass->getOneById($listid);
1025 $this->subscribedUsers[$listid] = $myList;
1026 $this->subscribedUsers[$listid]->nbusers = $nbsubscribed;
1027 }
1028 }
1029 }
1030
1031 if (!empty($lists)) {
1032
1033 foreach ($lists as $listid => $val) {
1034 if (empty($val)) {
1035 continue;
1036 }
1037
1038 if ($val == -1) {
1039 $dateColumn = 'unsubscribe_date';
1040 $status = -1;
1041 } else {
1042 $dateColumn = 'subscription_date';
1043 $status = 1;
1044 }
1045
1046 $nbsubscribed = 0;
1047 $listid = intval($listid);
1048 $query = 'INSERT IGNORE INTO #__acym_user_has_list (`list_id`,`user_id`,`'.$dateColumn.'`,`status`) VALUES ';
1049 $b = 0;
1050 $currentSubids = [];
1051 foreach ($this->allUserIds as $subid) {
1052 $subid = intval($subid);
1053 $currentSubids[] = $subid;
1054 $b++;
1055
1056 if ($b > 200) {
1057 $query = rtrim($query, ',');
1058 if ($val == -1) {
1059 $query .= ' ON DUPLICATE KEY UPDATE status = -1';
1060 $nbsubscribed = -acym_loadResult(
1061 'SELECT COUNT(*) FROM #__acym_listsub WHERE `list_id` = '.intval($listid).' AND status != -1 AND `user_id` IN ('.implode(',', $currentSubids).')'
1062 );
1063 }
1064 $affected = acym_query($query);
1065 $nbsubscribed += intval($affected);
1066 $b = 0;
1067 $currentSubids = [];
1068 $query = 'INSERT IGNORE INTO #__acym_user_has_list (`list_id`,`user_id`,`'.$dateColumn.'`,`status`) VALUES ';
1069 }
1070
1071 $query .= '('.intval($listid).','.intval($subid).','.acym_escapeDB($subdate).','.$status.'),';
1072 }
1073 $query = rtrim($query, ',');
1074 if ($val == -1) {
1075 $query .= ' ON DUPLICATE KEY UPDATE status = -1';
1076 if (!empty($currentSubids)) {
1077 $nbsubscribed = -acym_loadResult(
1078 'SELECT COUNT(*) FROM #__acym_listsub WHERE `list_id` = '.intval($listid).' AND status != -1 AND `user_id` IN ('.implode(',', $currentSubids).')'
1079 );
1080 }
1081 }
1082
1083 $affected = acym_query($query);
1084 $nbsubscribed += intval($affected);
1085
1086 if (isset($this->subscribedUsers[$listid])) {
1087 $this->subscribedUsers[$listid]->nbusers += $nbsubscribed;
1088 } else {
1089 $myList = $listClass->getOneById($listid);
1090 $myList->status = $val;
1091 $this->subscribedUsers[$listid] = $myList;
1092 $this->subscribedUsers[$listid]->nbusers = $nbsubscribed;
1093 }
1094 }
1095 }
1096 }
1097 }
1098