PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 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 All 37 releases
← All changes | admin/helpers/src/mail/parser.php +257 -214 1.8.7 → 1.8.16 View file →
@@ -13,251 +13,294 @@
13 13
14 14 /**
15 15 * Parses the email content for special elements.
16 16 *
17 - * @since 1.5.5
17 + * @since 1.15.2 (J) - 1.5.5 (WP)
18 18 */
19 19 final class VBOMailParser
20 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*\/?>/";
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 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 - }
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 44
45 - if (!$booking_rooms) {
46 - $booking_rooms = VikBooking::loadOrdersRoomsData($booking['id']);
47 - }
45 + if (!$booking_rooms) {
46 + $booking_rooms = VikBooking::loadOrdersRoomsData($booking['id']);
47 + }
48 48
49 - if (!$customer) {
50 - $customer = VikBooking::getCPinInstance()->getCustomerFromBooking($booking['id']);
51 - }
49 + if (!$customer) {
50 + $customer = VikBooking::getCPinInstance()->getCustomerFromBooking($booking['id']);
51 + }
52 52
53 - // build and set customer name
54 - $booking['customer_name'] = implode(' ', array_filter([($customer['first_name'] ?? ''), ($customer['last_name'] ?? '')]));
53 + // build and set customer name
54 + $booking['customer_name'] = implode(' ', array_filter([($customer['first_name'] ?? ''), ($customer['last_name'] ?? '')]));
55 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');
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 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);
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 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);
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 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);
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 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 - }
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 120
121 - if ($customer['pin'] ?? '') {
122 - $content = str_replace('{customer_pin}', $customer['pin'], $content);
123 - }
121 + if ($customer['pin'] ?? '') {
122 + $content = str_replace('{customer_pin}', $customer['pin'], $content);
123 + }
124 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));
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 129
130 - // access the model for shortening URLs
131 - $model = VBOModelShortenurl::getInstance($onlyRouted = true)->setBooking($booking);
130 + // access the model for shortening URLs
131 + $model = VBOModelShortenurl::getInstance($onlyRouted = true)->setBooking($booking);
132 132
133 - $content = str_replace('{booking_link}', $model->getShortUrl($book_link), $content);
133 + $content = str_replace('{booking_link}', $model->getShortUrl($book_link), $content);
134 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 - }
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 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);
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 178
179 - return $content;
180 - }
179 + return $content;
180 + }
181 181
182 - /**
183 - * Checks for any content wrapper HTML tags applied through
184 - * the Visual Editor during the composing of an email message.
185 - *
186 - * @param string $content the full email content.
187 - *
188 - * @return string
189 - */
190 - public static function checkWrapperSymbols($content)
191 - {
192 - $chunks = preg_split(static::$wrapper_pattern, (string)$content);
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';
193 203
194 - if (count($chunks) < 2) {
195 - // no content wrapper used in email message
196 - return $content;
197 - }
204 + return (bool) preg_match($pattern, $text);
205 + }
198 206
199 - $layout_opening = self::getWrapperLayout(true);
200 - $layout_closing = self::getWrapperLayout(false);
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 + }
201 221
202 - $final_content = '';
203 - foreach ($chunks as $piece => $part) {
204 - $is_even = ($piece === 0 || (($piece % 2) === 0));
205 - if (strlen(trim($part)) && !$is_even) {
206 - $final_content .= $layout_opening . $part . $layout_closing;
207 - } else {
208 - $final_content .= $part;
209 - }
210 - }
222 + return (new VCMAiHelperMarkdown($text))->toHtml();
223 + }
211 224
212 - return $final_content;
213 - }
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);
214 236
215 - /**
216 - * Returns the current mail wrapper layout HTML code.
217 - *
218 - * @param bool $opening whether to get the opening or closing HTML.
219 - *
220 - * @return string
221 - */
222 - public static function getWrapperLayout($opening = true)
223 - {
224 - // configuration field name
225 - $opt_name = 'mail_wrapper_layout_' . ($opening ? 'opening' : 'closing');
237 + if (count($chunks) < 2) {
238 + // no content wrapper used in email message
239 + return $content;
240 + }
226 241
227 - // access the configuration object
228 - $config = VBOFactory::getConfig();
242 + $layout_opening = self::getWrapperLayout(true);
243 + $layout_closing = self::getWrapperLayout(false);
229 244
230 - // get the current configuration value
231 - $layout = $config->get($opt_name);
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 + }
232 254
233 - if (!$layout) {
234 - // set and get default layout
235 - $layout = self::getWrapperDefaultLayout($opening);
236 - $config->set($opt_name, $layout);
237 - }
255 + return $final_content;
256 + }
238 257
239 - return $layout;
240 - }
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');
241 269
242 - /**
243 - * Returns the default mail wrapper layout HTML code.
244 - *
245 - * @param bool $opening whether to get the opening or closing HTML.
246 - *
247 - * @return string
248 - */
249 - private static function getWrapperDefaultLayout($opening = true)
250 - {
251 - $layout_opening = "\n";
252 - $layout_opening .= "<div style=\"background: #fdfdfd;padding: 30px 0;\">";
253 - $layout_opening .= "\n\t";
254 - $layout_opening .= "<div style=\"max-width: 600px;margin: 0 auto;background: #fff;padding: 30px;border: 1px solid #eee;border-radius: 6px\">";
255 - $layout_opening .= "\n";
270 + // access the configuration object
271 + $config = VBOFactory::getConfig();
256 272
257 - $layout_closing = "\n";
258 - $layout_closing .= "\t</div>\n";
259 - $layout_closing .= "</div>\n";
273 + // get the current configuration value
274 + $layout = $config->get($opt_name);
260 275
261 - return $opening ? $layout_opening : $layout_closing;
262 - }
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 + }
263 306 }