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

554 lines 18.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Helpers;
4
5 use AcyMailing\Classes\ListClass;
6 use AcyMailing\Classes\MailClass;
7 use AcyMailing\Core\AcymObject;
8
9 class ExportHelper extends AcymObject
10 {
11 var $eol = "\r\n";
12 var $before = '"';
13 var $after = '"';
14
15 public function setDownloadHeaders($filename = 'export', $extension = 'csv')
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($template)
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($mailName, $globalDonutsData, $globaline, $timeLinechart)
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($query, $columns, $type = 'global')
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($csvLines, $type = 'global')
231 {
232 $final = implode($this->eol, $csvLines);
233
234 if (ACYM_CMS === 'wordpress') @ob_get_clean();
235 $filename = 'export_stats_'.$type.'_'.date('Y-m-d');
236 $this->setDownloadHeaders($filename);
237 echo $final;
238
239 return '';
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($query, $fieldsToExport, $customFieldsToExport, $specialFieldsToExport, $separator = ',', $charset = 'UTF-8', $exportFile = null, $flagToRemove = 0)
254 {
255 $nbExport = $this->getExportLimit();
256 acym_displayErrors();
257 $encodingClass = new EncodingHelper();
258 $excelSecure = $this->config->get('export_excelsecurity', 0);
259
260 if (!in_array($separator, [',', ';'])) $separator = ',';
261 $separator = '"'.$separator.'"';
262
263 $allFieldsToExport = array_merge($fieldsToExport, $customFieldsToExport);
264
265 foreach ($allFieldsToExport as $key => $field) {
266 $allFieldsToExport[$key] = $encodingClass->change($field, 'UTF-8', $charset);
267 }
268
269 if (in_array('subscribe_date', $specialFieldsToExport)) {
270 $lists = $this->getLists();
271 foreach ($lists as $oneList) {
272 $allFieldsToExport[] = acym_translation('ACYM_SUBSCRIPTION_DATE').' '.acym_translation($oneList);
273 }
274 }
275
276 if (in_array('unsubscribe_date', $specialFieldsToExport)) {
277 $lists = $this->getLists();
278 foreach ($lists as $oneList) {
279 $allFieldsToExport[] = acym_translation('ACYM_UNSUBSCRIPTION_DATE').' '.acym_translation($oneList);
280 }
281 }
282
283 $firstLine = $this->before.implode($separator, $allFieldsToExport).$this->after.$this->eol;
284
285 if (empty($exportFile)) {
286 @ob_get_clean();
287 $filename = 'export_'.date('Y-m-d');
288 $this->setDownloadHeaders($filename);
289 echo $firstLine;
290 } else {
291 preg_match('#^(.+/)[^/]+$#', $exportFile, $folder);
292 if (!empty($folder[1]) && !file_exists($folder[1])) acym_createDir($folder[1]);
293
294 $fp = fopen($exportFile, 'w');
295 if (false === $fp) {
296 if ($flagToRemove !== 0) {
297 $automationHelper = new AutomationHelper();
298 $automationHelper->removeFlag($flagToRemove);
299 }
300
301 return acym_translationSprintf('ACYM_FAIL_SAVE_FILE', $exportFile);
302 }
303
304 $error = fwrite($fp, $firstLine);
305 if (false === $error) {
306 if ($flagToRemove !== 0) {
307 $automationHelper = new AutomationHelper();
308 $automationHelper->removeFlag($flagToRemove);
309 }
310
311 return acym_translationSprintf('ACYM_UNWRITABLE_FILE', $exportFile);
312 }
313 }
314
315 $start = 0;
316 do {
317 try {
318 $users = acym_loadObjectList($query.' LIMIT '.intval($start).', '.intval($nbExport), 'id');
319 } catch (\Exception $e) {
320 $users = false;
321 }
322 $start += $nbExport;
323
324 if ($users === false) {
325 $errorLine = $this->eol.$this->eol.'Error: '.(isset($e) ? $e->getMessage() : acym_getDBError());
326
327 if (empty($exportFile)) {
328 echo $errorLine;
329 } else {
330 $error = fwrite($fp, $errorLine);
331 if (false === $error) {
332 if ($flagToRemove !== 0) {
333 $automationHelper = new AutomationHelper();
334 $automationHelper->removeFlag($flagToRemove);
335 }
336
337 return acym_translationSprintf('ACYM_UNWRITABLE_FILE', $exportFile);
338 }
339 }
340 }
341
342 if (empty($users)) break;
343
344 foreach ($users as $userID => $oneUser) {
345 unset($oneUser->id);
346
347 $data = get_object_vars($oneUser);
348
349 if (!empty($customFieldsToExport)) {
350 $fieldIDs = array_keys($customFieldsToExport);
351 acym_arrayToInteger($fieldIDs);
352
353 $userCustomFields = acym_loadObjectList(
354 'SELECT `field_id`, `value`
355 FROM #__acym_user_has_field
356 WHERE user_id = '.intval($userID).' AND field_id IN ('.implode(',', $fieldIDs).')',
357 'field_id'
358 );
359
360 foreach ($customFieldsToExport as $fieldID => $fieldName) {
361 if (empty($userCustomFields[$fieldID])) {
362 $data[] = '';
363 } else {
364 $data[] = acym_triggerCmsHook(
365 'onUserExportFieldData',
366 [$userCustomFields[$fieldID]->value, $fieldID, $fieldName],
367 false
368 );
369 }
370 }
371 unset($userCustomFields);
372 }
373
374 if (isset($lists)) {
375 $userSubscriptions = acym_loadObjectList(
376 'SELECT `list_id`, `subscription_date`, `unsubscribe_date`
377 FROM #__acym_user_has_list
378 WHERE user_id = '.intval($userID),
379 'list_id'
380 );
381
382 if (in_array('subscribe_date', $specialFieldsToExport)) {
383 foreach ($lists as $listId => $oneList) {
384 $data[] = $userSubscriptions[$listId]->subscription_date ?? '';
385 }
386 }
387
388 if (in_array('unsubscribe_date', $specialFieldsToExport)) {
389 foreach ($lists as $listId => $oneList) {
390 $data[] = $userSubscriptions[$listId]->unsubscribe_date ?? '';
391 }
392 }
393
394 unset($userSubscriptions);
395 }
396
397 foreach ($data as &$oneData) {
398 if (is_null($oneData)) {
399 continue;
400 }
401
402 if ($excelSecure == 1) {
403 $firstCharacter = substr($oneData, 0, 1);
404 if (in_array($firstCharacter, ['=', '+', '-', '@'])) {
405 $oneData = ' '.$oneData;
406 }
407 }
408
409 $oneData = htmlspecialchars($oneData);
410 }
411
412 $dataexport = implode($separator, $data);
413 unset($data);
414
415 $oneLine = $this->before.$encodingClass->change($dataexport, 'UTF-8', $charset).$this->after.$this->eol;
416 if (empty($exportFile)) {
417 echo $oneLine;
418 } else {
419 $error = fwrite($fp, $oneLine);
420 if (false === $error) {
421 if ($flagToRemove !== 0) {
422 $automationHelper = new AutomationHelper();
423 $automationHelper->removeFlag($flagToRemove);
424 }
425
426 return acym_translationSprintf('ACYM_UNWRITABLE_FILE', $exportFile);
427 }
428 }
429 }
430
431 unset($users);
432 } while (true);
433
434 if ($flagToRemove !== 0) {
435 $automationHelper = new AutomationHelper();
436 $automationHelper->removeFlag($flagToRemove);
437 }
438
439 if (!empty($exportFile)) fclose($fp);
440
441 return '';
442 }
443
444 private function getExportLimit()
445 {
446 $serverLimit = acym_bytes(ini_get('memory_limit'));
447 if ($serverLimit > 150000000) {
448 return 50000;
449 } elseif ($serverLimit > 80000000) {
450 return 15000;
451 } else {
452 return 5000;
453 }
454 }
455
456 public function getExportChangesFileName($year, $month, $extension = true)
457 {
458 $filename = 'export_changes_'.$year.'_'.$month;
459
460 if ($extension) $filename .= '.csv';
461
462 return $filename;
463 }
464
465 public function getExportChangesFilePath()
466 {
467 $filename = $this->getExportChangesFileName(acym_date('now', 'Y'), acym_date('now', 'm'));
468
469 return acym_getLogPath($filename);
470 }
471
472 public function generateExportChangesFilePathConfigChanges()
473 {
474 $filename = $this->getExportChangesFileName(acym_date('now', 'Y'), acym_date('now', 'm'), false);
475
476 $i = 1;
477
478 $newFilename = $filename.'_'.$i.'.csv';
479
480 while (file_exists(acym_getLogPath($newFilename))) {
481 $i++;
482 $newFilename = $filename.'_'.$i.'.csv';
483 }
484
485 return acym_getLogPath($newFilename);
486 }
487
488 public function exportChanges($user, $fieldsToExport, $fieldName, $newValue, $oldValue)
489 {
490 if (empty($user) || empty($fieldName)) return;
491
492 $exportFullPath = $this->getExportChangesFilePath();
493
494 $separator = '","';
495 $content = '';
496
497 if (!file_exists($exportFullPath)) {
498 $columnsHeader = [];
499 foreach ($fieldsToExport as $field) {
500 $columnsHeader[] = $field;
501 }
502 $columnsHeader = array_merge(
503 $columnsHeader,
504 [
505 acym_translation('ACYM_FIELD_MODIFIED'),
506 acym_translation('ACYM_NEW_VALUE'),
507 acym_translation('ACYM_OLD_VALUE'),
508 acym_translation('ACYM_DATE'),
509 ]
510 );
511 $content .= $this->before.implode($separator, $columnsHeader).$this->after;
512 }
513
514
515 $columns = [];
516
517 foreach ($fieldsToExport as $field) {
518 $columns[] = empty($user[$field]) ? '' : $user[$field];
519 }
520
521 $columns = array_merge(
522 $columns,
523 [
524 $fieldName,
525 $newValue,
526 $oldValue,
527 acym_date(),
528 ]
529 );
530
531 $content .= $this->eol;
532 $content .= $this->before.implode($separator, $columns).$this->after;
533
534 file_put_contents(
535 $exportFullPath,
536 $content,
537 FILE_APPEND
538 );
539 }
540
541 public function cleanExportChangesFile()
542 {
543 $exportFolder = acym_getLogPath('');
544 $files = scandir($exportFolder);
545 if (empty($files)) return;
546
547 $filenameToSearch = $this->getExportChangesFileName(acym_date('2 month ago', 'Y'), acym_date('2 month ago', 'm'), false);
548 foreach ($files as $file) {
549 if (strpos($file, $filenameToSearch) === false) continue;
550 @unlink(acym_getLogPath($file));
551 }
552 }
553 }
554