widgetName = JText::translate('VBOREPORTRATESFLOW'); $this->widgetDescr = JText::translate('VBO_W_RATESFLOW_DESCR'); $this->widgetId = basename(__FILE__, '.php'); // define widget and icon and style name $this->widgetIcon = ''; $this->widgetStyleName = 'violet'; } /** * Custom method for this widget only to load the rates flow records. * 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. * * It's the actual rendering of the widget which also allows navigation. */ public function loadRatesFlowRecords() { $offset = VikRequest::getInt('offset', 0, 'request'); $length = VikRequest::getInt('length', $this->alterations_per_page, 'request'); $wrapper = VikRequest::getString('wrapper', '', 'request'); $fromdate = VikRequest::getString('fromdate', '', 'request'); $todate = VikRequest::getString('todate', '', 'request'); $step = VikRequest::getString('step', 'quarter', 'request'); $date_dir = VikRequest::getInt('date_dir', 0, 'request'); if (empty($fromdate)) { // default to current quarter (3 full months) $now_info = getdate(); $end_info = getdate(mktime(0, 0, 0, ($now_info['mon'] + 2), 1, $now_info['year'])); $to_ts = mktime(23, 59, 59, $end_info['mon'], date('t', $end_info[0]), $end_info['year']); $fromdate = date('Y-m-d', mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year'])); $todate = date('Y-m-d', $to_ts); } // convert dates to timestamps to support custom date formats $from_ts = VikBooking::getDateTimestamp($fromdate); $to_ts = VikBooking::getDateTimestamp($todate, 23, 59, 59); // check dates navigation and step $step_name = ''; if ($date_dir > 0 || $date_dir < 0) { // calculate forward or backward dates for navigation list($from_ts, $to_ts, $step_name) = $this->calcDatesNavigation($from_ts, $to_ts, $step, $date_dir); } // convert final dates to Y-m-d $fromdate = date('Y-m-d', $from_ts); $todate = date('Y-m-d', $to_ts); // get rates flow report instance $report = VikBooking::getReportInstance('rates_flow'); if (!$report) { // do nothing return; } // get VBO date format $vbo_df = $report->getDateFormat(); // set report options $report->setReportOptions(array( 'fetch' => 'alterations', 'krsort' => 'created_on', 'krorder' => 'DESC', 'fromdate' => $fromdate, 'todate' => $todate, 'lim' => $length, 'limstart' => $offset, )); // generate report data $alterations = $report->getReportValues(); $alterations = !is_array($alterations) ? array() : $alterations; // grab report columns for paging $columns = $report->getColumnsValues(); // check next page and next offset $has_next_page = (isset($columns['paging']) && !empty($columns['paging']['has_next_page'])); $next_offset = isset($columns['paging']) && !empty($columns['paging']['limstart']) ? $columns['paging']['limstart'] : 0; $current_page = isset($columns['paging']) && !empty($columns['paging']['page_num']) ? $columns['paging']['page_num'] : 1; // check festivities, by getting the next ones $fests = VikBooking::getFestivitiesInstance(); $next_fests = $fests->loadFestDates($fromdate, $todate); // start output buffering ob_start(); // error, warning or info messages should go on top if (strlen($report->getError())) { ?>
getError(); ?>
getWarning())) { ?>getWarning(); ?>
findDatesFestName($next_fests, $day_from_ts, $day_to_ts); $restr_infos = array(); if (is_object($rflow['data']['value']) && isset($rflow['data']['value']->Restrictions)) { $restr_obj = $rflow['data']['value']->Restrictions; if (isset($restr_obj->minLOS) && (int)$restr_obj->minLOS > 1) { array_push($restr_infos, JText::translate('VBOMINIMUMSTAY') . ': ' . $restr_obj->minLOS); } if (isset($restr_obj->cta)) { if ((is_bool($restr_obj->cta) && $restr_obj->cta === true) || (is_string($restr_obj->cta) && !strcasecmp($restr_obj->cta, 'true'))) { array_push($restr_infos, 'CTA'); } } if (isset($restr_obj->ctd)) { if ((is_bool($restr_obj->ctd) && $restr_obj->ctd === true) || (is_string($restr_obj->ctd) && !strcasecmp($restr_obj->ctd, 'true'))) { array_push($restr_infos, 'CTD'); } } } ?> parseReadableDateInterval($from_ts, $to_ts, $vbo_df, $step); // return an associative array of values return array( 'html' => $html_content, 'fromdate' => $fromdate, 'f_fromdate' => $format_from_date, 'todate' => $todate, 'f_todate' => $format_to_date, 'step' => $step, 'step_name' => $step_name, 'tot_records' => count($alterations), 'next_page' => (int)$has_next_page, 'page_num' => (int)$current_page, 'offset' => $next_offset, ); } /** * Preload the necessary CSS/JS assets. * * @return void */ public function preload() { // load assets $this->vbo_app->loadDatePicker(); } /** * Main method to invoke the widget. Contents will be loaded * through AJAX requests, not via PHP when the page loads. * * @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-ratesflow-' . $wrapper_instance; // get permissions $vbo_auth_pricing = JFactory::getUser()->authorise('core.vbo.pricing', 'com_vikbooking'); // get rates flow report instance $report = VikBooking::getReportInstance('rates_flow'); if (!$report || !$vbo_auth_pricing) { // display nothing return; } // get the total number of unique channels updated $tot_channels_updated = $report->countRatesFlowChannels(); if ($tot_channels_updated > 0 && ($this->alterations_per_page * $tot_channels_updated) < $this->lim_page_records) { $best_lim_page = floor($this->lim_page_records / $tot_channels_updated); $this->alterations_per_page = $best_lim_page > $this->alterations_per_page ? $best_lim_page : $this->alterations_per_page; } // count average records per page $avg_per_page = floor(($this->alterations_per_page + $this->lim_page_records) / 2); $avg_per_page = $avg_per_page < $this->alterations_per_page ? $this->alterations_per_page : $avg_per_page; // get minimum and maximum nights updated for dates filters list($mindate, $maxdate) = $report->getMinDatesRatesFlow(); $mindate = empty($mindate) ? time() : $mindate; $maxdate = empty($maxdate) ? $mindate : $maxdate; // dates navigation preference $cookie = JFactory::getApplication()->input->cookie; $cookie_step = $cookie->get('vbo_widget_ratesflow_step', 'quarter', 'string'); // determine default period name and dates $df = $report->getDateFormat(); $dtpicker_df = $report->getDateFormat('jui'); $now_info = getdate(); if ($cookie_step == 'weekend') { // next weekend $from_ts = strtotime("next Friday"); $to_ts = strtotime("next Sunday"); if ($to_ts < $from_ts) { // this weekend $from_ts = strtotime("last Friday"); } $fromdate = date($df, $from_ts); $todate = date($df, $to_ts); $period_name = JText::translate('VBOWEEKND'); } elseif ($cookie_step == 'month') { // current full month $from_ts = mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year']); $fromdate = date($df, $from_ts); $to_ts = mktime(23, 59, 59, $now_info['mon'], date('t', $now_info[0]), $now_info['year']); $todate = date($df, $to_ts); $period_name = JText::translate('VBPVIEWRESTRICTIONSTWO'); } else { // default to next 3 (total) months from today's month $from_ts = mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year']); $fromdate = date($df, $from_ts); $end_info = getdate(mktime(0, 0, 0, ($now_info['mon'] + 2), 1, $now_info['year'])); $to_ts = mktime(23, 59, 59, $end_info['mon'], date('t', $end_info[0]), $end_info['year']); $todate = date($df, $to_ts); $period_name = JText::translate('VBO_QUARTER'); } // take care of the readable dates interval list($format_from_date, $format_to_date) = $this->parseReadableDateInterval($from_ts, $to_ts, $df, $cookie_step); $interval_name = $format_from_date . ' - ' . $format_to_date; if ($format_from_date == $format_to_date) { $interval_name = $format_from_date; } ?> from_ts <= $day_from_ts && $fest->to_ts >= $day_to_ts) { // fest found wrapping all dates updated return $fest->trans_name; } if (isset($fest->all_timestamps) && count($fest->all_timestamps) > 1) { // check bridge-dates $earliest_ts = $fest->all_timestamps[0]; $latest_ts = $fest->all_timestamps[(count($fest->all_timestamps) - 1)] + 86399; if ($earliest_ts <= $day_from_ts && $latest_ts >= $day_to_ts) { // fest found with bridge-dates wrapping all dates updated return $fest->trans_name; } } // check if at least one date updated in the interval is matching $days_diff = floor(($day_to_ts - $day_from_ts) / 86400); $days_to_start = null; if ($fest->from_ts <= $day_from_ts && $fest->to_ts >= $day_from_ts) { // this is a closest match, because the first interval day updated is a fest $days_to_start = floor(($day_to_ts - $fest->to_ts) / 86400); } elseif ($fest->from_ts <= $day_to_ts && $fest->to_ts >= $day_to_ts) { // this is a closest match, because the last interval day updated is a fest $days_to_start = floor(($fest->from_ts - $day_from_ts) / 86400); } elseif ($earliest_ts > 0 && $earliest_ts <= $day_from_ts && $latest_ts >= $day_from_ts) { // this is a closest match, because the first interval day updated is a fest-bridge $days_to_start = floor(($day_to_ts - $latest_ts) / 86400); } elseif ($earliest_ts > 0 && $earliest_ts <= $day_to_ts && $latest_ts >= $day_to_ts) { // this is a closest match, because the last interval day updated is a fest-bridge $days_to_start = floor(($earliest_ts - $day_from_ts) / 86400); } if ($days_to_start !== null && abs($days_to_start) <= 4 && $days_diff < 15) { // push close match array_push($closest_matches, array( 'fest_name' => $fest->trans_name, 'days_diff' => $days_diff, 'days_start' => abs($days_to_start), )); } } // count closest matches, if any $tot_matches = count($closest_matches); if ($tot_matches === 1) { // return the first closest match return $closest_matches[0]['fest_name']; } if ($tot_matches > 1) { // sort closest matches usort($closest_matches, function($a, $b) { // sort by days diff ASC $sort_diff = ($a['days_diff'] < $b['days_diff']) ? -1 : 1; // sort by days to start ASC $sort_diff += ($a['days_start'] < $b['days_start']) ? -1 : 1; return $sort_diff; }); // return the best close match return $closest_matches[0]['fest_name']; } return false; } /** * Calculates the new dates interval for navigation according to step. * Internal method for this widget only. * * @param int $from_ts current from date timestamp. * @param int $to_ts current end date timestamp. * @param string $step the navigation step to take (weekend, month or quarter). * @param int $date_dir less than 0 backward nav, more than 0 forward. * * @return array the new from and end date timestamps, plus the step name. */ protected function calcDatesNavigation($from_ts, $to_ts, $step, $date_dir) { if (empty($from_ts) || (!($date_dir > 0) && !($date_dir < 0))) { return array($from_ts, $to_ts, ''); } // start date information $from_info = getdate($from_ts); // next or prev weekend if ($step == 'weekend') { if ($date_dir > 0) { // forward dates navigation $from_ts = strtotime("next Friday", $from_info[0]); $to_ts = strtotime("next Sunday", $from_info[0]); if ($to_ts < $from_ts) { // week after $to_ts = strtotime("+1 week", $to_ts); } } elseif ($date_dir < 0) { // backward dates navigation $from_ts = strtotime("last Friday", $from_info[0]); $to_ts = strtotime("last Sunday", $from_info[0]); if ($to_ts < $from_ts) { // week before $from_ts = strtotime("-1 week", $from_ts); } } return array($from_ts, $to_ts, JText::translate('VBOWEEKND')); } // next or prev month if ($step == 'month') { if ($date_dir > 0) { // forward dates navigation $from_ts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']); $to_ts = mktime(0, 0, 0, ($from_info['mon'] + 1), date('t', $from_ts), $from_info['year']); } elseif ($date_dir < 0) { // backward dates navigation $from_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']); $to_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), date('t', $from_ts), $from_info['year']); } return array($from_ts, $to_ts, JText::translate('VBPVIEWRESTRICTIONSTWO')); } // next or prev quarter by default if ($date_dir > 0) { // forward dates navigation $from_ts = mktime(0, 0, 0, ($from_info['mon'] + 3), 1, $from_info['year']); $end_ts = mktime(0, 0, 0, ($from_info['mon'] + 5), 1, $from_info['year']); $to_ts = mktime(0, 0, 0, ($from_info['mon'] + 5), date('t', $end_ts), $from_info['year']); } elseif ($date_dir < 0) { // backward dates navigation $from_ts = mktime(0, 0, 0, ($from_info['mon'] - 3), 1, $from_info['year']); $end_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']); $to_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), date('t', $end_ts), $from_info['year']); } return array($from_ts, $to_ts, JText::translate('VBO_QUARTER')); } /** * Parse an interval of date timestamps into a readable interval of dates according to step. * Internal method for this widget only. * * @param int $from_ts current from date timestamp. * @param int $to_ts current end date timestamp. * @param string $vbo_df the date format in VBO. * @param string $step the navigation step to take (weekend, month or quarter). * * @return array the formatted from and to date interval strings. */ protected function parseReadableDateInterval($from_ts, $to_ts, $vbo_df, $step) { $from_info = getdate($from_ts); $to_info = getdate($to_ts); $format_from_date = date($vbo_df, $from_ts); $format_to_date = date($vbo_df, $to_ts); if ($step == 'weekend') { // include day of week $format_from_date = VikBooking::sayWeekDay($from_info['wday']) . ', ' . $format_from_date; $format_to_date = VikBooking::sayWeekDay($to_info['wday']) . ', ' . $format_to_date; } elseif ($step == 'month') { // check if it's a full month to say its name if ($from_info['mon'] == $to_info['mon'] && $from_info['year'] == $to_info['year']) { if ($from_info['mday'] == 1 && $to_info['mday'] == date('t', $to_ts)) { $format_from_date = VikBooking::sayMonth($from_info['mon']) . ' ' . $from_info['year']; $format_to_date = VikBooking::sayMonth($to_info['mon']) . ' ' . $to_info['year']; } } } elseif ($step == 'quarter') { // check if it's a full quarter if ($from_info['mday'] == 1 && $to_info['mday'] == date('t', $to_ts)) { $format_from_date = VikBooking::sayMonth($from_info['mon']) . ' ' . $from_info['year']; $format_to_date = VikBooking::sayMonth($to_info['mon']) . ' ' . $to_info['year']; } } return array($format_from_date, $format_to_date); } }