File "operatortool.php"
Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/site/controllers/operatortool.php
File size: 20.36 KB
MIME-type: text/x-php
Charset: utf-8
<?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,
]);
}
}