PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.5
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.5
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 / dynamics / managetext / ManagetextInsertion.php

ManagetextInsertion.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.5, at back/dynamics/managetext/ManagetextInsertion.php

449 lines 17.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use AcyMailing\Classes\UserClass;
4
5 trait ManagetextInsertion
6 {
7 private array $noIfStatementTags = [];
8
9 public function replaceContent(object &$email): void
10 {
11 $this->replaceRandom($email);
12 $this->handleAnchors($email);
13 $this->fixPicturesOutlook($email);
14 }
15
16 public function replaceUserInformation(object &$email, ?object &$user, bool $send = true): void
17 {
18 $this->pluginHelper->cleanHtml($email->body);
19 $this->pluginHelper->replaceVideos($email->body);
20
21 $this->removeText($email);
22 $this->ifStatement($email, $user);
23 $this->replaceTranslations($email, $user);
24 $this->replaceConfigValues($email);
25 }
26
27 /**
28 * Replaces tags such as {trans:ACYM_MY_TRANSLATION}
29 */
30 private function replaceTranslations(&$email, $user)
31 {
32 //load the tags
33 $tags = $this->pluginHelper->extractTags($email, 'trans');
34 if (empty($tags)) {
35 return;
36 }
37
38 $tagsReplaced = [];
39 foreach ($tags as $i => $oneTag) {
40 $val = '';
41 // Complex tag (contains "|param1|param2|...") for dynamic text with values to replace
42 $arrayVal = [];
43 foreach ($oneTag as $valname => $oneValue) {
44 if ($valname == 'id') {
45 $val = trim(acym_stripTags($oneValue));
46 } elseif ($valname != 'default') {
47 // Recreate tags (not before to prevent premature end of tag when parsing)
48 $arrayVal[] = '{'.$valname.'}';
49 }
50 }
51
52 if (empty($val)) {
53 continue;
54 }
55 if (!empty($user->language)) {
56 $language = $user->language;
57 } elseif (!empty($email->language)) {
58 $language = $email->language;
59 } else {
60 $language = acym_getLanguageTag();
61 }
62
63 $previousLanguage = acym_setLanguage($language);
64 acym_loadLanguage($language);
65
66 static $done = false;
67 if (!$done && strpos($val, 'COM_USERS') !== false) {
68 $done = true;
69 //Com_users? Let's load the com_users language file then!
70
71 acym_loadLanguageFile('com_users');
72 }
73 if (!empty($arrayVal)) {
74 $translation = acym_translation($val);
75 $paramsIncluded = vsprintf($translation, $arrayVal);
76 if ($translation === $paramsIncluded) {
77 $translation = preg_replace(
78 '/\{[A-Z_]+\}/',
79 '%s',
80 $translation
81 );
82 $paramsIncluded = vsprintf($translation, $arrayVal);
83 }
84 $tagsReplaced[$i] = nl2br($paramsIncluded);
85 } else {
86 $tagsReplaced[$i] = acym_translation($val);
87 }
88
89 if (empty($previousLanguage)) $previousLanguage = acym_getLanguageTag();
90 acym_setLanguage($previousLanguage);
91 acym_loadLanguage($previousLanguage);
92 }
93
94 //We replace standard tags
95 $this->pluginHelper->replaceTags($email, $tagsReplaced, true);
96 }
97
98 private function replaceConfigValues(&$email)
99 {
100 $tags = $this->pluginHelper->extractTags($email, 'config');
101 if (empty($tags)) {
102 return;
103 }
104
105 $tagsReplaced = [];
106 foreach ($tags as $i => $oneTag) {
107 if (trim($oneTag->id) === 'sitename') {
108 // We do not allow all config values for now only sitename.
109 $tagsReplaced[$i] = acym_getCMSConfig('sitename');
110 }
111 }
112
113 $this->pluginHelper->replaceTags($email, $tagsReplaced, true);
114 }
115
116 private function replaceRandom(&$email)
117 {
118 $randTag = $this->pluginHelper->extractTags($email, 'rand');
119 if (empty($randTag)) {
120 return;
121 }
122 foreach ($randTag as $oneRandTag) {
123 $results[$oneRandTag->id] = explode(';', $oneRandTag->id);
124 $randNumber = acym_rand(0, count($results[$oneRandTag->id]) - 1);
125 $results[$oneRandTag->id][count($results[$oneRandTag->id])] = $results[$oneRandTag->id][$randNumber];
126 }
127
128 $tags = [];
129 foreach (array_keys($results) as $oneResult) {
130 $tags['{rand:'.$oneResult.'}'] = end($results[$oneResult]);
131 }
132
133 if (empty($tags)) {
134 return;
135 }
136 $this->pluginHelper->replaceTags($email, $tags, true);
137 }
138
139 private function ifStatement(&$email, $user, $loop = 1)
140 {
141 if (empty($email->id) || isset($this->noIfStatementTags[$email->id])) {
142 return;
143 }
144
145 $isAdmin = acym_isAdmin();
146
147 if ($loop > 3) {
148 if ($isAdmin) {
149 acym_display('You cannot have more than 3 nested {if} tags.', 'warning');
150 }
151
152 return;
153 }
154
155 $match = '#{if:(((?!{if).)*)}(((?!{if).)*){/if}#Uis';
156 $variables = ['subject', 'body', 'altbody', 'From', 'FromName', 'ReplyTo'];
157 $found = false;
158 $results = [];
159 foreach ($variables as $var) {
160 if (empty($email->$var)) continue;
161
162 if (is_array($email->$var)) {
163 foreach ($email->$var as $i => &$arrayField) {
164 if (empty($arrayField) || !is_array($arrayField)) continue;
165
166 foreach ($arrayField as $key => &$oneval) {
167 $found = preg_match_all($match, $oneval, $results[$var.$i.'-'.$key]) || $found;
168 if (empty($results[$var.$i.'-'.$key][0])) unset($results[$var.$i.'-'.$key]);
169 }
170 }
171 } else {
172 $found = preg_match_all($match, $email->$var, $results[$var]) || $found;
173 //we unset the results so that we won't handle it later... it will save some memory and processing
174 if (empty($results[$var][0])) unset($results[$var]);
175 }
176 }
177
178 //If we didn't find anything...
179 if (!$found) {
180 if ($loop == 1) {
181 $this->noIfStatementTags[$email->id] = true;
182 }
183
184 return;
185 }
186
187 // Handles error messages
188 static $alreadyHandled = false;
189
190 $userClass = new UserClass();
191 if (!empty($user->id)) {
192 $acymUser = $userClass->getOneByIdWithCustomFields($user->id);
193 }
194 $tags = [];
195 foreach ($results as $allresults) {
196 foreach ($allresults[0] as $i => $oneTag) {
197 if (empty($user->id)) {
198 $tags[$oneTag] = '';
199 continue;
200 }
201
202 //Don't need to process twice a tag we already have!
203 if (isset($tags[$oneTag])) {
204 continue;
205 }
206 //We explode each argument of the tag
207 $allresults[1][$i] = html_entity_decode($allresults[1][$i]);
208 if (
209 !preg_match('#^(.+)(!=|<|>|&gt;|&lt;|!~)([^=!<>~]+)$#is', $allresults[1][$i], $operators)
210 && !preg_match('#^(.+)(=|~)([^=!<>~]+)$#is', $allresults[1][$i], $operators)
211 ) {
212 if ($isAdmin) {
213 acym_enqueueMessage(acym_translationSprintf('ACYM_OPERATION_NOT_FOUND', $allresults[1][$i]), 'error');
214 }
215 $tags[$oneTag] = $allresults[3][$i];
216 continue;
217 }
218 $field = trim($operators[1]);
219 $prop = '';
220
221 $operatorsParts = explode('.', $operators[1]);
222 $type = 'acym';
223 if (count($operatorsParts) > 1 && in_array($operatorsParts[0], ['acym', 'joomla', 'var'])) {
224 $type = $operatorsParts[0];
225 unset($operatorsParts[0]);
226 $field = implode('.', $operatorsParts);
227 }
228
229 // Handle Joomla fields first
230 if ($type === 'joomla') {
231 if (!empty($user->userid)) {
232 if ($field === 'gid') {
233 //We have an userid... lets load its groups then!
234 $prop = implode(';', acym_loadResultArray('SELECT group_id FROM #__user_usergroup_map WHERE user_id = '.intval($user->userid)));
235 } else {
236 //We need to load the value for that field...
237 $juser = acym_loadObject('SELECT * FROM #__users WHERE id = '.intval($user->userid));
238 if (isset($juser->{$field})) {
239 $prop = strtolower($juser->{$field});
240 } else {
241 if ($isAdmin && !$alreadyHandled) {
242 acym_enqueueMessage('User variable not set: '.$field.' in '.$allresults[1][$i], 'error');
243 }
244 $alreadyHandled = true;
245 }
246 }
247 }
248 } elseif ($type === 'var') {
249 // Static var to have a tag inside an if condition, for example {if:var.{cbtag:name}=adrien}...{/if}
250 $prop = strtolower($field);
251 } else {
252 // AcyMailing fields
253 if (!isset($acymUser[$field])) {
254 // We make sure to display it only once
255 if ($isAdmin && !$alreadyHandled) {
256 acym_enqueueMessage('User variable not set: '.$field.' in '.$allresults[1][$i], 'error');
257 }
258 $alreadyHandled = true;
259 } else {
260 $prop = strtolower($acymUser[$field]);
261 }
262 }
263
264 $tags[$oneTag] = '';
265 $val = strtolower(trim($operators[3]));
266 $prop = acym_stripTags($prop);
267 //We can handle several propositions in one if it contains ; such as {if:category=34;214;53}...
268 if ($operators[2] === '=' && ($prop == $val || in_array($prop, explode(';', $val)) || in_array($val, explode(';', $prop)))) {
269 $tags[$oneTag] = $allresults[3][$i];
270 } elseif ($operators[2] === '!=' && $prop != $val) {
271 $tags[$oneTag] = $allresults[3][$i];
272 } elseif (($operators[2] === '>' || $operators[2] === '&gt;') && $prop > $val) {
273 $tags[$oneTag] = $allresults[3][$i];
274 } elseif (($operators[2] === '<' || $operators[2] === '&lt;') && $prop < $val) {
275 $tags[$oneTag] = $allresults[3][$i];
276 } elseif ($operators[2] === '~' && strpos($prop, $val) !== false) {
277 $tags[$oneTag] = $allresults[3][$i];
278 } elseif ($operators[2] === '!~' && strpos($prop, $val) === false) {
279 $tags[$oneTag] = $allresults[3][$i];
280 }
281 }
282 }
283
284 $this->pluginHelper->replaceTags($email, $tags, true);
285
286 $this->ifStatement($email, $user, $loop + 1);
287 }
288
289 private function removeText(&$email)
290 {
291 //Remove text
292 $removeText = '{reg},{/reg},{pub},{/pub}';
293 if (!empty($removeText)) {
294 $removeArray = explode(',', trim($removeText, ' ,'));
295 if (!empty($email->body)) {
296 $email->body = str_replace($removeArray, '', $email->body);
297 }
298 }
299
300
301 $removetags = 'youtube';
302 if (!empty($removetags)) {
303 $regex = [];
304 $removeArray = explode(',', trim($removetags, ' ,'));
305 foreach ($removeArray as $oneTag) {
306 if (empty($oneTag)) {
307 continue;
308 }
309 $regex[] = '#(?:{|%7B)'.preg_quote($oneTag, '#').'(?:}|%7D).*(?:{|%7B)/'.preg_quote($oneTag, '#').'(?:}|%7D)#Uis';
310 $regex[] = '#(?:{|%7B)'.preg_quote($oneTag, '#').'[^}]*(?:}|%7D)#Uis';
311 }
312
313 if (!empty($email->body)) {
314 $email->body = preg_replace($regex, '', $email->body);
315 }
316 }
317 }
318
319 private function handleAnchors(&$email)
320 {
321 if (empty($email->body)) return;
322
323 $newBody = preg_replace('/(<a +href="#[^"]*"[^>]*) target="_blank"([^>]*>)/Uis', '$1 $2', $email->body);
324
325 if (!empty($newBody)) $email->body = $newBody;
326 }
327
328 /**
329 * Make sure each image has a width/height
330 * This function can be used by plugins as well
331 */
332 public function fixPicturesOutlook(&$email)
333 {
334 $this->addPictureDimensions($email->body);
335 $this->addPictureAlign($email->body);
336 }
337
338 public function addPictureDimensions(&$html)
339 {
340 if (!preg_match_all('#(<img)([^>]*>)#i', $html, $results)) {
341 return;
342 }
343
344 $replace = [];
345 $widthheight = ['width', 'height'];
346 foreach ($results[0] as $num => $oneResult) {
347 $add = [];
348 foreach ($widthheight as $whword) {
349 //We only add it if the picture does not already have the height/width and only if the height is specified as px
350 //About the second regex, we make sure to ignore all line-height or other similar CSS styles by making sure it does not start with a character or an underscore or an hyphen.
351 if (preg_match('#'.$whword.' *=#i', $oneResult) || !preg_match('#[^a-z_\-]'.$whword.' *:([0-9 ]{1,8})px#i', $oneResult, $resultWH)) continue;
352
353 //We don't have the width= ... but we have one width:...
354 //We make sure it's not an empty one...
355 if (empty($resultWH[1])) continue;
356 //Let's add the width as img parameter then
357 $add[] = $whword.'="'.trim($resultWH[1]).'" ';
358 }
359
360 if (!empty($add)) {
361 $replace[$oneResult] = '<img '.implode(' ', $add).$results[2][$num];
362 }
363 }
364
365 //Nothing to do
366 if (!empty($replace)) {
367 $html = str_replace(array_keys($replace), $replace, $html);
368 preg_match_all('#(<img)([^>]*>)#i', $html, $results);
369 }
370
371 static $replace = [];
372 foreach ($results[0] as $num => $oneResult) {
373 if (isset($replace[$oneResult])) continue;
374 if (strpos($oneResult, 'width=') || strpos($oneResult, 'height=')) continue;
375 if (preg_match('#[^a-z_\-]width *:([0-9 ]{1,8})#i', $oneResult, $res)) continue;
376 if (preg_match('#[^a-z_\-]height *:([0-9 ]{1,8})#i', $oneResult, $res)) continue;
377 if (!preg_match('#src="([^"]*)"#i', $oneResult, $url)) continue;
378
379 //Ok we have an url, it's $url[1]
380 $imageUrl = $url[1];
381
382 //If the getimagesize failed, we don't want to do it again and again and again...
383 $replace[$oneResult] = $oneResult;
384
385 //We are nice guys... sometimes users use www. or not... so we convert both, same thing for httpS or http
386 $base = str_replace(['http://www.', 'https://www.', 'http://', 'https://'], '', ACYM_LIVE);
387 $replacements = ['https://www.'.$base, 'http://www.'.$base, 'https://'.$base, 'http://'.$base];
388 $localpict = false;
389 foreach ($replacements as $oneReplacement) {
390 if (strpos($imageUrl, $oneReplacement) === false) continue;
391
392 $imageUrl = str_replace(
393 [$oneReplacement, '/'],
394 [ACYM_ROOT, DS],
395 urldecode($imageUrl)
396 );
397 $localpict = true;
398 break;
399 }
400
401 //It's not a local picture... we skip it, we don't want to try to load it, it will take too much time
402 if (!$localpict) continue;
403
404 $dim = @getimagesize($imageUrl);
405 //could not load the picture...
406 if (!$dim) continue;
407 if (empty($dim[0]) || empty($dim[1])) continue;
408
409 $replace[$oneResult] = str_replace('<img', '<img width="'.$dim[0].'" height="'.$dim[1].'"', $oneResult);
410 }
411
412 if (!empty($replace)) {
413 $html = str_replace(array_keys($replace), $replace, $html);
414 }
415 }
416
417 public function addPictureAlign(&$html)
418 {
419 preg_match_all('#< *img([^>]*)>#Ui', $html, $allPictures);
420
421 foreach ($allPictures[0] as $i => $onePict) {
422 // 1 - We add a align="right" or align="left" for the pictures in order to have good result on Outlook 2007
423 if (strpos($onePict, 'align=') !== false) continue;
424 if (!preg_match('#(style="[^"]*)(float *: *)(right|left|top|bottom|middle)#Ui', $onePict, $pictParams)) continue;
425
426 $newPict = str_replace('<img', '<img align="'.$pictParams[3].'" ', $onePict);
427 $html = str_replace($onePict, $newPict, $html);
428
429
430 // 2 - We also add a hspace based on the margin parameter
431 if (strpos($onePict, 'hspace=') !== false) continue;
432
433 $hspace = 5;
434 if (preg_match('#margin(-right|-left)? *:([^";]*)#i', $onePict, $margins)) {
435 //If we have spaces, it may be a margin:34px 24px 78px; format.
436 $currentMargins = explode(' ', trim($margins[2]));
437 //If we have more than one param then we always use the second one (which is "right")... if not we use the first one
438 $myMargin = (count($currentMargins) > 1) ? $currentMargins[1] : $currentMargins[0];
439 //We use it only if it's in px
440 if (strpos($myMargin, 'px') !== false) $hspace = preg_replace('#[^0-9]#i', '', $myMargin);
441 }
442
443 $lastPict = str_replace('<img', '<img hspace="'.$hspace.'" ', $newPict);
444
445 $html = str_replace($newPict, $lastPict, $html);
446 }
447 }
448 }
449