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

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

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