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