Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
wp-content
/
plugins
/
vikbooking
/
site
/
controllers
:
operatortool.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * @package VikBooking * @subpackage com_vikbooking * @author Alessio Gaggii - E4J srl * @copyright Copyright (C) 2025 E4J srl. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE * @link https://vikwp.com */ // No direct access to this file defined('ABSPATH') or die('No script kiddies please!'); /** * VikBooking operator-tool controller. * * @since 1.17.6 (J) - 1.7.6 (WP) */ class VikBookingControllerOperatortool extends JControllerAdmin { /** * AJAX endpoint for the tool "guest_messaging" to load threads assigned * to manageable listing IDs by the currently logged operator account. */ public function loadGuestThreads() { $app = JFactory::getApplication(); if (!JSession::checkToken()) { // missing CSRF-proof token VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN')); } // the name of the tool to access $tool = 'guest_messaging'; try { // obtain data from the validation of the current operator and tool permissions list($operator, $permissions, $tool_uri) = VikBooking::getOperatorInstance()->authOperatorToolData($tool); } catch (Exception $e) { // abort VBOHttpDocument::getInstance($app)->close($e->getCode(), $e->getMessage()); } // get listings assigned to the current operator (no listings equals to all listings) $listings = array_filter( array_map('intval', (array) $permissions->get('rooms', [])) ); // attempt to require the chat handler from VCM try { VikBooking::getVcmChatInstance($oid = 0, $channel = null); } catch (Throwable $e) { // propagate the error code VBOHttpDocument::getInstance()->close($e->getCode(), 'Channel Manager not available.'); } // make sure VCM is available if (!class_exists('VCMChatHandler')) { // raise an error VBOHttpDocument::getInstance()->close(500, 'Channel Manager not available.'); } // current year Y and timestamp $current_y = date('Y'); $today_ymd = date('Y-m-d'); $current_ts = time(); $nowdf = VikBooking::getDateFormat(true); if ($nowdf == "%d/%m/%Y") { $df = 'd/m/Y'; } elseif ($nowdf == "%m/%d/%Y") { $df = 'm/d/Y'; } else { $df = 'Y/m/d'; } // load latest threads for the operator manageable listings $threads = VCMChatHandler::getLatestThreads([ 'sender' => 'guest', 'join_sender' => true, 'rooms' => $listings, 'start' => $app->input->getUInt('start', 0), 'limit' => $app->input->getUInt('limit', 20), ]); // map the threads by fetching the channel logo, if available $threads = array_map(function($thread) { if (empty($thread->channel) || !strcasecmp($thread->channel, 'vikbooking')) { return $thread; } $channel_logo = VikChannelManager::getLogosInstance($thread->channel)->getSmallLogoURL(); if (!empty($channel_logo)) { $thread->channel_logo = $channel_logo; } return $thread; }, $threads); // map the threads by appending the message date/time and status information $threads = array_map(function($thread) use ($df, $current_y, $today_ymd) { $str_checkin = ''; $str_checkout = ''; if (!empty($thread->b_checkin)) { $stay_info_in = getdate($thread->b_checkin); $stay_info_out = getdate($thread->b_checkout); $str_checkin = date('d', $thread->b_checkin); $str_checkin .= $stay_info_in['mon'] != $stay_info_out['mon'] ? ' ' . VikBooking::sayMonth($stay_info_in['mon'], $short = true) : ''; $str_checkout = date('d', $thread->b_checkout) . ' ' . VikBooking::sayMonth($stay_info_out['mon'], $short = true); if ($stay_info_in['year'] != $stay_info_out['year'] || $stay_info_in['year'] != $current_y || $stay_info_out['year'] != $current_y) { $str_checkout .= ' ' . $stay_info_in['year']; } } $thread->message_info = [ 'current_ts' => $current_ts, 'str_checkin' => $str_checkin, 'str_checkout' => $str_checkout, 'time' => JHtml::fetch('date', $thread->last_updated, 'H:i'), 'date' => JHtml::fetch('date', $thread->last_updated, str_replace('/', VikBooking::getDateSeparator(), $df)), 'is_today' => (JHtml::fetch('date', $thread->last_updated, 'Y-m-d') == $today_ymd), ]; return $thread; }, $threads); // send response to output VBOHttpDocument::getInstance($app)->json($threads); } /** * AJAX endpoint for the tool "guest_messaging" to render the chat for a booking made * for at least one manageable listing ID by the currently logged operator account. */ public function renderGuestBookingChat() { $app = JFactory::getApplication(); if (!JSession::checkToken()) { // missing CSRF-proof token VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN')); } // the name of the tool to access $tool = 'guest_messaging'; try { // obtain data from the validation of the current operator and tool permissions list($operator, $permissions, $tool_uri) = VikBooking::getOperatorInstance()->authOperatorToolData($tool); } catch (Exception $e) { // abort VBOHttpDocument::getInstance($app)->close($e->getCode(), $e->getMessage()); } // get listings assigned to the current operator (no listings equals to all listings) $listings = array_filter( array_map('intval', (array) $permissions->get('rooms', [])) ); // the booking ID for which the chat should be rendered $bid = $app->input->getUInt('bid', 0); if (!$bid) { VBOHttpDocument::getInstance($app)->close(400, 'Missing booking ID.'); } $booking = VikBooking::getBookingInfoFromID($bid); if (!$booking) { VBOHttpDocument::getInstance($app)->close(404, 'Booking record not found.'); } $booking_rooms = VikBooking::loadOrdersRoomsData($booking['id']); $booking_room_ids = array_column($booking_rooms, 'idroom'); if (!$booking_rooms || !$booking_room_ids) { VBOHttpDocument::getInstance($app)->close(404, 'No booking rooms found.'); } // ensure at least one listing of this booking is manageable by the operator permissions if ($listings && !array_intersect($listings, $booking_room_ids)) { VBOHttpDocument::getInstance($app)->close(403, 'Cannot access booking conversation.'); } // 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], 'googlevr') || !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($app)->close(500, 'Could not render chat.'); } // send response to output $chat = $messaging->renderChat([ 'hideThreads' => 1, ], $load_assets = false); VBOHttpDocument::getInstance($app)->json(['html' => $chat]); } /** * AJAX endpoint for the tool "guest_messaging" to load the listing details for a booking * made for at least one manageable listing ID by the currently logged operator account. */ public function loadGuestBookingListings() { $app = JFactory::getApplication(); if (!JSession::checkToken()) { // missing CSRF-proof token VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN')); } // the name of the tool to access $tool = 'guest_messaging'; try { // obtain data from the validation of the current operator and tool permissions list($operator, $permissions, $tool_uri) = VikBooking::getOperatorInstance()->authOperatorToolData($tool); } catch (Exception $e) { // abort VBOHttpDocument::getInstance($app)->close($e->getCode(), $e->getMessage()); } // get listings assigned to the current operator (no listings equals to all listings) $listings = array_filter( array_map('intval', (array) $permissions->get('rooms', [])) ); // the booking ID for which the listing details should be fetched $bid = $app->input->getUInt('bid', 0); $booking_rooms = VikBooking::loadOrdersRoomsData($bid); $booking_room_ids = array_column($booking_rooms, 'idroom'); if (!$booking_rooms || !$booking_room_ids) { VBOHttpDocument::getInstance($app)->close(404, 'No booking rooms found.'); } // ensure at least one listing of this booking is manageable by the operator permissions if ($listings && !array_intersect($listings, $booking_room_ids)) { VBOHttpDocument::getInstance($app)->close(403, 'Cannot access booking or listing details.'); } // send response to output VBOHttpDocument::getInstance($app)->json(['listings' => array_column($booking_rooms, 'room_name')]); } /** * AJAX endpoint for the tool "guest_messaging" to toggle the no-reply-needed thread status for a * booking made for at least one manageable listing ID by the currently logged operator account. */ public function toggleGuestThreadNoReplyNeeded() { $app = JFactory::getApplication(); $dbo = JFactory::getDbo(); if (!JSession::checkToken()) { // missing CSRF-proof token VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN')); } // the name of the tool to access $tool = 'guest_messaging'; try { // obtain data from the validation of the current operator and tool permissions list($operator, $permissions, $tool_uri) = VikBooking::getOperatorInstance()->authOperatorToolData($tool); } catch (Exception $e) { // abort VBOHttpDocument::getInstance($app)->close($e->getCode(), $e->getMessage()); } // get listings assigned to the current operator (no listings equals to all listings) $listings = array_filter( array_map('intval', (array) $permissions->get('rooms', [])) ); // the booking ID for which the listing details should be fetched $bid = $app->input->getUInt('bid', 0); $booking_rooms = VikBooking::loadOrdersRoomsData($bid); $booking_room_ids = array_column($booking_rooms, 'idroom'); if (!$booking_rooms || !$booking_room_ids) { VBOHttpDocument::getInstance($app)->close(404, 'No booking rooms found.'); } // ensure at least one listing of this booking is manageable by the operator permissions if ($listings && !array_intersect($listings, $booking_room_ids)) { VBOHttpDocument::getInstance($app)->close(403, 'Cannot access booking or listing details.'); } // gather thread ID and toggle status $id_thread = $app->input->getUInt('id_thread', 0); $status = $app->input->getUInt('status', 0); if (empty($id_thread)) { VBOHttpDocument::getInstance($app)->close(400, 'Missing booking thread.'); } // update the information on the database $dbo->setQuery( $dbo->getQuery(true) ->update($dbo->qn('#__vikchannelmanager_threads')) ->set($dbo->qn('no_reply_needed') . ' = ' . (!$status ? 1 : 0)) ->where($dbo->qn('id') . ' = ' . $id_thread) ->where($dbo->qn('idorder') . ' = ' . $bid) ); $dbo->execute(); // send response to output VBOHttpDocument::getInstance($app)->json(['status' => (!$status ? 1 : 0)]); } /** * AJAX endpoint for the tool "guest_messaging" to check for new thread messages. */ public function checkNewGuestThreads() { $app = JFactory::getApplication(); if (!JSession::checkToken()) { // missing CSRF-proof token VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN')); } // the name of the tool to access $tool = 'guest_messaging'; try { // obtain data from the validation of the current operator and tool permissions list($operator, $permissions, $tool_uri) = VikBooking::getOperatorInstance()->authOperatorToolData($tool); } catch (Exception $e) { // abort VBOHttpDocument::getInstance($app)->close($e->getCode(), $e->getMessage()); } // get listings assigned to the current operator (no listings equals to all listings) $listings = array_filter( array_map('intval', (array) $permissions->get('rooms', [])) ); // attempt to require the chat handler from VCM try { VikBooking::getVcmChatInstance($oid = 0, $channel = null); } catch (Throwable $e) { // propagate the error code VBOHttpDocument::getInstance()->close($e->getCode(), 'Channel Manager not available.'); } // make sure VCM is available if (!class_exists('VCMChatHandler')) { // raise an error VBOHttpDocument::getInstance()->close(500, 'Channel Manager not available.'); } // load the latest thread message for the operator manageable listings $threads = VCMChatHandler::getLatestThreads([ 'sender' => 'guest', 'join_sender' => true, 'rooms' => $listings, 'start' => $app->input->getUInt('start', 0), 'limit' => $app->input->getUInt('limit', 1), ]); if (!$threads) { // no thread messages at all VBOHttpDocument::getInstance()->json(['newThreads' => []]); } // get the latest date requested $last_date = $app->input->getString('last_date'); if (!$last_date) { // all thread messages are considered as new VBOHttpDocument::getInstance()->json(['newThreads' => $threads]); } // filter threads based on the given last date $threads = array_filter($threads, function($thread) use ($last_date) { return isset($thread->last_updated) && strtotime($thread->last_updated) > strtotime($last_date); }); // reset keys $threads = array_values($threads); // send response to output VBOHttpDocument::getInstance()->json(['newThreads' => $threads]); } /** * Base endpoint to generate an iCal calendar file for the task manager events (tasks) * of an operator. The authentication is performed through GET via the iCal URL. * * @since 1.18.0 (J) - 1.8.0 (WP) */ public function tm_ical() { $app = JFactory::getApplication(); // gather request values for authentication (base64(id:md5(auth_code))) $operator_signature = base64_decode(urldecode((string) $app->input->getBase64('opsid', ''))); // validate signature syntax if (!preg_match('/^([0-9]+):([0-9a-f]{32})$/i', $operator_signature, $matches)) { VBOHttpDocument::getInstance($app)->close(400, 'Bad URL.'); } $operatorId = (int) $matches[1]; $operatorHashedCode = $matches[2]; // get the operator record by ID $record = VikBooking::getOperatorInstance()->getOne($operatorId); // attempt to perform a manual authentication if (!$record || md5((string) $record['code']) != $operatorHashedCode || !VikBooking::getOperatorInstance()->authOperator($record['code'])) { VBOHttpDocument::getInstance($app)->close(401, 'Unauthorized'); } // the name of the tool to access $tool = 'task_manager'; try { // obtain data from the validation of the current operator and tool permissions list($operator, $permissions, $tool_uri) = VikBooking::getOperatorInstance()->authOperatorToolData($tool); } catch (Exception $e) { // abort VBOHttpDocument::getInstance($app)->close($e->getCode(), $e->getMessage()); } // check from the permissions whether tasks can be accepted by the operator, // hence null assigness should be included - use a different filter otherwise $tasksFilterName = ((bool) $permissions->get('accept_tasks', 0)) ? 'operator' : 'assignee'; // get operator (future) tasks list for one year worth of dates $tasks = VBOTaskModelTask::getInstance()->filterItems([ $tasksFilterName => $operator['id'], 'dates' => sprintf('%s:%s', date('Y-m-d'), date('Y-m-d', strtotime('+1 year'))), ]); // build and downlaod the calendar content VBOTaskOperatorIcal::getInstance() ->setCalendarSubscriber($app->input->getString('sub')) ->setOperator($operator) ->setPermissions($permissions) ->setTool($tool) ->setToolUri($tool_uri) ->setEvents($tasks) ->download($app); // close the application $app->close(); } /** * AJAX endpoint to render a layout file from an operator tool. * * @return void */ public function renderLayout() { $app = JFactory::getApplication(); if (!JSession::checkToken()) { // missing CSRF-proof token VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN')); } $tool = $app->input->getString('tool', ''); $type = $app->input->getString('type', ''); $data = (array) $app->input->get('data', [], 'array'); try { // obtain data from the validation of the current operator and tool permissions list($operator, $permissions, $tool_uri) = VikBooking::getOperatorInstance()->authOperatorToolData($tool); } catch (Exception $e) { // abort VBOHttpDocument::getInstance($app)->close($e->getCode(), $e->getMessage()); } if (empty($type)) { // invalid layout requested VBOHttpDocument::getInstance($app)->close(404, sprintf('Could not find the layout [%s] to render.', $type)); } // fetch the requested layout $layout_data = [ 'tool' => $tool, 'operator' => $operator, 'permissions' => $permissions, 'tool_uri' => $tool_uri, 'data' => $data, ]; try { $layout_html = JLayoutHelper::render($type, $layout_data, null, [ 'component' => 'com_vikbooking', 'client' => 'site', ]); } catch (Exception $e) { // raise the error caught VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage()); } // send the response to output VBOHttpDocument::getInstance($app)->json([ 'html' => $layout_html, ]); } }