widgetName = JText::translate('VBO_W_GUESTMESSAGES_TITLE'); $this->widgetDescr = JText::translate('VBO_W_GUESTMESSAGES_DESCR'); $this->widgetId = basename(__FILE__, '.php'); // define widget and icon and style name $this->widgetIcon = ''; $this->widgetStyleName = 'light-orange'; // today Y-m-d date $this->today_ymd = date('Y-m-d'); // the path to the VCM library $this->vcm_lib_path = VCM_SITE_PATH . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'lib.vikchannelmanager.php'; // whether VCM is available if (is_file($this->vcm_lib_path)) { if (!class_exists('VikChannelManager') || !method_exists('VikChannelManager', 'getLatestFromGuests')) { // VCM is outdated $this->vcm_exists = false; } // attempt to require the chat handler try { VikBooking::getVcmChatInstance($oid = 0, $channel = null); } catch (Exception $e) { // do nothing } // make sure VCM is up to date for this widget if (!class_exists('VCMChatHandler') || !method_exists('VCMChatHandler', 'loadChatAssets')) { // VCM is outdated (>= 1.8.11 required) $this->vcm_exists = false; } } else { $this->vcm_exists = false; } // avoid queries on certain pages, as VCM may not have been activated yet if (VBOPlatformDetection::isWordPress() && $this->vcm_exists) { global $pagenow; if (isset($pagenow) && in_array($pagenow, ['update.php', 'plugins.php', 'plugin-install.php'])) { $this->vcm_exists = false; } } } /** * Preload the necessary CSS/JS assets from VCM. * * @return void */ public function preload() { if ($this->vcm_exists) { // load chat assets from VCM VCMChatHandler::loadChatAssets(); // additional language defs JText::script('VBO_NO_REPLY_NEEDED'); JText::script('VBO_WANT_PROCEED'); } } /** * Custom method for this widget only to load the latest guest messages. * The method is called by the admin controller through an AJAX request. * The visibility should be public, it should not exit the process, and * an associative array is returned thanks to the request value "return":1. * * It's the actual rendering of the widget which also allows navigation. */ public function loadMessages() { $bid_convo = VikRequest::getString('bid_convo', '', 'request'); $offset = VikRequest::getInt('offset', 0, 'request'); $length = VikRequest::getInt('length', $this->messages_per_page, 'request'); $wrapper = VikRequest::getString('wrapper', '', 'request'); if (!$this->vcm_exists) { VBOHttpDocument::getInstance()->close(500, 'Vik Channel Manager is either not available or outdated'); } // load latest messages try { $latest_messages = VikChannelManager::getLatestFromGuests(['guest_messages'], $offset, $length); } catch (Exception $e) { // do nothing $latest_messages = []; } // the multitask data and notifications can request a specific conversation to be opened $bubble_convo = null; if ($bid_convo) { // make sure the requested booking ID was fetched from the most recent guest messages foreach ($latest_messages as $gmessage) { if ($bid_convo == $gmessage->idorder) { // specific conversation to bubble found $bubble_convo = $bid_convo; break; } if ($bid_convo == $gmessage->idorderota && strcasecmp((string)$gmessage->channel, 'vikbooking')) { // specific OTA conversation to bubble found $bubble_convo = $bid_convo; break; } } if (!$bubble_convo && class_exists('VCMChatMessaging')) { // updated VCM versions will allow us to fetch one conversation by booking ID $booking_messages = VCMChatMessaging::getInstance()->loadBookingGuestThreads($bid_convo); if ($booking_messages) { // append the requested conversation so that it will bubble $latest_messages = array_merge($latest_messages, $booking_messages); // turn flag on $bubble_convo = $bid_convo; } } } // current year Y and timestamp $current_y = date('Y'); $current_ts = time(); // start output buffering ob_start(); foreach ($latest_messages as $gmessage) { $gmessage_content = $gmessage->content; if (empty($gmessage_content)) { $gmessage_content = '.....'; } elseif (strlen($gmessage_content) > 90) { if (function_exists('mb_substr')) { $gmessage_content = mb_substr($gmessage_content, 0, 90, 'UTF-8'); } else { $gmessage_content = substr($gmessage_content, 0, 90); } $gmessage_content .= '...'; } ?>
guest_avatar)) { // highest priority goes to the profile picture, not always available ?> pic)) { // customer profile picture is not the same as the photo avatar ?> channel_logo)) { // channel logo goes as second option ?>
0) { // show backward navigation button ?>
0 && $offset === 0 ? $latest_messages[0]->last_updated : null; // get the HTML buffer $html_content = ob_get_contents(); ob_end_clean(); // return an associative array of values return [ 'html' => $html_content, 'tot_messages' => $tot_messages, 'offset' => ($offset + $length), 'latest_dt' => $latest_datetime, ]; } /** * Custom method for this widget only to watch the latest guest messages. * The method is called by the admin controller through an AJAX request. * The visibility should be public, it should not exit the process, and * any content sent to output will be returned to the AJAX response. * * Outputs the new number of messages found from the latest datetime. */ public function watchMessages() { $latest_dt = VikRequest::getString('latest_dt', '', 'request'); if (empty($latest_dt)) { echo '0'; return; } if (!$this->vcm_exists) { VBOHttpDocument::getInstance()->close(500, 'Vik Channel Manager is either not available or outdated'); } // load the latest guest message (one is sufficient) $latest_messages = []; try { $latest_messages = VikChannelManager::getLatestFromGuests(['guest_messages'], 0, 1); } catch (Exception $e) { // do nothing } if (!count($latest_messages) || $latest_messages[0]->last_updated == $latest_dt) { // no newest messages found echo '0'; return; } // print 1 to indicate that new messages should be reloaded echo '1'; } /** * Custom method for this widget only to render the chat of a booking. * The method is called by the admin controller through an AJAX request. * The visibility should be public, it should not exit the process, and * no values should be returned, as the response must be sent to output * in case the JS/CSS assets will be echoed within the response. * * Returns the necessary HTML code to render the chat. */ public function renderChat() { $bid = VikRequest::getInt('bid', 0, 'request'); $booking = VikBooking::getBookingInfoFromID($bid); if (!$booking) { VBOHttpDocument::getInstance()->close(404, 'Could not find booking'); } // initialize chat instance by getting the proper channel name if (empty($booking['channel'])) { // front-end reservation chat handler $chat_channel = 'vikbooking'; } else { $channelparts = explode('_', $booking['channel']); // check if this is a meta search channel $is_meta_search = false; if (preg_match("/(customer).*[0-9]$/", $channelparts[0]) || !strcasecmp($channelparts[0], 'googlehotel') || !strcasecmp($channelparts[0], 'trivago')) { $is_meta_search = empty($booking['idorderota']); } if ($is_meta_search) { // customer of type sales channel should use front-end reservation chat handler $chat_channel = 'vikbooking'; } else { // let the getInstance method validate the channel chat handler $chat_channel = $booking['channel']; } } $messaging = VikBooking::getVcmChatInstance($booking['id'], $chat_channel); if (is_null($messaging)) { VBOHttpDocument::getInstance()->close(500, 'Could not render chat'); } // send content to output echo $messaging->renderChat([ 'hideThreads' => 1, ], $load_assets = false); } /** * Custom method for this widget only to update the thread of a booking. * The method is called by the admin controller through an AJAX request. * The visibility should be public, it should not exit the process, and * no values should be returned, as the response must be sent to output. * * Returns a successful string or throws an error. */ public function setNoReplyNeededThread() { $bid = VikRequest::getInt('bid', 0, 'request'); $id_thread = VikRequest::getInt('id_thread', 0, 'request'); $booking = VikBooking::getBookingInfoFromID($bid); if (!$booking || empty($id_thread)) { VBOHttpDocument::getInstance()->close(404, 'Could not find booking thread'); } // build thread object for update $thread = new stdClass; $thread->id = $id_thread; $thread->idorder = $bid; $thread->no_reply_needed = 1; $dbo = JFactory::getDbo(); if (!$dbo->updateObject('#__vikchannelmanager_threads', $thread, ['id', 'idorder'])) { VBOHttpDocument::getInstance()->close(500, 'Could not update thread'); } echo '1'; } public function render(VBOMultitaskData $data = null) { // increase widget's instance counter static::$instance_counter++; // check whether the widget is being rendered via AJAX when adding it through the customizer $is_ajax = $this->isAjaxRendering(); // generate a unique ID for the guest messages wrapper instance $wrapper_instance = !$is_ajax ? static::$instance_counter : rand(); $wrapper_id = 'vbo-widget-guest-messages-' . $wrapper_instance; // this widget will work only if VCM is available and updated, and if permissions are met $vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking'); if (!$this->vcm_exists || !$vbo_auth_bookings) { return; } // multitask data event identifier for clearing intervals $js_intvals_id = ''; $bid_convo = 0; if ($data && $data->isModalRendering()) { // access Multitask data $js_intvals_id = $data->getModalJsIdentifier(); // check if a specific conversation should be opened if ($data->get('id_message', 0)) { $bid_convo = $data->getBookingId(); } else { $bid_convo = $this->options()->fetchBookingId(); } } ?>

widgetIcon; ?> widgetName; ?>

messages_per_page; $i++) { ?>