widgetName = JText::translate('VBMAINORDEREDIT'); $this->widgetDescr = JText::translate('JSEARCH_TOOLS'); $this->widgetId = basename(__FILE__, '.php'); // define widget and icon and style name $this->widgetIcon = ''; $this->widgetStyleName = 'light-blue'; } /** * Preload the necessary assets. * * @return void */ public function preload() { // load assets for contextual menu $this->vbo_app->loadContextMenuAssets(); // JS lang def JText::script('VBODASHSEARCHKEYS'); JText::script('VIKLOADING'); JText::script('VBDASHUPRESONE'); JText::script('VBOCHANNEL'); JText::script('VBCOUPON'); JText::script('VBCUSTOMERNOMINATIVE'); JText::script('VBO_COPY'); JText::script('VBO_COPIED'); JText::script('VBO_CONF_RM_OVERBOOKING_FLAG'); } /** * Custom method for this widget only to load the booking details. * 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. * In this case we return an array because this method requires "return":1. */ public function loadBookingDetails() { $app = JFactory::getApplication(); $dbo = JFactory::getDbo(); $wrapper = $app->input->getString('wrapper', ''); $booking_key = $app->input->getString('booking_key', ''); $booking_id = $app->input->getString('bid', ''); if (empty($booking_key) && empty($booking_id)) { // no bookings found VBOHttpDocument::getInstance()->close(400, JText::translate('VBOREPORTSERRNORESERV')); } $q = $dbo->getQuery(true) ->select($dbo->qn('o') . '.*') ->from($dbo->qn('#__vikbooking_orders', 'o')); if (!empty($booking_id)) { // search by booking ID or OTA booking ID only if (preg_match("/^[0-9]+$/", (string)$booking_id)) { // only numbers could be both website and OTA $q->where([ $dbo->qn('o.id') . ' = ' . (int)$booking_id, $dbo->qn('o.idorderota') . ' = ' . $dbo->q($booking_id), ], $glue = 'OR'); } else { // alphanumeric IDs can only belong to an OTA reservation $q->where($dbo->qn('o.idorderota') . ' = ' . $dbo->q($booking_id)); } } else { // search by different values $q->where(1); if (stripos($booking_key, 'id:') === 0) { // search by ID or OTA ID $seek_parts = explode('id:', $booking_key); $seek_value = trim($seek_parts[1]); $q->andWhere([ $dbo->qn('o.id') . ' = ' . $dbo->q($seek_value), $dbo->qn('o.idorderota') . ' = ' . $dbo->q($seek_value), ], $glue = 'OR'); } elseif (stripos($booking_key, 'otaid:') === 0) { // search by OTA Booking ID $seek_parts = explode('otaid:', $booking_key); $seek_value = trim($seek_parts[1]); $q->where($dbo->qn('o.idorderota') . ' = ' . $dbo->q($seek_value)); } elseif (stripos($booking_key, 'coupon:') === 0) { // search by coupon code $seek_parts = explode('coupon:', $booking_key); $seek_value = trim($seek_parts[1]); $q->where($dbo->qn('o.coupon') . ' LIKE ' . $dbo->q("%{$seek_value}%")); } elseif (stripos($booking_key, 'name:') === 0) { // search by customer nominative $seek_parts = explode('name:', $booking_key); $seek_value = trim($seek_parts[1]); $q->leftJoin($dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $dbo->qn('co.idorder') . ' = ' . $dbo->qn('o.id')); $q->leftJoin($dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $dbo->qn('c.id') . ' = ' . $dbo->qn('co.idcustomer')); $q->where('CONCAT_WS(\' \', ' . $dbo->qn('c.first_name') . ', ' . $dbo->qn('c.last_name') . ') LIKE ' . $dbo->q("%{$seek_value}%")); } else { // seek for various values $q->andWhere([ $dbo->qn('o.id') . ' = ' . $dbo->q($booking_key), $dbo->qn('o.confirmnumber') . ' = ' . $dbo->q($booking_key), $dbo->qn('o.idorderota') . ' = ' . $dbo->q($booking_key), ], $glue = 'OR'); } } $q->order($dbo->qn('id') . ' DESC'); $dbo->setQuery($q, 0, 1); $details = $dbo->loadAssoc(); if (!$details) { // no bookings found VBOHttpDocument::getInstance()->close(404, JText::translate('VBOREPORTSERRNORESERV')); } // get customer information $cpin = VikBooking::getCPinInstance(); $customer = $cpin->getCustomerFromBooking($details['id']); $customer_bookings_count = 0; if ($customer && !empty($customer['country'])) { if (is_file(implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'resources', 'countries', $customer['country'] . '.png']))) { $customer['country_img'] = ''; } // count customer bookings (any status) $customer_bookings_count = $cpin->countCustomerBookings((int) $customer['id']); } // availability helper $av_helper = VikBooking::getAvailabilityInstance(); // get rooms $booking_rooms = VikBooking::loadOrdersRoomsData($details['id']); // guests and tariffs information $tars = []; $guests = []; $tot_adults = 0; $tot_children = 0; $tot_pets = 0; foreach ($booking_rooms as $k => $booking_room) { $guests[] = [ 'adults' => $booking_room['adults'], 'children' => $booking_room['children'], 'pets' => $booking_room['pets'], 't_first_name' => $booking_room['t_first_name'], 't_last_name' => $booking_room['t_last_name'], ]; if (!$k || !$details['split_stay']) { $tot_adults += $booking_room['adults']; $tot_children += $booking_room['children']; $tot_pets += $booking_room['pets']; } $q = $dbo->getQuery(true) ->select('*') ->from($dbo->qn('#__vikbooking_dispcost')) ->where($dbo->qn('id') . ' = ' . (int)$booking_room['idtar']); $dbo->setQuery($q, 0, 1); $tars[($k + 1)] = $dbo->loadAssocList(); } // room stay dates in case of split stay (or modified room nights) $room_stay_dates = []; $room_stay_records = []; if ($details['split_stay']) { if ($details['status'] == 'confirmed') { $room_stay_dates = $av_helper->loadSplitStayBusyRecords($details['id']); } else { $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $details['id'], []); } } elseif (!$details['split_stay'] && $details['roomsnum'] > 1 && $details['days'] > 1 && $details['status'] == 'confirmed') { // load the occupied stay dates for each room in case they were modified $room_stay_records = $av_helper->loadSplitStayBusyRecords($details['id']); } // currency $otacurrency = !empty($details['channel']) && !empty($details['chcurrency']) ? $details['chcurrency'] : ''; $currencysymb = VikBooking::getCurrencySymb(); // channel name and reservation ID $otachannel_name = JText::translate('VBORDFROMSITE'); $otachannel_bid = ''; if (!empty($details['channel'])) { $channelparts = explode('_', $details['channel']); $otachannel = isset($channelparts[1]) && strlen((string)$channelparts[1]) ? $channelparts[1] : ucwords($channelparts[0]); $otachannel_name = $otachannel; $otachannel_bid = !empty($details['idorderota']) ? $details['idorderota'] : ''; } // readable dates $checkin_info = getdate($details['checkin']); $checkin_wday = JText::translate('VB'.strtoupper(substr($checkin_info['weekday'], 0, 3))); $checkout_info = getdate($details['checkout']); $checkout_wday = JText::translate('VB'.strtoupper(substr($checkout_info['weekday'], 0, 3))); $checkin_read_dt = $checkin_wday . ', ' . implode(' ', [$checkin_info['mday'], VikBooking::sayMonth($checkin_info['mon']), $checkin_info['year'], date('H:i', $details['checkin'])]); $checkout_read_dt = $checkout_wday . ', ' . implode(' ', [$checkout_info['mday'], VikBooking::sayMonth($checkout_info['mon']), $checkout_info['year'], date('H:i', $details['checkout'])]); // check for special requests $special_requests = VBOModelReservation::getInstance($details)->extractSpecialRequests(); // check for guest messaging $messaging_supported = class_exists('VCMChatMessaging'); $tot_guest_messages = 0; $tot_unread_messages = 0; $last_guest_messages = []; if ($messaging_supported) { $messaging_handler = VCMChatMessaging::getInstance($details); $tot_guest_messages = $messaging_handler->countBookingGuestMessages(); if ($tot_guest_messages) { $tot_unread_messages = $messaging_handler->countBookingGuestMessages($unread = true); if (method_exists($messaging_handler, 'loadBookingLatestMessages')) { /** * At the moment we save a query and we do not display * a snapshot of the latest guest messages. */ // $last_guest_messages = $messaging_handler->loadBookingLatestMessages($details['id'], 0, 5); } } } // load the Channel Manager notifications for this booking, if any $cm_notifications = []; $cm_channels = []; if (class_exists('VikChannelManager') && VikChannelManager::isAvailabilityRequest($api_channel = true)) { list($cm_notifications, $cm_channels) = $this->loadChannelManagerNotifications($details); } // check if we got data by clicking on a push/web notification $from_push = $this->options()->gotPushData(); // start output buffering ob_start(); ?>
getSmallLogoURL() : ''; $custpic_used = false; ?>
' . $status_type . ''; } $status_type .= ' / '; } $extra_status = $details['refund'] > 0 ? ' / ' . JText::translate('VBO_STATUS_REFUNDED') : ''; if ($details['status'] == "confirmed") { $saystaus = '' . $status_type . JText::translate('VBCONFIRMED') . $extra_status . ''; } elseif ($details['status'] == "standby") { $saystaus = '' . $status_type . JText::translate('VBSTANDBY') . $extra_status . ''; } else { $saystaus = '' . $status_type . JText::translate('VBCANCELLED') . $extra_status . ''; } echo $saystaus; if ($details['closure']) { ?>
1 ? 'VBMAILADULTS' : 'VBMAILADULT')); } if ($tot_children) { $guest_counters[] = $tot_children . ' ' . JText::translate(($tot_children > 1 ? 'VBMAILCHILDREN' : 'VBMAILCHILD')); } if ($tot_pets) { $guest_counters[] = $tot_pets . ' ' . JText::translate(($tot_pets > 1 ? 'VBO_PETS' : 'VBO_PET')); } echo implode(', ', $guest_counters); ?>
datesep, $this->df), $details['checkin']); ?>
datesep, $this->df), $details['checkout']); ?> ( 1 ? JText::translate('VBDAYS') : JText::translate('VBDAY')); ?>)
datesep, $this->df) . ' H:i', $details['ts']); ?>
setBooking($details); $short_url = $model->getShortUrl($book_link); ?>
 
 
 
$booking_room) { $num = $ind + 1; $room_icon = $details['split_stay'] && $ind > 0 ? 'random' : 'bed'; ?>
$rfeatures) { if ($rind != $booking_room['roomindex']) { continue; } foreach ($rfeatures as $fname => $fval) { if (strlen((string)$fval)) { $room_number = '#' . $fval; break 2; } } } } } if ($room_number) { ?>
1 && !$details['split_stay'] && isset($guests[$ind])) { // print guest details for this room $room_guest_counters = []; if (!empty($guests[$ind]['t_first_name']) || !empty($guests[$ind]['t_last_name'])) { $room_guest_counters[] = trim($guests[$ind]['t_first_name'] . ' ' . $guests[$ind]['t_last_name']); } if ($guests[$ind]['adults']) { $room_guest_counters[] = $guests[$ind]['adults'] . ' ' . JText::translate(($guests[$ind]['adults'] > 1 ? 'VBMAILADULTS' : 'VBMAILADULT')); } if ($guests[$ind]['children']) { $room_guest_counters[] = $guests[$ind]['children'] . ' ' . JText::translate(($guests[$ind]['children'] > 1 ? 'VBMAILCHILDREN' : 'VBMAILCHILD')); } if ($guests[$ind]['pets']) { $room_guest_counters[] = $guests[$ind]['pets'] . ' ' . JText::translate(($guests[$ind]['pets'] > 1 ? 'VBO_PETS' : 'VBO_PET')); } ?>
0) { if (!empty($booking_room['pkg_name'])) { // package echo $booking_room['pkg_name']; } else { // custom cost can have an OTA Rate Plan name if (!empty($booking_room['otarplan'])) { echo ucwords($booking_room['otarplan']); } else { echo JText::translate('VBOROOMCUSTRATEPLAN'); } } } elseif (!empty($tars[$num]) && !empty($tars[$num][0]['idprice'])) { $active_rplan_id = $tars[$num][0]['idprice']; echo VikBooking::getPriceName($tars[$num][0]['idprice']); } elseif (!empty($booking_room['otarplan'])) { echo ucwords($booking_room['otarplan']); } elseif (!$details['closure']) { echo JText::translate('VBOROOMNORATE'); } ?>
roomRateIncludedMeals($booking_room); } else { // fetch default included meals in the selected rate plan $included_meals = $active_rplan_id ? VBOMealplanManager::getInstance()->ratePlanIncludedMeals($active_rplan_id) : []; } if (!$included_meals && empty($booking_room['meals']) && !empty($details['idorderota']) && !empty($details['channel']) && !empty($details['custdata'])) { // attempt to fetch the included meal plans from the raw customer data or OTA reservation and room $included_meals = VBOMealplanManager::getInstance()->otaDataIncludedMeals($details, $booking_room); } if ($included_meals) { ?>
countNightsOfStay($room_stay_checkin, $room_stay_checkout); ?>
1 ? JText::translate('VBDAYS') : JText::translate('VBDAY')); ?> datesep, $this->df), $room_stay_checkin); ?> datesep, $this->df), $room_stay_checkout); ?>
countNightsOfStay($room_stay_checkin, $room_stay_checkout); if ($room_stay_checkin != $details['checkin'] || $room_stay_checkout != $details['checkout']) { ?>
1 ? JText::translate('VBDAYS') : JText::translate('VBDAY')); ?> datesep, $this->df), $room_stay_checkin); ?> datesep, $this->df), $room_stay_checkout); ?>
0) { ?>
filterItems(['id_order' => $details['id']]); if ($bookingTasks) { ?>
getTitle(); ?> getAreaName($task->getAreaID()); ?>
get('hasUnreadMessages', false); if ($taskUnreadMessages) { ?>
statusTypeExists($task->getStatus())) { $taskStatus = $taskManager->getStatusTypeInstance($task->getStatus()); ?>
getName(); ?>
bookingHasReminder($details['id']); $few_reminders = []; if ($has_reminders) { $few_reminders = $reminders_helper->loadReminders([ 'idorder' => $details['id'], 'onlyorder' => 1, 'completed' => 1, 'expired' => 1, ], 0, 5); } ?>
duedate)) { // calculate distance to expiration date from today $diff_data = $reminders_helper->relativeDatesDiff($reminder->duedate); } ?>
title); ?>
descr)) { ?>
descr); ?>
duedate)) { ?>
usetime) { ?> format('H:i'); ?>
 
1) { // display badge for returning customer ?>
'widget', 'customer' => $customer, ]; // render the permissions layout echo JLayoutHelper::render('customer.dropfiles', $layout_data); ?>
loadHistory(); if ($history_list) { ?>
$hist) { $html_descr = strpos((string) $hist['descr'], '<') !== false ? $hist['descr'] : nl2br((string) $hist['descr']); $text_descr = strip_tags((string) $hist['descr']); $text_lines = preg_split("/[\r\n]/", $text_descr); $read_more = false; if ($text_lines && count($text_lines) > 1) { $first_line = $text_lines[0]; unset($text_lines[0]); if (strlen($first_line) < strlen(implode('', $text_lines))) { $text_descr = $first_line; $read_more = true; } } $tip_info = [ JText::translate('VBOBOOKHISTORYLBLTOT') . ': ' . VikBooking::formatCurrencyNumber(VikBooking::numberFormat($hist['total']), $currencysymb), JText::translate('VBOBOOKHISTORYLBLTPAID') . ': ' . VikBooking::formatCurrencyNumber(VikBooking::numberFormat($hist['totpaid']), $currencysymb), ]; ?>
validType($hist['type'], true); ?>
$max_display_records) { // display button to display all history records ?>
 
$cm_notification) { $badge_class = 'success'; $badge_icon = 'check-circle'; if ($cm_notification['type'] == 0) { $badge_class = 'error'; $badge_icon = 'times-circle'; } elseif ($cm_notification['type'] == 2) { $badge_class = 'warning'; $badge_icon = 'exclamation-triangle'; } ?>
datesep, $this->df) . ' H:i:s', $cm_notification['ts']); ?>
$channel_notifs) { $logo_obj = VikBooking::getVcmChannelsLogo($cm_channels[$channel_key]['name'], true); $channel_logo = $logo_obj ? $logo_obj->getSmallLogoURL() : ''; if (!$channel_logo) { continue; } // build readable channel name $raw_ch_name = (string)$cm_channels[$channel_key]['name']; $lower_name = strtolower($raw_ch_name); $lower_name = preg_replace("/hotel$/", ' hotel', $lower_name); $channel_name = ucwords(preg_replace("/api$/", '', $lower_name)); ?>
$channel_notif) { // build update result string $upd_result = (string)$channel_notif['cont']; // strip "e4j." from the beginning of the string $upd_result = preg_replace("/^e4j./i", '', $upd_result); // strip ending hotel code "{hotelid 000000}" $upd_result = preg_replace("/\s?\{hotelid\s?[0-9A-Z]+\}$/i", '', $upd_result); // default values for extra logging $log_result = ''; $ruid_codes = []; // we expect this to be a channel update result if ($resp_ind) { // parse the response type if (preg_match("/^OK\.[A-Z\.]+\.AR_RS/i", $upd_result, $matches)) { // successful response example ("OK.Airbnb.AR_RS") $upd_result = trim(str_replace($matches[0], "OK {$channel_name}", $upd_result)); } elseif (preg_match("/^warning\.[A-Z\.]+\.AR_RS/i", $upd_result, $matches)) { // warning response example ("warning.Airbnb.AR_RS") $upd_result = trim(str_replace($matches[0], $channel_name, $upd_result)); $log_result = 'warning'; } elseif (preg_match("/^error\.[A-Z\.]+\.AR_RS/i", $upd_result, $matches)) { // error response example ("error.Airbnb.AR_RS") $upd_result = trim(str_replace($matches[0], $channel_name, $upd_result)); $log_result = 'error'; } // check for RUID codes (there could be more than one) if (preg_match_all("/RUID: \[.+\]/", $upd_result, $ruid_matches)) { $ruid_codes = is_array($ruid_matches[0]) ? $ruid_matches[0] : []; foreach ($ruid_matches[0] as $ruid_log) { // strip log from update result because it will be displayed separately $upd_result = str_replace($ruid_log, '', $upd_result); } } } if (in_array($upd_result, $channel_responses)) { // do not display duplicate responses from the same channel (i.e. duplicate unexpected notifications) continue; } // register channel response $channel_responses[] = $upd_result; ?>
$max_display_notifications) { // display button to show all channel manager notifications ?>
 
$html_content, ]; } /** * Main method to invoke the widget. Contents will be loaded * through AJAX requests, not via PHP when the page loads. * * @param ?VBOMultitaskData $data * * @return void */ 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 sticky notes wrapper instance $wrapper_instance = !$is_ajax ? static::$instance_counter : rand(); $wrapper_id = 'vbo-widget-bookdets-' . $wrapper_instance; // get permissions $vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking'); if (!$vbo_auth_bookings) { // display nothing return; } // check multitask data $load_bid = 0; $js_modal_id = ''; $is_modal_rendering = false; $selected_event = null; if ($data) { // access Multitask data $is_modal_rendering = $data->isModalRendering(); if ($is_modal_rendering) { // get modal JS identifier $js_modal_id = $data->getModalJsIdentifier(); } /** * This widget should not get the current ID from Multitask data * to avoid displaying duplicate contents, it should rather get * the ID from the injected options (i.e. new Push notification). */ $load_bid = $this->options()->fetchBookingId(); // booking selected event to eventually dispatch $selected_event = $this->getOption('selected_event'); } // check if we got data by clicking on a push notification from the ServiceWorker $from_push = $this->options()->gotPushData(); ?>

widgetIcon; ?> widgetName; ?>

" placeholder="" autocomplete="off" />
getOption('title', ''); $push_message = $this->getOption('message', ''); // check for notification action and "from" $push_action = $this->getOption('action', ''); $push_from = $this->getOption('from', ''); // notification severity $push_severity = $this->getOption('severity', 'info'); // replace new line characters with a white space $push_message = preg_replace("/[\r\n]/", ' ', $push_message); // get rid of white space characters (only white space, no new lines or tabs like "\s") used more than once in a row $push_message = preg_replace("/ {2,}/", ' ', $push_message); ?>
getQuery(true) ->select('*') ->from($dbo->qn('#__vikchannelmanager_notifications')) ->where($dbo->qn('from') . ' = ' . $dbo->q('VCM')) ->where($dbo->qn('idordervb') . ' = ' . (int)$booking['id']) ->order($dbo->qn('ts') . ' DESC'); $dbo->setQuery($q); $parents = $dbo->loadAssocList(); foreach ($parents as $k => $parent) { $q = $dbo->getQuery(true) ->select($dbo->qn(['type', 'cont', 'channel'])) ->from($dbo->qn('#__vikchannelmanager_notification_child')) ->where($dbo->qn('id_parent') . ' = ' . (int)$parent['id']) ->order($dbo->qn('id') . ' ASC'); $dbo->setQuery($q); $children = $dbo->loadAssocList(); // set children notifications by grouping them under each channel $parents[$k]['children'] = []; // fetch the details for each channel involved foreach ($children as $ind => $child) { if (empty($child['channel'])) { continue; } $channel = VikChannelManager::getChannel($child['channel']); if (!$channel) { // we don't want to list a notification with no channel details unset($children[$ind]); continue; } // set channel details $channels[$child['channel']] = $channel; if (!isset($parents[$k]['children'][$child['channel']])) { // open channel container $parents[$k]['children'][$child['channel']] = []; } // push channel notification details $parents[$k]['children'][$child['channel']][] = $child; } if (!$children) { // we don't want a notification with no children (channels notified) unset($parents[$k]); continue; } } if (!$channels) { // unset all notifications in case of no channel details $parents = []; } return [array_values($parents), $channels]; } }