PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.15
VikBooking Hotel Booking Engine & PMS v1.8.15
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 / widgets / latest_from_guests.php

latest_from_guests.php in VikBooking Hotel Booking Engine & PMS 1.8.15, at admin/helpers/widgets/latest_from_guests.php

848 lines 27.3 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 com_vikbooking
5 * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 /**
14 * Class handler for admin widget "latest from guests".
15 *
16 * @since 1.14.0 (J) - 1.4.0 (WP)
17 */
18 class VikBookingAdminWidgetLatestFromGuests extends VikBookingAdminWidget
19 {
20 /**
21 * The instance counter of this widget.
22 *
23 * @var int
24 */
25 protected static $instance_counter = -1;
26
27 /**
28 * Number of activities per page. Should be an even number.
29 *
30 * @var int
31 */
32 protected $activities_per_page = 6;
33
34 /**
35 * Number of activities per type. Should be half of numer per page.
36 *
37 * @var int
38 */
39 protected $activities_per_type = 3;
40
41 /**
42 * Today Y-m-d string
43 *
44 * @var string
45 */
46 protected $today_ymd = null;
47
48 /**
49 * The path to the VCM lib to see if it's available.
50 *
51 * @var string
52 */
53 protected $vcm_lib_path = '';
54
55 /**
56 * Tells whether VCM is installed and updated.
57 *
58 * @var bool
59 */
60 protected $vcm_exists = true;
61
62 /**
63 * List of new guest messages detected.
64 *
65 * @var array
66 *
67 * @since 1.17.6 (J) - 1.7.6 (WP)
68 */
69 protected $new_guest_messages = [];
70
71 /**
72 * Class constructor will define the widget name and identifier.
73 */
74 public function __construct()
75 {
76 // call parent constructor
77 parent::__construct();
78
79 $this->widgetName = JText::translate('VBO_W_LATESTFROMGUESTS_TITLE');
80 $this->widgetDescr = JText::translate('VBO_W_LATESTFROMGUESTS_DESCR');
81 $this->widgetId = basename(__FILE__, '.php');
82
83 /**
84 * Define widget and icon and style name.
85 *
86 * @since 1.15.0 (J) - 1.5.0 (WP)
87 */
88 $this->widgetIcon = '<i class="' . VikBookingIcons::i('comments') . '"></i>';
89 $this->widgetStyleName = 'green';
90
91 // activities per type is half the total number of activities
92 $this->activities_per_type = ceil($this->activities_per_page / 2);
93
94 // today Y-m-d date
95 $this->today_ymd = date('Y-m-d');
96
97 // the path to the VCM library
98 $this->vcm_lib_path = VCM_SITE_PATH . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'lib.vikchannelmanager.php';
99
100 // whether VCM is available
101 $this->vcm_exists = true;
102 if (is_file($this->vcm_lib_path)) {
103 // require the dependencies
104 if (!class_exists('VikChannelManager')) {
105 require_once $this->vcm_lib_path;
106 }
107 if (!method_exists('VikChannelManager', 'getLatestFromGuests')) {
108 // VCM is outdated
109 $this->vcm_exists = false;
110 }
111 } else {
112 $this->vcm_exists = false;
113 }
114
115 // avoid queries on certain pages, as VCM may not have been activated yet
116 if (VBOPlatformDetection::isWordPress() && $this->vcm_exists) {
117 global $pagenow;
118 $skip_pages = ['update.php', 'plugins.php', 'plugin-install.php'];
119 if (isset($pagenow) && in_array($pagenow, $skip_pages)) {
120 $this->vcm_exists = false;
121 }
122 }
123 }
124
125 /**
126 * This widget returns the latest activity information to schedule
127 * periodic watch data in order to be able to trigger notifications.
128 * No CSS/JS assets are needed during preloading.
129 *
130 * @return void|object
131 */
132 public function preload()
133 {
134 if (!$this->vcm_exists) {
135 return null;
136 }
137
138 /**
139 * Attach window event listener for updating the latest guest message
140 * ID upon syncing to prevent dispatching notifications for an open chat.
141 *
142 * @since 1.18.8 (J) - 1.8.8 (WP)
143 */
144 JFactory::getDocument()->addScriptDeclaration(<<<JS
145 window.addEventListener('chatsync', (e) => {
146 let newMessages = e?.detail?.messages;
147 if (!Array.isArray(newMessages) || !newMessages.length) {
148 return;
149 }
150 let latest = newMessages.shift();
151 newMessages.forEach((newMessage) => {
152 if (newMessage?.id > latest?.id) {
153 latest = newMessage;
154 }
155 });
156 Object.assign(VBOCore.widgets_watch_data.latest_from_guests, {
157 message_id: latest.id,
158 message_bid: e.detail?.thread?.idorder,
159 guest_message: latest.content,
160 });
161 if (VBOCore.broadcast_watch_data) {
162 VBOCore.broadcast_watch_data.postMessage(VBOCore.widgets_watch_data);
163 }
164 });
165 JS);
166
167 // use VCM to load the latest guest activity ids
168 $latest_activities = [];
169 try {
170 $latest_activities = VikChannelManager::getLatestFromGuests(['messages', 'reviews'], 0, 1);
171 } catch (Exception $e) {
172 // do nothing
173 }
174
175 if (!is_array($latest_activities) || !$latest_activities) {
176 return null;
177 }
178
179 // default watch-data values to monitor
180 $watch_data = new stdClass;
181 $watch_data->guest_review = 0;
182 $watch_data->guest_message = '';
183 $watch_data->message_id = 0;
184 $watch_data->message_bid = 0;
185
186 foreach ($latest_activities as $activity) {
187 if (isset($activity->id_review) && empty($watch_data->guest_review)) {
188 // this is a guest review
189 $watch_data->guest_review = $activity->id_review;
190 } elseif (!isset($activity->id_review) && empty($watch_data->message_id)) {
191 // this is a guest message
192 $watch_data->guest_message = $activity->content;
193 $watch_data->message_id = $activity->id_message ?? 0;
194 $watch_data->message_bid = $activity->idorder;
195 }
196 }
197
198 return $watch_data;
199 }
200
201 /**
202 * Checks for new notifications by using the previous preloaded watch-data.
203 *
204 * @param ?VBONotificationWatchdata $watch_data the preloaded watch-data object.
205 *
206 * @return array data object to watch next and notifications array.
207 *
208 * @see preload()
209 *
210 * @since 1.15.0 (J) - 1.5.0 (WP)
211 */
212 public function getNotifications(?VBONotificationWatchdata $watch_data = null)
213 {
214 // default empty values
215 $watch_next = null;
216 $notifications = null;
217
218 if (!$this->vcm_exists || !$watch_data) {
219 return [$watch_next, $notifications];
220 }
221
222 // grab latest watch data values
223 $latest_guest_review = (int)$watch_data->get('guest_review', 0);
224 $latest_guest_message = $watch_data->get('guest_message', '');
225 $latest_message_id = (int)$watch_data->get('message_id', 0);
226 $latest_message_bid = (int)$watch_data->get('message_bid', 0);
227
228 // use VCM to load the latest guest activity ids
229 try {
230 $latest_activities = VikChannelManager::getLatestFromGuests(['messages', 'reviews'], 0, 1);
231 } catch (Exception $e) {
232 // do nothing
233 $latest_activities = [];
234 }
235
236 if (!$latest_activities) {
237 return [$watch_next, $notifications];
238 }
239
240 // default watch-data next values to monitor
241 $watch_next = new stdClass;
242 $watch_next->guest_review = $latest_guest_review;
243 $watch_next->guest_message = $latest_guest_message;
244 $watch_next->message_id = $latest_message_id;
245 $watch_next->message_bid = $latest_message_bid;
246
247 // new notifications pool (if any)
248 $notifications = [];
249
250 foreach ($latest_activities as $activity) {
251 if (isset($activity->id_review)) {
252 // this is a guest review
253 if ($activity->id_review > $latest_guest_review) {
254 // set the next watch data value
255 $watch_next->guest_review = $activity->id_review;
256 // compose the notification(s) to dispatch for the guest review(s)
257 $review_notifications = VBONotificationScheduler::getInstance()->buildReviewDataObjects([$activity]);
258 if ($review_notifications) {
259 $notifications = array_merge($notifications, $review_notifications);
260 }
261 }
262 } else {
263 // this is a guest message
264 if (isset($activity->sender_type) && (!strcasecmp($activity->sender_type, 'host') || !strcasecmp($activity->sender_type, 'hotel'))) {
265 // we don't want threads started by the host/hotel
266 continue;
267 }
268
269 // make sure this is a more recent activity than the last one
270 if ($activity->id_message > $latest_message_id && ($activity->idorder != $latest_message_bid || $activity->content != $latest_guest_message)) {
271 // overwrite properties to watch at the next scheduled interval
272 $watch_next->guest_message = $activity->content;
273 $watch_next->message_id = $activity->id_message;
274 $watch_next->message_bid = $activity->idorder;
275
276 // make sure the same message was not previously dispatched through a Push notification
277 if (!$watch_data->matchPushedGuestMessage($activity)) {
278 // compose the notification(s) to dispatch for the guest message(s)
279 $guestmess_notifications = VBONotificationScheduler::getInstance()->buildGuestMessageDataObjects([$activity]);
280 if ($guestmess_notifications) {
281 $notifications = array_merge($notifications, $guestmess_notifications);
282 }
283
284 // set the new guest message
285 $this->new_guest_messages[] = [
286 'id' => $watch_next->message_id,
287 'bid' => $watch_next->message_bid,
288 ];
289 }
290 }
291 }
292 }
293
294 return [$watch_next, $notifications];
295 }
296
297 /**
298 * Checks for new events to be dispatched by using the previous preloaded watch-data.
299 * This method is called after getNotifications(), so we avoid querying the DB.
300 *
301 * @param ?VBONotificationWatchdata $watch_data the preloaded watch-data object.
302 *
303 * @return array list of event objects to dispatch, if any.
304 *
305 * @see preload()
306 *
307 * @since 1.17.6 (J) - 1.7.6 (WP)
308 */
309 public function getNotificationEvents(?VBONotificationWatchdata $watch_data = null)
310 {
311 if (!$this->new_guest_messages) {
312 return [];
313 }
314
315 // return the notification events to dispatch
316 return [
317 // propagate the new guest messages
318 'vbo-new-guest-messages' => [
319 'messages' => $this->new_guest_messages,
320 ],
321 // update the admin-dock badge count, if widget available
322 'vbo-admin-dock-update-badge' => [
323 'widgetId' => 'guest_messages',
324 'badgeCount' => count($this->new_guest_messages),
325 ],
326 ];
327 }
328
329 /**
330 * Custom method for this widget only to load the latest guest activities.
331 * The method is called by the admin controller through an AJAX request.
332 * The visibility should be public, it should not exit the process, and
333 * any content sent to output will be returned to the AJAX response.
334 *
335 * It's the actual rendering of the widget which also allows navigation.
336 */
337 public function loadActivities()
338 {
339 $offset = VikRequest::getInt('offset', 0, 'request');
340 $length = VikRequest::getInt('length', $this->activities_per_type, 'request');
341 $wrapper = VikRequest::getString('wrapper', '', 'request');
342
343 if (!$this->vcm_exists) {
344 echo 'e4j.error.missing or outdated VCM';
345 return;
346 }
347
348 // load latest activities
349 $latest_activities = [];
350 try {
351 $latest_activities = VikChannelManager::getLatestFromGuests(['messages', 'reviews'], $offset, $length);
352 } catch (Exception $e) {
353 // do nothing
354 }
355
356 foreach ($latest_activities as $activity) {
357 $activity_type = isset($activity->id_review) ? 'review' : 'message';
358 $activity_content = $activity->content;
359 if (empty($activity_content)) {
360 $activity_content = '.....';
361 } elseif (strlen($activity_content) > 90) {
362 if (function_exists('mb_substr')) {
363 $activity_content = mb_substr($activity_content, 0, 90, 'UTF-8');
364 } else {
365 $activity_content = substr($activity_content, 0, 90);
366 }
367 $activity_content .= '...';
368 }
369 // determine sender type in case of guest message
370 $sender = 'Hotel';
371 if ($activity_type == 'message' && isset($activity->sender_type)) {
372 if (strcasecmp($activity->sender_type, 'Hotel') && strcasecmp($activity->sender_type, 'Host')) {
373 $sender = 'Guest';
374 }
375 }
376
377 ?>
378 <div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-<?php echo $activity_type; ?>" onclick="vboWidgetLatestFromGuestsShowDetails('<?php echo $activity_type; ?>', '<?php echo $activity_type == 'message' ? $activity->idorder : $activity->id_review; ?>');">
379 <div class="vbo-dashboard-guest-activity-avatar">
380 <?php
381 if (!empty($activity->guest_avatar)) {
382 // highest priority goes to the profile picture, not always available
383 ?>
384 <img class="vbo-dashboard-guest-activity-avatar-profile" src="<?php echo $activity->guest_avatar; ?>" />
385 <?php
386 } elseif (!empty($activity->pic)) {
387 // customer profile picture is not the same as the photo avatar
388 ?>
389 <img class="vbo-dashboard-guest-activity-avatar-profile" src="<?php echo strpos($activity->pic, 'http') === 0 ? $activity->pic : VBO_SITE_URI . 'resources/uploads/' . $activity->pic; ?>" />
390 <?php
391 } elseif (!empty($activity->channel_logo)) {
392 // channel logo goes as second option
393 ?>
394 <img class="vbo-dashboard-guest-activity-avatar-profile" src="<?php echo $activity->channel_logo; ?>" />
395 <?php
396 } else {
397 // we use an icon as fallback
398 VikBookingIcons::e('user', 'vbo-dashboard-guest-activity-avatar-icon');
399 }
400 ?>
401 </div>
402 <div class="vbo-dashboard-guest-activity-content">
403 <div class="vbo-dashboard-guest-activity-content-head">
404 <div class="vbo-dashboard-guest-activity-content-info-details">
405 <h4><?php echo $activity->first_name . (!empty($activity->last_name) ? ' ' . $activity->last_name : ''); ?></h4>
406 <div class="vbo-dashboard-guest-activity-content-info-icon">
407 <?php
408 if ($activity_type == 'review') {
409 // we display an icon as well as the score
410 VikBookingIcons::e('star');
411 ?>
412 <span class="vbo-dashboard-guest-activity-content-info-rate"><?php echo round(($activity->score / 2), 1); ?></span>
413 <?php
414 } else {
415 // we use just an icon to tell that it's a chat guest message
416 if (empty($activity->read_dt) && $sender === 'Guest') {
417 // print also an icon to inform that the message was not read
418 VikBookingIcons::e('exclamation-circle');
419 echo ' ';
420 }
421 VikBookingIcons::e('comment-dots');
422 }
423 ?>
424 </div>
425 </div>
426 <div class="vbo-dashboard-guest-activity-content-info-date">
427 <?php
428 $activity_ts = strtotime($activity->last_updated);
429 ?>
430 <span><?php echo date('H:i', $activity_ts); ?></span>
431 <?php
432 if (date('Y-m-d', $activity_ts) != $this->today_ymd) {
433 // format and print the date
434 ?>
435 <span><?php echo date(str_replace('/', $this->datesep, $this->df), $activity_ts); ?></span>
436 <?php
437 } else {
438 // print "today"
439 ?>
440 <span><?php echo JText::translate('VBTODAY'); ?></span>
441 <?php
442 }
443 ?>
444 </div>
445 </div>
446 <div class="vbo-dashboard-guest-activity-content-info-msg">
447 <p><?php echo $activity_content; ?></p>
448 </div>
449 </div>
450 </div>
451 <?php
452 }
453
454 // append navigation
455 ?>
456 <div class="vbo-guestactivitywidget-commands">
457 <div class="vbo-guestactivitywidget-commands-main">
458 <?php
459 if ($offset > 0) {
460 // show backward navigation button
461 ?>
462 <div class="vbo-guestactivitywidget-command-chevron vbo-guestactivitywidget-command-prev">
463 <span class="vbo-guestactivitywidget-prev" onclick="vboWidgetLatestFromGuestsNavigate('<?php echo $wrapper; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span>
464 </div>
465 <?php
466 }
467 ?>
468 <div class="vbo-guestactivitywidget-command-chevron vbo-guestactivitywidget-command-next">
469 <span class="vbo-guestactivitywidget-next" onclick="vboWidgetLatestFromGuestsNavigate('<?php echo $wrapper; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span>
470 </div>
471 </div>
472 </div>
473 <?php
474
475 // append the total number of activities displayed, the current offset and the latest activity datetime
476 $tot_activities = count($latest_activities);
477 $latest_datetime = $tot_activities > 0 && $offset === 0 ? $latest_activities[0]->last_updated : null;
478
479 echo ';' . __FUNCTION__ . ';' . $tot_activities . ';' . __FUNCTION__ . ';' . $offset . ';' . __FUNCTION__ . ';' . $latest_datetime;
480 }
481
482 /**
483 * Custom method for this widget only to watch the latest guest activities.
484 * The method is called by the admin controller through an AJAX request.
485 * The visibility should be public, it should not exit the process, and
486 * any content sent to output will be returned to the AJAX response.
487 *
488 * Outputs the new number of activities found from the latest datetime.
489 */
490 public function watchActivities()
491 {
492 $latest_dt = VikRequest::getString('latest_dt', '', 'request');
493 if (empty($latest_dt)) {
494 echo '0';
495 return;
496 }
497
498 if (!$this->vcm_exists) {
499 echo 'e4j.error.missing or outdated VCM';
500 return;
501 }
502
503 // load the latest activity (one is sufficient)
504 $latest_activities = [];
505 try {
506 $latest_activities = VikChannelManager::getLatestFromGuests(['messages', 'reviews'], 0, 1);
507 } catch (Exception $e) {
508 // do nothing
509 }
510
511 if (!$latest_activities || $latest_activities[0]->last_updated == $latest_dt) {
512 // no newest activities found
513 echo '0';
514 return;
515 }
516
517 // print 1 to indicate that new activities should be reloaded
518 echo '1';
519 }
520
521 public function render(?VBOMultitaskData $data = null)
522 {
523 // increase widget's instance counter
524 static::$instance_counter++;
525
526 // check whether the widget is being rendered via AJAX when adding it through the customizer
527 $is_ajax = $this->isAjaxRendering();
528
529 // generate a unique ID for the latest from guests wrapper instance
530 $wrapper_instance = !$is_ajax ? static::$instance_counter : rand();
531 $wrapper_id = 'vbo-widget-latest-from-guests-' . $wrapper_instance;
532
533 // this widget will work only if VCM is available and updated, and if permissions are met
534 $vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking');
535 if (!$this->vcm_exists || !$vbo_auth_bookings) {
536 return;
537 }
538
539 // multitask data event identifier for intervals
540 $js_intvals_id = '';
541 if ($data && $data->isModalRendering()) {
542 $js_intvals_id = $data->getModalJsIdentifier();
543 }
544
545 ?>
546 <div class="vbo-admin-widget-wrapper">
547 <div class="vbo-admin-widget-head">
548 <h4><?php echo $this->widgetIcon; ?> <span><?php echo $this->widgetName; ?></span></h4>
549 </div>
550 <div id="<?php echo $wrapper_id; ?>" class="vbo-dashboard-guests-latest" data-offset="0" data-length="<?php echo $this->activities_per_type; ?>" data-latestdt="">
551 <div class="vbo-dashboard-guest-activities-inner">
552 <div class="vbo-dashboard-guest-activities-list">
553 <?php
554 for ($i = 0; $i < $this->activities_per_page; $i++) {
555 ?>
556 <div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">
557 <div class="vbo-dashboard-guest-activity-avatar">
558 <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>
559 </div>
560 <div class="vbo-dashboard-guest-activity-content">
561 <div class="vbo-dashboard-guest-activity-content-head">
562 <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>
563 </div>
564 <div class="vbo-dashboard-guest-activity-content-subhead">
565 <div class="vbo-skeleton-loading vbo-skeleton-loading-subtitle"></div>
566 </div>
567 <div class="vbo-dashboard-guest-activity-content-info-msg">
568 <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>
569 </div>
570 </div>
571 </div>
572 <?php
573 }
574 ?>
575 </div>
576 </div>
577 </div>
578 </div>
579
580 <?php
581 if (static::$instance_counter === 0 || $is_ajax) {
582 // HTML helper tag for URL routing and some JS functions should be loaded once per widget instance
583 $admin_file_base = VBOPlatformDetection::isWordPress() ? 'admin.php' : 'index.php';
584 ?>
585 <a class="vbo-widget-latest-from-guests-basenavuri" href="<?php echo $admin_file_base; ?>?option=com_vikbooking&task=editorder&cid[]=%d" style="display: none;"></a>
586
587 <script type="text/javascript">
588
589 /**
590 * Open the booking details page for the clicked activity
591 */
592 function vboWidgetLatestFromGuestsOpenBooking(id, url_suffix) {
593 var open_url = jQuery('.vbo-widget-latest-from-guests-basenavuri').first().attr('href');
594 open_url = open_url.replace('%d', id) + url_suffix;
595 // navigate
596 document.location.href = open_url;
597 }
598
599 /**
600 * Shows the details of either a message or a review.
601 */
602 function vboWidgetLatestFromGuestsShowDetails(type, id) {
603 if (type === 'message') {
604 VBOCore.handleDisplayWidgetNotification({
605 widget_id: 'guest_messages'
606 }, {
607 bid: id,
608 modal_options: {
609 suffix: 'widget_modal_inner_guest_messages',
610 }
611 });
612 } else if (type === 'review') {
613 VBOCore.handleDisplayWidgetNotification({
614 widget_id: 'guest_reviews'
615 }, {
616 review_id: id,
617 modal_options: {
618 suffix: 'widget_modal_inner_guest_reviews',
619 }
620 });
621 } else {
622 vboWidgetLatestFromGuestsOpenBooking(id, '');
623 }
624 }
625
626 /**
627 * Display the loading skeletons.
628 */
629 function vboWidgetLatestFromGuestsSkeletons(wrapper) {
630 var widget_instance = jQuery('#' + wrapper);
631 if (!widget_instance.length) {
632 return false;
633 }
634 widget_instance.find('.vbo-dashboard-guest-activities-list').html('');
635 for (var i = 0; i < <?php echo $this->activities_per_page; ?>; i++) {
636 var skeleton = '';
637 skeleton += '<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">';
638 skeleton += ' <div class="vbo-dashboard-guest-activity-avatar">';
639 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>';
640 skeleton += ' </div>';
641 skeleton += ' <div class="vbo-dashboard-guest-activity-content">';
642 skeleton += ' <div class="vbo-dashboard-guest-activity-content-head">';
643 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>';
644 skeleton += ' </div>';
645 skeleton += ' <div class="vbo-dashboard-guest-activity-content-subhead">';
646 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-subtitle"></div>';
647 skeleton += ' </div>';
648 skeleton += ' <div class="vbo-dashboard-guest-activity-content-info-msg">';
649 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>';
650 skeleton += ' </div>';
651 skeleton += ' </div>';
652 skeleton += '</div>';
653 // append skeleton
654 jQuery(skeleton).appendTo(widget_instance.find('.vbo-dashboard-guest-activities-list'));
655 }
656 }
657
658 /**
659 * Perform the request to load the latest activities.
660 */
661 function vboWidgetLatestFromGuestsLoad(wrapper) {
662 var widget_instance = jQuery('#' + wrapper);
663 if (!widget_instance.length) {
664 return false;
665 }
666 var current_offset = parseInt(widget_instance.attr('data-offset'));
667 var length_per_page = parseInt(widget_instance.attr('data-length'));
668
669 // the widget method to call
670 var call_method = 'loadActivities';
671
672 // make a request to load the activities
673 VBOCore.doAjax(
674 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
675 {
676 widget_id: "<?php echo $this->getIdentifier(); ?>",
677 call: call_method,
678 offset: current_offset,
679 length: length_per_page,
680 wrapper: wrapper,
681 tmpl: "component"
682 },
683 (response) => {
684 try {
685 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
686 if (!obj_res.hasOwnProperty(call_method)) {
687 console.error('Unexpected JSON response', obj_res);
688 return false;
689 }
690 // response must contain 4 values separated by ";call_method;"
691 var activities_data = obj_res[call_method].split(';' + call_method + ';');
692 if (activities_data.length != 4) {
693 return;
694 }
695 // replace HTML with new activities
696 widget_instance.find('.vbo-dashboard-guest-activities-list').html(activities_data[0]);
697 // check if latest datetime is set
698 if (activities_data[3].length) {
699 widget_instance.attr('data-latestdt', activities_data[3]);
700 }
701 // check results
702 if (!isNaN(activities_data[1]) && parseInt(activities_data[1]) < 1) {
703 // no results can indicate the offset is invalid or too high
704 if (!isNaN(activities_data[2]) && parseInt(activities_data[2]) > 0) {
705 // reset offset to 0
706 widget_instance.attr('data-offset', 0);
707 // show loading skeletons
708 vboWidgetLatestFromGuestsSkeletons(wrapper);
709 // reload the first page
710 vboWidgetLatestFromGuestsLoad(wrapper);
711 }
712 }
713 } catch(err) {
714 console.error('could not parse JSON response', err, response);
715 }
716 },
717 (error) => {
718 // remove the skeleton loading
719 widget_instance.find('.vbo-dashboard-guest-activities-list').find('.vbo-dashboard-guest-activity-skeleton').remove();
720 console.error(error);
721 }
722 );
723 }
724
725 /**
726 * Navigate between the various pages of the activities.
727 */
728 function vboWidgetLatestFromGuestsNavigate(wrapper, direction) {
729 var widget_instance = jQuery('#' + wrapper);
730 if (!widget_instance.length) {
731 return false;
732 }
733
734 // current offset
735 var current_offset = parseInt(widget_instance.attr('data-offset'));
736
737 // steps per type
738 var steps = <?php echo $this->activities_per_type; ?>;
739
740 // show loading skeletons
741 vboWidgetLatestFromGuestsSkeletons(wrapper);
742
743 // check direction and update offset for next nav
744 if (direction > 0) {
745 // navigate forward
746 widget_instance.attr('data-offset', (current_offset + steps));
747 } else {
748 // navigate backward
749 var new_offset = current_offset - steps;
750 new_offset = new_offset >= 0 ? new_offset : 0;
751 widget_instance.attr('data-offset', new_offset);
752 }
753
754 // launch navigation
755 vboWidgetLatestFromGuestsLoad(wrapper);
756 }
757
758 /**
759 * Watch periodically if there are new activities to be displayed.
760 */
761 function vboWidgetLatestFromGuestsWatch(wrapper) {
762 var widget_instance = jQuery('#' + wrapper);
763 if (!widget_instance.length) {
764 return false;
765 }
766
767 var latest_dt = widget_instance.attr('data-latestdt');
768 if (!latest_dt || !latest_dt.length) {
769 return false;
770 }
771
772 // the widget method to call
773 var call_method = 'watchActivities';
774
775 // make a request to watch the activities
776 VBOCore.doAjax(
777 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
778 {
779 widget_id: "<?php echo $this->getIdentifier(); ?>",
780 call: call_method,
781 latest_dt: latest_dt,
782 tmpl: "component"
783 },
784 (response) => {
785 try {
786 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
787 if (!obj_res.hasOwnProperty(call_method)) {
788 console.error('Unexpected JSON response', obj_res);
789 return false;
790 }
791 // response will contain the number of new activities
792 if (isNaN(obj_res[call_method]) || parseInt(obj_res[call_method]) < 1) {
793 // do nothing
794 return;
795 }
796 // new activities found, reset the offset and re-load the first page
797 widget_instance.attr('data-offset', 0);
798 // show loading skeletons
799 vboWidgetLatestFromGuestsSkeletons(wrapper);
800 // reload the first page
801 vboWidgetLatestFromGuestsLoad(wrapper);
802 } catch(err) {
803 console.error('could not parse JSON response', err, response);
804 }
805 },
806 (error) => {
807 // do nothing
808 console.error(error);
809 }
810 );
811 }
812
813 </script>
814 <?php
815 }
816 ?>
817
818 <script type="text/javascript">
819
820 jQuery(function() {
821
822 // when document is ready, load latest activities for this widget's instance
823 vboWidgetLatestFromGuestsLoad('<?php echo $wrapper_id; ?>');
824
825 // set interval for loading new activities automatically
826 var watch_intv = setInterval(function() {
827 vboWidgetLatestFromGuestsWatch('<?php echo $wrapper_id; ?>');
828 }, 60000);
829
830 <?php
831 if ($js_intvals_id) {
832 // widget can be dismissed through the modal
833 ?>
834 document.addEventListener(VBOCore.widget_modal_dismissed + '<?php echo $js_intvals_id; ?>', (e) => {
835 clearInterval(watch_intv);
836 });
837 <?php
838 }
839 ?>
840
841 });
842
843 </script>
844
845 <?php
846 }
847 }
848