PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.1
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Core / AcymPlugin.php

AcymPlugin.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.1, at back/Core/AcymPlugin.php

1,696 lines 62.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Core;
4
5 use AcyMailing\Classes\CampaignClass;
6 use AcyMailing\Classes\PluginClass;
7 use AcyMailing\Helpers\PluginHelper;
8 use Joomla\CMS\HTML\HTMLHelper;
9
10 class AcymPlugin extends AcymObject
11 {
12 public string $cms = 'all';
13 public string $name = '';
14 public bool $installed = true;
15
16 protected array $addonDefinition;
17 protected PluginHelper $pluginHelper;
18
19 protected int $rootCategoryId = 1;
20 protected array $categories;
21 protected array $catvalues = [];
22 private array $cats = [];
23 private array $subCategories = [];
24
25 protected array $tagvalues = [];
26
27 protected array $tags = [];
28 protected object $pageInfo;
29 protected array $searchFields = [];
30 protected string $querySelect = '';
31 protected string $query = '';
32 protected array $filters = [];
33 protected string $elementIdTable = '';
34 protected string $elementIdColumn = '';
35
36 public object $pluginDescription;
37 public object $generateCampaignResult;
38
39 protected ?object $defaultValues;
40 protected string $emailLanguage = '';
41 private int $campaignId;
42 private string $campaignType;
43
44 protected array $displayOptions = [];
45 protected array $replaceOptions = [];
46 protected array $elementOptions = [];
47 protected array $customOptions = [];
48
49 private array $sendingPlugins = [
50 'wp_mail_smtp' => 'WP Mail SMTP',
51 ];
52
53 public array $errors = [];
54 public string $logFilename = '';
55 protected int $responseCode;
56
57 private bool $active;
58 public array $settings = [];
59 private array $savedSettings = [];
60
61 public function __construct()
62 {
63 parent::__construct();
64
65 $this->elementOptions = ['wrappedText' => [acym_translation('ACYM_WRAPPED_TEXT')]];
66
67 //TODO only load this helper once even if 50 plugins are loaded
68 $this->pluginHelper = new PluginHelper();
69 $this->pageInfo = new \stdClass();
70
71 $this->pluginDescription = new \stdClass();
72 $this->pluginDescription->plugin = get_class($this);
73
74 $this->name = strtolower(substr($this->pluginDescription->plugin, 7));
75
76 $this->generateCampaignResult = new \stdClass();
77 $this->generateCampaignResult->status = true;
78 $this->generateCampaignResult->message = '';
79
80 $this->campaignId = acym_getVar('int', 'campaignId', 0);
81 $this->campaignType = acym_getVar('string', 'campaign_type', '');
82
83 $pluginClass = new PluginClass();
84 $currentAddon = $pluginClass->getOnePluginByFolderName($this->name);
85 $this->active = empty($currentAddon) || $currentAddon->active;
86
87 if (!empty($currentAddon->settings)) {
88 if (is_string($currentAddon->settings)) {
89 $this->savedSettings = json_decode($currentAddon->settings, true);
90 } else {
91 $this->savedSettings = $currentAddon->settings;
92 }
93 }
94
95 $this->logFilename = acym_getErrorLogFilename(empty($this->name) ? get_class($this) : $this->name);
96 }
97
98 protected function displaySelectionZone(string $zoneContent): void
99 {
100 ?>
101 <p class="acym__wysid__right__toolbar__p acym__wysid__right__toolbar__p__open acym__title">
102 <?php echo esc_html(acym_translation('ACYM_CONTENT_TO_INSERT')); ?><i class="acymicon-keyboard-arrow-up"></i>
103 </p>
104 <div class="acym__wysid__right__toolbar__design--show acym__wysid__right__toolbar__design acym__wysid__context__modal__container">
105 <?php echo $zoneContent; ?>
106 </div>
107 <?php
108 }
109
110 /**
111 * Called using ajax
112 */
113 public function displayListing(): void
114 {
115 echo $this->prepareListing();
116 }
117
118 public function prepareListing()
119 {
120 $this->pageInfo->limit = 10;
121 $this->pageInfo->page = acym_getVar('int', 'pagination_page_ajax', 1);
122 $this->pageInfo->start = ($this->pageInfo->page - 1) * $this->pageInfo->limit;
123 $this->pageInfo->search = acym_getVar('string', 'plugin_search', '');
124 $this->pageInfo->filter_cat = acym_getVar('int', 'plugin_category', 0);
125 $this->pageInfo->orderdir = 'DESC';
126 $this->pageInfo->loadMore = acym_getVar('boolean', 'loadMore', false);
127
128 if (!empty($this->pageInfo->search) && !empty($this->searchFields)) {
129 $searchVal = '%'.acym_getEscaped($this->pageInfo->search, true).'%';
130 $this->filters[] = implode(' LIKE '.acym_escapeDB($searchVal).' OR ', $this->searchFields).' LIKE '.acym_escapeDB($searchVal);
131 }
132
133 return '';
134 }
135
136 public function getElements(): array
137 {
138 $conditions = '';
139 if (!empty($this->filters)) {
140 $conditions = ' WHERE ('.implode(') AND (', $this->filters).')';
141 }
142
143 $ordering = '';
144 if (!empty($this->pageInfo->order)) {
145 $ordering = ' ORDER BY '.acym_secureDBColumn($this->pageInfo->order).' '.acym_secureDBColumn($this->pageInfo->orderdir);
146 }
147
148 $rows = acym_loadObjectList($this->querySelect.$this->query.$conditions.$ordering, '', $this->pageInfo->start, $this->pageInfo->limit);
149 $this->pageInfo->total = acym_loadResult('SELECT COUNT(*) '.$this->query.$conditions.$ordering);
150
151 if (!empty($this->defaultValues->id) && $this->defaultValues->defaultPluginTab === $this->name) {
152 $found = false;
153 foreach ($rows as $oneRow) {
154 if (intval($oneRow->{$this->elementIdColumn}) === intval($this->defaultValues->id)) {
155 $found = true;
156 }
157 }
158
159 if (!$found) {
160 $conditions = ' WHERE '.$this->elementIdTable.'.'.$this->elementIdColumn.' = '.intval($this->defaultValues->id);
161 $row = acym_loadObject($this->querySelect.$this->query.$conditions);
162 if (!empty($row)) {
163 $rows[] = $row;
164 }
165 }
166 }
167
168 return $rows;
169 }
170
171 protected function getFilteringZone(bool $categoryFilter = true): string
172 {
173 $result = '<div class="grid-x" id="plugin_listing_filters">
174 <div class="cell medium-6">
175 <input type="text" name="plugin_search" placeholder="'.esc_attr(acym_translation('ACYM_SEARCH')).'"/>
176 </div>
177 <div class="cell medium-6 grid-x">
178 <div class="cell hide-for-small-only medium-auto"></div>
179 <div class="cell medium-shrink">';
180
181 if ($categoryFilter) {
182 $result .= $this->getCategoryFilter();
183 }
184
185 $result .= '</div>
186 </div>
187 </div>';
188
189 return $result;
190 }
191
192 public function prepareWPCategories(string $type): void
193 {
194 $this->categories = acym_loadObjectList(
195 'SELECT cat.term_taxonomy_id AS id, parent.term_taxonomy_id AS parent_id, catdetails.name AS title
196 FROM `#__term_taxonomy` AS cat
197 JOIN `#__terms` AS catdetails ON cat.term_id = catdetails.term_id
198 LEFT JOIN `#__term_taxonomy` AS parent ON cat.parent = parent.term_id
199 WHERE cat.taxonomy = '.acym_escapeDB($type)
200 );
201 foreach ($this->categories as $i => $oneCat) {
202 if (empty($oneCat->parent_id)) {
203 $this->categories[$i]->parent_id = $this->rootCategoryId;
204 }
205 }
206 }
207
208 protected function getCategoryFilter()
209 {
210 $filter_cat = acym_getVar('int', 'plugin_category', 0);
211 $this->catvalues = [];
212 $this->catvalues[] = acym_selectOption(0, 'ACYM_ALL');
213
214 $this->cats = [];
215 $this->subCategories = [];
216 if (!empty($this->categories)) {
217 foreach ($this->categories as $oneCat) {
218 $this->cats[$oneCat->parent_id][] = $oneCat;
219 }
220 $this->handleChildrenCategories($this->rootCategoryId);
221 foreach ($this->categories as $oneCat) {
222 $this->subCategories[$oneCat->id] = $this->getSubCats($oneCat->id);
223 }
224 }
225
226 return acym_select(
227 $this->catvalues,
228 'plugin_category',
229 intval($filter_cat),
230 ['class' => 'plugin_category_select']
231 );
232 }
233
234 private function getSubCats(int $categoryId): array
235 {
236 $result = [$categoryId];
237 if (empty($this->cats[$categoryId])) {
238 return $result;
239 }
240
241 foreach ($this->cats[$categoryId] as $oneSubCategory) {
242 $result = array_merge($result, $this->getSubCats($oneSubCategory->id));
243 }
244
245 return $result;
246 }
247
248 protected function handleChildrenCategories(int $parent_id, int $level = 0): void
249 {
250 if (empty($this->cats[$parent_id])) return;
251
252 foreach ($this->cats[$parent_id] as $cat) {
253 $this->catvalues[] = acym_selectOption($cat->id, str_repeat(' - - ', $level).$cat->title);
254 $this->handleChildrenCategories($cat->id, $level + 1);
255 }
256 }
257
258 protected function getSubCategories(int $categoryId): array
259 {
260 $this->getCategoryFilter();
261
262 return $this->subCategories[$categoryId];
263 }
264
265 protected function autoContentOptions(array &$options, ?string $type = null): void
266 {
267 if ($type === 'event') {
268 $options[] = [
269 'title' => 'ACYM_FROM',
270 'type' => 'date',
271 'name' => 'from',
272 'default' => time(),
273 'relativeDate' => '+',
274 ];
275
276 $options[] = [
277 'title' => 'ACYM_TO',
278 'type' => 'date',
279 'name' => 'to',
280 'default' => '',
281 'relativeDate' => '+',
282 ];
283 } elseif ($type !== 'simple') {
284 $options[] = [
285 'title' => 'ACYM_MIN_PUBLISH_DATE',
286 'tooltip' => 'ACYM_MIN_PUBLISH_DATE_DESC',
287 'type' => 'date',
288 'name' => 'min_publish',
289 'default' => '',
290 'relativeDate' => '-',
291 ];
292 }
293
294 $options[] = [
295 'title' => 'ACYM_COLUMNS',
296 'type' => 'number',
297 'name' => 'cols',
298 'default' => 1,
299 ];
300
301 $options[] = [
302 'title' => 'ACYM_COLUMN_HORIZONTAL_PADDING',
303 'type' => 'number',
304 'name' => 'hpadding',
305 'default' => 10,
306 ];
307
308 $options[] = [
309 'title' => 'ACYM_COLUMN_VERTICAL_PADDING',
310 'type' => 'number',
311 'name' => 'vpadding',
312 'default' => 10,
313 ];
314
315 $options[] = [
316 'title' => 'ACYM_MAX_NB_ELEMENTS',
317 'type' => 'number',
318 'name' => 'max',
319 'default' => 20,
320 ];
321 }
322
323 protected function autoCampaignOptions(array &$options, bool $modified = false): void
324 {
325 if (empty($this->campaignId) && empty($this->campaignType)) {
326 return;
327 } elseif (!empty($this->campaignId) && (empty($this->campaignType) || $this->campaignType !== 'auto')) {
328 $campaignClass = new CampaignClass();
329 $campaign = $campaignClass->getOneById($this->campaignId);
330 if ($campaign->sending_type !== 'auto') return;
331 } elseif (empty($this->campaignId) && !empty($this->campaignType) && $this->campaignType !== 'auto') {
332 return;
333 }
334
335 $options[] = [
336 'title' => 'ACYM_DOCUMENTATION',
337 'type' => 'custom',
338 'name' => 'documentation',
339 'output' => '<a target="_blank" href="'.ACYM_DOCUMENTATION.'main-pages/campaigns/automatic-campaigns#dont-send-twice-the-same-content"><i class="acymicon-book"></i></a>',
340 'js' => '',
341 'section' => 'ACYM_AUTO_CAMPAIGNS_OPTIONS',
342 ];
343
344 if ($modified) {
345 $options[] = [
346 'title' => 'ACYM_DATE',
347 'type' => 'select',
348 'name' => 'datefilter',
349 'default' => 'onlynew',
350 'tooltip' => 'ACYM_ONLY_NEWLY_CREATED_DESC',
351 'options' => [
352 '' => 'ACYM_NO_FILTER',
353 'onlynew' => 'ACYM_ONLY_NEWLY_CREATED',
354 'onlymodified' => 'ACYM_ONLY_NEWLY_MODIFIED',
355 ],
356 'section' => 'ACYM_AUTO_CAMPAIGNS_OPTIONS',
357 ];
358 } else {
359 $options[] = [
360 'title' => 'ACYM_ONLY_NEWLY_CREATED',
361 'type' => 'boolean',
362 'name' => 'onlynew',
363 'default' => true,
364 'tooltip' => 'ACYM_ONLY_NEWLY_CREATED_DESC',
365 'section' => 'ACYM_AUTO_CAMPAIGNS_OPTIONS',
366 ];
367 }
368
369 $options[] = [
370 'title' => 'ACYM_MIN_NB_ELEMENTS',
371 'type' => 'number',
372 'name' => 'min',
373 'default' => 0,
374 'tooltip' => 'ACYM_MIN_NB_ELEMENTS_DESC',
375 'section' => 'ACYM_AUTO_CAMPAIGNS_OPTIONS',
376 ];
377 }
378
379 protected function getElementsListing(array $options): string
380 {
381 if ($this->pageInfo->loadMore) {
382 ob_start();
383 $this->displayInnerListing($options);
384
385 return ob_get_clean();
386 }
387 $listing = '<div id="plugin_listing" class="acym__popup__listing">';
388 $listing .= '<input type="hidden" name="plugin" value="'.esc_attr(get_class($this)).'" />';
389
390 // Column names
391 $listing .= '<div class="cell grid-x hide-for-small-only plugin_listing_headers">';
392 foreach ($options['header'] as $oneColumn) {
393 $class = empty($oneColumn['class']) ? '' : ' '.$oneColumn['class'];
394 $listing .= '<div class="cell plugin_listing_headers__title medium-'.$oneColumn['size'].$class.'">'.acym_translation($oneColumn['label']).'</div>';
395 }
396 $listing .= '</div>';
397
398 ob_start();
399 $this->displayInnerListing($options);
400 $listing .= ob_get_clean();
401
402 $listing .= '<input type="hidden" value="1" id="acym_pagination__ajax__load-more" name="acym_pagination__ajax__load-more">';
403 $listing .= '</div>';
404
405 return $listing;
406 }
407
408 private function displayInnerListing(array $options): void
409 {
410 if (empty($options['rows'])) {
411 if ($this->pageInfo->loadMore) {
412 echo '<h3 class="cell acym__listing__empty__load-more text-center">'.esc_html(acym_translation('ACYM_NO_MORE_RESULTS')).'</h3>';
413 } else {
414 echo '<h1 class="cell acym__listing__empty__search__modal text-center">'.esc_html(acym_translation('ACYM_NO_RESULTS_FOUND')).'</h1>';
415 }
416
417 return;
418 }
419
420 $selected = explode(',', acym_getVar('string', 'selected', ''));
421 if (!empty($this->defaultValues->id)) {
422 $selected = [$this->defaultValues->id];
423 }
424
425 foreach ($options['rows'] as $row) {
426 $class = 'cell grid-x acym__row__no-listing acym__listing__row__popup';
427 if (in_array($row->{$options['id']}, $selected)) {
428 $class .= ' selected_row';
429 }
430
431 echo '<div
432 class="'.esc_attr($class).'"
433 data-id="'.esc_attr($row->{$options['id']}).'"
434 onclick="applyContent'.esc_attr($this->name).'(\''.esc_attr($row->{$options['id']}).'\', this);">';
435
436 foreach ($options['header'] as $column => $oneColumn) {
437 $value = $row->$column;
438 $class = empty($oneColumn['class']) ? '' : ' '.$oneColumn['class'];
439
440 echo '<div class="cell medium-'.esc_attr($oneColumn['size'].$class).'">';
441
442 if (!empty($oneColumn['type'])) {
443 if ($oneColumn['type'] === 'date') {
444 if (empty($value)) {
445 echo '-';
446 } else {
447 if (!is_numeric($value) && $value != '0000-00-00 00:00:00') {
448 $value = strtotime($value);
449 }
450 $tooltip = acym_date($value, acym_translation('ACYM_DATE_FORMAT_LC2'));
451 acym_tooltip(
452 [
453 'hoveredText' => acym_date($value, acym_translation('ACYM_DATE_FORMAT_LC5')),
454 'textShownInTooltip' => $tooltip,
455 ]
456 );
457 }
458 } elseif ($oneColumn['type'] === 'int') {
459 echo intval($value);
460 } else {
461 echo esc_html($value);
462 }
463 } else {
464 echo esc_html($value);
465 }
466
467 echo '</div>';
468 }
469
470 echo '</div>';
471 }
472 }
473
474 protected function getCategoryListing(): string
475 {
476 $listing = '';
477 if (empty($this->catvalues)) {
478 $listing .= '<h1 class="cell acym__listing__empty__search__modal text-center">'.acym_translation('ACYM_NO_RESULTS_FOUND').'</h1>';
479
480 return $listing;
481 }
482
483 $listing .= '<div class="acym__popup__listing padding-0">';
484 $selected = [];
485 if (!empty($this->defaultValues->id) && strpos($this->defaultValues->id, '-')) {
486 $selected = explode('-', $this->defaultValues->id);
487 }
488 foreach ($this->catvalues as $oneCat) {
489 if (empty($oneCat->value)) continue;
490
491 $classes = 'cell grid-x acym__row__no-listing acym__listing__row__popup';
492 if (in_array($oneCat->value, $selected)) {
493 $classes .= ' selected_row';
494 }
495 $listing .= '<div class="'.$classes.'" data-id="'.intval($oneCat->value).'" onclick="applyContentauto'.esc_attr($this->name).'('.intval($oneCat->value).', this);">
496 <div class="cell medium-5">'.esc_html($oneCat->text).'</div>
497 </div>';
498 }
499 $listing .= '</div>';
500
501 return $listing;
502 }
503
504 protected function getTagListing(): string
505 {
506 $listing = '';
507 if (empty($this->tagvalues)) {
508 $listing .= '<h1 class="cell acym__listing__empty__search__modal text-center">'.acym_translation('ACYM_NO_RESULTS_FOUND').'</h1>';
509
510 return $listing;
511 }
512
513 $listing .= '<div class="acym__popup__listing padding-0">';
514 $selected = [];
515 if (!empty($this->defaultValues->id) && strpos($this->defaultValues->id, '-')) {
516 $selected = explode('-', $this->defaultValues->id);
517 }
518 $termIdName = 'term_id';
519 if ('wordpress' === ACYM_CMS) {
520 $termIdName = 'term_taxonomy_id';
521 }
522 foreach ($this->tagvalues as $oneTag) {
523 if (empty($oneTag->$termIdName)) continue;
524
525 $class = 'cell grid-x acym__row__no-listing acym__listing__row__popup';
526 if (in_array($oneTag->$termIdName, $selected)) {
527 $class .= ' selected_row';
528 }
529 $listing .= '<div class="'.esc_attr($class).'"
530 data-id="'.intval($oneTag->$termIdName).'"
531 onclick="applyContent'.esc_attr($this->name).'_tags('.intval($oneTag->$termIdName).', this);">
532 <div class="cell medium-5">'.esc_html($oneTag->name).'</div>
533 </div>';
534 }
535 $listing .= '</div>';
536
537 return $listing;
538 }
539
540 protected function replaceMultiple(object &$email)
541 {
542 $this->generateByCategory($email);
543 if (empty($this->tags)) return;
544
545 $this->pluginHelper->replaceTags($email, $this->tags, true);
546 }
547
548 protected function handleOrderBy(string &$query, object $parameter, ?string $table = null)
549 {
550 if (empty($parameter->order)) return;
551
552 $ordering = explode(',', $parameter->order);
553 if ($ordering[0] === 'rand') {
554 $query .= ' ORDER BY rand()';
555 } else {
556 $table = null === $table ? '' : $table.'.';
557 $column = $ordering[0];
558
559 if (strpos($column, '.') !== false) {
560 $parts = explode('.', $column, 2);
561 $table = acym_secureDBColumn($parts[0]).'.';
562 $column = $parts[1];
563 }
564
565 $query .= ' ORDER BY '.$table.'`'.acym_secureDBColumn(trim($column)).'` '.acym_secureDBColumn(trim($ordering[1]));
566 }
567 }
568
569 protected function handleMax(string &$query, object $parameter): void
570 {
571 if (empty($parameter->max)) {
572 $parameter->max = 20;
573 }
574 $query .= ' LIMIT '.intval($parameter->max);
575 }
576
577 protected function handleMin(array $elements, object $parameter): void
578 {
579 if (!empty($parameter->min) && count($elements) < $parameter->min) {
580 $this->generateCampaignResult->status = false;
581 $this->generateCampaignResult->message = acym_translationSprintf(
582 'ACYM_GENERATE_CAMPAIGN_NOT_ENOUGH_CONTENT',
583 $this->pluginDescription->name,
584 count($elements),
585 $parameter->min
586 );
587 }
588 }
589
590 protected function getLastGenerated(int $mailId): int
591 {
592 $campaignClass = new CampaignClass();
593
594 return $campaignClass->getLastGenerated($mailId);
595 }
596
597 /**
598 * Returns the individual elements tags based on a query result
599 */
600 protected function finalizeCategoryFormat(string $query, object $parameter, ?string $table = null): string
601 {
602 $this->handleOrderBy($query, $parameter, $table);
603 $this->handleMax($query, $parameter);
604
605 $elements = acym_loadResultArray($query);
606
607 return $this->formatIndividualTags($elements, $parameter);
608 }
609
610 protected function formatIndividualTags(array $elements, object $parameter): string
611 {
612 $this->handleMin($elements, $parameter);
613
614 if (empty($elements)) return '';
615
616 $elements = $this->groupByCategory($elements);
617
618 $customLayout = ACYM_CUSTOM_PLUGIN_LAYOUT.$this->name.'_auto.php';
619 if (file_exists($customLayout)) {
620 ob_start();
621 require $customLayout;
622
623 return ob_get_clean();
624 }
625
626 $arrayElements = $this->buildIndividualTags($elements, $parameter);
627
628 return $this->pluginHelper->getFormattedResult($arrayElements, $parameter);
629 }
630
631 protected function buildIndividualTags(array $elements, object $parameter): array
632 {
633 $arrayElements = [];
634 unset($parameter->id);
635
636 $i = 0;
637 foreach ($elements as $oneElementId) {
638 $args = [];
639 $args[] = $this->name.':'.$oneElementId;
640
641 foreach ($parameter as $oneParam => $val) {
642 if (is_bool($val)) {
643 $args[] = $oneParam;
644 } else {
645 $args[] = $oneParam.':'.$val;
646 }
647 }
648
649 if ($i % 2 === 1 && !empty($parameter->alternate)) {
650 $args[] = 'invert';
651 }
652
653 $arrayElements[] = '{'.implode('| ', $args).'}';
654
655 $i++;
656 }
657
658 return $arrayElements;
659 }
660
661 /**
662 * This method can be overridden in the add-on when a group by category option is added
663 */
664 protected function groupByCategory(array $elements)
665 {
666 return $elements;
667 }
668
669 protected function getSelectedArea(object $parameter): array
670 {
671 $allcats = explode('-', $parameter->id);
672 $selectedArea = [];
673 foreach ($allcats as $oneCat) {
674 if (empty($oneCat)) continue;
675 $selectedArea[] = intval($oneCat);
676 }
677
678 return $selectedArea;
679 }
680
681 protected function replaceOne(object &$email): void
682 {
683 $tags = $this->pluginHelper->extractTags($email, $this->name);
684 if (empty($tags)) return;
685
686 $newConfiguration = [
687 'dcontent_default_'.get_class($this) => json_encode(end($tags)),
688 ];
689 $this->config->saveConfig($newConfiguration);
690
691 if (method_exists($this, 'loadLibraries') && !$this->loadLibraries($email)) {
692 return;
693 }
694
695 $this->emailLanguage = $email->links_language;
696 $translationTool = $this->config->get('translate_content', 'no');
697
698 $tagsReplaced = [];
699 foreach ($tags as $i => $oneTag) {
700 if (isset($tagsReplaced[$i])) continue;
701
702 if (!empty($this->emailLanguage) && $translationTool !== 'no' && acym_isMultilingual()) {
703 $oneTag->id = $this->getTranslationId($oneTag->id, $translationTool);
704 }
705 $tagsReplaced[$i] = $this->replaceIndividualContent($oneTag);
706 }
707
708 $email->custom_view = file_exists(ACYM_CUSTOM_PLUGIN_LAYOUT.$this->name.'.html');
709
710 $this->pluginHelper->replaceTags($email, $tagsReplaced, true);
711 }
712
713 protected function initIndividualContent(object &$tag, string $query)
714 {
715 $element = acym_loadObject($query);
716
717 if (empty($element)) {
718 if (acym_isAdmin()) {
719 acym_enqueueMessage(acym_translationSprintf('ACYM_CONTENT_NOT_FOUND', $tag->id), 'notice');
720 }
721
722 return null;
723 }
724
725 if (empty($tag->display)) {
726 $tag->display = [];
727 } else {
728 $tag->display = explode(',', $tag->display);
729 }
730
731 return $element;
732 }
733
734 protected function getCustomLayoutVars(object $element): array
735 {
736 $varFields = [];
737 $varFields['{picthtml}'] = '';
738 foreach ($element as $fieldName => $oneField) {
739 if (is_object($oneField) || is_array($oneField)) {
740 $varFields['{'.$fieldName.'}'] = json_encode($oneField);
741 } else {
742 $varFields['{'.$fieldName.'}'] = $oneField;
743 }
744 }
745
746 return $varFields;
747 }
748
749 /**
750 * Handles the custom layouts and the pictures management
751 *
752 * @param string $htmlResult What will be inserted in the email
753 * @param object $insertionOptions Selected options when inserting dcontent
754 * @param array $dataShortcodes Data used as shortcodes in custom layouts
755 */
756 protected function finalizeElementFormat(string $htmlResult, object $insertionOptions, array $dataShortcodes): string
757 {
758 // Hidden feature, users can do a PHP custom view
759 $fullCustomLayoutPath = ACYM_CUSTOM_PLUGIN_LAYOUT.$this->name.'.php';
760 if ($this->config->get('php_overrides', 0) == 1 && file_exists($fullCustomLayoutPath)) {
761 ob_start();
762 require $fullCustomLayoutPath;
763 $viewContent = ob_get_clean();
764 $htmlResult = str_replace(array_keys($dataShortcodes), $dataShortcodes, $viewContent);
765 } else {
766 // Default custom view feature with only HTML for security reasons
767 $customLayoutPath = ACYM_CUSTOM_PLUGIN_LAYOUT.$this->name.'.html';
768 if (file_exists($customLayoutPath)) {
769 $dataShortcodes['{wrappedText}'] = $this->pluginHelper->wrappedText;
770 $viewContent = acym_fileGetContent($customLayoutPath);
771 $viewContentReplace = str_replace(array_keys($dataShortcodes), $dataShortcodes, $viewContent);
772 if ($viewContent !== $viewContentReplace) {
773 $htmlResult = $viewContentReplace;
774 }
775 }
776 }
777
778 $allShortcodesList = '<table><tr><th>Shortcode</th><th>Value</th></tr>';
779 foreach ($dataShortcodes as $name => $value) {
780 if (!is_string($value)) {
781 continue;
782 }
783
784 $allShortcodesList .= '<tr><td>'.trim($name, '{}').'</td><td>'.$value.'</td></tr>';
785 }
786 $allShortcodesList .= '</table>';
787 $htmlResult = str_replace('{allshortcodes}', $allShortcodesList, $htmlResult);
788
789 // Resize/remove pictures if needed
790 return $this->pluginHelper->managePicts($insertionOptions, $htmlResult);
791 }
792
793 protected function filtersFromConditions(array &$filters): void
794 {
795 $newFilters = [];
796
797 $this->onAcymDeclareConditions($newFilters);
798 foreach ($newFilters as $oneType) {
799 foreach ($oneType as $oneFilterName => $oneFilter) {
800 if (!empty($oneFilter->option)) $oneFilter->option = str_replace(['acym_condition', '[conditions]'], ['acym_action', '[filters]'], $oneFilter->option);
801 $filters[$oneFilterName] = $oneFilter;
802 }
803 }
804 }
805
806 protected function getElementTags(string $type, int $id): array
807 {
808 $tags = acym_loadObjectList(
809 'SELECT tags.`title`, tags.`alias`, tags.`id`
810 FROM #__tags AS tags
811 JOIN #__contentitem_tag_map AS map ON tags.`id` = map.`tag_id`
812 WHERE map.`type_alias` = '.acym_escapeDB($type).'
813 AND map.`content_item_id` = '.intval($id)
814 );
815
816 if (empty($tags)) return [];
817
818 $displayTags = [];
819 foreach ($tags as $oneTag) {
820 $displayTags[] = '<a href="index.php?option=com_tags&view=tag&id='.$oneTag->id.':'.$oneTag->alias.'" target="_blank">'.$oneTag->title.'</a>';
821 }
822
823 return $displayTags;
824 }
825
826 /**
827 * @return mixed
828 */
829 protected function getFormattedValue(array $fieldValues)
830 {
831 $field = $fieldValues[0];
832
833 if (!empty($field->fieldparams) && !is_array($field->fieldparams)) {
834 $field->fieldparams = json_decode($field->fieldparams, true);
835 }
836
837 switch ($field->type) {
838 case 'calendar':
839 $format = acym_translation($field->fieldparams['showtime'] == '1' ? 'ACYM_DATE_FORMAT_LC2' : 'ACYM_DATE_FORMAT_LC1');
840 $field->value = HTMLHelper::_('date', $field->value, $format);
841 break;
842
843 case 'checkboxes':
844 case 'list':
845 case 'radio':
846 $values = [];
847 foreach ($fieldValues as $oneValue) {
848 foreach ($field->fieldparams['options'] as $oneOPT) {
849 if ($oneOPT['value'] == $oneValue->value) {
850 $values[] = $oneOPT['name'];
851 break;
852 }
853 }
854 }
855
856 $field->value = implode(', ', $values);
857 break;
858
859 case 'dpcalendar':
860 if (empty($field->value)) {
861 $field->value = '';
862 break;
863 }
864
865 $title = acym_loadResult('SELECT `title` FROM #__categories WHERE `id` = '.intval($field->value));
866 if (empty($title)) {
867 $field->value = '';
868 } else {
869 $date = '#year='.acym_date('now', 'Y').'&month='.acym_date('now', 'm').'&day='.acym_date('now', 'd').'&view=month';
870 $field->value = '<a target="_blank" href="index.php?option=com_dpcalendar&view=calendar&id='.intval($field->value).$date.'">'.$title.'</a>';
871 }
872 break;
873
874 case 'imagelist':
875 if (!empty($field->fieldparams['directory']) && strlen($field->fieldparams['directory']) > 1) {
876 $field->value = '/'.$field->value;
877 } else {
878 $field->fieldparams['directory'] = '';
879 }
880 $field->value = '<img src="images/'.$field->fieldparams['directory'].$field->value.'" />';
881 break;
882
883 case 'media':
884 $value = '';
885 if (!empty($field->value)) {
886 if (substr($field->value, 0, 1) === '{') {
887 $value = json_decode($field->value, true);
888 if (!empty($value['imagefile'])) {
889 $alt = empty($value['alt_text']) ? '' : ' alt="'.esc_attr($value['alt_text']).'"';
890 $value = '<img src="'.$value['imagefile'].'"'.$alt.' />';
891 }
892 } else {
893 $value = '<img src="'.$field->value.'" alt="" />';
894 }
895 }
896
897 $field->value = $value;
898 break;
899
900 case 'repeatable':
901 if (!empty($field->value)) {
902 $values = is_string($field->value) ? json_decode($field->value) : $field->value;
903 $formattedValues = [];
904 foreach ($values as $oneSetOfValues) {
905 $formattedSet = [];
906 foreach ($oneSetOfValues as $oneLabel => $oneValue) {
907 if (empty($oneValue)) continue;
908
909 foreach ($field->fieldparams['fields'] as $oneField) {
910 if ($oneField['fieldname'] !== $oneLabel) continue;
911
912 if ($oneField['fieldtype'] === 'media') {
913 $oneValue = '<img src="'.$oneValue.'" alt="" />';
914 }
915
916 $formattedSet[] = $oneValue;
917 break;
918 }
919 }
920
921 if (empty($formattedSet)) continue;
922 $formattedValues[] = implode(', ', $formattedSet);
923 }
924
925 $field->value = empty($formattedSet) ? '' : '<ul><li>'.implode('</li><li>', $formattedValues).'</li></ul>';
926 }
927 break;
928
929 case 'sql':
930 if (empty($field->options)) {
931 $field->options = acym_loadObjectList($field->fieldparams['query'], 'value');
932 }
933
934 $field->value = $field->options[$field->value]->text;
935 break;
936
937 case 'url':
938 $field->value = '<a target="_blank" href="'.$field->value.'">'.$field->value.'</a>';
939 break;
940
941 case 'user':
942 $field->value = acym_currentUserName($field->value);
943 break;
944
945 case 'usergrouplist':
946 if (empty($field->usergroups)) {
947 $field->usergroups = acym_loadObjectList('SELECT id, title FROM #__usergroups', 'id');
948 }
949
950 if (empty($field->usergroups[$field->value])) {
951 $field->value = '';
952 break;
953 }
954 $field->value = $field->usergroups[$field->value]->title;
955 break;
956 case 'subform':
957 if (!empty($field->value)) {
958 $rows = is_string($field->value) ? json_decode($field->value, true) : $field->value;
959
960 $formattedValues = [];
961 foreach ($rows as $values) {
962 $fieldValues = [];
963 foreach ($values as $fieldIdentifier => $fieldValue) {
964 $fieldId = str_replace('field', '', $fieldIdentifier);
965 $oneFieldDefinition = acym_loadObject(
966 'SELECT `field`.*
967 FROM #__fields AS `field`
968 WHERE `field`.`id` = '.intval($fieldId)
969 );
970
971 if (empty($oneFieldDefinition)) {
972 break 3;
973 }
974
975 $multiCompatibleValues = [];
976 if (is_array($fieldValue)) {
977 foreach ($fieldValue as $oneFieldValue) {
978 $oneValue = clone $oneFieldDefinition;
979 $oneValue->value = $oneFieldValue;
980 $multiCompatibleValues[] = $oneValue;
981 }
982 } else {
983 $oneFieldDefinition->value = $fieldValue;
984 $multiCompatibleValues[] = $oneFieldDefinition;
985 }
986
987 $fieldValues[] = $oneFieldDefinition->label.': '.$this->getFormattedValue($multiCompatibleValues);
988 }
989 $formattedValues[] = implode(', ', $fieldValues);
990 }
991
992 $field->value = empty($formattedValues) ? '' : '<ul><li>'.implode('</li><li>', $formattedValues).'</li></ul>';
993 }
994 }
995
996 return $field->value;
997 }
998
999 protected function getLanguage(?string $elementLanguage = null, bool $onlyValue = false): string
1000 {
1001 $value = $this->emailLanguage;
1002 if (!empty($elementLanguage) && $elementLanguage !== '*') {
1003 $value = $elementLanguage;
1004 }
1005
1006 if (empty($value)) {
1007 return '';
1008 }
1009
1010 if (ACYM_CMS === 'joomla' && acym_isPluginActive('languagefilter')) {
1011 $languages = acym_loadObjectList('SELECT * FROM #__languages', 'lang_code');
1012 if (isset($languages[$value])) {
1013 $value = $languages[$value]->sef;
1014 } else {
1015 $value = substr($value, 0, strpos($value, '-'));
1016 }
1017
1018 if ($onlyValue) {
1019 return $value;
1020 }
1021
1022 return '&lang='.$value;
1023 } else {
1024 if ($onlyValue) {
1025 return $value;
1026 }
1027
1028 return '&language='.$value;
1029 }
1030 }
1031
1032 // Don't add types for $tag and $sef, for retro-compatibility
1033 protected function finalizeLink(string $link, $tag = null, $sef = true): string
1034 {
1035 if (acym_isPluginActive('languagefilter')) {
1036 if (strpos($link, 'lang=') === false && !empty($this->emailLanguage)) {
1037 $link .= strpos($link, '?') === false ? '?' : '&';
1038 $link .= 'lang='.substr($this->emailLanguage, 0, strpos($this->emailLanguage, '-'));
1039 }
1040 } else {
1041 if (strpos($link, 'language=') === false && !empty($this->emailLanguage)) {
1042 $link .= strpos($link, '?') === false ? '?' : '&';
1043 $link .= 'language='.$this->emailLanguage;
1044 }
1045 }
1046
1047 if (!empty($tag->autologin)) {
1048 $link .= (strpos($link, '?') ? '&' : '?').'autoSubId={subscriber:id}&subKey={subscriber:autologin_token|urlencode}';
1049 }
1050
1051 // Retro-compatibility for add-on versions before the v8.6.0
1052 if (is_bool($tag)) {
1053 $sef = $tag;
1054 }
1055
1056 return acym_frontendLink($link, false, $sef);
1057 }
1058
1059 protected function handleCustomFields(object $tag, array &$customFields): void
1060 {
1061 if (empty($tag->custom)) {
1062 return;
1063 }
1064
1065 $tag->custom = explode(',', $tag->custom);
1066 acym_arrayToInteger($tag->custom);
1067
1068 $rawfields = acym_loadObjectList(
1069 'SELECT `field`.*, `values`.`value`
1070 FROM #__fields_values AS `values`
1071 JOIN #__fields AS `field` ON `values`.`field_id` = `field`.`id`
1072 WHERE `values`.`item_id` = '.intval($tag->id).' AND `field`.`id` IN ('.implode(', ', $tag->custom).')'
1073 );
1074
1075 $fields = [];
1076 foreach ($rawfields as $field) {
1077 $fields[$field->id][] = $field;
1078 }
1079
1080 usort($fields, function ($a, $b) {
1081 return $a[0]->ordering <=> $b[0]->ordering;
1082 });
1083
1084 foreach ($fields as $fieldValues) {
1085 $value = $this->getFormattedValue($fieldValues);
1086
1087 if (empty($value)) continue;
1088
1089 $customFields[] = [
1090 $value,
1091 $fieldValues[0]->label,
1092 ];
1093 }
1094 }
1095
1096 protected function getIntro(string $text): string
1097 {
1098 $pageBreak = null;
1099 $possibleBreaks = [
1100 '<!--more-->',
1101 '<hr id="system-readmore" />',
1102 '<!--nextpage-->',
1103 ];
1104
1105 foreach ($possibleBreaks as $oneBreak) {
1106 if (strpos($text, $oneBreak) !== false) {
1107 $pageBreak = $oneBreak;
1108 break;
1109 }
1110 }
1111
1112 if (empty($pageBreak)) {
1113 return $text;
1114 }
1115
1116 $split = explode($pageBreak, $text, 2);
1117
1118 return array_shift($split);
1119 }
1120
1121 public function displayCustomViewEditor(): void
1122 {
1123 $plugin = new \stdClass();
1124 $plugin->folder_name = $this->name;
1125 $plugin->settings = $this->settings;
1126 $this->generateSettings($plugin);
1127
1128 if (empty($plugin->settings['custom_view'])) {
1129 return;
1130 }
1131
1132 echo '<p class="acym__wysid__right__toolbar__p acym__wysid__right__toolbar__p__open acym__title">';
1133 echo esc_html(acym_translation('ACYM_ADDON_SETTINGS'));
1134 echo '<i class="acymicon-keyboard-arrow-up"></i></p>';
1135 echo '<div class="acym__wysid__right__toolbar__design--show acym__wysid__right__toolbar__design acym__wysid__context__modal__container">';
1136 echo $plugin->settings['custom_view'];
1137 echo '</div>';
1138 }
1139
1140 public function generateSettings(object $plugin): bool
1141 {
1142 if (empty($plugin->settings)) {
1143 return false;
1144 }
1145
1146 if (array_key_exists('not_installed', $plugin->settings)) {
1147 $plugin->settings = 'not_installed';
1148
1149 return true;
1150 }
1151
1152 foreach ($plugin->settings as $key => $field) {
1153 $text = '';
1154 if (empty($field['type'])) {
1155 $plugin->settings[$key] = $text;
1156 continue;
1157 }
1158
1159 $id = $plugin->folder_name.'_'.$key;
1160 $name = $plugin->folder_name.'['.$key.']';
1161 if (!empty($field['label'])) {
1162 $field['label'] = acym_translation($field['label']);
1163 }
1164
1165 if (!empty($field['info'])) {
1166 ob_start();
1167 acym_info(
1168 [
1169 'textShownInTooltip' => acym_translation($field['info']),
1170 'classText' => 'wysid_tooltip',
1171 'isWarning' => !empty($field['info_warning']),
1172 ]
1173 );
1174 $field['label'] .= ob_get_clean();
1175 }
1176
1177 if ($field['type'] === 'checkbox') {
1178 $classLabel = 'shrink';
1179 $text .= '<label for="'.esc_attr($id).'" class="cell '.esc_attr($classLabel).'">'.esc_html($field['label']).'</label>';
1180 $text .= '<input id="'.esc_attr($id).'" class="cell shrink" type="checkbox" name="'.esc_attr($name).'" '.acym_checked(
1181 !empty($field['value']),
1182 true,
1183 false
1184 ).'>';
1185 } elseif ($field['type'] === 'switch') {
1186 ob_start();
1187 acym_switch([
1188 'name' => $name,
1189 'value' => $field['value'],
1190 'label' => $field['label'],
1191 'labelClass' => 'large-7',
1192 ]);
1193 $text .= ob_get_clean();
1194 } elseif ($field['type'] === 'select') {
1195 $text .= '<label class="cell shrink">'.$field['label'].'</label>';
1196 $text .= acym_select(
1197 $field['data'],
1198 $name,
1199 $field['value'],
1200 ['class' => 'acym__select'],
1201 'value',
1202 'text',
1203 false,
1204 true
1205 );
1206 } elseif ($field['type'] === 'multiple_select') {
1207 $text .= '<label class="cell shrink">'.$field['label'].'</label>';
1208 $text .= acym_selectMultiple(
1209 $field['data'],
1210 $name,
1211 empty($field['value']) ? [] : $field['value'],
1212 ['class' => 'acym__select']
1213 );
1214 } elseif ($field['type'] === 'text') {
1215 $text .= '<label class="cell shrink">'.$field['label'].'</label>';
1216 $text .= '<input type="text" name="'.$name.'" value="'.esc_attr($field['value']).'" class="cell shrink">';
1217 } elseif ($field['type'] === 'number') {
1218 $text .= '<label class="cell shrink">'.$field['label'].'</label>';
1219 $text .= '<input type="number" name="'.$name.'" value="'.esc_attr($field['value']).'" class="cell large-2 medium-5">';
1220 if (!empty($field['post_text'])) $text .= '<span class="cell shrink">'.strtolower($field['post_text']).'</span>';
1221 } elseif ($field['type'] === 'radio') {
1222 $text .= '<p class="cell">'.$field['label'].'</p>';
1223 ob_start();
1224 acym_radio(
1225 $field['data'],
1226 $name,
1227 $field['value']
1228 );
1229 $text .= ob_get_clean();
1230 } elseif ($field['type'] === 'date') {
1231 $text .= '<label class="cell shrink">'.$field['label'].'</label>';
1232 $text .= acym_dateField(
1233 $name,
1234 $field['value'],
1235 'cell shrink'
1236 );
1237 } elseif ($field['type'] === 'custom_view' && acym_isAdmin()) {
1238 $idCustomView = 'acym__plugins__installed__custom-view__'.$this->name;
1239 $ctrl = acym_getVar('string', 'ctrl', '');
1240 $classTooltip = $ctrl === 'dynamics' ? '' : 'wysid_tooltip';
1241 $text .= '<label class="cell">';
1242 $text .= acym_translation('ACYM_CUSTOM_VIEW');
1243 ob_start();
1244 acym_info(
1245 [
1246 'textShownInTooltip' => 'ACYM_CUSTOM_VIEW_DESC',
1247 'classText' => $classTooltip,
1248 ]
1249 );
1250 $text .= ob_get_clean();
1251 $text .= '</label>';
1252
1253 if (empty($field['tags'])) {
1254 $field['tags'] = [];
1255 }
1256
1257 ob_start();
1258 acym_info(
1259 [
1260 'textShownInTooltip' => acym_translation('ACYM_DYNAMIC_CONTENT_DESC'),
1261 ]
1262 );
1263 $tooltip = ob_get_clean();
1264
1265 $modalContent = '<div id="'.esc_attr($idCustomView).'" class="cell grid-x acym__plugins__installed__custom-view" acym-data-tags="'.esc_attr(
1266 json_encode($field['tags'])
1267 ).'">
1268 <h2 class="cell text-center acym__title__primary__color">'.acym_translationSprintf('ACYM_CUSTOM_VIEW_FOR_X', $this->pluginDescription->name).'</h2>
1269 <div class="cell grid-x acym__plugins__installed__custom-view__edit-container">
1270 <div class="acym__plugins__installed__custom-view__editor-loader grid-x cell align-center acym_vcenter" v-if="loading">
1271 <div class="cell shrink acym_loader_logo">
1272 '.acym_fileGetContent(ACYM_IMAGES.'logos/logo_grey.svg').'
1273 </div>
1274 </div>
1275 <vue-prism-editor :emitEvents="true" class="cell acym__plugins__installed__custom-view__code cell auto" v-model="code" :language="language" lineNumbers="true"></vue-prism-editor>
1276 <div class="cell grid-x medium-3 margin-left-1 acym__plugins__installed__custom-view__tags">
1277 <h3 class="acym__title acym__title__secondary cell text-center">'.esc_html(acym_translation('ACYM_DYNAMIC_CONTENT')).$tooltip.'</h3>
1278 <div class="cell acym__plugins__installed__custom-view__tag" v-for="(trad, tag) in tags" :key="tag" @click.prevent="insertTag(tag)">{{ trad }}</div>
1279 </div>
1280 </div>
1281 <div class="cell grid-x acym__plugins__installed__custom-view__actions acym_vcenter margin-top-1 padding-bottom-1">
1282 <div class="cell auto grid-x">
1283 <button type="button" class="cell shrink button-secondary button" @click="resetView">'.esc_html(acym_translation('ACYM_RESET_VIEW')).'</button>
1284 <div class="cell shrink margin-right-2 acym_vcenter margin-left-1">
1285 <i v-if="deleting" class="acymicon-spin acymicon-circle-o-notch" style="margin-bottom: 0; line-height: 26px;"></i>
1286 <span v-if="deleted">{{ messageDeleted }}</span>
1287 </div>
1288 </div>
1289 <div class="cell auto align-right grid-x">
1290 <div class="cell shrink margin-right-2 acym_vcenter">
1291 <i v-if="saving" class="acymicon-spin acymicon-circle-o-notch" style="margin-bottom: 0; line-height: 26px;"></i>
1292 <span v-if="saved">{{ messageSaved }}</span>
1293 </div>
1294 <button @click="save()" class="cell shrink button" type="button">'.esc_html(acym_translation('ACYM_SAVE_NEW_CUSTOM_VIEW_VERSION')).'</button>
1295 </div>
1296 </div>
1297 </div>';
1298 ob_start();
1299 acym_modal(
1300 acym_translation('ACYM_EDIT_CUSTOM_VIEW'),
1301 $modalContent,
1302 null,
1303 [
1304 'acym-data-plugins-id' => $idCustomView,
1305 'acym-data-plugin-class' => get_class($this),
1306 'acym-data-plugin-folder' => $this->name,
1307 ],
1308 ['class' => 'cell button']
1309 );
1310 $text .= ob_get_clean();
1311 } elseif ($field['type'] == 'multikeyvalue') {
1312 $text .= '<label class="cell shrink">'.$field['label'].'</label>';
1313
1314 $text .= '<div class="multikeyvalue_container grid-x">';
1315
1316 if (!empty($field['value'])) {
1317 $headers = json_decode($field['value'], true);
1318 foreach ($headers as $headerKey => $headerValue) {
1319 $text .= '<input type="text" class="cell" placeholder="'.acym_translation('ACYM_DKIM_KEY', true).'" value="'.esc_attr($headerKey).'"/>';
1320 $text .= '<input type="text" class="cell" placeholder="'.acym_translation('ACYM_VALUE', true).'" value="'.esc_attr($headerValue).'" />';
1321 $text .= '<div class="multikeyvalue_container_separator cell small-6"></div>';
1322 }
1323 }
1324
1325 $text .= '<input type="text" class="cell" placeholder="'.acym_translation('ACYM_DKIM_KEY', true).'" value=""/>';
1326 $text .= '<input type="text" class="cell" placeholder="'.acym_translation('ACYM_VALUE', true).'" value="" />';
1327
1328 $text .= '<button class="button multikeyvalue_container_new">'.acym_translation('ACYM_ADD_NEW').'</button>';
1329
1330 $text .= '<input type="hidden" name="'.$name.'" value="" />';
1331 $text .= '</div>';
1332 } elseif ($field['type'] == 'custom') {
1333 $text .= $field['content'];
1334 }
1335 $plugin->settings[$key] = $text;
1336 }
1337
1338 return true;
1339 }
1340
1341 /**
1342 * Called using ajax
1343 *
1344 * @param string $css This attribute is the name of the file in the folder css of the plugin OR it can be raw CSS
1345 */
1346 public function loadCSS(string $css, bool $raw = false, ?string $path = null): void
1347 {
1348 if (!$raw) {
1349 if (empty($path)) $path = ACYM_DYNAMICS_URL.$this->name;
1350 $css = $path.'/css/'.$css.'.css';
1351 }
1352 acym_addStyle($raw, $css);
1353 }
1354
1355 /**
1356 * @param string $js This attribute is the name of the file in the folder js of the plugin OR it can be raw Javascript
1357 */
1358 public function loadJavascript(string $js, bool $raw = false, ?string $path = null): void
1359 {
1360 if (!$raw) {
1361 if (empty($path)) $path = ACYM_DYNAMICS_URL.$this->name;
1362 $js = $path.'/js/'.$js.'.js';
1363 }
1364 acym_addScript($raw, $js);
1365 }
1366
1367 public function includeView(string $view, array $data = [], ?string $path = null): string
1368 {
1369 if (empty($path)) {
1370 $path = ACYM_ADDONS_FOLDER_PATH.$this->name.DS.'views'.DS.$view.'.php';
1371 } else {
1372 $path = $path.DS.'views'.DS.$view.'.php';
1373 }
1374
1375 if (!file_exists($path)) {
1376 throw new \Exception(esc_attr(acym_translation('ACYM_NON_EXISTING_PAGE')));
1377 }
1378
1379 ob_start();
1380 include $path;
1381
1382 return ob_get_clean();
1383 }
1384
1385 /**
1386 * This function will parse and show all settings
1387 */
1388 public function onAcymAddSettings(array &$plugins): void
1389 {
1390 if (method_exists($this, 'initSettings')) {
1391 $this->initSettings();
1392 }
1393
1394 foreach ($plugins as $key => $plugin) {
1395 if ($plugin->folder_name === $this->name) {
1396 if (!empty($plugin->settings)) {
1397 foreach ($plugin->settings as $keySettings => $value) {
1398 if (isset($this->settings[$keySettings]['value'])) {
1399 $this->settings[$keySettings]['value'] = $plugin->settings[$keySettings]['value'];
1400 }
1401 }
1402 }
1403 $plugins[$key]->settings = $this->settings;
1404
1405 $this->generateSettings($plugins[$key]);
1406 break;
1407 }
1408 }
1409 }
1410
1411 /**
1412 * @param mixed $default
1413 *
1414 * @return mixed
1415 */
1416 protected function getParam(string $name, $default = '')
1417 {
1418 return $this->savedSettings[$name]['value'] ?? $default;
1419 }
1420
1421 protected function getTranslationId(int $elementId, string $translationTool, bool $defaultLanguage = false)
1422 {
1423 return $elementId;
1424 }
1425
1426 public function filterSpecialMailsDailySend(array &$specialMails, int $time, string $mailType): void
1427 {
1428 // Only once a day
1429 $dailyHour = $this->config->get('daily_hour', '12');
1430 $dailyMinute = $this->config->get('daily_minute', '00');
1431 // The day it is currently based on the timezone specified in the CMS configuration
1432 $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
1433 // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
1434 $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$dailyHour.':'.$dailyMinute);
1435
1436 $campaignClass = new CampaignClass();
1437
1438 $tmpMails = [];
1439 foreach ($specialMails as $oneMail) {
1440 if ($oneMail->sending_type === $mailType) {
1441 $noNextTrigger = empty($oneMail->next_trigger);
1442 $nextTriggerIsNow = gmdate('m-d', $oneMail->next_trigger) == gmdate('m-d', $dayBasedOnCMSTimezoneAtSpecifiedHour);
1443 $nextTriggerIsInPast = $oneMail->next_trigger < $time;
1444 if ($time >= $dayBasedOnCMSTimezoneAtSpecifiedHour && ($noNextTrigger || $nextTriggerIsNow || $nextTriggerIsInPast)) {
1445 $oneMail->next_trigger = acym_getTime('tomorrow '.$dailyHour.':'.$dailyMinute);
1446 $oneMail->last_generated = $time;
1447 $campaignClass->save($oneMail);
1448 $tmpMails[] = $oneMail;
1449 }
1450 } else {
1451 $tmpMails[] = $oneMail;
1452 }
1453 }
1454 $specialMails = $tmpMails;
1455 }
1456
1457 protected function getIdsSelectAjax(): array
1458 {
1459 $ids = acym_getVar('string', 'id');
1460 if (empty($ids)) {
1461 return [];
1462 }
1463
1464 $ids = explode(',', $ids);
1465 acym_arrayToInteger($ids);
1466
1467 return $ids;
1468 }
1469
1470 protected function cleanExtensionContent(string $text)
1471 {
1472 if (ACYM_CMS === 'wordpress') {
1473 if (!acym_isExtensionActive('classic-editor/classic-editor.php') || strpos($text, '<!-- wp:') !== false) {
1474 return $text;
1475 }
1476
1477 return nl2br($text);
1478 } else {
1479 return preg_replace('#\{igallery[^}]+\}#Ui', '', $text);
1480 }
1481 }
1482
1483 protected function callApiSendingMethod(
1484 string $url,
1485 array $data = [],
1486 array $headers = [],
1487 string $type = 'GET',
1488 array $authentication = [],
1489 bool $dataDecoded = false,
1490 array $files = []
1491 ): array {
1492 $assocHeaders = [];
1493 foreach ($headers as $key => $value) {
1494 if (is_int($key)) {
1495 $parts = explode(':', $value, 2);
1496 if (count($parts) === 2) {
1497 $assocHeaders[trim($parts[0])] = trim($parts[1]);
1498 }
1499 } else {
1500 $assocHeaders[$key] = $value;
1501 }
1502 }
1503
1504 if (!empty($authentication)) {
1505 $assocHeaders['Authorization'] = 'Basic '.base64_encode($authentication['name'].':'.$authentication['pwd']);
1506 }
1507
1508 $result = acym_makeCurlCall(
1509 $url,
1510 [
1511 'method' => strtoupper($type),
1512 'headers' => $assocHeaders,
1513 'data' => $data,
1514 'files' => $files,
1515 // Some APIs (Mailgun) expect the data as a raw multipart body, not encoded
1516 'multipart' => $dataDecoded === true,
1517 'verifySsl' => false,
1518 ]
1519 );
1520
1521 $this->responseCode = (int)($result['status_code'] ?? 0);
1522 unset($result['status_code']);
1523
1524 if (isset($result['error'])) {
1525 return ['error_curl' => $result['error']];
1526 }
1527
1528 return $result;
1529 }
1530
1531 protected function getTestCredentialsSendingMethodButton(string $sendingMethodId): void
1532 {
1533 echo '<div class="cell grid-x margin-top-1 acym__sending__methods__credentials__test">
1534 <button type="button" sending-method-id="'.esc_attr($sendingMethodId).'" class="acym__configuration__sending__method-test cell shrink button button-secondary">
1535 '.esc_html(acym_translation('ACYM_TEST_CREDENTIALS')).'
1536 </button>
1537 <span class="acym__configuration__sending__method-icon cell shrink margin-left-1 acym_vcenter"></span>
1538 <span class="acym__configuration__sending__method-test__message cell shrink margin-left-1 acym_vcenter"></span>
1539 </div>';
1540 }
1541
1542 public function getCopySettingsButton(array $data, string $sendingMethodId, string $fromPlugin, bool $withContainer = true): void
1543 {
1544 if (empty($data[$fromPlugin.'_installed'])) {
1545 return;
1546 }
1547
1548 if ($withContainer) {
1549 echo '<div class="cell grid-x margin-top-1">';
1550 }
1551
1552 echo '<button
1553 type="button"
1554 class="cell shrink button button-secondary acym__configuration__copy__mail__settings"
1555 acym-data-plugin="'.esc_attr($fromPlugin).'"
1556 acym-data-method="'.esc_attr($sendingMethodId).'">
1557 '.esc_html(acym_translationSprintf('ACYM_COPY_SETTINGS_FROM', $this->sendingPlugins[$fromPlugin])).'
1558 </button>
1559 <span class="acym__configuration__sending__method-icon cell shrink margin-left-1 acym_vcenter"></span>';
1560
1561 if ($withContainer) {
1562 echo '</div>';
1563 }
1564 }
1565
1566 protected function getLinks(string $account = '', string $pricing = ''): void
1567 {
1568 if (empty($account) && empty($pricing)) {
1569 return;
1570 }
1571
1572 echo '<div class="cell grid-x acym-grid-margin-x shrink acym_vcenter"><p class="cell shrink">'.esc_html(acym_translation('ACYM_DONT_HAVE_ACCOUNT')).'</p>';
1573 if (!empty($account)) {
1574 echo '<a target="_blank" class="cell shrink" href="'.esc_url($account).'">'.esc_html(acym_translation('ACYM_CREATE_ONE')).'</a>';
1575 }
1576 if (!empty($account) && !empty($pricing)) {
1577 echo '<p class="cell shrink">'.esc_html(strtolower(acym_translation('ACYM_OR'))).'</p>';
1578 }
1579 if (!empty($pricing)) {
1580 echo '<a target="_blank" class="cell shrink" href="'.esc_url($pricing).'">'.esc_html(acym_translation('ACYM_CHECK_THEIR_PRICING')).'</a>';
1581 }
1582 echo '</div>';
1583 }
1584
1585 public function onAcymGetSendingMethodsSelected(array &$data): void
1586 {
1587 if (ACYM_CMS === 'wordpress') {
1588 $this->config->load();
1589 }
1590
1591 $mailerMethod = $this->config->get('mailer_method', 'phpmail');
1592 foreach ($data['sendingMethods'] as $key => $sendingMethod) {
1593 $data['sendingMethods'][$key]['selected'] = $key == $mailerMethod;
1594 }
1595 }
1596
1597 public function errorCallback(): void
1598 {
1599 foreach ($this->errors as $error) {
1600 acym_logError($error, empty($this->name) ? get_class($this) : $this->name);
1601 }
1602
1603 $this->errors = [];
1604 }
1605
1606 public function isLogFileEmpty(): bool
1607 {
1608 return !acym_isLogFileErrorExist(empty($this->name) ? get_class($this) : $this->name);
1609 }
1610
1611 public function initCustomView(bool $customFields = false): void
1612 {
1613 $page = acym_getVar('cmd', 'page');
1614 if (is_array($page)) return;
1615 if (!empty($page) && is_string($page)) {
1616 $page = str_replace(ACYM_COMPONENT.'_', '', $page);
1617 }
1618 $ctrl = acym_getVar('cmd', 'ctrl', $page);
1619 if (!in_array($ctrl, ['plugins', 'dynamics'])) return;
1620
1621 $task = acym_getVar('cmd', 'task', 'installed');
1622
1623 // Installed add-ons page or installed + editor
1624 if (($ctrl === 'plugins' && $task === 'installed') || ($this->active && $ctrl === 'dynamics' && $task === 'trigger')) {
1625 $this->initElementOptionsCustomView();
1626 $this->initReplaceOptionsCustomView();
1627 if ($customFields) $this->initCustomOptionsCustomView();
1628 }
1629 }
1630
1631 public function processDateToCheck(array $options): \DateTime
1632 {
1633 $dateNowWithTimeZone = acym_date('now', 'Y-m-d h:i:s');
1634 $dateToCheck = new \DateTime($dateNowWithTimeZone);
1635 $interval = new \DateInterval('P'.intval($options['days']).'D');
1636 if ($options['relative'] === 'before') {
1637 $dateToCheck->add($interval);
1638 } else {
1639 $dateToCheck->sub($interval);
1640 }
1641
1642 return $dateToCheck;
1643 }
1644
1645 public function onAcymCheckInstalled(bool &$installed): void
1646 {
1647 $installed = $this->installed;
1648 }
1649
1650 protected function getLinkTranslated(string $link): string
1651 {
1652 if (empty($this->emailLanguage)) return $link;
1653 if (!acym_isMultilingual()) return $link;
1654
1655 $translationTool = $this->config->get('translate_content', 'no');
1656 if ($translationTool !== 'wpml') return $link;
1657
1658 if (!acym_isExtensionActive('sitepress-multilingual-cms/sitepress.php')) return $link;
1659
1660 $languageCode = substr($this->emailLanguage, 0, 2);
1661
1662 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML hook for integration.
1663 return apply_filters('wpml_permalink', $link, $languageCode);
1664 }
1665
1666 protected function replaceShortcode(string $content): string
1667 {
1668 $content = do_shortcode($content);
1669
1670 return preg_replace('#<!-- wp:shortcode -->(.*)<!-- /wp:shortcode -->#Uis', '$1', $content);
1671 }
1672
1673 protected function fixDivStructure(string $text): string
1674 {
1675 $nbOpeningDivs = substr_count($text, '<div');
1676 $nbClosingDivs = substr_count($text, '</div>');
1677
1678 if ($nbOpeningDivs > $nbClosingDivs) {
1679 $text .= str_repeat('</div>', $nbOpeningDivs - $nbClosingDivs);
1680 } elseif ($nbOpeningDivs < $nbClosingDivs) {
1681 $text = str_repeat('<div>', $nbClosingDivs - $nbOpeningDivs).$text;
1682 }
1683
1684 return $text;
1685 }
1686
1687 protected function getBounceAddress(object $mailerHelper): string
1688 {
1689 if (method_exists($mailerHelper, 'getSendSettings')) {
1690 return $mailerHelper->getSendSettings('bounce_email');
1691 } else {
1692 return $this->config->get('bounce_email');
1693 }
1694 }
1695 }
1696