PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.6.4
VikBooking Hotel Booking Engine & PMS v1.6.4
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 / guest_messages.php

guest_messages.php in VikBooking Hotel Booking Engine & PMS 1.6.4, at admin/helpers/widgets/guest_messages.php

930 lines 31.1 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 "guest messages".
15 *
16 * @since 1.16.0 (J) - 1.6.0 (WP)
17 */
18 class VikBookingAdminWidgetGuestMessages 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 messages per page. Should be an even number.
29 *
30 * @var int
31 */
32 protected $messages_per_page = 6;
33
34 /**
35 * Today Y-m-d string
36 *
37 * @var string
38 */
39 protected $today_ymd = null;
40
41 /**
42 * The path to the VCM lib to see if it's available.
43 *
44 * @var string
45 */
46 protected $vcm_lib_path = '';
47
48 /**
49 * Tells whether VCM is installed and updated.
50 *
51 * @var bool
52 */
53 protected $vcm_exists = true;
54
55 /**
56 * Class constructor will define the widget name and identifier.
57 */
58 public function __construct()
59 {
60 // call parent constructor
61 parent::__construct();
62
63 $this->widgetName = JText::translate('VBO_W_GUESTMESSAGES_TITLE');
64 $this->widgetDescr = JText::translate('VBO_W_GUESTMESSAGES_DESCR');
65 $this->widgetId = basename(__FILE__, '.php');
66
67 // define widget and icon and style name
68 $this->widgetIcon = '<i class="' . VikBookingIcons::i('comment-dots') . '"></i>';
69 $this->widgetStyleName = 'light-orange';
70
71 // today Y-m-d date
72 $this->today_ymd = date('Y-m-d');
73
74 // the path to the VCM library
75 $this->vcm_lib_path = VCM_SITE_PATH . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'lib.vikchannelmanager.php';
76
77 // whether VCM is available
78 if (is_file($this->vcm_lib_path)) {
79 if (!class_exists('VikChannelManager') || !method_exists('VikChannelManager', 'getLatestFromGuests')) {
80 // VCM is outdated
81 $this->vcm_exists = false;
82 }
83
84 // attempt to require the chat handler
85 try {
86 VikBooking::getVcmChatInstance($oid = 0, $channel = null);
87 } catch (Exception $e) {
88 // do nothing
89 }
90
91 // make sure VCM is up to date for this widget
92 if (!class_exists('VCMChatHandler') || !method_exists('VCMChatHandler', 'loadChatAssets')) {
93 // VCM is outdated (>= 1.8.11 required)
94 $this->vcm_exists = false;
95 }
96 } else {
97 $this->vcm_exists = false;
98 }
99
100 // avoid queries on certain pages, as VCM may not have been activated yet
101 if (defined('ABSPATH') && $this->vcm_exists) {
102 global $pagenow;
103 if (isset($pagenow) && in_array($pagenow, ['update.php', 'plugins.php', 'plugin-install.php'])) {
104 $this->vcm_exists = false;
105 }
106 }
107 }
108
109 /**
110 * Preload the necessary CSS/JS assets from VCM.
111 *
112 * @return void
113 */
114 public function preload()
115 {
116 if ($this->vcm_exists) {
117 // load chat assets from VCM
118 VCMChatHandler::loadChatAssets();
119
120 // additional language defs
121 JText::script('VBO_NO_REPLY_NEEDED');
122 JText::script('VBO_WANT_PROCEED');
123 }
124 }
125
126 /**
127 * Custom method for this widget only to load the latest guest messages.
128 * The method is called by the admin controller through an AJAX request.
129 * The visibility should be public, it should not exit the process, and
130 * an associative array is returned thanks to the request value "return":1.
131 *
132 * It's the actual rendering of the widget which also allows navigation.
133 */
134 public function loadMessages()
135 {
136 $bid_convo = VikRequest::getInt('bid_convo', 0, 'request');
137 $offset = VikRequest::getInt('offset', 0, 'request');
138 $length = VikRequest::getInt('length', $this->messages_per_page, 'request');
139 $wrapper = VikRequest::getString('wrapper', '', 'request');
140
141 if (!$this->vcm_exists) {
142 VBOHttpDocument::getInstance()->close(500, 'Vik Channel Manager is either not available or outdated');
143 }
144
145 // load latest messages
146 $latest_messages = [];
147 try {
148 $latest_messages = VikChannelManager::getLatestFromGuests(['guest_messages'], $offset, $length);
149 } catch (Exception $e) {
150 // do nothing
151 }
152
153 // the multitask data and notifications can request a specific conversation to be opened
154 $bubble_convo = null;
155
156 // current year Y and timestamp
157 $current_y = date('Y');
158 $current_ts = time();
159
160 // start output buffering
161 ob_start();
162
163 foreach ($latest_messages as $gmessage) {
164 $gmessage_content = $gmessage->content;
165 if (empty($gmessage_content)) {
166 $gmessage_content = '.....';
167 } elseif (strlen($gmessage_content) > 90) {
168 if (function_exists('mb_substr')) {
169 $gmessage_content = mb_substr($gmessage_content, 0, 90, 'UTF-8');
170 } else {
171 $gmessage_content = substr($gmessage_content, 0, 90);
172 }
173 $gmessage_content .= '...';
174 }
175
176 if ($bid_convo && $bid_convo == $gmessage->idorder) {
177 // specific conversation to bubble found
178 $bubble_convo = $bid_convo;
179 }
180
181 ?>
182 <div class="vbo-dashboard-guest-activity vbo-w-guestmessages-message<?php echo empty($gmessage->read_dt) ? ' vbo-w-guestmessages-message-new' : ''; ?>" data-idorder="<?php echo $gmessage->idorder; ?>" data-idthread="<?php echo !empty($gmessage->id_thread) ? $gmessage->id_thread : ''; ?>" data-idmessage="<?php echo !empty($gmessage->id_message) ? $gmessage->id_message : ''; ?>" onclick="vboWidgetGuestMessagesOpenChat('<?php echo $gmessage->idorder; ?>');">
183 <div class="vbo-dashboard-guest-activity-avatar">
184 <?php
185 if (!empty($gmessage->guest_avatar)) {
186 // highest priority goes to the profile picture, not always available
187 ?>
188 <img class="vbo-dashboard-guest-activity-avatar-profile" src="<?php echo $gmessage->guest_avatar; ?>" />
189 <?php
190 } elseif (!empty($gmessage->pic)) {
191 // customer profile picture is not the same as the photo avatar
192 ?>
193 <img class="vbo-dashboard-guest-activity-avatar-profile" src="<?php echo strpos($gmessage->pic, 'http') === 0 ? $gmessage->pic : VBO_SITE_URI . 'resources/uploads/' . $gmessage->pic; ?>" />
194 <?php
195 } elseif (!empty($gmessage->channel_logo)) {
196 // channel logo goes as second option
197 ?>
198 <img class="vbo-dashboard-guest-activity-avatar-profile" src="<?php echo $gmessage->channel_logo; ?>" />
199 <?php
200 } else {
201 // we use an icon as fallback
202 VikBookingIcons::e('user', 'vbo-dashboard-guest-activity-avatar-icon');
203 }
204 ?>
205 </div>
206 <div class="vbo-dashboard-guest-activity-content">
207 <div class="vbo-dashboard-guest-activity-content-head">
208 <div class="vbo-dashboard-guest-activity-content-info-details">
209 <h4 class="vbo-w-guestmessages-message-gtitle"><?php
210 echo $gmessage->first_name . (!empty($gmessage->last_name) ? ' ' . $gmessage->last_name : '');
211 if (empty($gmessage->read_dt)) {
212 // print also an icon to inform that the message was not read
213 echo ' ';
214 VikBookingIcons::e('exclamation-circle', 'message-new');
215 }
216 ?></h4>
217 <div class="vbo-dashboard-guest-activity-content-info-icon">
218 <?php
219 if (!empty($gmessage->b_status)) {
220 switch ($gmessage->b_status) {
221 case 'standby':
222 $badge_class = 'badge-warning';
223 $badge_text = JText::translate('VBSTANDBY');
224 break;
225 case 'cancelled':
226 $badge_class = 'badge-danger';
227 $badge_text = JText::translate('VBCANCELLED');
228 break;
229 default:
230 $badge_class = 'badge-success';
231 $badge_text = JText::translate('VBCONFIRMED');
232 if (!empty($gmessage->b_checkout) && $gmessage->b_checkout < $current_ts) {
233 $badge_text = JText::translate('VBOCHECKEDSTATUSOUT');
234 }
235 break;
236 }
237 ?>
238 <span class="badge <?php echo $badge_class; ?>"><?php echo $badge_text; ?></span>
239 <?php
240 }
241 if (!empty($gmessage->b_checkin)) {
242 $stay_info_in = getdate($gmessage->b_checkin);
243 $stay_info_out = getdate($gmessage->b_checkout);
244 $str_checkin = date('d', $gmessage->b_checkin);
245 $str_checkin .= $stay_info_in['mon'] != $stay_info_out['mon'] ? ' ' . VikBooking::sayMonth($stay_info_in['mon'], $short = true) : '';
246 $str_checkout = date('d', $gmessage->b_checkout) . ' ' . VikBooking::sayMonth($stay_info_in['mon'], $short = true);
247 if ($stay_info_in['year'] != $stay_info_out['year'] || $stay_info_in['year'] != $current_y || $stay_info_out['year'] != $current_y) {
248 $str_checkout .= ' ' . $stay_info_in['year'];
249 }
250 ?>
251 <span class="vbo-w-guestmessages-message-staydates">
252 <span class="vbo-w-guestmessages-message-staydates-in"><?php echo $str_checkin; ?></span>
253 <span class="vbo-w-guestmessages-message-staydates-sep">-</span>
254 <span class="vbo-w-guestmessages-message-staydates-out"><?php echo $str_checkout; ?></span>
255 </span>
256 <?php
257 }
258 ?>
259 </div>
260 </div>
261 <div class="vbo-dashboard-guest-activity-content-info-date">
262 <?php
263 $gmessage_ts = strtotime($gmessage->last_updated);
264 ?>
265 <span><?php echo date('H:i', $gmessage_ts); ?></span>
266 <?php
267 if (date('Y-m-d', $gmessage_ts) != $this->today_ymd) {
268 // format and print the date
269 ?>
270 <span><?php echo date(str_replace('/', $this->datesep, $this->df), $gmessage_ts); ?></span>
271 <?php
272 } else {
273 // print "today"
274 ?>
275 <span><?php echo JText::translate('VBTODAY'); ?></span>
276 <?php
277 }
278 ?>
279 </div>
280 </div>
281 <div class="vbo-dashboard-guest-activity-content-info-msg">
282 <p><?php echo $gmessage_content; ?></p>
283 </div>
284 </div>
285 </div>
286 <?php
287 }
288
289 // append navigation
290 ?>
291 <div class="vbo-guestactivitywidget-commands">
292 <div class="vbo-guestactivitywidget-commands-main">
293 <?php
294 if ($offset > 0) {
295 // show backward navigation button
296 ?>
297 <div class="vbo-guestactivitywidget-command-chevron vbo-guestactivitywidget-command-prev">
298 <span class="vbo-guestactivitywidget-prev" onclick="vboWidgetGuestMessagesNavigate('<?php echo $wrapper; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span>
299 </div>
300 <?php
301 }
302 ?>
303 <div class="vbo-guestactivitywidget-command-chevron vbo-guestactivitywidget-command-next">
304 <span class="vbo-guestactivitywidget-next" onclick="vboWidgetGuestMessagesNavigate('<?php echo $wrapper; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span>
305 </div>
306 </div>
307 </div>
308 <?php
309
310 // check if we should bubble a specific conversation
311 if ($bubble_convo) {
312 ?>
313 <script type="text/javascript">
314 setTimeout(() => {
315 vboWidgetGuestMessagesOpenChat('<?php echo $bubble_convo; ?>');
316 }, 300);
317 </script>
318 <?php
319 }
320
321 // append the total number of messages displayed, the current offset and the latest message datetime
322 $tot_messages = count($latest_messages);
323 $latest_datetime = $tot_messages > 0 && $offset === 0 ? $latest_messages[0]->last_updated : null;
324
325 // get the HTML buffer
326 $html_content = ob_get_contents();
327 ob_end_clean();
328
329 // return an associative array of values
330 return [
331 'html' => $html_content,
332 'tot_messages' => $tot_messages,
333 'offset' => ($offset + $length),
334 'latest_dt' => $latest_datetime,
335 ];
336 }
337
338 /**
339 * Custom method for this widget only to watch the latest guest messages.
340 * The method is called by the admin controller through an AJAX request.
341 * The visibility should be public, it should not exit the process, and
342 * any content sent to output will be returned to the AJAX response.
343 *
344 * Outputs the new number of messages found from the latest datetime.
345 */
346 public function watchMessages()
347 {
348 $latest_dt = VikRequest::getString('latest_dt', '', 'request');
349 if (empty($latest_dt)) {
350 echo '0';
351 return;
352 }
353
354 if (!$this->vcm_exists) {
355 VBOHttpDocument::getInstance()->close(500, 'Vik Channel Manager is either not available or outdated');
356 }
357
358 // load the latest guest message (one is sufficient)
359 $latest_messages = [];
360 try {
361 $latest_messages = VikChannelManager::getLatestFromGuests(['guest_messages'], 0, 1);
362 } catch (Exception $e) {
363 // do nothing
364 }
365
366 if (!count($latest_messages) || $latest_messages[0]->last_updated == $latest_dt) {
367 // no newest messages found
368 echo '0';
369 return;
370 }
371
372 // print 1 to indicate that new messages should be reloaded
373 echo '1';
374 }
375
376 /**
377 * Custom method for this widget only to render the chat of a booking.
378 * The method is called by the admin controller through an AJAX request.
379 * The visibility should be public, it should not exit the process, and
380 * no values should be returned, as the response must be sent to output
381 * in case the JS/CSS assets will be echoed within the response.
382 *
383 * Returns the necessary HTML code to render the chat.
384 */
385 public function renderChat()
386 {
387 $bid = VikRequest::getInt('bid', 0, 'request');
388
389 $booking = VikBooking::getBookingInfoFromID($bid);
390
391 if (!$booking) {
392 VBOHttpDocument::getInstance()->close(404, 'Could not find booking');
393 }
394
395 // initialize chat instance by getting the proper channel name
396 if (empty($booking['channel'])) {
397 // front-end reservation chat handler
398 $chat_channel = 'vikbooking';
399 } else {
400 $channelparts = explode('_', $booking['channel']);
401 // check if this is a meta search channel
402 $is_meta_search = false;
403 if (preg_match("/(customer).*[0-9]$/", $channelparts[0]) || !strcasecmp($channelparts[0], 'googlehotel') || !strcasecmp($channelparts[0], 'trivago')) {
404 $is_meta_search = empty($booking['idorderota']);
405 }
406 if ($is_meta_search) {
407 // customer of type sales channel should use front-end reservation chat handler
408 $chat_channel = 'vikbooking';
409 } else {
410 // let the getInstance method validate the channel chat handler
411 $chat_channel = $booking['channel'];
412 }
413 }
414 $messaging = VikBooking::getVcmChatInstance($booking['id'], $chat_channel);
415
416 if (is_null($messaging)) {
417 VBOHttpDocument::getInstance()->close(500, 'Could not render chat');
418 }
419
420 // send content to output
421 echo $messaging->renderChat([
422 'hideThreads' => 1,
423 ], $load_assets = false);
424 }
425
426 /**
427 * Custom method for this widget only to update the thread of a booking.
428 * The method is called by the admin controller through an AJAX request.
429 * The visibility should be public, it should not exit the process, and
430 * no values should be returned, as the response must be sent to output.
431 *
432 * Returns a successful string or throws an error.
433 */
434 public function setNoReplyNeededThread()
435 {
436 $bid = VikRequest::getInt('bid', 0, 'request');
437 $id_thread = VikRequest::getInt('id_thread', 0, 'request');
438
439 $booking = VikBooking::getBookingInfoFromID($bid);
440
441 if (!$booking || empty($id_thread)) {
442 VBOHttpDocument::getInstance()->close(404, 'Could not find booking thread');
443 }
444
445 // build thread object for update
446 $thread = new stdClass;
447 $thread->id = $id_thread;
448 $thread->idorder = $bid;
449 $thread->no_reply_needed = 1;
450
451 $dbo = JFactory::getDbo();
452
453 if (!$dbo->updateObject('#__vikchannelmanager_threads', $thread, ['id', 'idorder'])) {
454 VBOHttpDocument::getInstance()->close(500, 'Could not update thread');
455 }
456
457 echo '1';
458 }
459
460 public function render(VBOMultitaskData $data = null)
461 {
462 // increase widget's instance counter
463 static::$instance_counter++;
464
465 // check whether the widget is being rendered via AJAX when adding it through the customizer
466 $is_ajax = $this->isAjaxRendering();
467
468 // generate a unique ID for the guest messages wrapper instance
469 $wrapper_instance = !$is_ajax ? static::$instance_counter : rand();
470 $wrapper_id = 'vbo-widget-guest-messages-' . $wrapper_instance;
471
472 // this widget will work only if VCM is available and updated, and if permissions are met
473 $vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking');
474 if (!$this->vcm_exists || !$vbo_auth_bookings) {
475 return;
476 }
477
478 // multitask data event identifier for clearing intervals
479 $js_intvals_id = '';
480 $bid_convo = 0;
481 if ($data && $data->isModalRendering()) {
482 $js_intvals_id = $data->getModalJsIdentifier();
483 // check if a specific conversation should be opened
484 if ($data->get('id_message', 0)) {
485 $bid_convo = $data->getBookingId();
486 }
487 }
488
489 ?>
490 <div class="vbo-admin-widget-wrapper">
491 <div class="vbo-admin-widget-head">
492 <h4><?php echo $this->widgetIcon; ?> <span><?php echo $this->widgetName; ?></span></h4>
493 </div>
494 <div id="<?php echo $wrapper_id; ?>" class="vbo-dashboard-guests-latest" data-offset="0" data-length="<?php echo $this->messages_per_page; ?>" data-eventsid="<?php echo $js_intvals_id; ?>" data-latestdt="">
495 <div class="vbo-dashboard-guest-messages-inner">
496 <div class="vbo-dashboard-guest-messages-list">
497 <?php
498 for ($i = 0; $i < $this->messages_per_page; $i++) {
499 ?>
500 <div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">
501 <div class="vbo-dashboard-guest-activity-avatar">
502 <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>
503 </div>
504 <div class="vbo-dashboard-guest-activity-content">
505 <div class="vbo-dashboard-guest-activity-content-head">
506 <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>
507 </div>
508 <div class="vbo-dashboard-guest-activity-content-subhead">
509 <div class="vbo-skeleton-loading vbo-skeleton-loading-subtitle"></div>
510 </div>
511 <div class="vbo-dashboard-guest-activity-content-info-msg">
512 <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>
513 </div>
514 </div>
515 </div>
516 <?php
517 }
518 ?>
519 </div>
520 </div>
521 </div>
522 </div>
523
524 <?php
525 if (static::$instance_counter === 0 || $is_ajax) {
526 // HTML helper tag for URL routing and some JS functions should be loaded once per widget instance
527 $admin_file_base = VBOPlatformDetection::isWordPress() ? 'admin.php' : 'index.php';
528 ?>
529 <a class="vbo-widget-guest-messages-basenavuri" href="<?php echo $admin_file_base; ?>?option=com_vikbooking&task=editorder&cid[]=%d" style="display: none;"></a>
530
531 <script type="text/javascript">
532
533 /**
534 * Open the chat for the clicked booking guest message
535 */
536 function vboWidgetGuestMessagesOpenChat(id) {
537 // clicked message
538 var message_el = jQuery('.vbo-w-guestmessages-message[data-idorder="' + id + '"]').first();
539
540 if (message_el.hasClass('vbo-w-guestmessages-message-new')) {
541 // get rid of the "new/unread" status
542 message_el.removeClass('vbo-w-guestmessages-message-new');
543 if (message_el.find('i.message-new').length) {
544 message_el.find('i.message-new').remove();
545 }
546 }
547
548 // modal events unique id to avoid conflicts
549 var eventsid = message_el.closest('.vbo-dashboard-guests-latest').attr('data-eventsid') || (Math.floor(Math.random() * 100000));
550
551 // define unique modal event names to avoid conflicts
552 var modal_dismiss_event = 'dismiss-modal-wguestmessages-chat' + eventsid;
553 var modal_loading_event = 'loading-modal-wguestmessages-chat' + eventsid;
554
555 // base navigation URI to booking details
556 var booking_base_uri = jQuery('.vbo-widget-guest-messages-basenavuri').first().attr('href');
557
558 // build modal content
559 var chat_head_title = jQuery('<span></span>');
560 var chat_head_title_wrap = jQuery('<span></span>').addClass('vbo-modal-wguestmessages-chat-info');
561 var chat_head_title_top = jQuery('<span></span>').addClass('vbo-modal-wguestmessages-chat-info-customer');
562 var chat_head_title_bot = jQuery('<span></span>').addClass('vbo-modal-wguestmessages-chat-info-booking');
563
564 var chat_head_title_img = jQuery('<span></span>').addClass('vbo-modal-wguestmessages-chat-guestavatar');
565 var chat_head_title_txt = jQuery('<span></span>').addClass('vbo-modal-wguestmessages-chat-guestname');
566 var chat_head_title_bid = jQuery('<span></span>').addClass('vbo-modal-wguestmessages-chat-guestbid');
567 chat_head_title_bid.append('<a class="badge badge-info" href="' + booking_base_uri.replace('%d', id) + '" target="_blank">' + id + '</a>');
568
569 var guest_avatar = message_el.find('.vbo-dashboard-guest-activity-avatar img');
570 var guest_name = message_el.find('.vbo-w-guestmessages-message-gtitle').text();
571 chat_head_title_txt.text(guest_name);
572 if (guest_avatar && guest_avatar.length) {
573 chat_head_title_img.append(guest_avatar.clone());
574 chat_head_title.append(chat_head_title_img);
575 }
576 chat_head_title_top.append(chat_head_title_txt);
577 chat_head_title_top.append(chat_head_title_bid);
578
579 chat_head_title_bot.append(message_el.find('.vbo-dashboard-guest-activity-content-info-icon').html());
580
581 // register callback for no-reply-needed click
582 var no_reply_needed_el = jQuery('<a></a>').addClass('label').attr('href', 'JavaScript: void(0);').text(Joomla.JText._('VBO_NO_REPLY_NEEDED'));
583 no_reply_needed_el.on('click', function() {
584 if (confirm(Joomla.JText._('VBO_WANT_PROCEED'))) {
585 var id_thread = message_el.attr('data-idthread');
586 if (!id_thread || !id_thread.length) {
587 return false;
588 }
589
590 // perform the request to flag the thread as no-reply-needed
591 var call_method = 'setNoReplyNeededThread';
592
593 VBOCore.doAjax(
594 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
595 {
596 widget_id: "<?php echo $this->getIdentifier(); ?>",
597 call: call_method,
598 bid: id,
599 id_thread: id_thread,
600 tmpl: "component"
601 },
602 (response) => {
603 try {
604 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
605 if (!obj_res.hasOwnProperty(call_method)) {
606 console.error('Unexpected JSON response', obj_res);
607 return false;
608 }
609
610 // emit the event to close (dismiss) the modal
611 VBOCore.emitEvent(modal_dismiss_event);
612
613 // reload the widget
614 vboWidgetGuestMessagesLoad(message_el.closest('.vbo-dashboard-guests-latest').attr('id'));
615 } catch(err) {
616 console.error('could not parse JSON response', err, response);
617 }
618 },
619 (error) => {
620 console.error(error);
621 }
622 );
623 }
624 });
625 chat_head_title_bot.append(no_reply_needed_el);
626
627 chat_head_title_wrap.append(chat_head_title_top);
628 chat_head_title_wrap.append(chat_head_title_bot);
629
630 chat_head_title.append(chat_head_title_wrap);
631
632 // display modal
633 var chat_modal_body = VBOCore.displayModal({
634 suffix: 'wguestmessages-chat',
635 extra_class: 'vbo-modal-rounded vbo-modal-tall vbo-modal-nofooter',
636 title: chat_head_title,
637 dismiss_event: modal_dismiss_event,
638 onDismiss: () => {
639 if (typeof VCMChat !== 'undefined') {
640 VCMChat.getInstance().destroy();
641 }
642 },
643 loading_event: modal_loading_event,
644 });
645
646 // start loading
647 VBOCore.emitEvent(modal_loading_event);
648
649 // perform the request to render the chat in the apposite modal wrapper
650 var call_method = 'renderChat';
651
652 VBOCore.doAjax(
653 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
654 {
655 widget_id: "<?php echo $this->getIdentifier(); ?>",
656 call: call_method,
657 bid: id,
658 tmpl: "component"
659 },
660 (response) => {
661 // stop loading
662 VBOCore.emitEvent(modal_loading_event);
663
664 try {
665 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
666 if (!obj_res.hasOwnProperty(call_method)) {
667 console.error('Unexpected JSON response', obj_res);
668 return false;
669 }
670
671 // append HTML code to render the chat
672 chat_modal_body.html(obj_res[call_method]);
673
674 // register scroll to bottom with a small delay
675 setTimeout(() => {
676 if (typeof VCMChat !== 'undefined') {
677 VCMChat.getInstance().scrollToBottom();
678 }
679 }, 150);
680 } catch(err) {
681 console.error('could not parse JSON response', err, response);
682 }
683 },
684 (error) => {
685 // stop loading
686 VBOCore.emitEvent(modal_loading_event);
687 // display error
688 console.error(error);
689 alert(error.responseText);
690 }
691 );
692 }
693
694 /**
695 * Display the loading skeletons.
696 */
697 function vboWidgetGuestMessagesSkeletons(wrapper) {
698 var widget_instance = jQuery('#' + wrapper);
699 if (!widget_instance.length) {
700 return false;
701 }
702 widget_instance.find('.vbo-dashboard-guest-messages-list').html('');
703 for (var i = 0; i < <?php echo $this->messages_per_page; ?>; i++) {
704 var skeleton = '';
705 skeleton += '<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">';
706 skeleton += ' <div class="vbo-dashboard-guest-activity-avatar">';
707 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>';
708 skeleton += ' </div>';
709 skeleton += ' <div class="vbo-dashboard-guest-activity-content">';
710 skeleton += ' <div class="vbo-dashboard-guest-activity-content-head">';
711 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>';
712 skeleton += ' </div>';
713 skeleton += ' <div class="vbo-dashboard-guest-activity-content-subhead">';
714 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-subtitle"></div>';
715 skeleton += ' </div>';
716 skeleton += ' <div class="vbo-dashboard-guest-activity-content-info-msg">';
717 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>';
718 skeleton += ' </div>';
719 skeleton += ' </div>';
720 skeleton += '</div>';
721 // append skeleton
722 jQuery(skeleton).appendTo(widget_instance.find('.vbo-dashboard-guest-messages-list'));
723 }
724 }
725
726 /**
727 * Perform the request to load the latest messages.
728 */
729 function vboWidgetGuestMessagesLoad(wrapper, bid_convo) {
730 var widget_instance = jQuery('#' + wrapper);
731 if (!widget_instance.length) {
732 return false;
733 }
734 var current_offset = parseInt(widget_instance.attr('data-offset'));
735 var length_per_page = parseInt(widget_instance.attr('data-length'));
736
737 // the widget method to call
738 var call_method = 'loadMessages';
739
740 // make a request to load the messages
741 VBOCore.doAjax(
742 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
743 {
744 widget_id: "<?php echo $this->getIdentifier(); ?>",
745 call: call_method,
746 return: 1,
747 bid_convo: bid_convo,
748 offset: current_offset,
749 length: length_per_page,
750 wrapper: wrapper,
751 tmpl: "component"
752 },
753 (response) => {
754 try {
755 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
756 if (!obj_res.hasOwnProperty(call_method)) {
757 console.error('Unexpected JSON response', obj_res);
758 return false;
759 }
760
761 // replace HTML with new messages
762 widget_instance.find('.vbo-dashboard-guest-messages-list').html(obj_res[call_method]['html']);
763
764 // check if latest datetime is set
765 if (obj_res[call_method]['latest_dt']) {
766 widget_instance.attr('data-latestdt', obj_res[call_method]['latest_dt']);
767 }
768
769 // check results
770 if (!isNaN(obj_res[call_method]['tot_messages']) && parseInt(obj_res[call_method]['tot_messages']) < 1) {
771 // no results can indicate the offset is invalid or too high
772 if (!isNaN(current_offset) && parseInt(current_offset) > 0) {
773 // reset offset to 0
774 widget_instance.attr('data-offset', 0);
775 // show loading skeletons
776 vboWidgetGuestMessagesSkeletons(wrapper);
777 // reload the first page
778 vboWidgetGuestMessagesLoad(wrapper);
779 }
780 }
781 } catch(err) {
782 console.error('could not parse JSON response', err, response);
783 }
784 },
785 (error) => {
786 // remove the skeleton loading
787 widget_instance.find('.vbo-dashboard-guest-messages-list').find('.vbo-dashboard-guest-activity-skeleton').remove();
788 console.error(error);
789 }
790 );
791 }
792
793 /**
794 * Navigate between the various pages of the messages.
795 */
796 function vboWidgetGuestMessagesNavigate(wrapper, direction) {
797 var widget_instance = jQuery('#' + wrapper);
798 if (!widget_instance.length) {
799 return false;
800 }
801
802 // current offset
803 var current_offset = parseInt(widget_instance.attr('data-offset'));
804
805 // steps per type
806 var steps = <?php echo $this->messages_per_page; ?>;
807
808 // show loading skeletons
809 vboWidgetGuestMessagesSkeletons(wrapper);
810
811 // check direction and update offset for next nav
812 if (direction > 0) {
813 // navigate forward
814 widget_instance.attr('data-offset', (current_offset + steps));
815 } else {
816 // navigate backward
817 var new_offset = current_offset - steps;
818 new_offset = new_offset >= 0 ? new_offset : 0;
819 widget_instance.attr('data-offset', new_offset);
820 }
821
822 // launch navigation
823 vboWidgetGuestMessagesLoad(wrapper);
824 }
825
826 /**
827 * Watch periodically if there are new messages to be displayed.
828 */
829 function vboWidgetGuestMessagesWatch(wrapper) {
830 var widget_instance = jQuery('#' + wrapper);
831 if (!widget_instance.length) {
832 return false;
833 }
834
835 var latest_dt = widget_instance.attr('data-latestdt');
836 if (!latest_dt || !latest_dt.length) {
837 return false;
838 }
839
840 // the widget method to call
841 var call_method = 'watchMessages';
842
843 // make a request to watch the messages
844 VBOCore.doAjax(
845 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
846 {
847 widget_id: "<?php echo $this->getIdentifier(); ?>",
848 call: call_method,
849 latest_dt: latest_dt,
850 tmpl: "component"
851 },
852 (response) => {
853 try {
854 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
855 if (!obj_res.hasOwnProperty(call_method)) {
856 console.error('Unexpected JSON response', obj_res);
857 return false;
858 }
859 // response will contain the number of new messages
860 if (isNaN(obj_res[call_method]) || parseInt(obj_res[call_method]) < 1) {
861 // do nothing
862 return;
863 }
864 // new messages found, reset the offset and re-load the first page
865 widget_instance.attr('data-offset', 0);
866 // show loading skeletons
867 vboWidgetGuestMessagesSkeletons(wrapper);
868 // reload the first page
869 vboWidgetGuestMessagesLoad(wrapper);
870 } catch(err) {
871 console.error('could not parse JSON response', err, response);
872 }
873 },
874 (error) => {
875 // do nothing
876 console.error(error);
877 }
878 );
879 }
880
881 </script>
882 <?php
883 }
884 ?>
885
886 <script type="text/javascript">
887
888 jQuery(function() {
889
890 // when document is ready, load latest messages for this widget's instance
891 vboWidgetGuestMessagesLoad('<?php echo $wrapper_id; ?>', '<?php echo $bid_convo; ?>');
892
893 // make sure we've got no other chat instances on the same page (editorder)
894 if (jQuery('#vbmessagingdiv').length) {
895 if (typeof VCMChat !== 'undefined') {
896 VCMChat.getInstance().destroy();
897 }
898 jQuery('#vbmessagingdiv').html('');
899 }
900
901 // set interval for loading new messages automatically
902 var watch_intv = setInterval(function() {
903 vboWidgetGuestMessagesWatch('<?php echo $wrapper_id; ?>');
904 }, 60000);
905
906 <?php
907 if ($js_intvals_id) {
908 // widget can be dismissed through the modal
909 ?>
910 document.addEventListener(VBOCore.widget_modal_dismissed + '<?php echo $js_intvals_id; ?>', (e) => {
911 // clear interval for notifications
912 clearInterval(watch_intv);
913
914 if (jQuery('#vbmessagingdiv').length) {
915 // reload the page for the previously removed chat in the editorder page
916 location.reload();
917 }
918 });
919 <?php
920 }
921 ?>
922
923 });
924
925 </script>
926
927 <?php
928 }
929 }
930