PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / admin / helpers / src / mail / parser.php

parser.php in VikBooking Hotel Booking Engine & PMS trunk, at admin/helpers/src/mail/parser.php

307 lines 12.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Parses the email content for special elements.
16 *
17 * @since 1.15.2 (J) - 1.5.5 (WP)
18 */
19 final class VBOMailParser
20 {
21 /**
22 * @var string the default mail wrapper regex pattern (HTML tag in visual editor)
23 */
24 private static $wrapper_pattern = "/<hr class=\"vbo-editor-hl-mailwrapper\"\s*\/?>/";
25
26 /**
27 * Applies a common method for replacing the special tags within a content.
28 * This will NOT include the conditional text rule tags.
29 *
30 * @param string $content The content to parse.
31 * @param array $booking Booking record.
32 * @param array $booking_rooms List of rooms booked.
33 * @param array $customer Optional customer record.
34 *
35 * @return string
36 *
37 * @since 1.17.6 (J) - 1.7.6 (WP)
38 */
39 public static function replaceSpecialTags(string $content, array $booking, array $booking_rooms, array $customer = [])
40 {
41 if (!$booking || empty($booking['id'])) {
42 return $content;
43 }
44
45 if (!$booking_rooms) {
46 $booking_rooms = VikBooking::loadOrdersRoomsData($booking['id']);
47 }
48
49 if (!$customer) {
50 $customer = VikBooking::getCPinInstance()->getCustomerFromBooking($booking['id']);
51 }
52
53 // build and set customer name
54 $booking['customer_name'] = implode(' ', array_filter([($customer['first_name'] ?? ''), ($customer['last_name'] ?? '')]));
55
56 $vbo_df = VikBooking::getDateFormat();
57 $datesep = VikBooking::getDateSeparator();
58 $df = $vbo_df == "%d/%m/%Y" ? 'd/m/Y' : ($vbo_df == "%m/%d/%Y" ? 'm/d/Y' : 'Y-m-d');
59
60 $content = str_replace('{customer_name}', $booking['customer_name'], $content);
61 $content = str_replace('{booking_id}', $booking['id'], $content);
62 $content = str_replace('{checkin_date}', date(str_replace("/", $datesep, $df), $booking['checkin']), $content);
63 $content = str_replace('{checkout_date}', date(str_replace("/", $datesep, $df), $booking['checkout']), $content);
64 $content = str_replace('{num_nights}', $booking['days'], $content);
65
66 $rooms_booked = [];
67 $tot_adults = 0;
68 $tot_children = 0;
69 $tot_guests = 0;
70 foreach ($booking_rooms as $broom) {
71 if (isset($rooms_booked[$broom['room_name']])) {
72 $rooms_booked[$broom['room_name']] += 1;
73 } else {
74 $rooms_booked[$broom['room_name']] = 1;
75 }
76 $tot_adults += (int) $broom['adults'];
77 $tot_children += (int) $broom['children'];
78 $tot_guests += ((int) $broom['adults'] + (int) $broom['children']);
79 }
80 $content = str_replace('{tot_adults}', $tot_adults, $content);
81 $content = str_replace('{tot_children}', $tot_children, $content);
82 $content = str_replace('{tot_guests}', $tot_guests, $content);
83
84 $rooms_booked_quant = [];
85 foreach ($rooms_booked as $rname => $quant) {
86 $rooms_booked_quant[] = ($quant > 1 ? $quant.' ' : '').$rname;
87 }
88 $content = str_replace('{rooms_booked}', implode(', ', $rooms_booked_quant), $content);
89
90 $content = str_replace('{currency}', VikBooking::getCurrencySymb(), $content);
91 if (isset($booking['total'])) {
92 $content = str_replace('{total}', VikBooking::numberFormat($booking['total']), $content);
93 }
94 if (isset($booking['totpaid'])) {
95 $content = str_replace('{total_paid}', VikBooking::numberFormat($booking['totpaid']), $content);
96 }
97 if (isset($booking['total']) && isset($booking['totpaid'])) {
98 // calculate the outstanding balance
99 $remaining_bal = $booking['total'] - $booking['totpaid'];
100 $damage_deposit_payment = VBORoomHelper::getInstance()->getDamageDepositSplitPayment($booking, $booking_rooms);
101 $prev_dd_payments = [];
102 if ($damage_deposit_payment['damagedep_gross'] ?? 0) {
103 $prev_dd_payments = VikBooking::getBookingHistoryInstance($booking['id'])
104 ->getEventsWithData('PN', function($data) {
105 return (is_object($data) && !empty($data->damage_deposit));
106 });
107 if (!$prev_dd_payments) {
108 // check also the first payment event in case of OTA bookings
109 $prev_dd_payments = VikBooking::getBookingHistoryInstance($booking['id'])
110 ->getEventsWithData('P0', function($data) {
111 return (is_object($data) && !empty($data->damage_deposit));
112 });
113 }
114 }
115 if ($prev_dd_payments && !empty($booking['tot_damage_dep'])) {
116 $remaining_bal -= $booking['tot_damage_dep'];
117 }
118 $content = str_replace('{remaining_balance}', VikBooking::numberFormat($remaining_bal), $content);
119 }
120
121 if ($customer['pin'] ?? '') {
122 $content = str_replace('{customer_pin}', $customer['pin'], $content);
123 }
124
125 $use_sid = empty($booking['sid']) && !empty($booking['idorderota']) ? $booking['idorderota'] : $booking['sid'];
126 $bestitemid = VikBooking::findProperItemIdType(['booking'], (!empty($booking['lang']) ? $booking['lang'] : null));
127 $lang_suffix = $bestitemid && !empty($booking['lang']) ? '&lang=' . $booking['lang'] : '';
128 $book_link = VikBooking::externalroute("index.php?option=com_vikbooking&view=booking&sid=" . $use_sid . "&ts=" . $booking['ts'] . $lang_suffix, false, (!empty($bestitemid) ? $bestitemid : null));
129
130 // access the model for shortening URLs
131 $model = VBOModelShortenurl::getInstance($onlyRouted = true)->setBooking($booking);
132
133 $content = str_replace('{booking_link}', $model->getShortUrl($book_link), $content);
134
135 // rooms distinctive features parsing
136 preg_match_all('/\{roomfeature ([a-zA-Z0-9 ]+)\}/U', $content, $matches);
137 if (isset($matches[1]) && $matches[1]) {
138 foreach ($matches[1] as $reqf) {
139 $rooms_features = [];
140 foreach ($booking_rooms as $broom) {
141 $distinctive_features = [];
142 $rparams = !empty($broom['params']) && is_string($broom['params']) ? ((array) json_decode($broom['params'], true)) : [];
143 if (($rparams['features'] ?? []) && ($broom['roomindex'] ?? '') && ($rparams['features'][$broom['roomindex']] ?? [])) {
144 $distinctive_features = $rparams['features'][$broom['roomindex']];
145 }
146 if (!$distinctive_features) {
147 continue;
148 }
149 $feature_found = false;
150 foreach ($distinctive_features as $dfk => $dfv) {
151 if (stripos($dfk, $reqf) !== false) {
152 $feature_found = $dfk;
153 if (strlen(trim($dfk)) == strlen(trim($reqf))) {
154 break;
155 }
156 }
157 }
158 if ($feature_found !== false && strlen((string) $distinctive_features[$feature_found])) {
159 $rooms_features[] = $distinctive_features[$feature_found];
160 }
161 }
162 if ($rooms_features) {
163 $rpval = implode(', ', $rooms_features);
164 } else {
165 $rpval = '';
166 }
167 $content = str_replace("{roomfeature " . $reqf . "}", $rpval, $content);
168 }
169 }
170
171 /**
172 * Parse all Door Access Control tags.
173 *
174 * @since 1.18.4 (J) - 1.8.4 (WP)
175 */
176 VBOFactory::getDoorAccessControl()
177 ->parseTokens((new VBOBookingRegistry($booking, $booking_rooms)), $content);
178
179 return $content;
180 }
181
182 /**
183 * Tells if a given string contains markdown syntax.
184 *
185 * @param string $text The text string to evaluate.
186 *
187 * @return bool True if markdown syntax is detected.
188 *
189 * @since 1.18.8 (J) - 1.8.8 (WP)
190 */
191 public static function containsMarkdown(string $text)
192 {
193 $pattern = '/
194 \*\*(.+?)\*\* | # Bold
195 (?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*) | # Italic
196 ~~(.+?)~~ | # Strikethrough
197 \[(.+?)\]\((.+?)\) | # Link
198 ^\#{1,3}\s | # Headings h1-h3
199 ^\-\s | # Unordered list
200 ^\d+\.\s | # Ordered list
201 ^>\s # Blockquote
202 /xm';
203
204 return (bool) preg_match($pattern, $text);
205 }
206
207 /**
208 * Attempts to convert markdown syntax into HTML from a given text string.
209 *
210 * @param string $text The text string to evaluate.
211 *
212 * @return string The manipulated string eventually containing HTML tags.
213 *
214 * @since 1.18.8 (J) - 1.8.8 (WP)
215 */
216 public static function markdownToHtml(string $text)
217 {
218 if (!class_exists('VCMAiHelperMarkdown')) {
219 return $text;
220 }
221
222 return (new VCMAiHelperMarkdown($text))->toHtml();
223 }
224
225 /**
226 * Checks for any content wrapper HTML tags applied through
227 * the Visual Editor during the composing of an email message.
228 *
229 * @param string $content the full email content.
230 *
231 * @return string
232 */
233 public static function checkWrapperSymbols($content)
234 {
235 $chunks = preg_split(static::$wrapper_pattern, (string)$content);
236
237 if (count($chunks) < 2) {
238 // no content wrapper used in email message
239 return $content;
240 }
241
242 $layout_opening = self::getWrapperLayout(true);
243 $layout_closing = self::getWrapperLayout(false);
244
245 $final_content = '';
246 foreach ($chunks as $piece => $part) {
247 $is_even = ($piece === 0 || (($piece % 2) === 0));
248 if (strlen(trim($part)) && !$is_even) {
249 $final_content .= $layout_opening . $part . $layout_closing;
250 } else {
251 $final_content .= $part;
252 }
253 }
254
255 return $final_content;
256 }
257
258 /**
259 * Returns the current mail wrapper layout HTML code.
260 *
261 * @param bool $opening whether to get the opening or closing HTML.
262 *
263 * @return string
264 */
265 public static function getWrapperLayout($opening = true)
266 {
267 // configuration field name
268 $opt_name = 'mail_wrapper_layout_' . ($opening ? 'opening' : 'closing');
269
270 // access the configuration object
271 $config = VBOFactory::getConfig();
272
273 // get the current configuration value
274 $layout = $config->get($opt_name);
275
276 if (!$layout) {
277 // set and get default layout
278 $layout = self::getWrapperDefaultLayout($opening);
279 $config->set($opt_name, $layout);
280 }
281
282 return $layout;
283 }
284
285 /**
286 * Returns the default mail wrapper layout HTML code.
287 *
288 * @param bool $opening whether to get the opening or closing HTML.
289 *
290 * @return string
291 */
292 private static function getWrapperDefaultLayout($opening = true)
293 {
294 $layout_opening = "\n";
295 $layout_opening .= "<div style=\"background: #fdfdfd;padding: 30px 0;\">";
296 $layout_opening .= "\n\t";
297 $layout_opening .= "<div style=\"max-width: 600px;margin: 0 auto;background: #fff;padding: 30px;border: 1px solid #eee;border-radius: 6px\">";
298 $layout_opening .= "\n";
299
300 $layout_closing = "\n";
301 $layout_closing .= "\t</div>\n";
302 $layout_closing .= "</div>\n";
303
304 return $opening ? $layout_opening : $layout_closing;
305 }
306 }
307