widgetName = JText::translate('VBO_W_NOTIFSCENTER_TITLE'); $this->widgetDescr = JText::translate('VBO_W_NOTIFSCENTER_DESCR'); $this->widgetId = basename(__FILE__, '.php'); $this->widgetIcon = ''; $this->widgetStyleName = 'light-orange'; } /** * This widget monitors the latest notification record ID to schedule * periodic watch data in order to be able to update the badge counter. * * @return object */ public function preload() { // JS lang defs JText::script('VBO_NOMORE_NOTIFS'); JText::script('VBO_NO_NOTIFS'); // load assets for datepicker $this->vbo_app->loadDatePicker(); // count the number of unread notifications $watch_data = new stdClass; $watch_data->badge_count = (new VBONotificationCenter) ->countUnread(); return $watch_data; } /** * Checks for new notifications by using the previous preloaded watch-data. * This widget will actually never dispatch any notifications, but only events. * * @param ?VBONotificationWatchdata $watch_data the preloaded watch-data object. * * @return array data object to watch next and notifications array. * * @see preload() */ public function getNotifications(?VBONotificationWatchdata $watch_data = null) { // default empty values $watch_next = null; $notifications = []; if (!$watch_data) { return [$watch_next, $notifications]; } // get the number of unread notifications $unread = (new VBONotificationCenter) ->countUnread(); // build the next watch data for this widget $watch_next = new stdClass; $watch_next->badge_count = $unread; // no notifications to dispatch ever, we simply update the next watch data return [$watch_next, $notifications]; } /** * Checks for new events to be dispatched by using the previous preloaded watch-data. * * @param ?VBONotificationWatchdata $watch_data the preloaded watch-data object. * * @return array list of event objects to dispatch, if any. * * @see preload() */ public function getNotificationEvents(?VBONotificationWatchdata $watch_data = null) { if (!$watch_data) { return []; } // check the number of unread notifications $unread = (new VBONotificationCenter) ->countUnread(); if ((int) $watch_data->get('badge_count', 0) == $unread) { // nothing has changed return []; } // return the notification events to dispatch return [ 'vbo-badge-count' => [ 'badge_count' => $unread, ], ]; } /** * Custom method for this widget only to load the notifications from a new group. * 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. * * @return array */ public function loadGroupNotifications() { $input = JFactory::getApplication()->input; $wrapper = $input->getString('wrapper', ''); $group = $input->getString('group', ''); $from_date = $input->getString('from_date', ''); $to_date = $input->getString('to_date', ''); $unread = $input->getBool('unread', false); // access the notification-center object $notifCenter = new VBONotificationCenter; // build query filters $filters = []; if ($group) { $filters['group'] = $group; } if ($from_date || $to_date) { // make the filter a non-scalar value $filters['createdon'] = []; if ($from_date) { // push filter with operand $filters['createdon'][] = [ 'operand' => '>=', 'value' => $from_date . ' 00:00:00', ]; } if ($to_date) { // push filter with operand $filters['createdon'][] = [ 'operand' => '<=', 'value' => $to_date . ' 23:59:59', ]; } } if ($unread) { // make the filter a non-scalar value $filters['read'] = [ [ 'operand' => '=', 'value' => '0', ], ]; } // get and build the latest notifications for the requested group $html_content = $this->buildNotificationsHTML( $notifCenter->loadNotifications(0, $this->records_per_page, $filters) ); // return an associative array of values return [ 'html' => $html_content, 'pages_count' => ceil($notifCenter->countFoundNotifications() / $this->records_per_page), ]; } /** * Custom method for this widget only to load the next page of notifications. * 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. * * @return array */ public function loadNextNotifications() { $input = JFactory::getApplication()->input; $wrapper = $input->getString('wrapper', ''); $group = $input->getString('group', ''); $from_date = $input->getString('from_date', ''); $to_date = $input->getString('to_date', ''); $unread = $input->getBool('unread', false); $page_num = $input->getUInt('page_num', 1); $page_num = $page_num ?: 1; // access the notification-center object $notifCenter = new VBONotificationCenter; // build query filters $filters = []; if ($group) { $filters['group'] = $group; } if ($from_date || $to_date) { // make the filter a non-scalar value $filters['createdon'] = []; if ($from_date) { // push filter with operand $filters['createdon'][] = [ 'operand' => '>=', 'value' => $from_date . ' 00:00:00', ]; } if ($to_date) { // push filter with operand $filters['createdon'][] = [ 'operand' => '<=', 'value' => $to_date . ' 23:59:59', ]; } } if ($unread) { // make the filter a non-scalar value $filters['read'] = [ [ 'operand' => '=', 'value' => '0', ], ]; } // determine the query limit start $lim_start = ($page_num - 1) * $this->records_per_page; // get and build the latest notifications for the requested group $html_content = $this->buildNotificationsHTML( $notifCenter->loadNotifications($lim_start, $this->records_per_page, $filters), ($page_num - 1) ); // return an associative array of values return [ 'html' => $html_content, 'page_number' => $page_num, 'pages_count' => ceil($notifCenter->countFoundNotifications() / $this->records_per_page), ]; } /** * Custom method for this widget only to mark some/all notifications as read. * 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. * * @return array */ public function markNotificationsRead() { $input = JFactory::getApplication()->input; $notification_ids = $input->getUInt('notif_ids', [], 'array'); $mark_all = $input->getUInt('mark_all', 0); if (!$notification_ids && !$mark_all) { // do not proceed or all notifications would be marked as read VBOHttpDocument::getInstance()->close(400, 'No notification IDs to mark as read'); } // access the notification-center object $notifCenter = new VBONotificationCenter; // update the given notification IDs as read and obtain the groups involved $groups = $notifCenter->readNotifications($notification_ids); if (!$groups) { VBOHttpDocument::getInstance()->close(404, 'No notifications found for marking as read'); } // build a list of badge counters per group $group_badge_counters = []; foreach ($groups as $group) { // determine the group key for dispatching the dedicated event $group_key = 'vbo-badge-count' . ($group ? '-' . $group : ''); // count the unread notifications for this group identifier $group_badge_counters[$group_key] = $notifCenter->countUnread($group ?: ''); } // return an associative array of group-badge counters return $group_badge_counters; } /** * Custom method for this widget only to count the unread notifications per group. * 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. * * @return array */ public function countUnreadNotifications() { // access the notification-center object $notifCenter = new VBONotificationCenter; // return an associative array of group-badge counters return [ 'vbo-badge-count' => $notifCenter->countUnread(), ]; } /** * Custom method for this widget only to read some criteria-matching notifications. * 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. * * @return array */ public function readMatchingNotifications() { $input = JFactory::getApplication()->input; $criteria = $input->get('criteria', [], 'array'); $read_count = 0; if (is_array($criteria) && $criteria) { // access the notification-center object $notifCenter = new VBONotificationCenter; // read the matching notifications $read_count = $notifCenter->readMatchingNotifications($criteria); } return [ 'read_count' => $read_count, ]; } /** * Main method to invoke the widget. * * @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 wrapper instance $wrapper_instance = !$is_ajax ? static::$instance_counter : rand(); $wrapper_id = 'vbo-widget-notifscenter-' . $wrapper_instance; // check permissions $vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking'); if (!$vbo_auth_bookings) { // permissions are not met return; } // check multitask data $in_menu = false; $suggest_push = false; $js_modal_id = ''; $is_modal_rendering = false; if ($data) { // access Multitask data $is_modal_rendering = $data->isModalRendering(); if ($is_modal_rendering) { // get modal JS identifier $js_modal_id = $data->getModalJsIdentifier(); } /** * Check the MultitaskOptions to see if the widget is rendered within * the admin-menu through the Notifications Center trigger button. */ $in_menu = (bool) $this->options()->get('inMenu'); $suggest_push = $in_menu && $this->options()->get('suggestPush'); } // get minimum and maximum dates for datepicker filters list($mindate, $maxdate) = $this->getMinDatesNotifications(); $mindate = empty($mindate) ? time() : $mindate; $maxdate = empty($maxdate) ? $mindate : $maxdate; // access the notification-center object $notifCenter = new VBONotificationCenter; // load the latest notifications $notifications = $notifCenter->loadNotifications(0, $this->records_per_page); // immediately count the number of pages to show all notifications $pages_count = ceil($notifCenter->countFoundNotifications() / $this->records_per_page); ?>

widgetIcon; ?> widgetName; ?>

vbo_app->printYesNoButtons('onlyunread', JText::translate('VBYES'), JText::translate('VBNO'), 0, 1, 0); ?>
getGroups() as $k => $group) { $group_badge_val = $group['badge_count'] ?: ''; $group_badge_node = $group_badge_val ? '' . $group_badge_val . '' : ''; ?>
= $this->max_groups) { break; } } ?>
buildNotificationsHTML($notifications); ?>

$notif) { // obtain notification date info $date_info = VBORemindersHelper::getInstance()->relativeDatesDiff(JHtml::fetch('date', $notif->createdon, 'Y-m-d H:i:s')); // build relative date $relative_dt = $date_info['relative']; if ($date_info['past'] && ($date_info['today'] || ($date_info['yesterday'] && !$date_info['days']))) { // recent date to be expressed as hours, minutes or seconds ago if ($date_info['hours']) { $rel_time_str = $date_info['hours'] . ' ' . JText::translate(($date_info['hours'] == 1 ? 'VBO_HOUR' : 'VBCONFIGONETENEIGHT')); } elseif ($date_info['minutes']) { $rel_time_str = $date_info['minutes'] . ' ' . JText::translate(($date_info['minutes'] == 1 ? 'VBO_MINUTE' : 'VBTRKDIFFMINS')); } else { $rel_time_str = $date_info['seconds'] . ' ' . JText::translate(($date_info['seconds'] == 1 ? 'VBO_SECOND' : 'VBTRKDIFFSECS')); } $relative_dt = JText::sprintf('VBO_REL_EXP_PAST', strtolower($rel_time_str)); } elseif ($date_info['past'] && $date_info['yesterday'] && $date_info['days'] == 1 && $date_info['hours'] < 6) { // less than 30 hours ago $rel_time_str = ($date_info['hours'] + 24) . ' ' . JText::translate('VBCONFIGONETENEIGHT'); $relative_dt = JText::sprintf('VBO_REL_EXP_PAST', strtolower($rel_time_str)); } // build a human-readable date and time if ($date_info['today']) { $human_dtime = JHtml::fetch('date', $notif->createdon, 'H:i:s'); } else { $human_dtime = JHtml::fetch('date', $notif->createdon, str_replace("/", $this->datesep, $this->df) . ' H:i:s'); } // get channel logo and name $channel_logo = ''; $channel_name = ''; if (!empty($notif->channel)) { $ch_logo_obj = VikBooking::getVcmChannelsLogo($notif->channel, true); $channel_logo = is_object($ch_logo_obj) ? $ch_logo_obj->getSmallLogoURL() : ''; $channelparts = explode('_', $notif->channel); $channel_name = isset($channelparts[1]) && strlen((string)$channelparts[1]) ? $channelparts[1] : ucwords($channelparts[0]); } // check if the notification group belongs to specific types $group_website = !strcasecmp($notif->group, 'website'); $group_guests = !strcasecmp($notif->group, 'guests'); // determine the notification group icon and color $group_badge_icon = ''; $group_badge_cnt = ''; $group_badge_cls = ''; if (!strcasecmp($notif->group, 'website')) { $group_badge_icon = 'clipboard-list'; $group_badge_cls = 'vbo-badge-group-green'; if (in_array($notif->type, ['p0', 'pn'])) { $group_badge_icon = 'credit-card'; } elseif ($notif->type == 'info') { $group_badge_icon = 'bullhorn'; } elseif ($notif->type == 'chat.newmessage') { $group_badge_icon = 'comment-dots'; } if (in_array($notif->type, ['cr', 'cw', 'ob'])) { $group_badge_cls = 'vbo-badge-group-orange'; } } elseif (!strcasecmp($notif->group, 'otas')) { $group_badge_icon = 'cloud'; $group_badge_cls = 'vbo-badge-group-purple'; if (in_array($notif->type, ['po', 'vcc_balance', 'vcc_balance_updated'])) { $group_badge_icon = 'credit-card'; } if (in_array($notif->type, ['cc', 'ob'])) { $group_badge_cls = 'vbo-badge-group-orange'; } } elseif (!strcasecmp($notif->group, 'guests')) { $group_badge_icon = 'comment-dots'; $group_badge_cls = 'vbo-badge-group-lightblue'; } elseif (!strcasecmp($notif->group, 'cm')) { $group_badge_icon = 'bullhorn'; $group_badge_cls = 'vbo-badge-group-lightblue'; } elseif (!strcasecmp($notif->group, 'operators')) { $group_badge_icon = 'user-tie'; $group_badge_cls = 'vbo-badge-group-orange'; if (in_array($notif->type, ['chat.newmessage'])) { $group_badge_icon = 'comment-dots'; } if ($notif->type == 'task.unassigned') { $group_badge_icon = 'tasks'; $group_badge_cls = 'vbo-badge-group-red'; } } elseif (!strcasecmp($notif->group, 'reports')) { $group_badge_icon = 'cash-register'; if (strpos($notif->type, 'error') !== false) { $group_badge_cls = 'vbo-badge-group-red'; } else { $group_badge_cls = 'vbo-badge-group-green'; } } elseif (!strcasecmp($notif->group, 'ai')) { $group_badge_cnt = ''; if (strpos($notif->type, 'error') !== false) { $group_badge_cls = 'vbo-badge-group-red'; } elseif ($notif->type == 'training.drafts') { $group_badge_cls = 'vbo-badge-group-orange'; } else { $group_badge_cls = 'vbo-badge-group-green'; } } elseif (!strcasecmp($notif->group, 'dac')) { $group_badge_icon = 'fingerprint'; $group_badge_cls = 'vbo-badge-group-green'; if (strpos((string) $notif->type, '.nok') !== false) { $group_badge_cls = 'vbo-badge-group-red'; } } elseif (!strcasecmp($notif->group, 'webhook')) { $group_badge_icon = 'satellite-dish'; $group_badge_cls = 'vbo-badge-group-lightblue'; if (strpos((string) $notif->type, '.nok') !== false || strpos((string) $notif->type, 'error') !== false) { $group_badge_cls = 'vbo-badge-group-red'; } if (strpos($notif->type, 'mfa') !== false) { $group_badge_icon = 'shield'; $group_badge_cls = 'vbo-badge-group-orange'; } } ?>
avatar)) { // use notification avatar to support co-hosts, co-traveler and other chat users ?> customer_pic)) { // use customer profile picture ?> avatar)) { // use notification avatar ?> customer_name)) { // use customer initials ?> getCustomerInitials($notif->customer_name); ?> " />
title) { if (preg_match("/^VBO/", $notif->title)) { /** * @todo to be removed on next updates. */ $notif->title = JText::translate($notif->title); } ?>
title; ?> customer_name) { ?> customer_name; ?>
idorder || $notif->idorderota) { ?> idorder ?: $notif->idorderota; ?>
summary && (!$group_website || !strcasecmp($notif->type, 'info') || strpos($notif->type, 'chat') === 0)) { ?>
summary); ?>
cta_data) && (!empty($notif->cta_data->url) || !empty($notif->cta_data->widget))) { // call-to-action data available $cta_btn_lbl = !empty($notif->cta_data->label) ? $notif->cta_data->label : JText::translate('VBO_TAKE_ACTION'); ?>
cta_data->url)) { // open "remote" URL ?> cta_data->widget)) { // open a specific admin-widget with the necessary options $def_widget_options = [ 'modal_options' => [ 'suffix' => 'widget_modal_inner_' . $notif->cta_data->widget, ], ]; if (is_object($notif->cta_data->widget_options) && !isset($notif->cta_data->widget_options->modal_options)) { // inject the suffix property $notif->cta_data->widget_options->modal_options = new stdClass; $notif->cta_data->widget_options->modal_options->suffix = 'widget_modal_inner_' . $notif->cta_data->widget; } $js_options = !empty($notif->cta_data->widget_options) ? json_encode($notif->cta_data->widget_options) : json_encode($def_widget_options); $js_command = 'VBOCore.handleDisplayWidgetNotification({widget_id: "' . $notif->cta_data->widget . '"}, ' . $js_options . ');'; ?>
setQuery( $dbo->getQuery(true) ->select('MIN(' . $dbo->qn('createdon') . ') AS ' . $dbo->qn('mindate')) ->select('MAX(' . $dbo->qn('createdon') . ') AS ' . $dbo->qn('maxdate')) ->from($dbo->qn('#__vikbooking_notifications')) ); $info_dates = $dbo->loadObject(); if ($info_dates && !empty($info_dates->mindate)) { $mindate = strtotime(JHtml::fetch('date', $info_dates->mindate, 'Y-m-d')); $maxdate = strtotime(JHtml::fetch('date', $info_dates->maxdate, 'Y-m-d')); } return [$mindate, $maxdate]; } }