PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.7
VikBooking Hotel Booking Engine & PMS v1.8.7
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
vikbooking / admin / helpers / src / notification / elements.php

elements.php in VikBooking Hotel Booking Engine & PMS 1.8.7, at admin/helpers/src/notification/elements.php

452 lines 10.6 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 Alessio Gaggii - E4J s.r.l.
6 * @copyright Copyright (C) 2024 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 * Notification elements registry to store a record for the Notification Center.
16 *
17 * @since 1.16.8 (J) - 1.6.8 (WP)
18 * @since 1.18.3 (J) - 1.8.3 (WP) refactoring makes the class no longer "final".
19 */
20 class VBONotificationElements extends JObject
21 {
22 /**
23 * Valid notification group enumerations.
24 *
25 * @var array
26 */
27 private $groupEnums = [
28 // notifications from Website
29 'website',
30 // notifications from OTAs
31 'otas',
32 // notifications from Channel Manager
33 'cm',
34 // notifications from Guests
35 'guests',
36 // notifications from Operators
37 'operators',
38 // notifications from (PMS) Reports
39 'reports',
40 // notifications from AI
41 'ai',
42 // notifications from Door Access Control
43 'dac',
44 ];
45
46 /**
47 * Default notification type.
48 *
49 * @var string
50 */
51 private $defaultType = 'info';
52
53 /**
54 * Constructs the proper notification elements object according to the notification type.
55 *
56 * @param array|object $notification The notification properties.
57 *
58 * @return VBONotificationElements
59 *
60 * @since 1.18.3 (J) - 1.8.3 (WP)
61 */
62 public static function getInstance($notification)
63 {
64 $notification = (array) $notification;
65
66 // access the notification type
67 $type = ucfirst(preg_replace('/[^a-z0-9]+/', '', strtolower((string) ($notification['type'] ?? 'null'))));
68
69 // build the expected notification class name
70 $ncn = __CLASS__ . $type;
71
72 if (class_exists($ncn)) {
73 return new $ncn($notification);
74 }
75
76 // construct a "generic" notification elements object
77 return new static($notification);
78 }
79
80 /**
81 * Determines and returns the group to which the notification belongs.
82 *
83 * @return string
84 */
85 public function getGroup()
86 {
87 // check if the group was already determined
88 $group = (string) $this->get('_group', '');
89
90 if (!$group) {
91 // access the notification sender property
92 $group = strtolower((string) $this->get('sender', $this->groupEnums[0]));
93
94 // validate the group property
95 $group = in_array($group, $this->groupEnums) ? $group : $this->groupEnums[0];
96
97 // cache determined group
98 $this->set('_group', $group);
99 }
100
101 return $group;
102 }
103
104 /**
105 * Returns the notification type.
106 *
107 * @return string
108 */
109 public function getType()
110 {
111 // access the notification type
112 $type = strtolower((string) $this->get('type', $this->defaultType));
113
114 // ensure the maximum length of 32 chars is respected
115 return $this->shortenString($type, 32, $this->defaultType);
116 }
117
118 /**
119 * Returns the notification title.
120 *
121 * @return string
122 */
123 public function getTitle()
124 {
125 // access the notification title
126 $title = (string) $this->get('title', '');
127
128 // try to guess the title
129 if (!$title) {
130 // check for virtual credit card balance
131 if (strpos($this->getType(), 'vcc_balance') === 0) {
132 $title = JText::translate('VBO_VCC_BALANCE');
133 if ($title == 'VBO_VCC_BALANCE') {
134 $title = ucwords(str_replace('_', ' ', $this->getType()));
135 }
136 }
137 }
138
139 // ensure the maximum length of 64 chars is respected
140 return $this->shortenString($title, 64);
141 }
142
143 /**
144 * Returns the notification summary.
145 *
146 * @return string
147 */
148 public function getSummary()
149 {
150 // access the notification summary
151 $summary = (string) $this->get('summary', '');
152
153 // normalize the summary, if needed
154 if ($summary && $this->getChannel() && !strcasecmp($this->getType(), 'lvf')) {
155 // listing verification framework (LVF)
156 $summary = JText::sprintf('VBO_VERIFY_LISTING_INFO', $summary);
157 }
158
159 // ensure the maximum length of 256 chars is respected
160 return $this->shortenString($summary, 256);
161 }
162
163 /**
164 * Returns the notification avatar URI.
165 *
166 * @return string
167 *
168 * @since 1.18.0 (J) - 1.8.0 (WP)
169 */
170 public function getAvatar()
171 {
172 // access the notification avatar
173 $avatar = (string) $this->get('avatar', '');
174
175 // turn full internal URIs into relative URIs
176 $avatar = str_replace(JUri::root(), '', $avatar);
177
178 // ensure the maximum length of 256 chars is respected
179 return $this->shortenString($avatar, 256);
180 }
181
182 /**
183 * Builds and returns the notification call-to-action data.
184 *
185 * @return null|string
186 */
187 public function getCallToActionData()
188 {
189 $cta_data = [];
190
191 // check if a widget identifier was provided
192 $widget = (string) $this->get('widget', '');
193 if ($widget) {
194 $cta_data['widget'] = $widget;
195 }
196
197 // check if some widget options were provided
198 $widget_options = (array) $this->get('widget_options', []);
199 if ($widget_options) {
200 $cta_data['widget_options'] = $widget_options;
201 }
202
203 // check if a notification URL was provided
204 $cta_url = (string) $this->get('cta_url', $this->get('url'));
205 if ($cta_url) {
206 $cta_data['url'] = $cta_url;
207 }
208
209 // check if a custom label was provided
210 if ($cta_data && is_string($this->get('label'))) {
211 $cta_data['label'] = $this->get('label');
212 }
213
214 if (!$cta_data) {
215 // attempt to determine the CTA payload to set
216 if (strpos($this->getType(), 'vcc_balance') === 0 && ($this->getReservationID() || $this->getOTAReservationID())) {
217 // set call-to-action for Virtual Terminal admin-widget for VCC balance
218 $cta_data = [
219 'label' => JText::translate('VBO_CC_DOCHARGE'),
220 'widget' => 'virtual_terminal',
221 'widget_options' => [
222 'bid' => $this->getReservationID() ?: $this->getOTAReservationID(),
223 ],
224 ];
225 } elseif (!strcasecmp($this->getType(), 'guest_message') && ($this->getReservationID() || $this->getOTAReservationID())) {
226 // set call-to-action for Guest Messages admin-widget to reply to the guest message
227 $cta_data = [
228 'label' => JText::translate('VBO_REPLY'),
229 'widget' => 'guest_messages',
230 'widget_options' => [
231 'bid' => $this->getReservationID() ?: $this->getOTAReservationID(),
232 ],
233 ];
234 } elseif (!strcasecmp($this->getType(), 'ob') && $this->getReservationID()) {
235 // set call-to-action for Bookings Calendar admin-widget in case of overbooking
236 $cta_data = [
237 'widget' => 'bookings_calendar',
238 'widget_options' => [
239 'bid' => $this->getReservationID(),
240 'overbooking' => 1,
241 ],
242 ];
243 }
244 }
245
246 return $cta_data ? json_encode($cta_data) : null;
247 }
248
249 /**
250 * Returns the VikBooking reservation ID.
251 *
252 * @return null|int
253 */
254 public function getReservationID()
255 {
256 $res_id = $this->get('idorder', null);
257
258 return $res_id ? (int) $res_id : null;
259 }
260
261 /**
262 * Returns the OTA reservation ID.
263 *
264 * @return null|string
265 */
266 public function getOTAReservationID()
267 {
268 $ota_res_id = $this->get('idorderota', null);
269
270 return $ota_res_id ? (string) $ota_res_id : null;
271 }
272
273 /**
274 * Returns the channel name.
275 *
276 * @return null|string
277 */
278 public function getChannel()
279 {
280 $channel = $this->get('channel', null);
281
282 return $channel ? (string) $channel : null;
283 }
284
285 /**
286 * Returns the notification date and time.
287 *
288 * @return string
289 */
290 public function getDate()
291 {
292 try {
293 $date = JFactory::getDate($this->get('date') ?: 'now');
294 } catch(Exception $e) {
295 $date = JFactory::getDate();
296 }
297
298 return $date->toSql();
299 }
300
301 /**
302 * Returns the notification signature.
303 *
304 * @return string
305 */
306 public function getSignature()
307 {
308 $signature = (string) $this->get('_signature', '');
309
310 if (!$signature) {
311 $signature = $this->buildSignature();
312 }
313
314 return $signature;
315 }
316
317 /**
318 * Sets the notification signature.
319 *
320 * @return self
321 */
322 public function setSignature(string $signature = '')
323 {
324 $this->set('_signature', $signature);
325
326 return $this;
327 }
328
329 /**
330 * Builds, sets and returns the notification signature.
331 *
332 * @return string
333 *
334 * @since 1.18.3 (J) - 1.8.3 (WP) signature also contains "idorderota".
335 */
336 public function buildSignature()
337 {
338 // build notification signature elements
339 $elements = [
340 'id' => $this->get('id', $this->get('notification_id', 0)),
341 'idorder' => $this->get('idorder', 0),
342 'idorderota' => $this->get('idorderota', 0),
343 'sender' => $this->get('sender', ''),
344 'type' => $this->get('type', ''),
345 'title' => $this->get('title', ''),
346 'summary' => $this->get('summary', ''),
347 ];
348
349 // build notification signature string
350 $signature = md5(serialize($elements));
351
352 // set signature string
353 $this->setSignature($signature);
354
355 return $signature;
356 }
357
358 /**
359 * Method invoked after storing a notification for eventually executing actions.
360 * Specific notification (class) types should implement their own actions.
361 *
362 * @return void
363 *
364 * @since 1.18.3 (J) - 1.8.3 (WP)
365 */
366 public function postflight()
367 {
368 return;
369 }
370
371 /**
372 * Returns the notification error code.
373 *
374 * @return int
375 */
376 public function getErrorCode()
377 {
378 return (int) $this->get('_errorCode', 500);
379 }
380
381 /**
382 * Sets the notification error code.
383 *
384 * @return self
385 */
386 public function setErrorCode($code = 500)
387 {
388 $this->set('_errorCode', $code);
389
390 return $this;
391 }
392
393 /**
394 * Ensures a string reflects the given length, or it will be eventually shortened or replaced.
395 *
396 * @param string $value the string to check.
397 * @param int $length the length to reflect.
398 * @param string $fallback optional string to replace as fallback.
399 *
400 * @return string
401 */
402 private function shortenString(string $value, int $length, string $fallback = '')
403 {
404 if (strlen($value) <= $length || $length <= 0) {
405 // length is safe
406 return $value;
407 }
408
409 if ($fallback) {
410 // replace string with provided fallback
411 return $fallback;
412 }
413
414 // shorten the string to the desired length
415 if (!function_exists('mb_strlen')) {
416 // use a regular sub-string without multi-byte support
417 return rtrim(substr($value, 0, $length - 3), '.,?!;:#\'"([{ ') . '...';
418 }
419
420 // calculate string length
421 $size = strlen($value);
422 $mb_size = mb_strlen($value);
423 $ch_diff = $size - $mb_size;
424
425 if ($ch_diff <= 0) {
426 // no multi-byte chars found
427 return rtrim(substr($value, 0, $length - 3), '.,?!;:#\'"([{ ') . '...';
428 }
429
430 // safely construct the string with one multibyte char per time
431 $mb_value = '';
432 $mb_char_start = 0;
433 while (strlen($mb_value) < $length - 3) {
434 // get a one-char multi-byte portion
435 $mb_portion = mb_substr($value, $mb_char_start, 1, 'UTF-8');
436
437 if (strlen($mb_value . $mb_portion) > $length) {
438 // abort to not exceed the length
439 return $mb_value;
440 }
441
442 // add portion to string value
443 $mb_value .= $mb_portion;
444
445 // increase chart start counter
446 $mb_char_start++;
447 }
448
449 return rtrim($mb_value, '.,?!;:#\'"([{ ') . '...';
450 }
451 }
452