PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.1.4
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.1.4
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 / ExportHelper.php

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

571 lines 19.2 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\MailClass;
7 use AcyMailing\Core\AcymObject;
8
9 class ExportHelper extends AcymObject
10 {
11 private string $eol = "\r\n";
12 private string $before = '"';
13 private string $after = '"';
14
15 public function setDownloadHeaders(string $filename = 'export', string $extension = 'csv'): void
16 {
17 acym_header('Pragma: public');
18 acym_header('Expires: 0');
19 acym_header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
20
21 acym_header('Content-Type: application/force-download');
22 acym_header('Content-Type: application/octet-stream');
23 acym_header('Content-Type: application/download');
24
25 acym_header('Content-Disposition: attachment; filename='.$filename.'.'.$extension);
26 acym_header('Content-Transfer-Encoding: binary');
27 }
28
29 public function exportTemplate(object $template): void
30 {
31 $name = preg_replace('#[^a-z0-9]#Uis', '_', $template->name);
32 $name = preg_replace('#_+#s', '_', $name);
33 $exportFolder = ACYM_ROOT.ACYM_MEDIA_FOLDER.'tmp'.DS.$name;
34 acym_createFolder($exportFolder);
35
36 $template->body = acym_absoluteURL($template->body);
37 $images = [];
38 preg_match_all('#<img[^>]* src="([^"]+)"#Uis', $template->body, $images);
39
40 if (!empty($images[1])) {
41 $imagesFolder = $exportFolder.DS.'images';
42 acym_createFolder($imagesFolder);
43
44 $replace = [];
45 foreach ($images[1] as $oneImage) {
46 if (isset($replace[$oneImage])) continue;
47
48 $location = str_replace(
49 [ACYM_LIVE, '/'],
50 [ACYM_ROOT, DS],
51 $oneImage
52 );
53 if (strpos($location, 'http') === 0) continue;
54
55 if (!file_exists($location)) continue;
56
57 $filename = basename($location);
58 while (file_exists($imagesFolder.DS.$filename)) {
59 $filename = rand(0, 99).$filename;
60 }
61
62 acym_copyFile($location, $imagesFolder.DS.$filename);
63 $replace[$oneImage] = 'images/'.$filename;
64 }
65
66 $template->body = str_replace(array_keys($replace), $replace, $template->body);
67 }
68
69 $structure = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
70 <html xmlns="http://www.w3.org/1999/xhtml">
71 <head>
72 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
73
74 $dataToCopy = [
75 'fromname',
76 'fromemail',
77 'replyname',
78 'replyemail',
79 'subject',
80 'settings',
81 ];
82 foreach ($dataToCopy as $oneData) {
83 if (empty($template->$oneData)) continue;
84 $structure .= "\n".'<meta name="'.$oneData.'" content="'.acym_escape($template->$oneData).'" />';
85 }
86
87 $structure .= "\n".'<title>'.$template->name.'</title>
88 </head>
89 <body>
90 '.$template->body.'
91 </body>
92 </html>';
93
94 acym_writeFile($exportFolder.DS.'template.html', $structure);
95
96 $thumbnail = acym_getMailThumbnail($template->thumbnail);
97 $thumbnail = str_replace(ACYM_LIVE, ACYM_ROOT, $thumbnail);
98 acym_copyFile($thumbnail, $exportFolder.DS.'thumbnail.png');
99
100 if (!empty($template->stylesheet)) {
101 acym_createFolder($exportFolder.DS.'css');
102 acym_writeFile($exportFolder.DS.'css'.DS.'custom.css', $template->stylesheet);
103 }
104
105 $zipFiles = [];
106 $folders = acym_getFolders($exportFolder, '.', true, true);
107 array_push($folders, $exportFolder);
108 foreach ($folders as $folder) {
109 $files = acym_getFiles($folder, '.', false, true);
110 foreach ($files as $file) {
111 $posSlash = strrpos($file, '/');
112 $posASlash = strrpos($file, '\\');
113 $pos = ($posSlash < $posASlash) ? $posASlash : $posSlash;
114 if (!empty($pos)) $file = substr_replace($file, DS, $pos, 1);
115
116 $data = acym_fileGetContent($file);
117 $zipFiles[] = [
118 'name' => str_replace(
119 $exportFolder.DS,
120 '',
121 $file
122 ),
123 'data' => $data,
124 ];
125 }
126 }
127
128 acym_createArchive($exportFolder, $zipFiles);
129
130 acym_deleteFolder($exportFolder);
131
132 $this->setDownloadHeaders($name, 'zip');
133 echo acym_fileGetContent($exportFolder.'.zip');
134 acym_deleteFile($exportFolder.'.zip');
135
136 exit;
137 }
138
139 public function exportStatsFormattedCSV(string $mailName, array $globalDonutsData, array $globaline, string $timeLinechart): void
140 {
141 $nbExport = $this->getExportLimit();
142 acym_displayErrors();
143
144 if ($timeLinechart === 'month') {
145 $timeLinechart = acym_translation('ACYM_MONTHLY_STATS');
146 } elseif ($timeLinechart === 'week') {
147 $timeLinechart = acym_translation('ACYM_WEEKLY_STATS');
148 } else {
149 $timeLinechart = acym_translation('ACYM_DAILY_STATS');
150 }
151
152 $separator = '","';
153
154 $csvLines = [];
155 $csvLines[] = $this->before.$mailName.$this->after;
156
157 $csvLines[] = $this->eol;
158
159 $globalDonutsTitle = [
160 acym_translation('ACYM_SUCCESSFULLY_SENT'),
161 acym_translation('ACYM_OPEN_RATE'),
162 acym_translation('ACYM_CLICK_RATE'),
163 acym_translation('ACYM_BOUNCE_RATE'),
164 acym_translation('ACYM_UNSUBSCRIBE_RATE'),
165 ];
166
167 $csvLines[] = $this->before.implode($separator, $globalDonutsTitle).$this->after;
168 $csvLines[] = $this->before.implode($separator, $globalDonutsData).$this->after;
169
170 $csvLines[] = $this->eol;
171
172 $csvLines[] = $this->before.$timeLinechart.$separator.acym_translation('ACYM_OPEN').$separator.acym_translation('ACYM_CLICK').$this->after;
173
174 $i = 0;
175 foreach ($globaline as $date => $value) {
176 if ($i > $nbExport) break;
177 $csvLines[] = $this->before.$date.$separator.$value['open'].$separator.$value['click'].$this->after;
178 $i++;
179 }
180
181 $this->finishStatsExport($csvLines);
182 }
183
184 public function exportStatsFullCSV(string $query, array $columns, string $type = 'global'): void
185 {
186 $mailsStats = acym_loadObjectList($query);
187 $mailClass = new MailClass();
188 $mailClass->exceptKeysDecode = ['name'];
189 $mailsStats = $mailClass->decode($mailsStats);
190 $nbExport = $this->getExportLimit();
191 acym_displayErrors();
192
193 $separator = '","';
194 $csvLines = [];
195
196 $csvLines[] = $this->before.implode($separator, $columns).$this->after;
197
198 $valueNeedNumber = ['click'];
199
200 $i = 0;
201 foreach ($mailsStats as $mailStat) {
202 if ($i > $nbExport) break;
203 $oneLine = [];
204 foreach ($columns as $key => $trad) {
205 $key = strpos($key, '.') !== false ? explode('.', $key) : $key;
206 if (is_array($key)) $key = $key[1];
207 $line = in_array($key, $valueNeedNumber) && empty($mailStat->{$key}) ? 0 : $mailStat->{$key};
208 if (is_null($line)) {
209 $line = '';
210 }
211 if ($key === 'bounce_details' && !empty($line)) {
212 $line = @unserialize($line);
213 if ($line !== false) {
214 $formattedLine = '';
215 foreach ($line as $detailKey => $detailValue) {
216 $formattedLine .= $detailKey.': '.$detailValue.'; ';
217 }
218 $line = rtrim($formattedLine, '; ');
219 }
220 }
221 $oneLine[] = htmlspecialchars($line, ENT_QUOTES, 'UTF-8');
222 }
223 $csvLines[] = $this->before.implode($separator, $oneLine).$this->after;
224 $i++;
225 }
226
227 $this->finishStatsExport($csvLines, $type);
228 }
229
230 private function finishStatsExport(array $csvLines, string $type = 'global'): void
231 {
232 $final = implode($this->eol, $csvLines);
233
234 if (ACYM_CMS === 'wordpress') {
235 @ob_get_clean();
236 }
237 $filename = 'export_stats_'.$type.'_'.date('Y-m-d');
238 $this->setDownloadHeaders($filename);
239 echo $final;
240 }
241
242 private function getLists(): array
243 {
244 static $lists = null;
245 if (empty($lists)) {
246 $listClass = new ListClass();
247 $lists = $listClass->getAllWithIdName();
248 }
249
250 return $lists;
251 }
252
253 public function exportCSV(
254 string $query,
255 array $fieldsToExport,
256 array $customFieldsToExport,
257 array $specialFieldsToExport,
258 string $separator = ',',
259 string $charset = 'UTF-8',
260 string $exportFile = '',
261 int $flagToRemove = 0
262 ): string {
263 $nbExport = $this->getExportLimit();
264 acym_displayErrors();
265 $encodingClass = new EncodingHelper();
266 $excelSecure = $this->config->get('export_excelsecurity', 0);
267
268 if (!in_array($separator, [',', ';'])) $separator = ',';
269 $separator = '"'.$separator.'"';
270
271 $allFieldsToExport = array_merge($fieldsToExport, $customFieldsToExport);
272
273 foreach ($allFieldsToExport as $key => $field) {
274 $allFieldsToExport[$key] = $encodingClass->change($field, 'UTF-8', $charset);
275 }
276
277 if (in_array('subscribe_date', $specialFieldsToExport)) {
278 $lists = $this->getLists();
279 foreach ($lists as $oneList) {
280 $allFieldsToExport[] = acym_translation('ACYM_SUBSCRIPTION_DATE').' '.acym_translation($oneList);
281 }
282 }
283
284 if (in_array('unsubscribe_date', $specialFieldsToExport)) {
285 $lists = $this->getLists();
286 foreach ($lists as $oneList) {
287 $allFieldsToExport[] = acym_translation('ACYM_UNSUBSCRIPTION_DATE').' '.acym_translation($oneList);
288 }
289 }
290
291 $firstLine = $this->before.implode($separator, $allFieldsToExport).$this->after.$this->eol;
292
293 if (empty($exportFile)) {
294 @ob_get_clean();
295 $filename = 'export_'.date('Y-m-d');
296 $this->setDownloadHeaders($filename);
297 echo $firstLine;
298 } else {
299 preg_match('#^(.+/)[^/]+$#', $exportFile, $folder);
300 if (!empty($folder[1]) && !file_exists($folder[1])) acym_createDir($folder[1]);
301
302 $fp = fopen($exportFile, 'w');
303 if (false === $fp) {
304 if ($flagToRemove !== 0) {
305 $automationHelper = new AutomationHelper();
306 $automationHelper->removeFlag($flagToRemove);
307 }
308
309 return acym_translationSprintf('ACYM_FAIL_SAVE_FILE', $exportFile);
310 }
311
312 $error = fwrite($fp, $firstLine);
313 if (false === $error) {
314 if ($flagToRemove !== 0) {
315 $automationHelper = new AutomationHelper();
316 $automationHelper->removeFlag($flagToRemove);
317 }
318
319 return acym_translationSprintf('ACYM_UNWRITABLE_FILE', $exportFile);
320 }
321 }
322
323 $start = 0;
324 do {
325 try {
326 $users = acym_loadObjectList($query.' LIMIT '.intval($start).', '.intval($nbExport), 'id');
327 } catch (\Exception $e) {
328 $users = false;
329 }
330 $start += $nbExport;
331
332 if ($users === false) {
333 $errorLine = $this->eol.$this->eol.'Error: '.(isset($e) ? $e->getMessage() : acym_getDBError());
334
335 if (empty($exportFile)) {
336 echo $errorLine;
337 } else {
338 $error = fwrite($fp, $errorLine);
339 if (false === $error) {
340 if ($flagToRemove !== 0) {
341 $automationHelper = new AutomationHelper();
342 $automationHelper->removeFlag($flagToRemove);
343 }
344
345 return acym_translationSprintf('ACYM_UNWRITABLE_FILE', $exportFile);
346 }
347 }
348 }
349
350 if (empty($users)) break;
351
352 foreach ($users as $userID => $oneUser) {
353 unset($oneUser->id);
354
355 $data = get_object_vars($oneUser);
356
357 if (!empty($customFieldsToExport)) {
358 $fieldIDs = array_keys($customFieldsToExport);
359 acym_arrayToInteger($fieldIDs);
360
361 $userCustomFields = acym_loadObjectList(
362 'SELECT `field_id`, `value`
363 FROM #__acym_user_has_field
364 WHERE user_id = '.intval($userID).' AND field_id IN ('.implode(',', $fieldIDs).')',
365 'field_id'
366 );
367
368 foreach ($customFieldsToExport as $fieldID => $fieldName) {
369 if (empty($userCustomFields[$fieldID])) {
370 $data[] = '';
371 } else {
372 $data[] = acym_triggerCmsHook(
373 'onUserExportFieldData',
374 [$userCustomFields[$fieldID]->value, $fieldID, $fieldName],
375 false
376 );
377 }
378 }
379 unset($userCustomFields);
380 }
381
382 if (isset($lists)) {
383 $userSubscriptions = acym_loadObjectList(
384 'SELECT `list_id`, `subscription_date`, `unsubscribe_date`
385 FROM #__acym_user_has_list
386 WHERE user_id = '.intval($userID),
387 'list_id'
388 );
389
390 if (in_array('subscribe_date', $specialFieldsToExport)) {
391 foreach ($lists as $listId => $oneList) {
392 $data[] = $userSubscriptions[$listId]->subscription_date ?? '';
393 }
394 }
395
396 if (in_array('unsubscribe_date', $specialFieldsToExport)) {
397 foreach ($lists as $listId => $oneList) {
398 $data[] = $userSubscriptions[$listId]->unsubscribe_date ?? '';
399 }
400 }
401
402 unset($userSubscriptions);
403 }
404
405 foreach ($data as &$oneData) {
406 if (is_null($oneData)) {
407 continue;
408 }
409
410 if ($excelSecure == 1) {
411 $firstCharacter = substr($oneData, 0, 1);
412 if (in_array($firstCharacter, ['=', '+', '-', '@'])) {
413 $oneData = ' '.$oneData;
414 }
415 }
416
417 $oneData = htmlspecialchars($oneData);
418 }
419
420 $dataexport = implode($separator, $data);
421 unset($data);
422
423 $oneLine = $this->before.$encodingClass->change($dataexport, 'UTF-8', $charset).$this->after.$this->eol;
424 if (empty($exportFile)) {
425 echo $oneLine;
426 } else {
427 $error = fwrite($fp, $oneLine);
428 if (false === $error) {
429 if ($flagToRemove !== 0) {
430 $automationHelper = new AutomationHelper();
431 $automationHelper->removeFlag($flagToRemove);
432 }
433
434 return acym_translationSprintf('ACYM_UNWRITABLE_FILE', $exportFile);
435 }
436 }
437 }
438
439 unset($users);
440 } while (true);
441
442 if ($flagToRemove !== 0) {
443 $automationHelper = new AutomationHelper();
444 $automationHelper->removeFlag($flagToRemove);
445 }
446
447 if (!empty($exportFile)) fclose($fp);
448
449 return '';
450 }
451
452 private function getExportLimit(): int
453 {
454 $serverLimit = acym_bytes(ini_get('memory_limit'));
455 if ($serverLimit > 150000000) {
456 return 50000;
457 } elseif ($serverLimit > 80000000) {
458 return 15000;
459 } else {
460 return 5000;
461 }
462 }
463
464 public function getExportChangesFileName(string $year, string $month, bool $extension = true): string
465 {
466 $filename = 'export_changes_'.$year.'_'.$month;
467
468 if ($extension) {
469 $filename .= '.csv';
470 }
471
472 return $filename;
473 }
474
475 public function getExportChangesFilePath(): string
476 {
477 $filename = $this->getExportChangesFileName(acym_date('now', 'Y'), acym_date('now', 'm'));
478
479 return acym_getLogPath($filename);
480 }
481
482 public function generateExportChangesFilePathConfigChanges(): string
483 {
484 $filename = $this->getExportChangesFileName(acym_date('now', 'Y'), acym_date('now', 'm'), false);
485
486 $i = 1;
487
488 $newFilename = $filename.'_'.$i.'.csv';
489
490 while (file_exists(acym_getLogPath($newFilename))) {
491 $i++;
492 $newFilename = $filename.'_'.$i.'.csv';
493 }
494
495 return acym_getLogPath($newFilename);
496 }
497
498 public function exportChanges(array $user, array $fieldsToExport, string $fieldName, $newValue, $oldValue): void
499 {
500 if (empty($user) || empty($fieldName)) {
501 return;
502 }
503
504 $exportFullPath = $this->getExportChangesFilePath();
505
506 $separator = '","';
507 $content = '';
508
509 if (!file_exists($exportFullPath)) {
510 $columnsHeader = [];
511 foreach ($fieldsToExport as $field) {
512 $columnsHeader[] = $field;
513 }
514 $columnsHeader = array_merge(
515 $columnsHeader,
516 [
517 acym_translation('ACYM_FIELD_MODIFIED'),
518 acym_translation('ACYM_NEW_VALUE'),
519 acym_translation('ACYM_OLD_VALUE'),
520 acym_translation('ACYM_DATE'),
521 ]
522 );
523 $content .= $this->before.implode($separator, $columnsHeader).$this->after;
524 }
525
526
527 $columns = [];
528
529 foreach ($fieldsToExport as $field) {
530 $columns[] = empty($user[$field]) ? '' : $user[$field];
531 }
532
533 $columns = array_merge(
534 $columns,
535 [
536 $fieldName,
537 $newValue,
538 $oldValue,
539 acym_date(),
540 ]
541 );
542
543 $content .= $this->eol;
544 $content .= $this->before.implode($separator, $columns).$this->after;
545
546 file_put_contents(
547 $exportFullPath,
548 $content,
549 FILE_APPEND
550 );
551 }
552
553 public function cleanExportChangesFile(): void
554 {
555 $exportFolder = acym_getLogPath();
556 $files = scandir($exportFolder);
557 if (empty($files)) {
558 return;
559 }
560
561 $filenameToSearch = $this->getExportChangesFileName(acym_date('2 month ago', 'Y'), acym_date('2 month ago', 'm'), false);
562 foreach ($files as $file) {
563 if (strpos($file, $filenameToSearch) === false) {
564 continue;
565 }
566
567 @unlink(acym_getLogPath($file));
568 }
569 }
570 }
571