PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.11.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.11.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 / Classes / MailClass.php

MailClass.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 10.11.1, at back/Classes/MailClass.php

1,603 lines 57.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\Classes;
4
5 use AcyMailing\Helpers\MailerHelper;
6 use AcyMailing\Helpers\PaginationHelper;
7 use AcyMailing\Helpers\PluginHelper;
8 use AcyMailing\Core\AcymClass;
9
10 class MailClass extends AcymClass
11 {
12 const FIELDS_ENCODING = [
13 'name',
14 'subject',
15 'body',
16 'autosave',
17 'preheader',
18 ];
19
20 const TYPE_STANDARD = 'standard';
21 const TYPE_NOTIFICATION = 'notification';
22 const TYPE_OVERRIDE = 'override';
23 const TYPE_WELCOME = 'welcome';
24 const TYPE_UNSUBSCRIBE = 'unsubscribe';
25 const TYPE_AUTOMATION = 'automation';
26 const TYPE_FOLLOWUP = 'followup';
27 const TYPE_MAILBOX_ACTION = 'mailbox_action';
28 const TYPE_TEMPLATE = 'template';
29 const TYPE_SCENARIO = 'scenario';
30
31 const ALL_TYPES = [
32 self::TYPE_STANDARD,
33 self::TYPE_NOTIFICATION,
34 self::TYPE_OVERRIDE,
35 self::TYPE_WELCOME,
36 self::TYPE_UNSUBSCRIBE,
37 self::TYPE_AUTOMATION,
38 self::TYPE_FOLLOWUP,
39 self::TYPE_MAILBOX_ACTION,
40 self::TYPE_TEMPLATE,
41 self::TYPE_SCENARIO,
42 ];
43
44 const TYPES_TRANSACTIONAL = [
45 self::TYPE_NOTIFICATION,
46 self::TYPE_OVERRIDE,
47 self::TYPE_WELCOME,
48 self::TYPE_UNSUBSCRIBE,
49 self::TYPE_TEMPLATE,
50 self::TYPE_SCENARIO,
51 ];
52
53 const TYPES_WITH_STATS = [
54 self::TYPE_STANDARD,
55 self::TYPE_AUTOMATION,
56 self::TYPE_WELCOME,
57 self::TYPE_UNSUBSCRIBE,
58 self::TYPE_FOLLOWUP,
59 self::TYPE_MAILBOX_ACTION,
60 self::TYPE_SCENARIO,
61 ];
62
63 const TYPES_NO_NAME = [
64 self::TYPE_NOTIFICATION,
65 self::TYPE_OVERRIDE,
66 ];
67
68 const MAX_ALLOWED_TEMPLATE_FILES = 100;
69 const MAX_ALLOWED_TEMPLATE_SIZE = 20971520;
70 const ALLOWED_TEMPLATE_FILE_TYPES = [
71 'image/jpeg',
72 'image/png',
73 'image/gif',
74 'text/html',
75 'image/x-ms-bmp',
76 'image/bmp',
77 'text/plain',
78 'image/vnd.microsoft.icon',
79 ];
80
81 private array $templateNames = [];
82 public array $exceptKeysDecode = [];
83
84 public function __construct()
85 {
86 parent::__construct();
87
88 $this->table = 'mail';
89 $this->pkey = 'id';
90 }
91
92 public function getMatchingElements(array $settings = []): array
93 {
94 $query = 'SELECT mail.* FROM #__acym_mail AS mail';
95 $queryCount = 'SELECT COUNT(mail.id) AS total FROM #__acym_mail AS mail';
96
97 $filters = [];
98 $tagJoin = '';
99
100 if (!empty($settings['tag'])) {
101 $tagJoin = ' JOIN #__acym_tag AS tag ON mail.id = tag.id_element ';
102 $filters[] = 'tag.name = '.acym_escapeDB($settings['tag']);
103 $filters[] = 'tag.type = "mail"';
104 }
105 $query .= $tagJoin;
106 $queryCount .= $tagJoin;
107
108 if (!empty($settings['search'])) {
109 $filters[] = 'mail.name LIKE '.acym_escapeDB('%'.acym_utf8Encode($settings['search']).'%');
110 }
111
112 if (!empty($settings['gettingTemplates'])) {
113 $filters[] = 'mail.type = '.acym_escapeDB(self::TYPE_TEMPLATE);
114 if (!acym_isAdmin()) {
115 $currentUserId = acym_currentUserId();
116 $condition = 'mail.creator_id = '.intval($currentUserId);
117
118 $userGroups = acym_getGroupsByUser($currentUserId);
119 if (!empty($userGroups)) {
120 $condition .= ' OR access LIKE "%,'.implode(',%" OR access LIKE "%,', $userGroups).',%"';
121 }
122
123 $filters[] = $condition;
124 }
125 } else {
126 if (!empty($settings['automation']) || empty($settings['onlyStandard'])) {
127 $filters[] = 'mail.type != '.acym_escapeDB(self::TYPE_NOTIFICATION);
128 $filters[] = 'mail.type != '.acym_escapeDB(self::TYPE_OVERRIDE);
129 } else {
130 $filters[] = 'mail.type IN ('.acym_escapeDB(self::TYPE_STANDARD).', '.acym_escapeDB(self::TYPE_TEMPLATE).')';
131 }
132
133 if (empty($settings['automation'])) {
134 $filters[] = 'mail.type IN ('.acym_escapeDB(self::TYPE_TEMPLATE).', '.acym_escapeDB(self::TYPE_WELCOME).', '.acym_escapeDB(self::TYPE_UNSUBSCRIBE).')';
135 }
136
137 if (!empty($settings['creator_id'])) {
138 $mailTypeCondition = 'mail.type IN ('.acym_escapeDB(self::TYPE_TEMPLATE).', '.acym_escapeDB(self::TYPE_WELCOME).', '.acym_escapeDB(self::TYPE_UNSUBSCRIBE).')';
139 $userGroups = acym_getGroupsByUser($settings['creator_id']);
140 $groupCondition = '(mail.access LIKE "%,'.implode(',%" OR mail.access LIKE "%,', $userGroups).',%")';
141 $filter = 'mail.creator_id = '.intval($settings['creator_id']).' OR ('.$mailTypeCondition.' AND '.$groupCondition.')';
142
143 if (!acym_isAdmin() && !empty($settings['element_tab'])) {
144 $filter = '('.$filter.')
145 OR list.cms_user_id = '.intval($settings['creator_id']).'
146 OR (list.access LIKE "%,'.implode(',%" OR list.access LIKE "%,', $userGroups).',%")';
147 }
148
149 $filters['list'] = '('.$filter.')';
150 }
151 }
152
153 $filters[] = 'mail.parent_id IS NULL';
154
155 if (!empty($settings['drag_editor'])) {
156 $filters[] = 'mail.drag_editor = 1';
157 }
158
159 if (!empty($settings['element_tab'])) {
160 $query = 'SELECT DISTINCT mail.*, mail_stat.sent as subscribers, mail_stat.open_unique FROM #__acym_mail AS mail';
161 $query .= ' LEFT JOIN #__acym_mail_stat AS mail_stat ON mail.id = mail_stat.mail_id ';
162 if (!empty($settings['tag'])) {
163 $query .= ' JOIN #__acym_tag AS tag ON mail.id = tag.id_element ';
164 }
165 if (!acym_isAdmin()) {
166 $query .= ' LEFT JOIN #__acym_list AS list ON list.'.acym_secureDBColumn($settings['element_tab']).'_id = mail.id';
167 }
168 $filters[] = 'mail.type = '.acym_escapeDB($settings['element_tab']);
169 }
170
171 if (!empty($filters)) {
172 $query .= ' WHERE ('.implode(') AND (', $filters).')';
173 if (!acym_isAdmin()) {
174 unset($filters['list']);
175 }
176 $queryCount .= ' WHERE ('.implode(') AND (', $filters).')';
177 }
178
179 if (!empty($settings['ordering']) && !empty($settings['ordering_sort_order'])) {
180 $query .= ' ORDER BY mail.'.acym_secureDBColumn($settings['ordering']).' '.acym_secureDBColumn(strtoupper($settings['ordering_sort_order']));
181 }
182
183 if (empty($settings['offset']) || $settings['offset'] < 0) {
184 $settings['offset'] = 0;
185 }
186
187 if (empty($settings['elementsPerPage']) || $settings['elementsPerPage'] < 1) {
188 $pagination = new PaginationHelper();
189 $settings['elementsPerPage'] = $pagination->getListLimit();
190 }
191
192 $results['elements'] = $this->decode(acym_loadObjectList($query, '', $settings['offset'], $settings['elementsPerPage']));
193 $results['total'] = acym_loadObject($queryCount);
194
195 if (!empty($settings['element_tab'])) {
196 $this->getAllListIdsForWelcomeUnsub($results['elements'], $settings['element_tab']);
197 }
198
199 $results['status'] = [];
200
201 if (!empty($settings['element_tab'])) {
202 $urlClickClass = new UrlClickClass();
203 for ($i = 0; $i < count($results['elements']); $i++) {
204 $results['elements'][$i]->open = 0;
205 if (!empty($results['elements'][$i]->subscribers)) {
206 $results['elements'][$i]->open = number_format($results['elements'][$i]->open_unique / $results['elements'][$i]->subscribers * 100, 2);
207
208 $clicksNb = $urlClickClass->getNumberUsersClicked([$results['elements'][$i]->id]);
209 $results['elements'][$i]->click = number_format($clicksNb / $results['elements'][$i]->subscribers * 100, 2);
210 }
211 }
212 }
213
214 return $results;
215 }
216
217 private function getAllListIdsForWelcomeUnsub(array $elements, string $type): void
218 {
219 if (empty($elements)) {
220 return;
221 }
222
223 $column = $type === self::TYPE_WELCOME ? 'welcome_id' : 'unsubscribe_id';
224
225 foreach ($elements as $element) {
226 $element->lists = acym_loadObjectList('SELECT color, name FROM #__acym_list WHERE '.acym_secureDBColumn($column).' = '.intval($element->id));
227 }
228 }
229
230 public function getAll(?string $key = null): array
231 {
232 $allMails = parent::getAll($key);
233
234 return $this->decode($allMails);
235 }
236
237 public function getOneById(int $id, bool $needTranslatedSettings = false): ?object
238 {
239 $mail = $this->decode(parent::getOneById($id));
240
241 if (!empty($mail)) {
242 $tagsClass = new TagClass();
243 $mail->tags = $tagsClass->getAllTagsByElementId(TagClass::TYPE_MAIL, intval($id));
244 }
245
246 if (isset($mail->access) && !is_array($mail->access)) $mail->access = explode(',', $mail->access);
247
248 if (!empty($mail->parent_id) && $needTranslatedSettings) {
249 $this->getTranslatedSettingsMail($mail);
250 }
251
252 return $mail;
253 }
254
255 private function getTranslatedSettingsMail(object $mail): void
256 {
257 $parentMailTranslation = acym_loadResult('SELECT translation FROM #__acym_mail WHERE id = '.intval($mail->parent_id));
258 $parentMailTranslation = empty($parentMailTranslation) ? [] : json_decode($parentMailTranslation, true);
259
260 $translationConfig = $this->config->get('sender_info_translation', '');
261 $translationConfig = empty($translationConfig) ? [] : json_decode($translationConfig, true);
262
263 if (!empty($parentMailTranslation[$mail->language])) {
264 $senderTranslation = $parentMailTranslation[$mail->language];
265 } elseif (!empty($translationConfig[$mail->language])) {
266 $senderTranslation = $translationConfig[$mail->language];
267 $senderTranslation['reply_to_email'] = $senderTranslation['replyto_email'];
268 $senderTranslation['reply_to_name'] = $senderTranslation['replyto_name'];
269 } else {
270 return;
271 }
272
273 $mail->from_name = $senderTranslation['from_name'];
274 $mail->from_email = $senderTranslation['from_email'];
275 $mail->reply_to_name = $senderTranslation['reply_to_name'];
276 $mail->reply_to_email = $senderTranslation['reply_to_email'];
277 }
278
279 public function getOneByName(string $name, bool $needTranslatedSettings = false, string $type = ''): ?object
280 {
281 $query = 'SELECT * FROM #__acym_mail WHERE `parent_id` IS NULL AND `name` = '.acym_escapeDB(acym_utf8Encode($name));
282 if (!empty($type)) {
283 $query .= ' AND `type` = '.acym_escapeDB($type);
284 }
285
286 $mail = acym_loadObject($query);
287
288 if (empty($mail)) {
289 return null;
290 }
291
292 $mail = $this->decode($mail);
293
294 if (!empty($mail)) {
295 $tagsClass = new TagClass();
296 $mail->tags = $tagsClass->getAllTagsByElementId(TagClass::TYPE_MAIL, intval($mail->id));
297 }
298
299 if (!empty($mail->parent_id) && $needTranslatedSettings) {
300 $this->getTranslatedSettingsMail($mail);
301 }
302
303 return $mail;
304 }
305
306 public function getMailsByType(?string $typeMail, array $settings): array
307 {
308 if (empty($settings['key'])) {
309 $settings['key'] = '';
310 }
311 if (empty($settings['offset'])) {
312 $settings['offset'] = 0;
313 }
314 if (empty($settings['mailsPerPage'])) {
315 $settings['mailsPerPage'] = 12;
316 }
317 $search = $settings['filters']['search'] ?? '';
318
319 $query = 'SELECT * FROM #__acym_mail AS mail';
320 $queryCount = 'SELECT count(*) FROM #__acym_mail AS mail';
321
322 $filters = [];
323 if (!empty($typeMail)) {
324 $filters[] = 'mail.type = '.acym_escapeDB($typeMail);
325 }
326
327 if (!empty($search)) {
328 $filters[] = 'mail.name LIKE '.acym_escapeDB('%'.$search.'%');
329 }
330
331 if (!empty($filters)) {
332 $query .= ' WHERE ('.implode(') AND (', $filters).')';
333 $queryCount .= ' WHERE ('.implode(') AND (', $filters).')';
334 }
335
336 $query .= ' ORDER BY id DESC';
337
338 $results['mails'] = $this->decode(acym_loadObjectList($query, $settings['key'], $settings['offset'], $settings['mailsPerPage']));
339 $results['total'] = acym_loadResult($queryCount);
340
341 return $results;
342 }
343
344 public function getAllListsWithCountSubscribersByMailIds(array $ids): array
345 {
346 acym_arrayToInteger($ids);
347 if (empty($ids)) {
348 return [];
349 }
350
351 $query = 'SELECT mailLists.list_id, mailLists.mail_id, list.*, COUNT(userLists.user_id) AS subscribers
352 FROM #__acym_mail_has_list AS mailLists
353 JOIN #__acym_list AS list ON mailLists.list_id = list.id
354 LEFT JOIN #__acym_user_has_list AS userLists
355 JOIN #__acym_user AS acyuser ON userLists.user_id = acyuser.id
356 AND userLists.status = 1
357 AND acyuser.active = 1 ';
358
359 if ($this->config->get('require_confirmation', 1) == 1) {
360 $query .= ' AND acyuser.confirmed = 1 ';
361 }
362
363 $query .= 'ON list.id = userLists.list_id
364 WHERE mailLists.mail_id IN ('.implode(',', $ids).')
365 GROUP BY mailLists.list_id, mailLists.mail_id';
366
367 acym_query('SET SQL_BIG_SELECTS=1');
368
369 return acym_loadObjectList($query);
370 }
371
372 public function getAllListsByMailIds(array $ids): array
373 {
374 acym_arrayToInteger($ids);
375 if (empty($ids)) {
376 return [];
377 }
378
379 $query = 'SELECT mailLists.mail_id, list.name, list.color
380 FROM #__acym_mail_has_list AS mailLists
381 JOIN #__acym_list AS list ON mailLists.list_id = list.id
382 WHERE mailLists.mail_id IN ('.implode(',', $ids).')';
383
384 return acym_loadObjectList($query);
385 }
386
387 public function getAllListsByMailId(int $id): array
388 {
389 $mail = $this->getOneById($id);
390 if (empty($mail)) {
391 return [];
392 }
393
394 if (self::TYPE_WELCOME === $mail->type) {
395 $query = 'SELECT * FROM #__acym_list WHERE welcome_id = '.intval($id);
396 } elseif (self::TYPE_FOLLOWUP === $mail->type) {
397 $query = 'SELECT list.* FROM #__acym_followup_has_mail AS followup_mail
398 JOIN #__acym_followup AS followup ON followup.id = followup_mail.followup_id AND followup_mail.mail_id = '.intval($id).'
399 JOIN #__acym_list AS list ON list.id = followup.list_id';
400 } else {
401 $query = 'SELECT list.*
402 FROM #__acym_mail_has_list AS mailLists
403 JOIN #__acym_mail AS mail ON (mailLists.mail_id = mail.id OR mailLists.mail_id = mail.parent_id) AND mail.id = '.intval($id).'
404 JOIN #__acym_list AS list ON mailLists.list_id = list.id';
405 }
406
407 return acym_loadObjectList($query, 'id');
408 }
409
410 public function getAllListsByMailIdAndUserId(int $mailId, int $userId): array
411 {
412 if (empty($mailId) || empty($userId)) {
413 return [];
414 }
415
416 $query = 'SELECT list.* FROM #__acym_list AS list';
417 $query .= ' JOIN #__acym_user_has_list AS userlist ON list.id = userlist.list_id AND userlist.user_id = '.intval($userId);
418 $query .= ' JOIN #__acym_mail_has_list AS maillist ON maillist.list_id = list.id AND maillist.mail_id = '.intval($mailId);
419
420 return acym_loadObjectList($query);
421 }
422
423 public function save(object $element): ?int
424 {
425 $mail = clone $element;
426
427 if (isset($mail->tags)) {
428 $tags = $mail->tags;
429 unset($mail->tags);
430 }
431
432 if (empty($mail->id)) {
433 $mail->creation_date = acym_date('now', 'Y-m-d H:i:s', false);
434 if (empty($mail->creator_id)) $mail->creator_id = acym_currentUserId();
435 }
436
437 $mail = $this->encode($mail);
438
439 $mail->autosave = null;
440
441
442 if (isset($mail->thumbnail) && ((empty($mail->thumbnail) && !is_null($mail->thumbnail)) || strpos($mail->thumbnail, 'data:image/png;base64') !== false)) {
443 unset($mail->thumbnail);
444 }
445
446 if (!isset($mail->access)) {
447 $mail->access = '';
448 }
449
450 foreach ($mail as $oneAttribute => $value) {
451 if (empty($value) || in_array($oneAttribute, ['thumbnail', 'settings'])) {
452 continue;
453 }
454
455 if ($oneAttribute === 'access' && is_array($mail->$oneAttribute)) {
456 $value = ','.trim(implode(',', $mail->$oneAttribute), ',').',';
457 $mail->$oneAttribute = $value;
458 }
459
460 if (is_array($value)) {
461 $mail->$oneAttribute = json_encode($value);
462 }
463
464 if (in_array($oneAttribute, ['body', 'headers'])) {
465 $mail->$oneAttribute = preg_replace('#<input[^>]*value="[^"]*"[^>]*>#Uis', '', $mail->$oneAttribute);
466 $mail->$oneAttribute = preg_replace('#<script.*</script>#Uis', '', $mail->$oneAttribute);
467
468 $mail->$oneAttribute = str_replace(' contenteditable="true"', '', $mail->$oneAttribute);
469 } else {
470 $mail->$oneAttribute = strip_tags($mail->$oneAttribute);
471 }
472 }
473
474 $mailID = parent::save($mail);
475
476 if (!empty($mailID) && isset($tags)) {
477 $tagClass = new TagClass();
478 $tagClass->setTags(TagClass::TYPE_MAIL, intval($mailID), $tags);
479 }
480
481 return $mailID;
482 }
483
484 public function autoSave(object $mail, string $language = 'main'): bool
485 {
486 if (empty($mail->id)) {
487 return false;
488 }
489
490 $mail->autosave = str_replace(' contenteditable="true"', '', $mail->autosave);
491
492 if (acym_isMultilingual() && $language !== 'main') {
493 $translationId = $this->getTranslationId($mail->id, $language);
494 if (empty($translationId)) {
495 $parentCopy = $this->getOneById($mail->id);
496 if (empty($parentCopy)) {
497 return false;
498 }
499
500 unset($parentCopy->id);
501 $parentCopy->parent_id = $mail->id;
502 $parentCopy->language = $language;
503 $translationId = $this->save($parentCopy);
504 }
505 $mail->id = $translationId;
506 }
507
508 $mail = $this->encode($mail);
509
510 return (bool)parent::save($mail);
511 }
512
513 public function delete(array $elements): int
514 {
515 if (empty($elements)) return 0;
516
517 $this->deleteMediaFolder($elements);
518 acym_arrayToInteger($elements);
519
520 $favoriteTemplate = (int)$this->config->get('favorite_template', 0);
521 if (in_array($favoriteTemplate, $elements)) {
522 $this->config->saveConfig(['favorite_template' => 0]);
523 }
524
525 $allThumbnailToDelete = acym_loadResultArray('SELECT DISTINCT thumbnail FROM #__acym_mail WHERE id IN ('.implode(',', $elements).')');
526
527 $translations = acym_loadResultArray('SELECT id FROM #__acym_mail WHERE parent_id IN ('.implode(',', $elements).')');
528 $elements = array_merge($elements, $translations);
529 if (!empty($translations)) {
530 acym_query('UPDATE #__acym_mail SET `parent_id` = null WHERE `id` IN ('.implode(',', $translations).')');
531 }
532
533 acym_query('UPDATE #__acym_list SET welcome_id = null WHERE welcome_id IN ('.implode(',', $elements).')');
534 acym_query('UPDATE #__acym_list SET unsubscribe_id = null WHERE unsubscribe_id IN ('.implode(',', $elements).')');
535 acym_query('DELETE FROM #__acym_queue WHERE mail_id IN ('.implode(',', $elements).')');
536 acym_query('DELETE FROM #__acym_mail_has_list WHERE mail_id IN ('.implode(',', $elements).')');
537 acym_query('DELETE FROM #__acym_tag WHERE `type` = "mail" AND `id_element` IN ('.implode(',', $elements).')');
538 acym_query('DELETE FROM #__acym_user_stat WHERE mail_id IN ('.implode(',', $elements).')');
539 acym_query('DELETE FROM #__acym_url_click WHERE mail_id IN ('.implode(',', $elements).')');
540 acym_query('DELETE FROM #__acym_mail_stat WHERE mail_id IN ('.implode(',', $elements).')');
541 acym_query('DELETE FROM #__acym_followup_has_mail WHERE mail_id IN ('.implode(',', $elements).')');
542 acym_query('DELETE FROM #__acym_mail_archive WHERE mail_id IN ('.implode(',', $elements).')');
543
544 $return = parent::delete($elements);
545
546 $this->deleteUnusedThumbnails($allThumbnailToDelete);
547
548 return $return;
549 }
550
551 public function deleteMediaFolder(array $elements): void
552 {
553 if (empty($elements)) {
554 return;
555 }
556
557 acym_arrayToInteger($elements);
558 $results = acym_loadResultArray('SELECT mail_settings FROM #__acym_mail WHERE mail_settings IS NOT NULL AND id IN ('.implode(',', $elements).')');
559
560 foreach ($results as $template) {
561 $settings = json_decode($template, true);
562
563 if (empty($settings['media_folder']) || !file_exists(ACYM_TEMPLATE.$settings['media_folder'])) {
564 continue;
565 }
566 acym_deleteFolder(ACYM_TEMPLATE.$settings['media_folder']);
567 }
568 }
569
570 public function deleteUnusedThumbnails(array $thumbnails): void
571 {
572 if (empty($thumbnails)) {
573 return;
574 }
575
576 foreach ($thumbnails as $key => $oneThumb) {
577 $thumbnails[$key] = acym_escapeDB($oneThumb);
578 }
579
580 $stillUsedThumbnails = acym_loadResultArray('SELECT thumbnail FROM #__acym_mail WHERE thumbnail IN ('.implode(',', $thumbnails).')');
581 $thumbnailToDelete = array_diff($thumbnails, $stillUsedThumbnails);
582 foreach ($thumbnailToDelete as $one) {
583 if (!empty($one) && file_exists(ACYM_UPLOAD_FOLDER_THUMBNAIL.$one)) {
584 unlink(ACYM_UPLOAD_FOLDER_THUMBNAIL.$one);
585 }
586 }
587 }
588
589 public function deleteOneAttachment(int $mailId, int $idAttachment): bool
590 {
591 if (empty($mailId)) {
592 return false;
593 }
594
595 $mail = $this->getOneById($mailId);
596
597 $attachments = $mail->attachments;
598 if (empty($attachments)) {
599 return false;
600 }
601 $decodedAttach = json_decode($attachments, true);
602 unset($decodedAttach[$idAttachment]);
603 $newAttachments = [];
604 if (!empty($decodedAttach)) {
605 foreach ($decodedAttach as $oneAttach) {
606 $newAttachments[] = $oneAttach;
607 }
608 }
609 $attachDb = json_encode($newAttachments);
610
611 return (bool)acym_query('UPDATE #__acym_mail SET attachments = '.acym_escapeDB($attachDb).' WHERE id = '.intval($mailId).' OR parent_id = '.intval($mailId));
612 }
613
614 public function createTemplateFile(int $id): string
615 {
616 if (empty($id)) {
617 return '';
618 }
619
620 $template = $this->getOneById($id);
621 if (empty($template->id)) {
622 return '';
623 }
624
625 $css = $this->buildCSS($template->stylesheet);
626 if (empty($css)) {
627 return '';
628 }
629
630 acym_createDir(ACYM_TEMPLATE.'css');
631
632 $cssfile = ACYM_TEMPLATE.'css'.DS.'template_'.$id.'.css';
633 if (acym_writeFile($cssfile, $css)) {
634 return $cssfile;
635 } else {
636 acym_enqueueMessage('Could not create the file '.$cssfile, 'error');
637
638 return '';
639 }
640 }
641
642 public function buildCSS(?string $stylesheet): string
643 {
644 if (empty($stylesheet)) {
645 return '';
646 }
647
648 $inline = '';
649
650 if (preg_match_all('#@import[^;]*;#is', $stylesheet, $results)) {
651 foreach ($results[0] as $oneResult) {
652 $inline .= trim($oneResult)."\n";
653 $stylesheet = str_replace($oneResult, '', $stylesheet);
654 }
655 }
656
657 $inline .= $stylesheet;
658
659 return $inline;
660 }
661
662 public function doupload(): bool
663 {
664 $zipFilePath = $this->uploadTemplate();
665 if (empty($zipFilePath)) {
666 return false;
667 }
668
669 $templateFolder = $this->extractTemplate($zipFilePath);
670 if (empty($templateFolder)) {
671 return false;
672 }
673
674 if (!$this->installExtractedTemplate($templateFolder)) {
675 return false;
676 }
677
678 return true;
679 }
680
681 private function uploadTemplate(): ?string
682 {
683 $importFile = acym_getVar('file', 'uploadedfile', '', 'files');
684
685 $fileError = $importFile['error'];
686 if ($fileError > 0) {
687 switch ($fileError) {
688 case 1:
689 acym_enqueueMessage(acym_translation('ACYM_FILE_UPLOAD_ERROR_1'), 'error');
690
691 return null;
692 case 2:
693 acym_enqueueMessage(acym_translation('ACYM_FILE_UPLOAD_ERROR_2'), 'error');
694
695 return null;
696 case 3:
697 acym_enqueueMessage(acym_translation('ACYM_FILE_UPLOAD_ERROR_3'), 'error');
698
699 return null;
700 case 4:
701 acym_enqueueMessage(acym_translation('ACYM_FILE_UPLOAD_ERROR_4'), 'error');
702
703 return null;
704 default:
705 acym_enqueueMessage(acym_translationSprintf('ACYM_FILE_UPLOAD_ERROR_5', $fileError), 'error');
706
707 return null;
708 }
709 }
710
711 if (empty($importFile['name'])) {
712 acym_enqueueMessage(acym_translation('ACYM_BROWSE_FILE'), 'error');
713
714 return null;
715 }
716
717 $uploadPath = acym_cleanPath(ACYM_ROOT.ACYM_MEDIA_FOLDER.'templates');
718
719 if (!is_writable($uploadPath)) {
720 @chmod($uploadPath, '0755');
721 if (!is_writable($uploadPath)) {
722 acym_enqueueMessage(acym_translationSprintf('ACYM_WRITABLE_FOLDER', $uploadPath), 'warning');
723 }
724 }
725
726 if (!(bool)ini_get('file_uploads')) {
727 acym_enqueueMessage(acym_translationSprintf('ACYM_FILE_UPLOAD_ERROR_6'), 'error');
728
729 return null;
730 }
731
732 if (!extension_loaded('zlib')) {
733 acym_raiseError(500, acym_translation('ACYM_MISSING_ZLIB'));
734
735 return null;
736 }
737
738 $filename = strtolower(acym_makeSafeFile($importFile['name']));
739 $extension = strtolower(substr($filename, strrpos($filename, '.') + 1));
740
741 $allowedFileType = true;
742 if ($extension !== 'zip') {
743 $allowedFileType = false;
744 } elseif (!in_array($importFile['type'], ['application/zip', 'application/x-zip-compressed'])) {
745 $allowedFileType = false;
746 } elseif (function_exists('mime_content_type') && !in_array(mime_content_type($importFile['tmp_name']), ['application/zip', 'application/x-zip-compressed'])) {
747 $allowedFileType = false;
748 }
749
750 if (!$allowedFileType) {
751 acym_enqueueMessage(acym_translationSprintf('ACYM_ACCEPTED_TYPE', $extension, 'zip'), 'error');
752
753 return null;
754 }
755
756 if (class_exists('ZipArchive')) {
757 $zip = new \ZipArchive();
758 if (filesize($importFile['tmp_name']) > self::MAX_ALLOWED_TEMPLATE_SIZE || !$zip->open($importFile['tmp_name']) || $zip->count() > self::MAX_ALLOWED_TEMPLATE_FILES) {
759 acym_enqueueMessage(acym_translation('ACYM_UPLOADED_ARCHIVE_NOT_ALLOWED'), 'error');
760
761 return null;
762 }
763
764 $zip->close();
765 }
766
767 $tmpPath = acym_getCMSConfig('tmp_path', ACYM_MEDIA.'tmp'.DS);
768 $zipFilePath = acym_cleanPath($tmpPath.DS.$filename);
769
770 $uploaded = acym_uploadFile($importFile['tmp_name'], $zipFilePath);
771 if (!$uploaded) {
772 acym_enqueueMessage(acym_translationSprintf('ACYM_FILE_UPLOAD_ERROR_7', $importFile['tmp_name'], $zipFilePath), 'error');
773
774 return null;
775 }
776
777 return $zipFilePath;
778 }
779
780 private function extractTemplate(string $zipFilePath, bool $deleteZip = true): ?string
781 {
782 $extractdir = acym_cleanPath(dirname($zipFilePath).DS.uniqid().uniqid().uniqid().'_template');
783
784 $result = acym_extractArchive($zipFilePath, $extractdir);
785
786 if (!$result) {
787 acym_enqueueMessage(acym_translationSprintf('ACYM_FILE_UPLOAD_ERROR_9', $zipFilePath, $extractdir), 'error');
788
789 return null;
790 }
791
792 if ($deleteZip) {
793 acym_deleteFile($zipFilePath);
794 }
795
796 $allFiles = acym_getFiles($extractdir, '.', true, true, [], []);
797 foreach ($allFiles as $oneFile) {
798 if (function_exists('mime_content_type')) {
799 $fileType = mime_content_type($oneFile);
800 if (in_array($fileType, self::ALLOWED_TEMPLATE_FILE_TYPES) && preg_match('#\.(jpg|gif|png|jpeg|ico|bmp|html|htm|css|webp)$#i', $oneFile)) {
801 continue;
802 }
803 } elseif (preg_match('#\.(jpg|gif|png|jpeg|ico|bmp|html|htm|css|webp)$#i', $oneFile)) {
804 continue;
805 }
806
807 if (acym_deleteFile($oneFile)) {
808 acym_enqueueMessage(acym_translationSprintf('ACYM_FILE_UPLOAD_ERROR_8', $oneFile), 'warning');
809 }
810 }
811
812 return $extractdir;
813 }
814
815 public function installExtractedTemplate(string $templateFolder, bool $deleteFolder = true): bool
816 {
817 if ($this->detectTemplates($templateFolder)) {
818 $messages = $this->templateNames;
819 array_unshift($messages, acym_translationSprintf('ACYM_TEMPLATES_INSTALL', count($this->templateNames)));
820 acym_enqueueMessage($messages);
821 if ($deleteFolder && is_dir($templateFolder)) {
822 acym_deleteFolder($templateFolder);
823 }
824
825 return true;
826 }
827
828 acym_enqueueMessage(acym_translationSprintf('ACYM_FILE_UPLOAD_ERROR_10'), 'error');
829 if ($deleteFolder && is_dir($templateFolder)) {
830 acym_deleteFolder($templateFolder);
831 }
832
833 return false;
834 }
835
836 public function detectTemplates(string $folder): bool
837 {
838 $allFiles = acym_getFiles($folder);
839 if (!empty($allFiles)) {
840 $keyTmpl = array_search('template.html', $allFiles);
841 if ($keyTmpl !== false) {
842 if ($this->installTemplate($folder.DS.$allFiles[$keyTmpl])) {
843 return true;
844 }
845 }
846 foreach ($allFiles as $oneFile) {
847 if (preg_match('#^.*(html|htm)$#i', $oneFile)) {
848 if ($this->installTemplate($folder.DS.$oneFile)) {
849 return true;
850 }
851 }
852 }
853 }
854
855 $status = false;
856 $allFolders = acym_getFolders($folder);
857 if (!empty($allFolders)) {
858 foreach ($allFolders as $oneFolder) {
859 $status = $this->detectTemplates($folder.DS.$oneFolder) || $status;
860 }
861 }
862
863 return $status;
864 }
865
866 private function installTemplate(string $filepath): bool
867 {
868 $fileContent = acym_fileGetContent($filepath);
869
870 $newTemplate = new \stdClass();
871
872 if (strpos($filepath, '_template') === false) {
873 $newTemplate->name = basename(dirname($filepath));
874 } else {
875 $newTemplate->name = basename($filepath);
876 $newTemplate->name = substr($newTemplate->name, 0, strrpos($newTemplate->name, '.'));
877 }
878
879 $newTemplate->name = trim(preg_replace('#[^a-z0-9]#i', ' ', $newTemplate->name));
880
881 if (preg_match('#< *title[^>]*>(.*)< */ *title *>#Uis', $fileContent, $results) && !empty($results[1])) $newTemplate->name = $results[1];
882
883 if (preg_match('#< *meta *name="fromname" *content="([^"]*)"#Uis', $fileContent, $results) && !empty($results[1])) $newTemplate->fromname = $results[1];
884 if (preg_match('#< *meta *name="fromemail" *content="([^"]*)"#Uis', $fileContent, $results) && !empty($results[1])) $newTemplate->fromemail = $results[1];
885 if (preg_match('#< *meta *name="replyname" *content="([^"]*)"#Uis', $fileContent, $results) && !empty($results[1])) $newTemplate->replyname = $results[1];
886 if (preg_match('#< *meta *name="replyemail" *content="([^"]*)"#Uis', $fileContent, $results) && !empty($results[1])) $newTemplate->replyemail = $results[1];
887 if (preg_match('#< *meta *name="subject" *content="([^"]*)"#Uis', $fileContent, $results) && !empty($results[1])) $newTemplate->subject = $results[1];
888 if (preg_match('#< *meta *name="settings" *content="([^"]*)"#Uis', $fileContent, $results) && !empty($results[1])) {
889 $newTemplate->settings = htmlspecialchars_decode($results[1]);
890 }
891
892 $newFolder = preg_replace('#[^a-z0-9]#i', '_', strtolower($newTemplate->name));
893 $newTemplateFolder = $newFolder;
894 $i = 1;
895 while (is_dir(ACYM_TEMPLATE.$newTemplateFolder)) {
896 $newTemplateFolder = $newFolder.'_'.$i;
897 $i++;
898 }
899 $newTemplate->mail_settings = ['media_folder' => $newTemplateFolder];
900
901 $moveResult = acym_copyFolder(dirname($filepath), ACYM_TEMPLATE.$newTemplateFolder);
902 if ($moveResult !== true) {
903 acym_display([acym_translationSprintf('ACYM_ERROR_COPYING_FOLDER_TO', dirname($filepath), ACYM_TEMPLATE.$newTemplateFolder), $moveResult], 'error');
904
905 return false;
906 }
907
908 if (!file_exists(ACYM_TEMPLATE.$newTemplateFolder.DS.'index.html')) {
909 $indexFile = '<html><body bgcolor="#FFFFFF"></body></html>';
910 acym_writeFile(ACYM_TEMPLATE.$newTemplateFolder.DS.'index.html', $indexFile);
911 }
912
913 $fileContent = str_replace(
914 [
915 'src="./',
916 'src="../',
917 'src="images/',
918 'url("images/',
919 'url(&quot;images/',
920 ],
921 [
922 'src="'.ACYM_TEMPLATE_URL.$newTemplateFolder.'/',
923 'src="'.ACYM_TEMPLATE_URL,
924 'src="'.ACYM_TEMPLATE_URL.$newTemplateFolder.'/images/',
925 'url("'.ACYM_TEMPLATE_URL.$newTemplateFolder.'/images/',
926 'url(&quot;'.ACYM_TEMPLATE_URL.$newTemplateFolder.'/images/',
927 ],
928 $fileContent
929 );
930
931 $fileContent = preg_replace('#(src|background)[ ]*=[ ]*\"(?!(https?://|/))(?:\.\./|\./)?#', '$1="'.ACYM_TEMPLATE_URL.$newTemplateFolder.'/', $fileContent);
932
933 if (preg_match('#< *body[^>]*>(.*)< */ *body *>#Uis', $fileContent, $results)) {
934 $newTemplate->body = $results[1];
935 } else {
936 $newTemplate->body = $fileContent;
937 }
938
939 $newTemplate->stylesheet = '';
940 if (preg_match_all('#< *style[^>]*>(.*)< */ *style *>#Uis', $fileContent, $results)) {
941 $newTemplate->stylesheet .= preg_replace('#(<!--|-->)#s', '', implode("\n", $results[1]));
942 }
943 $cssFiles = [];
944 $cssFiles[ACYM_TEMPLATE.$newTemplateFolder] = acym_getFiles(ACYM_TEMPLATE.$newTemplateFolder, '\.css$');
945 $subFolders = acym_getFolders(ACYM_TEMPLATE.$newTemplateFolder);
946 foreach ($subFolders as $oneFolder) {
947 $cssFiles[ACYM_TEMPLATE.$newTemplateFolder.DS.$oneFolder] = acym_getFiles(ACYM_TEMPLATE.$newTemplateFolder.DS.$oneFolder, '\.css$');
948 }
949
950 foreach ($cssFiles as $cssFolder => $cssFile) {
951 if (empty($cssFile)) continue;
952 $newTemplate->stylesheet .= "\n".acym_fileGetContent($cssFolder.DS.reset($cssFile));
953 }
954
955 if (!empty($newTemplate->stylesheet)) {
956 if (preg_match('#body *\{[^\}]*background-color:([^;\}]*)[;\}]#Uis', $newTemplate->stylesheet, $backgroundresults)) {
957 $newTemplate->stylesheet = preg_replace('#(body *\{[^\}]*)background-color:[^;\}]*[;\}]#Uis', '$1', $newTemplate->stylesheet);
958 }
959
960 $quickstyle = [
961 'tag_h1' => 'h1',
962 'tag_h2' => 'h2',
963 'tag_h3' => 'h3',
964 'tag_h4' => 'h4',
965 'tag_h5' => 'h5',
966 'tag_h6' => 'h6',
967 'tag_a' => 'a',
968 'tag_ul' => 'ul',
969 'tag_li' => 'li',
970 'acym_unsub' => '\.acym_unsub',
971 'acym_online' => '\.acym_online',
972 'acym_title' => '\.acym_title',
973 'acym_content' => '\.acym_content',
974 'acym_readmore' => '\.acym_readmore',
975 ];
976 foreach ($quickstyle as $styledb => $oneStyle) {
977 if (preg_match('#[^a-z\. ,] *'.$oneStyle.' *{([^}]*)}#Uis', $newTemplate->stylesheet, $quickstyleresults)) {
978 $newTemplate->stylesheet = str_replace($quickstyleresults[0], '', $newTemplate->stylesheet);
979 }
980 }
981 }
982
983 $foldersForPicts = [$newTemplateFolder];
984 $otherFolders = acym_getFolders(ACYM_TEMPLATE.$newTemplateFolder);
985 foreach ($otherFolders as $oneFold) {
986 $foldersForPicts[] = $newTemplateFolder.DS.$oneFold;
987 }
988 $allPictures = [];
989 foreach ($foldersForPicts as $oneFolder) {
990 $allPictures[$oneFolder] = acym_getFiles(ACYM_TEMPLATE.$oneFolder);
991 }
992
993 $uploadsFolder = ACYM_UPLOAD_FOLDER_THUMBNAIL;
994
995 $thumbNb = intval($this->config->get('numberThumbnail', 2));
996
997 foreach ($allPictures as $folder => $pictfolders) {
998 foreach ($pictfolders as $onePict) {
999 if (!preg_match('#\.(jpg|gif|png|jpeg|ico|bmp)$#i', $onePict)) continue;
1000 if (preg_match('#(thumbnail|screenshot|muestra)#i', $onePict)) {
1001 $thumbNb++;
1002 $newNamePict = 'thumbnail_'.$thumbNb.'.png';
1003 copy(ACYM_TEMPLATE.str_replace(DS, '/', $folder).'/'.$onePict, $uploadsFolder.$newNamePict);
1004 $newTemplate->thumbnail = $newNamePict;
1005 }
1006 }
1007 }
1008
1009 $this->config->saveConfig(['numberThumbnail' => $thumbNb]);
1010
1011 $newTemplate->drag_editor = strpos($newTemplate->body, 'acym__wysid__template__content') !== false ? 1 : 0;
1012 $newTemplate->type = self::TYPE_TEMPLATE;
1013 $newTemplate->creation_date = acym_date('now', 'Y-m-d H:i:s', false);
1014
1015 $this->save($newTemplate);
1016
1017 $this->templateNames[] = $newTemplate->name;
1018
1019 return true;
1020 }
1021
1022 public function sendAutomation(int $mailId, array $userIds, string $sendingDate, array $automationAdmin = [])
1023 {
1024 if (empty($mailId)) {
1025 return acym_translationSprintf('ACYM_EMAILS_ADDED_QUEUE', 0);
1026 }
1027
1028 if (empty($sendingDate)) {
1029 return acym_translation('ACYM_WRONG_DATE');
1030 }
1031
1032 if (empty($userIds)) {
1033 return acym_translation('ACYM_USER_NOT_FOUND');
1034 }
1035
1036 acym_arrayToInteger($userIds);
1037
1038 $nbSent = $this->sendNotification($mailId, $userIds, $automationAdmin);
1039 if (!is_null($nbSent)) {
1040 return $nbSent;
1041 }
1042
1043 $result = acym_query(
1044 'INSERT IGNORE INTO #__acym_queue (`mail_id`, `user_id`, `sending_date`)
1045 SELECT '.intval($mailId).', user.id, '.acym_escapeDB($sendingDate).'
1046 FROM #__acym_user AS user
1047 WHERE user.active = 1 AND user.id IN ('.implode(',', $userIds).')'
1048 );
1049
1050 if ($result === 0) {
1051 return acym_translation('ACYM_CAMPAIGN_ALREADY_QUEUED');
1052 }
1053
1054 $mailStatClass = new MailStatClass();
1055 $mailStat = $mailStatClass->getOneRowByMailId($mailId);
1056
1057 $newMailStat = new \stdClass();
1058 $newMailStat->mail_id = intval($mailId);
1059 $newMailStat->total_subscribers = intval($result);
1060 if (empty($mailStat)) {
1061 $newMailStat->send_date = $sendingDate;
1062 }
1063
1064 $mailStatClass->save($newMailStat);
1065
1066 return $result;
1067 }
1068
1069 private function sendNotification(int $mailId, array $userIds, array $automationAdmin): ?int
1070 {
1071 if (empty($automationAdmin['user_id'])) {
1072 return null;
1073 }
1074
1075 $userClass = new UserClass();
1076 $mail = $this->getOneById($mailId);
1077 $user = $userClass->getOneById($automationAdmin['user_id']);
1078
1079 if (empty($mail) || empty($user)) {
1080 return 0;
1081 }
1082
1083 $mailerHelper = new MailerHelper();
1084 $pluginHelper = new PluginHelper();
1085 $extractedTags = $pluginHelper->extractTags($mail, 'subscriber');
1086
1087 $isNotification = false;
1088 foreach ($extractedTags as $dtext => $oneTag) {
1089 if (empty($oneTag->info) || $oneTag->info !== 'current') {
1090 continue;
1091 }
1092
1093 $isNotification = true;
1094
1095 if (!empty($user->{$oneTag->id})) {
1096 $mailerHelper->addParam(str_replace(['{', '}'], '', $dtext), $user->{$oneTag->id});
1097 }
1098 }
1099
1100 if (!$isNotification) {
1101 if (isset($automationAdmin['automationAdmin']) && $automationAdmin['automationAdmin']) {
1102 return 0;
1103 } else {
1104 return null;
1105 }
1106 }
1107
1108 $userFields = $userClass->getAllUserFields($user);
1109 foreach ($userFields as $map => $value) {
1110 $mailerHelper->addParam('subscriber:'.$map.'|info:current', $value);
1111 $mailerHelper->addParam('user:'.$map, $value);
1112 }
1113
1114 $emailsSent = 0;
1115 foreach ($userIds as $userId) {
1116 if ($mailerHelper->sendOne($mail->id, $userId)) {
1117 $emailsSent++;
1118 }
1119 }
1120
1121 return $emailsSent;
1122 }
1123
1124 public function encode($mails = [])
1125 {
1126 $isArray = true;
1127 if (!is_array($mails)) {
1128 $mails = [$mails];
1129
1130 $isArray = false;
1131 }
1132
1133 $encodedMails = array_map([$this, 'removePoweredByAcyMailing'], $mails);
1134
1135 $encodedMails = array_map([$this, 'utf8Encode'], $encodedMails);
1136
1137 return $isArray ? $encodedMails : $encodedMails[0];
1138 }
1139
1140 public function decode($mails = [])
1141 {
1142 if (is_null($mails)) {
1143 return null;
1144 }
1145
1146 $isArray = true;
1147 if (!is_array($mails)) {
1148 $mails = [$mails];
1149
1150 $isArray = false;
1151 }
1152
1153 $decodedMails = array_map([$this, 'utf8Decode'], $mails);
1154
1155 $decodedMails = array_map([$this, 'addPoweredByAcyMailing'], $decodedMails);
1156
1157 foreach ($decodedMails as $i => $oneMail) {
1158 if (!isset($oneMail->access) || is_array($oneMail->access)) continue;
1159 $decodedMails[$i]->access = empty($oneMail->access) ? '' : explode(',', $oneMail->access);
1160 }
1161
1162 return $isArray ? $decodedMails : $decodedMails[0];
1163 }
1164
1165 protected function utf8Decode($mail)
1166 {
1167 if (!empty($mail)) {
1168 foreach (self::FIELDS_ENCODING as $oneField) {
1169 if (in_array($oneField, $this->exceptKeysDecode)) {
1170 continue;
1171 }
1172
1173 if (is_array($mail)) {
1174 if (empty($mail[$oneField])) continue;
1175 $value = &$mail[$oneField];
1176 } else {
1177 if (empty($mail->$oneField)) continue;
1178 $value = &$mail->$oneField;
1179 }
1180
1181 $value = acym_utf8Decode($value);
1182 }
1183 }
1184
1185 return $mail;
1186 }
1187
1188 private function addPoweredByAcyMailing(object $mail): object
1189 {
1190 if (empty($mail->body)) {
1191 return $mail;
1192 }
1193
1194 if ($this->config->get('display_built_by', 0) != 1) {
1195 if (strpos($mail->body, 'acym__powered_by_acymailing') !== false) {
1196 $mailBodyDom = new \DOMDocument();
1197 @$mailBodyDom->loadHTML('<?xml encoding="utf-8" ?>'.$mail->body);
1198 $tables = $mailBodyDom->getElementsByTagName('table');
1199 foreach ($tables as $table) {
1200 if ($table->getAttribute('id') != 'acym__powered_by_acymailing') continue;
1201 $table->parentNode->removeChild($table);
1202 break;
1203 }
1204 $mail->body = $mailBodyDom->saveHTML();
1205 }
1206
1207 return $mail;
1208 }
1209
1210 if (strpos($mail->body, 'acym__powered_by_acymailing') !== false) {
1211 return $mail;
1212 }
1213
1214 $isWysidEditor = strpos($mail->body, 'acym__wysid__template') !== false;
1215 $urlPoweredByImage = ACYM_IMAGES.'editor/poweredby_black.png';
1216
1217 if (!$isWysidEditor) {
1218 $poweredByHTML = '<p id="acym__powered_by_acymailing">';
1219 $poweredByHTML .= '<a href="'.ACYM_ACYMAILING_WEBSITE.'?utm_campaign=powered_by_v7&utm_source=acymailing_plugin&utm_medium=built_with_footer" target="blank">';
1220 $poweredByHTML .= '<img alt="Email built with AcyMailing" height="40" width="199" style="height: 40px; width:199px; max-width: 100%; height: auto; box-sizing: border-box; padding: 0 5px; display: block; margin-left: auto; margin-right: auto;" src="'.$urlPoweredByImage.'"/>';
1221 $poweredByHTML .= '</a></p>';
1222
1223 $mail->body = $mail->body.$poweredByHTML;
1224 } else {
1225 $poweredByWYSID = '<table id="acym__powered_by_acymailing" class="row" bgcolor="#ffffff" style="background-color: transparent" cellpadding="0" cellspacing="0" border="0">
1226 <tbody bgcolor style="background-color: inherit;">
1227 <tr>
1228 <th class="small-12 medium-12 large-12 columns" valign="top" style="height: auto;">
1229 <table border="0" cellpadding="0" cellspacing="0"
1230 style="min-height: 0px; display: table; height: auto;">
1231 <tbody style="min-height: 0px; display: table-row-group;">
1232 <tr
1233 style="position: relative; top: inherit; left: inherit; right: inherit; bottom: inherit; height: auto;">
1234 <td class="large-12">
1235 <div style="position: relative;">
1236 <p style="word-break: break-word; text-align: center;">
1237 <a href="'.ACYM_ACYMAILING_WEBSITE.'?utm_campaign=powered_by_v7&utm_source=acymailing_plugin&utm_medium=built_with_footer" target="_blank">
1238 <img src="'.$urlPoweredByImage.'"
1239 title="poweredby" alt="Email built with AcyMailing"
1240 style="height: 40px; width:199px; max-width: 100%; height: auto; box-sizing: border-box; padding: 0px 5px; display: inline-block; margin-left: auto; margin-right: auto;"
1241 height="40" width="199">
1242 </a>
1243 </p>
1244 </div>
1245 </td>
1246 </tr>
1247 </tbody>
1248 </table>
1249 </th>
1250 </tr>
1251 </tbody>
1252 </table>';
1253
1254 $mailBodyDom = new \DOMDocument();
1255 @$mailBodyDom->loadHTML('<?xml encoding="utf-8" ?>'.$mail->body);
1256
1257 $htmlToAddDom = new \DOMDocument();
1258 @$htmlToAddDom->loadHTML('<?xml encoding="utf-8" ?>'.$poweredByWYSID);
1259 $tableToAdd = $htmlToAddDom->getElementById('acym__powered_by_acymailing');
1260
1261 $tds = $mailBodyDom->getElementsByTagName('td');
1262 foreach ($tds as $td) {
1263 $classes = explode(' ', $td->getAttribute('class'));
1264 if (!in_array('acym__wysid__row', $classes)) continue;
1265 if (!empty($tableToAdd)) $td->appendChild($mailBodyDom->importNode($tableToAdd, true));
1266 break;
1267 }
1268 $mail->body = $mailBodyDom->saveHTML();
1269 }
1270
1271 return $mail;
1272 }
1273
1274 protected function removePoweredByAcyMailing(object $mail): object
1275 {
1276 if (empty($mail->body) || strpos($mail->body, 'acym__wysid__template') !== false) {
1277 return $mail;
1278 }
1279
1280 $regexPoweredByAcyMailing = '#<p id="acym__powered_by_acymailing.*<\/p>#U';
1281
1282 $mail->body = preg_replace($regexPoweredByAcyMailing, '', $mail->body);
1283
1284 return $mail;
1285 }
1286
1287 protected function utf8Encode($mail)
1288 {
1289 if (!empty($mail)) {
1290 foreach (self::FIELDS_ENCODING as $oneField) {
1291 if (is_array($mail)) {
1292 if (empty($mail[$oneField])) continue;
1293 $value = &$mail[$oneField];
1294 } else {
1295 if (empty($mail->$oneField)) continue;
1296 $value = &$mail->$oneField;
1297 }
1298
1299 $value = acym_utf8Encode($value);
1300 }
1301 }
1302
1303 return $mail;
1304 }
1305
1306 public function getTranslationId(int $parentId, string $langCode): int
1307 {
1308 return (int)acym_loadResult(
1309 'SELECT `id`
1310 FROM #__acym_mail
1311 WHERE `parent_id` = '.intval($parentId).'
1312 AND `language` = '.acym_escapeDB($langCode)
1313 );
1314 }
1315
1316 public function getTranslationsById(int $mailId, bool $full = false, bool $includeParent = false)
1317 {
1318 $data = $full ? '*' : '`language`, `subject`, `preheader`, `body`, `autosave`';
1319 $where = $includeParent ? ' OR `id` = '.intval($mailId) : '';
1320
1321 return $this->decode(
1322 acym_loadObjectList(
1323 'SELECT '.$data.'
1324 FROM #__acym_mail
1325 WHERE `parent_id` = '.intval($mailId).$where,
1326 'language'
1327 )
1328 );
1329 }
1330
1331 public function deleteByTranslationLang(array $languageCodes): void
1332 {
1333 if (empty($languageCodes)) {
1334 return;
1335 }
1336
1337 foreach ($languageCodes as $key => $oneLangCode) {
1338 $languageCodes[$key] = acym_escapeDB($oneLangCode);
1339 }
1340
1341 $this->delete(
1342 acym_loadResultArray(
1343 'SELECT `id`
1344 FROM #__acym_mail
1345 WHERE `parent_id` IS NOT NULL
1346 AND `language` IN ('.implode(', ', $languageCodes).')'
1347 )
1348 );
1349 }
1350
1351 public function getMultilingualMails(int $parentId): array
1352 {
1353 $mails = $this->decode(
1354 acym_loadObjectList(
1355 'SELECT * FROM #__acym_mail WHERE parent_id = '.intval($parentId).' OR id = '.intval($parentId),
1356 'language'
1357 )
1358 );
1359
1360 return $this->translateMailSettings($mails);
1361 }
1362
1363 private function translateMailSettings(array $mails): array
1364 {
1365 if (empty($mails)) {
1366 return $mails;
1367 }
1368
1369 $defaultLanguage = $this->config->get('multilingual_default');
1370
1371 if (empty($mails[$defaultLanguage])) {
1372 $firstMail = reset($mails);
1373 if (empty($firstMail) || empty($firstMail->parent_id)) {
1374 return $mails;
1375 }
1376 $mainMail = $this->getOneById($firstMail->parent_id);
1377 } else {
1378 $mainMail = $mails[$defaultLanguage];
1379 }
1380
1381 if (!empty($mainMail->translation)) {
1382 $translation = json_decode($mainMail->translation, true);
1383
1384 foreach ($mails as $lang => $mail) {
1385 if (empty($mail->parent_id) || empty($translation[$lang])) {
1386 continue;
1387 }
1388
1389 if (!empty($translation[$lang]['from_name'])) $mail->from_name = $translation[$lang]['from_name'];
1390 if (!empty($translation[$lang]['from_email'])) $mail->from_email = $translation[$lang]['from_email'];
1391 if (!empty($translation[$lang]['reply_to_name'])) $mail->reply_to_name = $translation[$lang]['reply_to_name'];
1392 if (!empty($translation[$lang]['reply_to_email'])) $mail->reply_to_email = $translation[$lang]['reply_to_email'];
1393 }
1394 }
1395
1396 return $mails;
1397 }
1398
1399 public function getMailAttachments(int $mailId)
1400 {
1401 return acym_loadResult('SELECT attachments FROM #__acym_mail WHERE id = '.intval($mailId));
1402 }
1403
1404 public function isTransactionalMail(object $mail): bool
1405 {
1406 return $mail->type !== self::TYPE_STANDARD;
1407 }
1408
1409 public function isOneTimeMail(object $mail): bool
1410 {
1411 return in_array(
1412 $mail->type,
1413 [
1414 self::TYPE_NOTIFICATION,
1415 self::TYPE_OVERRIDE,
1416 self::TYPE_WELCOME,
1417 self::TYPE_UNSUBSCRIBE,
1418 self::TYPE_TEMPLATE,
1419 ]
1420 );
1421 }
1422
1423 public function getAutomaticMailIds(array $mailIds): array
1424 {
1425 acym_arrayToInteger($mailIds);
1426
1427 $query = 'SELECT DISTINCT campaign2.mail_id
1428 FROM #__acym_campaign AS campaign1
1429 JOIN #__acym_campaign AS campaign2 ON campaign1.id = campaign2.parent_id
1430 WHERE campaign1.mail_id IN ('.implode(',', $mailIds).')';
1431 $generatedMailIds = acym_loadResultArray($query);
1432
1433 if (empty($generatedMailIds)) return [];
1434
1435 acym_arrayToInteger($generatedMailIds);
1436
1437 return $generatedMailIds;
1438 }
1439
1440 public function getMultilingualMailIds(array $mailIds): array
1441 {
1442 acym_arrayToInteger($mailIds);
1443
1444 $query = 'SELECT DISTINCT mail2.id
1445 FROM #__acym_mail AS mail1
1446 JOIN #__acym_mail AS mail2 ON mail1.id = mail2.parent_id
1447 WHERE mail1.id IN ('.implode(',', $mailIds).')';
1448
1449 $multipleMailIds = acym_loadResultArray($query);
1450
1451 if (empty($multipleMailIds)) return [];
1452
1453 acym_arrayToInteger($multipleMailIds);
1454
1455 return $multipleMailIds;
1456 }
1457
1458 public function duplicateMail(int $mailId, ?string $type = null): ?object
1459 {
1460 $mail = $this->getOneById($mailId);
1461
1462 if (empty($mail)) {
1463 return null;
1464 }
1465
1466 $newMail = new \stdClass();
1467 $newMail->name = $mail->name.'_copy';
1468 $newMail->thumbnail = '';
1469 $newMail->type = empty($type) ? $mail->type : $type;
1470 $newMail->drag_editor = $mail->drag_editor;
1471 $newMail->body = $mail->body;
1472 $newMail->subject = $mail->subject;
1473 $newMail->from_name = $mail->from_name;
1474 $newMail->from_email = $mail->from_email;
1475 $newMail->reply_to_name = $mail->reply_to_name;
1476 $newMail->reply_to_email = $mail->reply_to_email;
1477 $newMail->bcc = $mail->bcc;
1478 $newMail->settings = $mail->settings;
1479 $newMail->stylesheet = $mail->stylesheet;
1480 $newMail->attachments = $mail->attachments;
1481 $newMail->headers = $mail->headers;
1482 $newMail->preheader = $mail->preheader;
1483
1484 $newMail->id = $this->save($newMail);
1485
1486 return $newMail;
1487 }
1488
1489 public function getTemplatesForMailboxAction(): array
1490 {
1491 return $this->decode(
1492 acym_loadObjectList(
1493 'SELECT `id`, `name`
1494 FROM #__acym_mail
1495 WHERE `type` = '.acym_escapeDB(self::TYPE_TEMPLATE).'
1496 AND `body` LIKE '.acym_escapeDB('%'.acym_utf8Encode('{emailcontent}').'%')
1497 )
1498 );
1499 }
1500
1501 public function hasUserAccess(int $mailId): bool
1502 {
1503 $userId = acym_currentUserId();
1504 if (empty($userId)) {
1505 return false;
1506 }
1507
1508 if (acym_isAdmin()) {
1509 return true;
1510 }
1511
1512 return acym_loadResult(
1513 'SELECT COUNT(*)
1514 FROM #__acym_mail
1515 WHERE id = '.intval($mailId).'
1516 AND (creator_id = '.intval($userId).'
1517 OR type = '.acym_escapeDB(self::TYPE_TEMPLATE).')'
1518 ) > 0;
1519 }
1520
1521 public function getVersionsById(int $mailId, bool $includeParent = false): array
1522 {
1523 $where = $includeParent ? ' OR `id` = '.intval($mailId) : '';
1524
1525 return $this->decode(
1526 acym_loadObjectList(
1527 'SELECT *
1528 FROM #__acym_mail
1529 WHERE `parent_id` = '.intval($mailId).$where.' ORDER BY id ASC',
1530 'id'
1531 )
1532 );
1533 }
1534
1535 public function getParentAndChildMails(int $parentId): array
1536 {
1537 return $this->decode(
1538 acym_loadObjectList(
1539 'SELECT * FROM #__acym_mail WHERE parent_id = '.intval($parentId).' OR id = '.intval($parentId),
1540 'id'
1541 )
1542 );
1543 }
1544
1545 public function getAllTemplatesByType(array $options): array
1546 {
1547 $limit = $options['limit'] ?? 10;
1548 $offset = $options['offset'] ?? 0;
1549 $filters = $options['filters'] ?? [];
1550 $search = $filters['search'] ?? '';
1551 $templateType = $filters['templateType'] ?? '';
1552
1553 $conditions = ['`type` = '.acym_escapeDB(self::TYPE_TEMPLATE)];
1554
1555 if (!empty($search)) {
1556 $conditions[] = '(name LIKE '.acym_escapeDB('%'.$search.'%').' OR subject LIKE '.acym_escapeDB('%'.$search.'%').')';
1557 }
1558
1559 if (!empty($templateType)) {
1560 if ($templateType === 'drag') {
1561 $conditions[] = 'drag_editor = 1';
1562 } else {
1563 $conditions[] = 'drag_editor = 0';
1564 }
1565 }
1566
1567 $query = 'SELECT * FROM #__acym_mail WHERE '.implode(' AND ', $conditions);
1568
1569 return $this->decode(acym_loadObjectList($query, '', $offset, $limit));
1570 }
1571
1572 public function updateFollowupPriority(bool $previouslyActive, bool $active): void
1573 {
1574 if (!$previouslyActive && $active) {
1575 acym_query(
1576 'UPDATE `#__acym_queue` AS `queue`
1577 INNER JOIN `#__acym_mail` AS `mail` ON `queue`.`mail_id` = `mail`.`id`
1578 SET `queue`.`priority` = 1
1579 WHERE `mail`.`type` = '.acym_escapeDB(self::TYPE_FOLLOWUP)
1580 );
1581 }
1582 }
1583
1584 public function getCampaignIdByMailId(int $mailId): int
1585 {
1586 $mainMailId = $this->getMainMailId($mailId);
1587
1588 return intval(acym_loadResult('SELECT id FROM #__acym_campaign WHERE mail_id = '.intval($mainMailId)));
1589 }
1590
1591 public function getMainMailId(int $mailId): int
1592 {
1593 $parentId = acym_loadResult('SELECT parent_id FROM #__acym_mail WHERE id = '.intval($mailId));
1594
1595 return empty($parentId) ? $mailId : intval($parentId);
1596 }
1597
1598 public function getMailType(int $mailId): string
1599 {
1600 return acym_loadResult('SELECT type FROM #__acym_mail WHERE id = '.intval($mailId));
1601 }
1602 }
1603