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 +273 -74 1.6.4 → 1.8.16 View file →
@@ -13,95 +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 - * Checks for any content wrapper HTML tags applied through
28 - * the Visual Editor during the composing of an email message.
29 - *
30 - * @param string $content the full email content.
31 - *
32 - * @return string
33 - */
34 - public static function checkWrapperSymbols($content)
35 - {
36 - $chunks = preg_split(static::$wrapper_pattern, (string)$content);
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 + }
37 44
38 - if (count($chunks) < 2) {
39 - // no content wrapper used in email message
40 - return $content;
41 - }
45 + if (!$booking_rooms) {
46 + $booking_rooms = VikBooking::loadOrdersRoomsData($booking['id']);
47 + }
42 48
43 - $layout_opening = self::getWrapperLayout(true);
44 - $layout_closing = self::getWrapperLayout(false);
49 + if (!$customer) {
50 + $customer = VikBooking::getCPinInstance()->getCustomerFromBooking($booking['id']);
51 + }
45 52
46 - $final_content = '';
47 - foreach ($chunks as $piece => $part) {
48 - $is_even = ($piece === 0 || (($piece % 2) === 0));
49 - if (strlen(trim($part)) && !$is_even) {
50 - $final_content .= $layout_opening . $part . $layout_closing;
51 - } else {
52 - $final_content .= $part;
53 - }
54 - }
53 + // build and set customer name
54 + $booking['customer_name'] = implode(' ', array_filter([($customer['first_name'] ?? ''), ($customer['last_name'] ?? '')]));
55 55
56 - return $final_content;
57 - }
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');
58 59
59 - /**
60 - * Returns the current mail wrapper layout HTML code.
61 - *
62 - * @param bool $opening whether to get the opening or closing HTML.
63 - *
64 - * @return string
65 - */
66 - public static function getWrapperLayout($opening = true)
67 - {
68 - // configuration field name
69 - $opt_name = 'mail_wrapper_layout_' . ($opening ? 'opening' : 'closing');
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);
70 65
71 - // access the configuration object
72 - $config = VBOFactory::getConfig();
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);
73 83
74 - // get the current configuration value
75 - $layout = $config->get($opt_name);
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);
76 89
77 - if (!$layout) {
78 - // set and get default layout
79 - $layout = self::getWrapperDefaultLayout($opening);
80 - $config->set($opt_name, $layout);
81 - }
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 + }
82 120
83 - return $layout;
84 - }
121 + if ($customer['pin'] ?? '') {
122 + $content = str_replace('{customer_pin}', $customer['pin'], $content);
123 + }
85 124
86 - /**
87 - * Returns the default mail wrapper layout HTML code.
88 - *
89 - * @param bool $opening whether to get the opening or closing HTML.
90 - *
91 - * @return string
92 - */
93 - private static function getWrapperDefaultLayout($opening = true)
94 - {
95 - $layout_opening = "\n";
96 - $layout_opening .= "<div style=\"background: #fdfdfd;padding: 30px 0;\">";
97 - $layout_opening .= "\n\t";
98 - $layout_opening .= "<div style=\"max-width: 600px;margin: 0 auto;background: #fff;padding: 30px;border: 1px solid #eee;border-radius: 6px\">";
99 - $layout_opening .= "\n";
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));
100 129
101 - $layout_closing = "\n";
102 - $layout_closing .= "\t</div>\n";
103 - $layout_closing .= "</div>\n";
130 + // access the model for shortening URLs
131 + $model = VBOModelShortenurl::getInstance($onlyRouted = true)->setBooking($booking);
104 132
105 - return $opening ? $layout_opening : $layout_closing;
106 - }
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 + }
107 306 }