File "rates_flow.php"
Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/admin/helpers/widgets/rates_flow.php
File size: 38.01 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* @package VikBooking
* @subpackage com_vikbooking
* @author Alessio Gaggii - e4j - Extensionsforjoomla.com
* @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
* @link https://vikwp.com
*/
defined('ABSPATH') or die('No script kiddies please!');
/**
* Class handler for admin widget "rates flow".
*
* @since 1.15.0 (J) - 1.5.0 (WP)
*/
class VikBookingAdminWidgetRatesFlow extends VikBookingAdminWidget
{
/**
* The instance counter of this widget. Since we do not load individual parameters
* for each widget's instance, we use a static counter to determine its settings.
*
* @var int
*/
protected static $instance_counter = -1;
/**
* Default number of rates flow alterations per page.
*
* @var int
*/
protected $alterations_per_page = 2;
/**
* Suggested limit of records per page.
*
* @var int
*/
protected $lim_page_records = 8;
/**
* Class constructor will define the widget name and identifier.
*/
public function __construct()
{
// call parent constructor
parent::__construct();
$this->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 = '<i class="' . VikBookingIcons::i('chart-line') . '"></i>';
$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())) {
?>
<p class="err"><?php echo $report->getError(); ?></p>
<?php
}
if (strlen($report->getWarning())) {
?>
<p class="warn"><?php echo $report->getWarning(); ?></p>
<?php
}
if (!count($alterations)) {
?>
<p class="info"><?php echo JText::translate('VBONORESULTS'); ?></p>
<?php
}
// loop through all rates flow records
foreach ($alterations as $rflow) {
// attempt to get the festivity name for the updated dates
$day_from_ts = strtotime($rflow['day_from']['value']);
$day_to_ts = strtotime($rflow['day_to']['value']);
$fest_name = $this->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');
}
}
}
?>
<div class="vbo-widget-ratesflow-record" data-recordid="<?php echo $rflow['id']['value']; ?>">
<div class="vbo-widget-ratesflow-avatar">
<?php
if (!empty($rflow['channel_id']['_logo'])) {
// channel logo has got the highest priority
?>
<img class="vbo-widget-ratesflow-avatar-profile" src="<?php echo $rflow['channel_id']['_logo']; ?>" />
<?php
} else {
// we use an icon as fallback
VikBookingIcons::e('hotel', 'vbo-widget-ratesflow-avatar-icon');
}
?>
</div>
<div class="vbo-widget-ratesflow-content">
<div class="vbo-widget-ratesflow-content-head">
<div class="vbo-widget-ratesflow-content-info-details">
<h4><?php echo $rflow['channel_id']['display_value']; ?></h4>
<div class="vbo-widget-ratesflow-content-info-dates">
<span class="vbo-widget-ratesflow-date-from"><?php echo $rflow['day_from']['display_value']; ?></span>
<?php
if ($rflow['day_from']['value'] != $rflow['day_to']['value']) {
?>
<span class="vbo-widget-ratesflow-date-sep">-</span>
<span class="vbo-widget-ratesflow-date-to"><?php echo $rflow['day_to']['display_value']; ?></span>
<?php
}
if (!empty($fest_name)) {
?>
<span class="vbo-widget-ratesflow-date-fest"><?php echo $fest_name; ?></span>
<?php
}
?>
</div>
</div>
<div class="vbo-widget-ratesflow-content-info-rates">
<?php
$higher_rates = null;
$higher_class = '';
$higher_icon = 'equals';
$higher_pcfee = 0;
if (!empty($rflow['base_fee']) && !empty($rflow['base_fee']['value'])) {
if ($rflow['base_fee']['value'] < $rflow['nightly_fee']['value']) {
$higher_rates = true;
$higher_class = ' vbo-widget-ratesflow-rate-changes-higher';
$higher_icon = 'sort-up';
$higher_pcfee = round((($rflow['nightly_fee']['value'] * 100 / $rflow['base_fee']['value']) - 100), 2);
} elseif ($rflow['base_fee']['value'] > $rflow['nightly_fee']['value']) {
$higher_rates = false;
$higher_class = ' vbo-widget-ratesflow-rate-changes-lower';
$higher_icon = 'sort-down';
$higher_pcfee = round((($rflow['base_fee']['value'] * 100 / $rflow['nightly_fee']['value']) - 100), 2);
}
}
?>
<div class="vbo-widget-ratesflow-nightly-rate">
<span><?php echo $rflow['nightly_fee']['display_value']; ?></span>
</div>
<div class="vbo-widget-ratesflow-rate-changes<?php echo $higher_class; ?>">
<?php
if (!empty($rflow['channel_alter']['value'])) {
?>
<div class="vbo-widget-ratesflow-rate-alter">
<span><?php echo $rflow['channel_alter']['display_value']; ?></span>
</div>
<?php
}
?>
<div class="vbo-widget-ratesflow-rate-growth">
<span><?php VikBookingIcons::e($higher_icon); ?> <?php echo $higher_pcfee; ?>%</span>
</div>
</div>
</div>
</div>
<div class="vbo-widget-ratesflow-content-info-msg">
<span class="vbo-widget-ratesflow-content-info-msg-room"><?php echo $rflow['vbo_room_id']['display_value']; ?></span>
<?php
if (count($restr_infos)) {
?>
<span class="vbo-widget-ratesflow-content-info-msg-restr"><?php echo implode(', ', $restr_infos); ?></span>
<?php
}
?>
<span class="vbo-widget-ratesflow-content-info-msg-cdate"><?php echo $rflow['created_on']['display_value']; ?></span>
</div>
</div>
</div>
<?php
}
// append navigation
?>
<div class="vbo-widget-commands vbo-widget-commands-right">
<div class="vbo-widget-commands-main">
<?php
if ($offset > 0) {
// show backward navigation button
?>
<div class="vbo-widget-command-chevron vbo-widget-command-prev">
<span class="vbo-widget-command-chevron-prev" onclick="vboWidgetRatesFlowNavigate('<?php echo $wrapper; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span>
</div>
<?php
}
if ($has_next_page) {
// show forward navigation button
?>
<div class="vbo-widget-command-chevron vbo-widget-command-next">
<span class="vbo-widget-command-chevron-next" onclick="vboWidgetRatesFlowNavigate('<?php echo $wrapper; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span>
</div>
<?php
}
?>
</div>
</div>
<?php
// get the HTML buffer
$html_content = ob_get_contents();
ob_end_clean();
// take care of the readable dates interval
list($format_from_date, $format_to_date) = $this->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;
}
?>
<div id="<?php echo $wrapper_id; ?>" class="vbo-admin-widget-wrapper" data-instance="<?php echo $wrapper_instance; ?>" data-offset="0" data-nowoffset="0" data-prevoffset="-1" data-length="<?php echo $this->alterations_per_page; ?>">
<div class="vbo-admin-widget-head">
<div class="vbo-admin-widget-head-inline">
<h4><?php echo $this->widgetIcon; ?> <span><?php echo $this->widgetName; ?></span></h4>
<div class="vbo-admin-widget-head-commands">
<div class="vbo-reportwidget-commands">
<div class="vbo-reportwidget-commands-main">
<div class="vbo-reportwidget-command-dates">
<div class="vbo-reportwidget-period-name"><?php echo $period_name; ?></div>
<div class="vbo-reportwidget-period-date"><?php echo $interval_name; ?></div>
</div>
<div class="vbo-reportwidget-command-chevron vbo-reportwidget-command-prev">
<span class="vbo-widget-ratesflow-dt-prev" onclick="vboWidgetRatesFlowDatesNav('<?php echo $wrapper_id; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span>
</div>
<div class="vbo-reportwidget-command-chevron vbo-reportwidget-command-next">
<span class="vbo-widget-ratesflow-dt-next" onclick="vboWidgetRatesFlowDatesNav('<?php echo $wrapper_id; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span>
</div>
</div>
<div class="vbo-reportwidget-command-dots">
<span class="vbo-widget-command-togglefilters vbo-widget-ratesflow-togglefilters" onclick="vboWidgetRatesFlowToggleFilters('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
</div>
</div>
<div class="vbo-reportwidget-filters">
<div class="vbo-reportwidget-filter">
<span class="vbo-reportwidget-datepicker">
<?php VikBookingIcons::e('calendar', 'vbo-widget-ratesflow-caltrigger'); ?>
<input type="text" class="vbo-ratesflow-dtpicker-from" value="<?php echo $fromdate; ?>" />
</span>
</div>
<div class="vbo-reportwidget-filter">
<span class="vbo-reportwidget-datepicker">
<?php VikBookingIcons::e('calendar', 'vbo-widget-ratesflow-caltrigger'); ?>
<input type="text" class="vbo-ratesflow-dtpicker-to" value="<?php echo $todate; ?>" />
</span>
</div>
<div class="vbo-reportwidget-filter">
<select class="vbo-ratesflow-period-nav" onchange="vboWidgetRatesFlowChangePeriod(this.value);">
<option value="weekend"<?php echo $cookie_step == 'weekend' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBOWEEKND'); ?></option>
<option value="month"<?php echo $cookie_step == 'month' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBPVIEWRESTRICTIONSTWO'); ?></option>
<option value="quarter"<?php echo $cookie_step == 'quarter' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_QUARTER'); ?></option>
</select>
</div>
<div class="vbo-reportwidget-filter vbo-reportwidget-filter-confirm">
<button type="button" class="btn vbo-config-btn" onclick="vboWidgetRatesFlowChangeDates('<?php echo $wrapper_id; ?>');"><?php echo JText::translate('VBADMINNOTESUPD'); ?></button>
</div>
</div>
</div>
</div>
</div>
<div class="vbo-widget-ratesflow-wrap">
<div class="vbo-widget-ratesflow-inner">
<div class="vbo-widget-ratesflow-list">
<?php
for ($i = 0; $i < $avg_per_page; $i++) {
?>
<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">
<div class="vbo-dashboard-guest-activity-avatar">
<div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>
</div>
<div class="vbo-dashboard-guest-activity-content">
<div class="vbo-dashboard-guest-activity-content-head">
<div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>
</div>
<div class="vbo-dashboard-guest-activity-content-info-msg">
<div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>
</div>
</div>
</div>
<?php
}
?>
</div>
</div>
</div>
</div>
<?php
if (static::$instance_counter === 0 || $is_ajax) {
/**
* Print the JS code only once for all instances of this widget.
* The real rendering is made through AJAX, not when the page loads.
*/
?>
<script type="text/javascript">
/**
* Display the loading skeletons.
*/
function vboWidgetRatesFlowSkeletons(wrapper) {
var widget_instance = jQuery('#' + wrapper);
if (!widget_instance.length) {
return false;
}
widget_instance.find('.vbo-widget-ratesflow-list').html('');
for (var i = 0; i < <?php echo $avg_per_page; ?>; i++) {
var skeleton = '';
skeleton += '<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">';
skeleton += ' <div class="vbo-dashboard-guest-activity-avatar">';
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>';
skeleton += ' </div>';
skeleton += ' <div class="vbo-dashboard-guest-activity-content">';
skeleton += ' <div class="vbo-dashboard-guest-activity-content-head">';
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>';
skeleton += ' </div>';
skeleton += ' <div class="vbo-dashboard-guest-activity-content-info-msg">';
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>';
skeleton += ' </div>';
skeleton += ' </div>';
skeleton += '</div>';
// append skeleton
jQuery(skeleton).appendTo(widget_instance.find('.vbo-widget-ratesflow-list'));
}
}
/**
* Perform the request to load the rates flow records.
*/
function vboWidgetRatesFlowLoad(wrapper, dates_direction) {
var widget_instance = jQuery('#' + wrapper);
if (!widget_instance.length) {
return false;
}
// check if a navigation of dates was requested (0 = no dates nav)
if (typeof dates_direction === 'undefined') {
dates_direction = 0;
}
// get vars for making the request
var current_offset = parseInt(widget_instance.attr('data-offset'));
var length_per_page = parseInt(widget_instance.attr('data-length'));
var from_date = widget_instance.find('.vbo-ratesflow-dtpicker-from').val();
var to_date = widget_instance.find('.vbo-ratesflow-dtpicker-to').val();
var dates_step = widget_instance.find('.vbo-ratesflow-period-nav').val();
// the widget method to call
var call_method = 'loadRatesFlowRecords';
// make a request to load the rates flow records
VBOCore.doAjax(
"<?php echo $this->getExecWidgetAjaxUri(); ?>",
{
widget_id: "<?php echo $this->getIdentifier(); ?>",
call: call_method,
return: 1,
offset: current_offset,
length: length_per_page,
fromdate: from_date,
todate: to_date,
step: dates_step,
date_dir: dates_direction,
wrapper: wrapper,
tmpl: "component"
},
function(response) {
try {
var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
if (!obj_res.hasOwnProperty(call_method)) {
console.error('Unexpected JSON response', obj_res);
return false;
}
// adjust current offset for backward navigation
if (obj_res[call_method]['page_num'] && obj_res[call_method]['page_num'] > 1) {
widget_instance.attr('data-nowoffset', current_offset);
} else {
// first page
widget_instance.attr('data-nowoffset', '0');
}
// replace HTML with new rates flow records
widget_instance.find('.vbo-widget-ratesflow-list').html(obj_res[call_method]['html']);
// check results
var tot_records = obj_res[call_method]['tot_records'] || 0;
var new_offset = obj_res[call_method]['offset'] || 0;
if (tot_records) {
// set new offset for pagination next nav
widget_instance.attr('data-offset', new_offset);
}
if (dates_direction > 0 || dates_direction < 0) {
// set new dates calculated
try {
// construct a solid date object without using the raw Y-m-d string as only argument
var dfrom_parts = obj_res[call_method]['fromdate'].split('-');
var dfrom_obj = new Date(parseInt(dfrom_parts[0]), (parseInt(dfrom_parts[1]) - 1), parseInt(dfrom_parts[2]), 0, 0, 0);
widget_instance.find('.vbo-ratesflow-dtpicker-from').datepicker('setDate', dfrom_obj);
// restore the original minimum and maximum dates for the "to date" calendar to avoid issues setting a date
widget_instance.find('.vbo-ratesflow-dtpicker-to').datepicker('option', {
minDate: "<?php echo date($df, $mindate); ?>",
maxDate: "<?php echo date($df, $maxdate); ?>",
});
// do the same for the to date by using a precise date object instance
var dto_parts = obj_res[call_method]['todate'].split('-');
var dto_obj = new Date(parseInt(dto_parts[0]), (parseInt(dto_parts[1]) - 1), parseInt(dto_parts[2]), 0, 0, 0);
widget_instance.find('.vbo-ratesflow-dtpicker-to').datepicker('setDate', dto_obj);
} catch(e) {
// just log the error
console.error(e);
}
}
// update step name
if (obj_res[call_method]['step_name'] && obj_res[call_method]['step_name'].length) {
widget_instance.find('.vbo-reportwidget-period-name').text(obj_res[call_method]['step_name']);
}
// update readable dates interval
var readable_period = obj_res[call_method]['f_fromdate'] + ' - ' + obj_res[call_method]['f_todate'];
if (obj_res[call_method]['f_fromdate'] == obj_res[call_method]['f_todate']) {
// a full month does not need an interval
readable_period = obj_res[call_method]['f_fromdate'];
}
widget_instance.find('.vbo-reportwidget-period-date').text(readable_period);
if (!isNaN(tot_records) && parseInt(tot_records) < 1) {
// no results can indicate the offset is invalid or too high
if (!isNaN(new_offset) && parseInt(new_offset) > 0) {
// reset offset to 0
widget_instance.attr('data-offset', 0);
// show loading skeletons
vboWidgetRatesFlowSkeletons(wrapper);
// reload the first page
vboWidgetRatesFlowLoad(wrapper);
}
}
} catch(err) {
console.error('could not parse JSON response', err, response);
}
},
function(error) {
// remove the skeleton loading
widget_instance.find('.vbo-widget-ratesflow-list').find('.vbo-dashboard-guest-activity-skeleton').remove();
console.error(error);
}
);
}
/**
* Navigate between the various pages of the rates flow records.
*/
function vboWidgetRatesFlowNavigate(wrapper, direction) {
var widget_instance = jQuery('#' + wrapper);
if (!widget_instance.length) {
return false;
}
// show loading skeletons
vboWidgetRatesFlowSkeletons(wrapper);
var instance_val = widget_instance.attr('data-instance');
// check direction and update offsets for nav
if (direction > 0) {
// navigate forward
var current_offset = widget_instance.attr('data-nowoffset');
widget_instance.attr('data-prevoffset', current_offset);
// push history
try {
vbo_w_ratesflow_history[instance_val].unshift(current_offset);
} catch(e) {
// do nothing
}
} else {
// navigate backward
var prev_offset = widget_instance.attr('data-prevoffset');
// check and update history
try {
var last_offset = vbo_w_ratesflow_history[instance_val].shift();
if (typeof last_offset !== 'undefined') {
widget_instance.attr('data-prevoffset', last_offset);
prev_offset = last_offset;
}
} catch(e) {
// do nothing
}
prev_offset = prev_offset >= 0 ? prev_offset : 0;
widget_instance.attr('data-offset', prev_offset);
}
// launch navigation
vboWidgetRatesFlowLoad(wrapper);
}
/**
* Navigate between the date steps and load the rates flow records.
*/
function vboWidgetRatesFlowDatesNav(wrapper, direction) {
var widget_instance = jQuery('#' + wrapper);
if (!widget_instance.length) {
return false;
}
// show loading skeletons
vboWidgetRatesFlowSkeletons(wrapper);
// reset offsets to the first state
widget_instance.attr('data-offset', '0');
widget_instance.attr('data-nowoffset', '0');
widget_instance.attr('data-prevoffset', '-1');
// launch dates navigation and load records
vboWidgetRatesFlowLoad(wrapper, direction);
}
/**
* Load rates flow records for the selected dates.
*/
function vboWidgetRatesFlowChangeDates(wrapper) {
var widget_instance = jQuery('#' + wrapper);
if (!widget_instance.length) {
return false;
}
// hide filters when making a new request
widget_instance.find('.vbo-reportwidget-filters').hide();
// show loading skeletons
vboWidgetRatesFlowSkeletons(wrapper);
// reset offsets to the first state
widget_instance.attr('data-offset', '0');
widget_instance.attr('data-nowoffset', '0');
widget_instance.attr('data-prevoffset', '-1');
// load data
vboWidgetRatesFlowLoad(wrapper);
}
/**
* Toggle dates and step filters.
*/
function vboWidgetRatesFlowToggleFilters(wrapper) {
var widget_instance = jQuery('#' + wrapper);
if (!widget_instance.length) {
return false;
}
widget_instance.find('.vbo-reportwidget-filters').toggle();
}
/**
* Datepicker dates selection.
*/
function vboWidgetRatesFlowCheckDates(selectedDate, inst) {
if (selectedDate === null || inst === null) {
return;
}
var cur_from_date = jQuery(this).val();
if (jQuery(this).hasClass("vbo-ratesflow-dtpicker-from") && cur_from_date.length) {
var nowstart = jQuery(this).datepicker("getDate");
var nowstartdate = new Date(nowstart.getTime());
jQuery(".vbo-ratesflow-dtpicker-to").datepicker("option", {minDate: nowstartdate});
}
}
/**
* Navigation period cookie onchange.
*/
function vboWidgetRatesFlowChangePeriod(period) {
// update cookie for the step selected
var nd = new Date();
nd.setTime(nd.getTime() + (365*24*60*60*1000));
document.cookie = "vbo_widget_ratesflow_step=" + period + "; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
}
</script>
<?php
}
?>
<script type="text/javascript">
// declare widgets object history array
if (typeof vbo_w_ratesflow_history === 'undefined') {
vbo_w_ratesflow_history = {
"<?php echo $wrapper_instance; ?>": []
};
}
jQuery(function() {
// render datepicker calendars for dates navigation
jQuery('#<?php echo $wrapper_id; ?>').find('.vbo-ratesflow-dtpicker-from, .vbo-ratesflow-dtpicker-to').datepicker({
minDate: "<?php echo date($df, $mindate); ?>",
maxDate: "<?php echo date($df, $maxdate); ?>",
yearRange: "<?php echo date('Y', $mindate); ?>:<?php echo date('Y', $maxdate); ?>",
changeMonth: true,
changeYear: true,
dateFormat: "<?php echo $dtpicker_df; ?>",
onSelect: vboWidgetRatesFlowCheckDates
});
// triggering for datepicker calendar icons
jQuery('i.vbo-widget-ratesflow-caltrigger').click(function() {
var jdp = jQuery(this).parent().find('input.hasDatepicker');
if (jdp.length) {
jdp.focus();
}
});
// when document is ready, load rates flow records for this widget's instance
vboWidgetRatesFlowLoad('<?php echo $wrapper_id; ?>');
});
</script>
<?php
}
/**
* Checks if the given range of dates belong to a festivity.
* Internal method for this widget only.
*
* @param array $next_fests the list of festivities to parse.
* @param int $day_from_ts from date timestamp.
* @param int $day_to_ts to date timestamp.
*
* @return mixed string fest name on success, or false.
*/
protected function findDatesFestName($next_fests, $day_from_ts, $day_to_ts)
{
if (!is_array($next_fests) || empty($day_from_ts) || empty($day_to_ts)) {
return false;
}
// we also support closest matches
$closest_matches = array();
foreach ($next_fests as $fest_data) {
if (empty($fest_data['festinfo'])) {
continue;
}
$fest = $fest_data['festinfo'][0];
$earliest_ts = 0;
$latest_ts = 0;
if ($fest->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);
}
}