| 1 |
<?php |
| 2 |
/** |
| 3 |
* @package VikBooking |
| 4 |
* @subpackage com_vikbooking |
| 5 |
* @author Alessio Gaggii - e4j - Extensionsforjoomla.com |
| 6 |
* @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved. |
| 7 |
* @license GNU General Public License version 2 or later; see LICENSE |
| 8 |
* @link https://vikwp.com |
| 9 |
*/ |
| 10 |
|
| 11 |
defined('ABSPATH') or die('No script kiddies please!'); |
| 12 |
|
| 13 |
/** |
| 14 |
* Class handler for admin widget "rates flow". |
| 15 |
* |
| 16 |
* @since 1.15.0 (J) - 1.5.0 (WP) |
| 17 |
*/ |
| 18 |
class VikBookingAdminWidgetRatesFlow extends VikBookingAdminWidget |
| 19 |
{ |
| 20 |
/** |
| 21 |
* The instance counter of this widget. Since we do not load individual parameters |
| 22 |
* for each widget's instance, we use a static counter to determine its settings. |
| 23 |
* |
| 24 |
* @var int |
| 25 |
*/ |
| 26 |
protected static $instance_counter = -1; |
| 27 |
|
| 28 |
/** |
| 29 |
* Default number of rates flow alterations per page. |
| 30 |
* |
| 31 |
* @var int |
| 32 |
*/ |
| 33 |
protected $alterations_per_page = 2; |
| 34 |
|
| 35 |
/** |
| 36 |
* Suggested limit of records per page. |
| 37 |
* |
| 38 |
* @var int |
| 39 |
*/ |
| 40 |
protected $lim_page_records = 8; |
| 41 |
|
| 42 |
/** |
| 43 |
* Class constructor will define the widget name and identifier. |
| 44 |
*/ |
| 45 |
public function __construct() |
| 46 |
{ |
| 47 |
// call parent constructor |
| 48 |
parent::__construct(); |
| 49 |
|
| 50 |
$this->widgetName = JText::translate('VBOREPORTRATESFLOW'); |
| 51 |
$this->widgetDescr = JText::translate('VBO_W_RATESFLOW_DESCR'); |
| 52 |
$this->widgetId = basename(__FILE__, '.php'); |
| 53 |
|
| 54 |
// define widget and icon and style name |
| 55 |
$this->widgetIcon = '<i class="' . VikBookingIcons::i('chart-line') . '"></i>'; |
| 56 |
$this->widgetStyleName = 'violet'; |
| 57 |
} |
| 58 |
|
| 59 |
/** |
| 60 |
* Custom method for this widget only to load the rates flow records. |
| 61 |
* The method is called by the admin controller through an AJAX request. |
| 62 |
* The visibility should be public, it should not exit the process, and |
| 63 |
* any content sent to output will be returned to the AJAX response. |
| 64 |
* In this case we return an array because this method requires "return":1. |
| 65 |
* |
| 66 |
* It's the actual rendering of the widget which also allows navigation. |
| 67 |
*/ |
| 68 |
public function loadRatesFlowRecords() |
| 69 |
{ |
| 70 |
$offset = VikRequest::getInt('offset', 0, 'request'); |
| 71 |
$length = VikRequest::getInt('length', $this->alterations_per_page, 'request'); |
| 72 |
$wrapper = VikRequest::getString('wrapper', '', 'request'); |
| 73 |
|
| 74 |
$fromdate = VikRequest::getString('fromdate', '', 'request'); |
| 75 |
$todate = VikRequest::getString('todate', '', 'request'); |
| 76 |
$step = VikRequest::getString('step', 'quarter', 'request'); |
| 77 |
$date_dir = VikRequest::getInt('date_dir', 0, 'request'); |
| 78 |
if (empty($fromdate)) { |
| 79 |
// default to current quarter (3 full months) |
| 80 |
$now_info = getdate(); |
| 81 |
$end_info = getdate(mktime(0, 0, 0, ($now_info['mon'] + 2), 1, $now_info['year'])); |
| 82 |
$to_ts = mktime(23, 59, 59, $end_info['mon'], date('t', $end_info[0]), $end_info['year']); |
| 83 |
$fromdate = date('Y-m-d', mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year'])); |
| 84 |
$todate = date('Y-m-d', $to_ts); |
| 85 |
} |
| 86 |
|
| 87 |
// convert dates to timestamps to support custom date formats |
| 88 |
$from_ts = VikBooking::getDateTimestamp($fromdate); |
| 89 |
$to_ts = VikBooking::getDateTimestamp($todate, 23, 59, 59); |
| 90 |
|
| 91 |
// check dates navigation and step |
| 92 |
$step_name = ''; |
| 93 |
if ($date_dir > 0 || $date_dir < 0) { |
| 94 |
// calculate forward or backward dates for navigation |
| 95 |
list($from_ts, $to_ts, $step_name) = $this->calcDatesNavigation($from_ts, $to_ts, $step, $date_dir); |
| 96 |
} |
| 97 |
|
| 98 |
// convert final dates to Y-m-d |
| 99 |
$fromdate = date('Y-m-d', $from_ts); |
| 100 |
$todate = date('Y-m-d', $to_ts); |
| 101 |
|
| 102 |
// get rates flow report instance |
| 103 |
$report = VikBooking::getReportInstance('rates_flow'); |
| 104 |
if (!$report) { |
| 105 |
// do nothing |
| 106 |
return; |
| 107 |
} |
| 108 |
|
| 109 |
// get VBO date format |
| 110 |
$vbo_df = $report->getDateFormat(); |
| 111 |
|
| 112 |
// set report options |
| 113 |
$report->setReportOptions(array( |
| 114 |
'fetch' => 'alterations', |
| 115 |
'krsort' => 'created_on', |
| 116 |
'krorder' => 'DESC', |
| 117 |
'fromdate' => $fromdate, |
| 118 |
'todate' => $todate, |
| 119 |
'lim' => $length, |
| 120 |
'limstart' => $offset, |
| 121 |
)); |
| 122 |
|
| 123 |
// generate report data |
| 124 |
$alterations = $report->getReportValues(); |
| 125 |
$alterations = !is_array($alterations) ? array() : $alterations; |
| 126 |
|
| 127 |
// grab report columns for paging |
| 128 |
$columns = $report->getColumnsValues(); |
| 129 |
|
| 130 |
// check next page and next offset |
| 131 |
$has_next_page = (isset($columns['paging']) && !empty($columns['paging']['has_next_page'])); |
| 132 |
$next_offset = isset($columns['paging']) && !empty($columns['paging']['limstart']) ? $columns['paging']['limstart'] : 0; |
| 133 |
$current_page = isset($columns['paging']) && !empty($columns['paging']['page_num']) ? $columns['paging']['page_num'] : 1; |
| 134 |
|
| 135 |
// check festivities, by getting the next ones |
| 136 |
$fests = VikBooking::getFestivitiesInstance(); |
| 137 |
$next_fests = $fests->loadFestDates($fromdate, $todate); |
| 138 |
|
| 139 |
// start output buffering |
| 140 |
ob_start(); |
| 141 |
|
| 142 |
// error, warning or info messages should go on top |
| 143 |
if (strlen($report->getError())) { |
| 144 |
?> |
| 145 |
<p class="err"><?php echo $report->getError(); ?></p> |
| 146 |
<?php |
| 147 |
} |
| 148 |
if (strlen($report->getWarning())) { |
| 149 |
?> |
| 150 |
<p class="warn"><?php echo $report->getWarning(); ?></p> |
| 151 |
<?php |
| 152 |
} |
| 153 |
if (!count($alterations)) { |
| 154 |
?> |
| 155 |
<p class="info"><?php echo JText::translate('VBONORESULTS'); ?></p> |
| 156 |
<?php |
| 157 |
} |
| 158 |
|
| 159 |
// loop through all rates flow records |
| 160 |
foreach ($alterations as $rflow) { |
| 161 |
// attempt to get the festivity name for the updated dates |
| 162 |
$day_from_ts = strtotime($rflow['day_from']['value']); |
| 163 |
$day_to_ts = strtotime($rflow['day_to']['value']); |
| 164 |
$fest_name = $this->findDatesFestName($next_fests, $day_from_ts, $day_to_ts); |
| 165 |
|
| 166 |
$restr_infos = array(); |
| 167 |
if (is_object($rflow['data']['value']) && isset($rflow['data']['value']->Restrictions)) { |
| 168 |
$restr_obj = $rflow['data']['value']->Restrictions; |
| 169 |
if (isset($restr_obj->minLOS) && (int)$restr_obj->minLOS > 1) { |
| 170 |
array_push($restr_infos, JText::translate('VBOMINIMUMSTAY') . ': ' . $restr_obj->minLOS); |
| 171 |
} |
| 172 |
if (isset($restr_obj->cta)) { |
| 173 |
if ((is_bool($restr_obj->cta) && $restr_obj->cta === true) || (is_string($restr_obj->cta) && !strcasecmp($restr_obj->cta, 'true'))) { |
| 174 |
array_push($restr_infos, 'CTA'); |
| 175 |
} |
| 176 |
} |
| 177 |
if (isset($restr_obj->ctd)) { |
| 178 |
if ((is_bool($restr_obj->ctd) && $restr_obj->ctd === true) || (is_string($restr_obj->ctd) && !strcasecmp($restr_obj->ctd, 'true'))) { |
| 179 |
array_push($restr_infos, 'CTD'); |
| 180 |
} |
| 181 |
} |
| 182 |
} |
| 183 |
|
| 184 |
?> |
| 185 |
<div class="vbo-widget-ratesflow-record" data-recordid="<?php echo $rflow['id']['value']; ?>"> |
| 186 |
<div class="vbo-widget-ratesflow-avatar"> |
| 187 |
<?php |
| 188 |
if (!empty($rflow['channel_id']['_logo'])) { |
| 189 |
// channel logo has got the highest priority |
| 190 |
?> |
| 191 |
<img class="vbo-widget-ratesflow-avatar-profile" src="<?php echo $rflow['channel_id']['_logo']; ?>" /> |
| 192 |
<?php |
| 193 |
} else { |
| 194 |
// we use an icon as fallback |
| 195 |
VikBookingIcons::e('hotel', 'vbo-widget-ratesflow-avatar-icon'); |
| 196 |
} |
| 197 |
?> |
| 198 |
</div> |
| 199 |
<div class="vbo-widget-ratesflow-content"> |
| 200 |
<div class="vbo-widget-ratesflow-content-head"> |
| 201 |
<div class="vbo-widget-ratesflow-content-info-details"> |
| 202 |
<h4><?php echo $rflow['channel_id']['display_value']; ?></h4> |
| 203 |
<div class="vbo-widget-ratesflow-content-info-dates"> |
| 204 |
<span class="vbo-widget-ratesflow-date-from"><?php echo $rflow['day_from']['display_value']; ?></span> |
| 205 |
<?php |
| 206 |
if ($rflow['day_from']['value'] != $rflow['day_to']['value']) { |
| 207 |
?> |
| 208 |
<span class="vbo-widget-ratesflow-date-sep">-</span> |
| 209 |
<span class="vbo-widget-ratesflow-date-to"><?php echo $rflow['day_to']['display_value']; ?></span> |
| 210 |
<?php |
| 211 |
} |
| 212 |
if (!empty($fest_name)) { |
| 213 |
?> |
| 214 |
<span class="vbo-widget-ratesflow-date-fest"><?php echo $fest_name; ?></span> |
| 215 |
<?php |
| 216 |
} |
| 217 |
?> |
| 218 |
</div> |
| 219 |
</div> |
| 220 |
<div class="vbo-widget-ratesflow-content-info-rates"> |
| 221 |
<?php |
| 222 |
$higher_rates = null; |
| 223 |
$higher_class = ''; |
| 224 |
$higher_icon = 'equals'; |
| 225 |
$higher_pcfee = 0; |
| 226 |
if (!empty($rflow['base_fee']) && !empty($rflow['base_fee']['value'])) { |
| 227 |
if ($rflow['base_fee']['value'] < $rflow['nightly_fee']['value']) { |
| 228 |
$higher_rates = true; |
| 229 |
$higher_class = ' vbo-widget-ratesflow-rate-changes-higher'; |
| 230 |
$higher_icon = 'sort-up'; |
| 231 |
$higher_pcfee = round((($rflow['nightly_fee']['value'] * 100 / $rflow['base_fee']['value']) - 100), 2); |
| 232 |
} elseif ($rflow['base_fee']['value'] > $rflow['nightly_fee']['value']) { |
| 233 |
$higher_rates = false; |
| 234 |
$higher_class = ' vbo-widget-ratesflow-rate-changes-lower'; |
| 235 |
$higher_icon = 'sort-down'; |
| 236 |
$higher_pcfee = round((($rflow['base_fee']['value'] * 100 / $rflow['nightly_fee']['value']) - 100), 2); |
| 237 |
} |
| 238 |
} |
| 239 |
?> |
| 240 |
<div class="vbo-widget-ratesflow-nightly-rate"> |
| 241 |
<span><?php echo $rflow['nightly_fee']['display_value']; ?></span> |
| 242 |
</div> |
| 243 |
<div class="vbo-widget-ratesflow-rate-changes<?php echo $higher_class; ?>"> |
| 244 |
<?php |
| 245 |
if (!empty($rflow['channel_alter']['value'])) { |
| 246 |
?> |
| 247 |
<div class="vbo-widget-ratesflow-rate-alter"> |
| 248 |
<span><?php echo $rflow['channel_alter']['display_value']; ?></span> |
| 249 |
</div> |
| 250 |
<?php |
| 251 |
} |
| 252 |
?> |
| 253 |
<div class="vbo-widget-ratesflow-rate-growth"> |
| 254 |
<span><?php VikBookingIcons::e($higher_icon); ?> <?php echo $higher_pcfee; ?>%</span> |
| 255 |
</div> |
| 256 |
</div> |
| 257 |
</div> |
| 258 |
</div> |
| 259 |
<div class="vbo-widget-ratesflow-content-info-msg"> |
| 260 |
<span class="vbo-widget-ratesflow-content-info-msg-room"><?php echo $rflow['vbo_room_id']['display_value']; ?></span> |
| 261 |
<?php |
| 262 |
if (count($restr_infos)) { |
| 263 |
?> |
| 264 |
<span class="vbo-widget-ratesflow-content-info-msg-restr"><?php echo implode(', ', $restr_infos); ?></span> |
| 265 |
<?php |
| 266 |
} |
| 267 |
?> |
| 268 |
<span class="vbo-widget-ratesflow-content-info-msg-cdate"><?php echo $rflow['created_on']['display_value']; ?></span> |
| 269 |
</div> |
| 270 |
</div> |
| 271 |
</div> |
| 272 |
<?php |
| 273 |
} |
| 274 |
|
| 275 |
// append navigation |
| 276 |
?> |
| 277 |
<div class="vbo-widget-commands vbo-widget-commands-right"> |
| 278 |
<div class="vbo-widget-commands-main"> |
| 279 |
<?php |
| 280 |
if ($offset > 0) { |
| 281 |
// show backward navigation button |
| 282 |
?> |
| 283 |
<div class="vbo-widget-command-chevron vbo-widget-command-prev"> |
| 284 |
<span class="vbo-widget-command-chevron-prev" onclick="vboWidgetRatesFlowNavigate('<?php echo $wrapper; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span> |
| 285 |
</div> |
| 286 |
<?php |
| 287 |
} |
| 288 |
if ($has_next_page) { |
| 289 |
// show forward navigation button |
| 290 |
?> |
| 291 |
<div class="vbo-widget-command-chevron vbo-widget-command-next"> |
| 292 |
<span class="vbo-widget-command-chevron-next" onclick="vboWidgetRatesFlowNavigate('<?php echo $wrapper; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span> |
| 293 |
</div> |
| 294 |
<?php |
| 295 |
} |
| 296 |
?> |
| 297 |
</div> |
| 298 |
</div> |
| 299 |
<?php |
| 300 |
|
| 301 |
// get the HTML buffer |
| 302 |
$html_content = ob_get_contents(); |
| 303 |
ob_end_clean(); |
| 304 |
|
| 305 |
// take care of the readable dates interval |
| 306 |
list($format_from_date, $format_to_date) = $this->parseReadableDateInterval($from_ts, $to_ts, $vbo_df, $step); |
| 307 |
|
| 308 |
// return an associative array of values |
| 309 |
return array( |
| 310 |
'html' => $html_content, |
| 311 |
'fromdate' => $fromdate, |
| 312 |
'f_fromdate' => $format_from_date, |
| 313 |
'todate' => $todate, |
| 314 |
'f_todate' => $format_to_date, |
| 315 |
'step' => $step, |
| 316 |
'step_name' => $step_name, |
| 317 |
'tot_records' => count($alterations), |
| 318 |
'next_page' => (int)$has_next_page, |
| 319 |
'page_num' => (int)$current_page, |
| 320 |
'offset' => $next_offset, |
| 321 |
); |
| 322 |
} |
| 323 |
|
| 324 |
/** |
| 325 |
* Preload the necessary CSS/JS assets. |
| 326 |
* |
| 327 |
* @return void |
| 328 |
*/ |
| 329 |
public function preload() |
| 330 |
{ |
| 331 |
// load assets |
| 332 |
$this->vbo_app->loadDatePicker(); |
| 333 |
} |
| 334 |
|
| 335 |
/** |
| 336 |
* Main method to invoke the widget. Contents will be loaded |
| 337 |
* through AJAX requests, not via PHP when the page loads. |
| 338 |
* |
| 339 |
* @return void |
| 340 |
*/ |
| 341 |
public function render(?VBOMultitaskData $data = null) |
| 342 |
{ |
| 343 |
// increase widget's instance counter |
| 344 |
static::$instance_counter++; |
| 345 |
|
| 346 |
// check whether the widget is being rendered via AJAX when adding it through the customizer |
| 347 |
$is_ajax = $this->isAjaxRendering(); |
| 348 |
|
| 349 |
// generate a unique ID for the sticky notes wrapper instance |
| 350 |
$wrapper_instance = !$is_ajax ? static::$instance_counter : rand(); |
| 351 |
$wrapper_id = 'vbo-widget-ratesflow-' . $wrapper_instance; |
| 352 |
|
| 353 |
// get permissions |
| 354 |
$vbo_auth_pricing = JFactory::getUser()->authorise('core.vbo.pricing', 'com_vikbooking'); |
| 355 |
|
| 356 |
// get rates flow report instance |
| 357 |
$report = VikBooking::getReportInstance('rates_flow'); |
| 358 |
if (!$report || !$vbo_auth_pricing) { |
| 359 |
// display nothing |
| 360 |
return; |
| 361 |
} |
| 362 |
|
| 363 |
// get the total number of unique channels updated |
| 364 |
$tot_channels_updated = $report->countRatesFlowChannels(); |
| 365 |
if ($tot_channels_updated > 0 && ($this->alterations_per_page * $tot_channels_updated) < $this->lim_page_records) { |
| 366 |
$best_lim_page = floor($this->lim_page_records / $tot_channels_updated); |
| 367 |
$this->alterations_per_page = $best_lim_page > $this->alterations_per_page ? $best_lim_page : $this->alterations_per_page; |
| 368 |
} |
| 369 |
|
| 370 |
// count average records per page |
| 371 |
$avg_per_page = floor(($this->alterations_per_page + $this->lim_page_records) / 2); |
| 372 |
$avg_per_page = $avg_per_page < $this->alterations_per_page ? $this->alterations_per_page : $avg_per_page; |
| 373 |
|
| 374 |
// get minimum and maximum nights updated for dates filters |
| 375 |
list($mindate, $maxdate) = $report->getMinDatesRatesFlow(); |
| 376 |
$mindate = empty($mindate) ? time() : $mindate; |
| 377 |
$maxdate = empty($maxdate) ? $mindate : $maxdate; |
| 378 |
|
| 379 |
// dates navigation preference |
| 380 |
$cookie = JFactory::getApplication()->input->cookie; |
| 381 |
$cookie_step = $cookie->get('vbo_widget_ratesflow_step', 'quarter', 'string'); |
| 382 |
|
| 383 |
// determine default period name and dates |
| 384 |
$df = $report->getDateFormat(); |
| 385 |
$dtpicker_df = $report->getDateFormat('jui'); |
| 386 |
$now_info = getdate(); |
| 387 |
if ($cookie_step == 'weekend') { |
| 388 |
// next weekend |
| 389 |
$from_ts = strtotime("next Friday"); |
| 390 |
$to_ts = strtotime("next Sunday"); |
| 391 |
if ($to_ts < $from_ts) { |
| 392 |
// this weekend |
| 393 |
$from_ts = strtotime("last Friday"); |
| 394 |
} |
| 395 |
$fromdate = date($df, $from_ts); |
| 396 |
$todate = date($df, $to_ts); |
| 397 |
$period_name = JText::translate('VBOWEEKND'); |
| 398 |
} elseif ($cookie_step == 'month') { |
| 399 |
// current full month |
| 400 |
$from_ts = mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year']); |
| 401 |
$fromdate = date($df, $from_ts); |
| 402 |
$to_ts = mktime(23, 59, 59, $now_info['mon'], date('t', $now_info[0]), $now_info['year']); |
| 403 |
$todate = date($df, $to_ts); |
| 404 |
$period_name = JText::translate('VBPVIEWRESTRICTIONSTWO'); |
| 405 |
} else { |
| 406 |
// default to next 3 (total) months from today's month |
| 407 |
$from_ts = mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year']); |
| 408 |
$fromdate = date($df, $from_ts); |
| 409 |
$end_info = getdate(mktime(0, 0, 0, ($now_info['mon'] + 2), 1, $now_info['year'])); |
| 410 |
$to_ts = mktime(23, 59, 59, $end_info['mon'], date('t', $end_info[0]), $end_info['year']); |
| 411 |
$todate = date($df, $to_ts); |
| 412 |
$period_name = JText::translate('VBO_QUARTER'); |
| 413 |
} |
| 414 |
|
| 415 |
// take care of the readable dates interval |
| 416 |
list($format_from_date, $format_to_date) = $this->parseReadableDateInterval($from_ts, $to_ts, $df, $cookie_step); |
| 417 |
$interval_name = $format_from_date . ' - ' . $format_to_date; |
| 418 |
if ($format_from_date == $format_to_date) { |
| 419 |
$interval_name = $format_from_date; |
| 420 |
} |
| 421 |
|
| 422 |
?> |
| 423 |
<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; ?>"> |
| 424 |
<div class="vbo-admin-widget-head"> |
| 425 |
<div class="vbo-admin-widget-head-inline"> |
| 426 |
<h4><?php echo $this->widgetIcon; ?> <span><?php echo $this->widgetName; ?></span></h4> |
| 427 |
<div class="vbo-admin-widget-head-commands"> |
| 428 |
|
| 429 |
<div class="vbo-reportwidget-commands"> |
| 430 |
<div class="vbo-reportwidget-commands-main"> |
| 431 |
<div class="vbo-reportwidget-command-dates"> |
| 432 |
<div class="vbo-reportwidget-period-name"><?php echo $period_name; ?></div> |
| 433 |
<div class="vbo-reportwidget-period-date"><?php echo $interval_name; ?></div> |
| 434 |
</div> |
| 435 |
<div class="vbo-reportwidget-command-chevron vbo-reportwidget-command-prev"> |
| 436 |
<span class="vbo-widget-ratesflow-dt-prev" onclick="vboWidgetRatesFlowDatesNav('<?php echo $wrapper_id; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span> |
| 437 |
</div> |
| 438 |
<div class="vbo-reportwidget-command-chevron vbo-reportwidget-command-next"> |
| 439 |
<span class="vbo-widget-ratesflow-dt-next" onclick="vboWidgetRatesFlowDatesNav('<?php echo $wrapper_id; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span> |
| 440 |
</div> |
| 441 |
</div> |
| 442 |
<div class="vbo-reportwidget-command-dots"> |
| 443 |
<span class="vbo-widget-command-togglefilters vbo-widget-ratesflow-togglefilters" onclick="vboWidgetRatesFlowToggleFilters('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('ellipsis-v'); ?></span> |
| 444 |
</div> |
| 445 |
</div> |
| 446 |
<div class="vbo-reportwidget-filters"> |
| 447 |
<div class="vbo-reportwidget-filter"> |
| 448 |
<span class="vbo-reportwidget-datepicker"> |
| 449 |
<?php VikBookingIcons::e('calendar', 'vbo-widget-ratesflow-caltrigger'); ?> |
| 450 |
<input type="text" class="vbo-ratesflow-dtpicker-from" value="<?php echo $fromdate; ?>" /> |
| 451 |
</span> |
| 452 |
</div> |
| 453 |
<div class="vbo-reportwidget-filter"> |
| 454 |
<span class="vbo-reportwidget-datepicker"> |
| 455 |
<?php VikBookingIcons::e('calendar', 'vbo-widget-ratesflow-caltrigger'); ?> |
| 456 |
<input type="text" class="vbo-ratesflow-dtpicker-to" value="<?php echo $todate; ?>" /> |
| 457 |
</span> |
| 458 |
</div> |
| 459 |
<div class="vbo-reportwidget-filter"> |
| 460 |
<select class="vbo-ratesflow-period-nav" onchange="vboWidgetRatesFlowChangePeriod(this.value);"> |
| 461 |
<option value="weekend"<?php echo $cookie_step == 'weekend' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBOWEEKND'); ?></option> |
| 462 |
<option value="month"<?php echo $cookie_step == 'month' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBPVIEWRESTRICTIONSTWO'); ?></option> |
| 463 |
<option value="quarter"<?php echo $cookie_step == 'quarter' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_QUARTER'); ?></option> |
| 464 |
</select> |
| 465 |
</div> |
| 466 |
<div class="vbo-reportwidget-filter vbo-reportwidget-filter-confirm"> |
| 467 |
<button type="button" class="btn vbo-config-btn" onclick="vboWidgetRatesFlowChangeDates('<?php echo $wrapper_id; ?>');"><?php echo JText::translate('VBADMINNOTESUPD'); ?></button> |
| 468 |
</div> |
| 469 |
</div> |
| 470 |
|
| 471 |
</div> |
| 472 |
</div> |
| 473 |
</div> |
| 474 |
<div class="vbo-widget-ratesflow-wrap"> |
| 475 |
<div class="vbo-widget-ratesflow-inner"> |
| 476 |
<div class="vbo-widget-ratesflow-list"> |
| 477 |
<?php |
| 478 |
for ($i = 0; $i < $avg_per_page; $i++) { |
| 479 |
?> |
| 480 |
<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton"> |
| 481 |
<div class="vbo-dashboard-guest-activity-avatar"> |
| 482 |
<div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div> |
| 483 |
</div> |
| 484 |
<div class="vbo-dashboard-guest-activity-content"> |
| 485 |
<div class="vbo-dashboard-guest-activity-content-head"> |
| 486 |
<div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div> |
| 487 |
</div> |
| 488 |
<div class="vbo-dashboard-guest-activity-content-info-msg"> |
| 489 |
<div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div> |
| 490 |
</div> |
| 491 |
</div> |
| 492 |
</div> |
| 493 |
<?php |
| 494 |
} |
| 495 |
?> |
| 496 |
</div> |
| 497 |
</div> |
| 498 |
</div> |
| 499 |
</div> |
| 500 |
<?php |
| 501 |
|
| 502 |
if (static::$instance_counter === 0 || $is_ajax) { |
| 503 |
/** |
| 504 |
* Print the JS code only once for all instances of this widget. |
| 505 |
* The real rendering is made through AJAX, not when the page loads. |
| 506 |
*/ |
| 507 |
?> |
| 508 |
<script type="text/javascript"> |
| 509 |
|
| 510 |
/** |
| 511 |
* Display the loading skeletons. |
| 512 |
*/ |
| 513 |
function vboWidgetRatesFlowSkeletons(wrapper) { |
| 514 |
var widget_instance = jQuery('#' + wrapper); |
| 515 |
if (!widget_instance.length) { |
| 516 |
return false; |
| 517 |
} |
| 518 |
widget_instance.find('.vbo-widget-ratesflow-list').html(''); |
| 519 |
for (var i = 0; i < <?php echo $avg_per_page; ?>; i++) { |
| 520 |
var skeleton = ''; |
| 521 |
skeleton += '<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">'; |
| 522 |
skeleton += ' <div class="vbo-dashboard-guest-activity-avatar">'; |
| 523 |
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>'; |
| 524 |
skeleton += ' </div>'; |
| 525 |
skeleton += ' <div class="vbo-dashboard-guest-activity-content">'; |
| 526 |
skeleton += ' <div class="vbo-dashboard-guest-activity-content-head">'; |
| 527 |
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>'; |
| 528 |
skeleton += ' </div>'; |
| 529 |
skeleton += ' <div class="vbo-dashboard-guest-activity-content-info-msg">'; |
| 530 |
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>'; |
| 531 |
skeleton += ' </div>'; |
| 532 |
skeleton += ' </div>'; |
| 533 |
skeleton += '</div>'; |
| 534 |
// append skeleton |
| 535 |
jQuery(skeleton).appendTo(widget_instance.find('.vbo-widget-ratesflow-list')); |
| 536 |
} |
| 537 |
} |
| 538 |
|
| 539 |
/** |
| 540 |
* Perform the request to load the rates flow records. |
| 541 |
*/ |
| 542 |
function vboWidgetRatesFlowLoad(wrapper, dates_direction) { |
| 543 |
var widget_instance = jQuery('#' + wrapper); |
| 544 |
if (!widget_instance.length) { |
| 545 |
return false; |
| 546 |
} |
| 547 |
|
| 548 |
// check if a navigation of dates was requested (0 = no dates nav) |
| 549 |
if (typeof dates_direction === 'undefined') { |
| 550 |
dates_direction = 0; |
| 551 |
} |
| 552 |
|
| 553 |
// get vars for making the request |
| 554 |
var current_offset = parseInt(widget_instance.attr('data-offset')); |
| 555 |
var length_per_page = parseInt(widget_instance.attr('data-length')); |
| 556 |
var from_date = widget_instance.find('.vbo-ratesflow-dtpicker-from').val(); |
| 557 |
var to_date = widget_instance.find('.vbo-ratesflow-dtpicker-to').val(); |
| 558 |
var dates_step = widget_instance.find('.vbo-ratesflow-period-nav').val(); |
| 559 |
|
| 560 |
// the widget method to call |
| 561 |
var call_method = 'loadRatesFlowRecords'; |
| 562 |
|
| 563 |
// make a request to load the rates flow records |
| 564 |
VBOCore.doAjax( |
| 565 |
"<?php echo $this->getExecWidgetAjaxUri(); ?>", |
| 566 |
{ |
| 567 |
widget_id: "<?php echo $this->getIdentifier(); ?>", |
| 568 |
call: call_method, |
| 569 |
return: 1, |
| 570 |
offset: current_offset, |
| 571 |
length: length_per_page, |
| 572 |
fromdate: from_date, |
| 573 |
todate: to_date, |
| 574 |
step: dates_step, |
| 575 |
date_dir: dates_direction, |
| 576 |
wrapper: wrapper, |
| 577 |
tmpl: "component" |
| 578 |
}, |
| 579 |
function(response) { |
| 580 |
try { |
| 581 |
var obj_res = typeof response === 'string' ? JSON.parse(response) : response; |
| 582 |
if (!obj_res.hasOwnProperty(call_method)) { |
| 583 |
console.error('Unexpected JSON response', obj_res); |
| 584 |
return false; |
| 585 |
} |
| 586 |
|
| 587 |
// adjust current offset for backward navigation |
| 588 |
if (obj_res[call_method]['page_num'] && obj_res[call_method]['page_num'] > 1) { |
| 589 |
widget_instance.attr('data-nowoffset', current_offset); |
| 590 |
} else { |
| 591 |
// first page |
| 592 |
widget_instance.attr('data-nowoffset', '0'); |
| 593 |
} |
| 594 |
|
| 595 |
// replace HTML with new rates flow records |
| 596 |
widget_instance.find('.vbo-widget-ratesflow-list').html(obj_res[call_method]['html']); |
| 597 |
|
| 598 |
// check results |
| 599 |
var tot_records = obj_res[call_method]['tot_records'] || 0; |
| 600 |
var new_offset = obj_res[call_method]['offset'] || 0; |
| 601 |
if (tot_records) { |
| 602 |
// set new offset for pagination next nav |
| 603 |
widget_instance.attr('data-offset', new_offset); |
| 604 |
} |
| 605 |
|
| 606 |
if (dates_direction > 0 || dates_direction < 0) { |
| 607 |
// set new dates calculated |
| 608 |
try { |
| 609 |
// construct a solid date object without using the raw Y-m-d string as only argument |
| 610 |
var dfrom_parts = obj_res[call_method]['fromdate'].split('-'); |
| 611 |
var dfrom_obj = new Date(parseInt(dfrom_parts[0]), (parseInt(dfrom_parts[1]) - 1), parseInt(dfrom_parts[2]), 0, 0, 0); |
| 612 |
widget_instance.find('.vbo-ratesflow-dtpicker-from').datepicker('setDate', dfrom_obj); |
| 613 |
|
| 614 |
// restore the original minimum and maximum dates for the "to date" calendar to avoid issues setting a date |
| 615 |
widget_instance.find('.vbo-ratesflow-dtpicker-to').datepicker('option', { |
| 616 |
minDate: "<?php echo date($df, $mindate); ?>", |
| 617 |
maxDate: "<?php echo date($df, $maxdate); ?>", |
| 618 |
}); |
| 619 |
|
| 620 |
// do the same for the to date by using a precise date object instance |
| 621 |
var dto_parts = obj_res[call_method]['todate'].split('-'); |
| 622 |
var dto_obj = new Date(parseInt(dto_parts[0]), (parseInt(dto_parts[1]) - 1), parseInt(dto_parts[2]), 0, 0, 0); |
| 623 |
widget_instance.find('.vbo-ratesflow-dtpicker-to').datepicker('setDate', dto_obj); |
| 624 |
} catch(e) { |
| 625 |
// just log the error |
| 626 |
console.error(e); |
| 627 |
} |
| 628 |
} |
| 629 |
|
| 630 |
// update step name |
| 631 |
if (obj_res[call_method]['step_name'] && obj_res[call_method]['step_name'].length) { |
| 632 |
widget_instance.find('.vbo-reportwidget-period-name').text(obj_res[call_method]['step_name']); |
| 633 |
} |
| 634 |
|
| 635 |
// update readable dates interval |
| 636 |
var readable_period = obj_res[call_method]['f_fromdate'] + ' - ' + obj_res[call_method]['f_todate']; |
| 637 |
if (obj_res[call_method]['f_fromdate'] == obj_res[call_method]['f_todate']) { |
| 638 |
// a full month does not need an interval |
| 639 |
readable_period = obj_res[call_method]['f_fromdate']; |
| 640 |
} |
| 641 |
widget_instance.find('.vbo-reportwidget-period-date').text(readable_period); |
| 642 |
|
| 643 |
if (!isNaN(tot_records) && parseInt(tot_records) < 1) { |
| 644 |
// no results can indicate the offset is invalid or too high |
| 645 |
if (!isNaN(new_offset) && parseInt(new_offset) > 0) { |
| 646 |
// reset offset to 0 |
| 647 |
widget_instance.attr('data-offset', 0); |
| 648 |
// show loading skeletons |
| 649 |
vboWidgetRatesFlowSkeletons(wrapper); |
| 650 |
// reload the first page |
| 651 |
vboWidgetRatesFlowLoad(wrapper); |
| 652 |
} |
| 653 |
} |
| 654 |
} catch(err) { |
| 655 |
console.error('could not parse JSON response', err, response); |
| 656 |
} |
| 657 |
}, |
| 658 |
function(error) { |
| 659 |
// remove the skeleton loading |
| 660 |
widget_instance.find('.vbo-widget-ratesflow-list').find('.vbo-dashboard-guest-activity-skeleton').remove(); |
| 661 |
console.error(error); |
| 662 |
} |
| 663 |
); |
| 664 |
} |
| 665 |
|
| 666 |
/** |
| 667 |
* Navigate between the various pages of the rates flow records. |
| 668 |
*/ |
| 669 |
function vboWidgetRatesFlowNavigate(wrapper, direction) { |
| 670 |
var widget_instance = jQuery('#' + wrapper); |
| 671 |
if (!widget_instance.length) { |
| 672 |
return false; |
| 673 |
} |
| 674 |
|
| 675 |
// show loading skeletons |
| 676 |
vboWidgetRatesFlowSkeletons(wrapper); |
| 677 |
|
| 678 |
var instance_val = widget_instance.attr('data-instance'); |
| 679 |
|
| 680 |
// check direction and update offsets for nav |
| 681 |
if (direction > 0) { |
| 682 |
// navigate forward |
| 683 |
var current_offset = widget_instance.attr('data-nowoffset'); |
| 684 |
widget_instance.attr('data-prevoffset', current_offset); |
| 685 |
// push history |
| 686 |
try { |
| 687 |
vbo_w_ratesflow_history[instance_val].unshift(current_offset); |
| 688 |
} catch(e) { |
| 689 |
// do nothing |
| 690 |
} |
| 691 |
} else { |
| 692 |
// navigate backward |
| 693 |
var prev_offset = widget_instance.attr('data-prevoffset'); |
| 694 |
// check and update history |
| 695 |
try { |
| 696 |
var last_offset = vbo_w_ratesflow_history[instance_val].shift(); |
| 697 |
if (typeof last_offset !== 'undefined') { |
| 698 |
widget_instance.attr('data-prevoffset', last_offset); |
| 699 |
prev_offset = last_offset; |
| 700 |
} |
| 701 |
} catch(e) { |
| 702 |
// do nothing |
| 703 |
} |
| 704 |
prev_offset = prev_offset >= 0 ? prev_offset : 0; |
| 705 |
widget_instance.attr('data-offset', prev_offset); |
| 706 |
} |
| 707 |
|
| 708 |
// launch navigation |
| 709 |
vboWidgetRatesFlowLoad(wrapper); |
| 710 |
} |
| 711 |
|
| 712 |
/** |
| 713 |
* Navigate between the date steps and load the rates flow records. |
| 714 |
*/ |
| 715 |
function vboWidgetRatesFlowDatesNav(wrapper, direction) { |
| 716 |
var widget_instance = jQuery('#' + wrapper); |
| 717 |
if (!widget_instance.length) { |
| 718 |
return false; |
| 719 |
} |
| 720 |
|
| 721 |
// show loading skeletons |
| 722 |
vboWidgetRatesFlowSkeletons(wrapper); |
| 723 |
|
| 724 |
// reset offsets to the first state |
| 725 |
widget_instance.attr('data-offset', '0'); |
| 726 |
widget_instance.attr('data-nowoffset', '0'); |
| 727 |
widget_instance.attr('data-prevoffset', '-1'); |
| 728 |
|
| 729 |
// launch dates navigation and load records |
| 730 |
vboWidgetRatesFlowLoad(wrapper, direction); |
| 731 |
} |
| 732 |
|
| 733 |
/** |
| 734 |
* Load rates flow records for the selected dates. |
| 735 |
*/ |
| 736 |
function vboWidgetRatesFlowChangeDates(wrapper) { |
| 737 |
var widget_instance = jQuery('#' + wrapper); |
| 738 |
if (!widget_instance.length) { |
| 739 |
return false; |
| 740 |
} |
| 741 |
|
| 742 |
// hide filters when making a new request |
| 743 |
widget_instance.find('.vbo-reportwidget-filters').hide(); |
| 744 |
|
| 745 |
// show loading skeletons |
| 746 |
vboWidgetRatesFlowSkeletons(wrapper); |
| 747 |
|
| 748 |
// reset offsets to the first state |
| 749 |
widget_instance.attr('data-offset', '0'); |
| 750 |
widget_instance.attr('data-nowoffset', '0'); |
| 751 |
widget_instance.attr('data-prevoffset', '-1'); |
| 752 |
|
| 753 |
// load data |
| 754 |
vboWidgetRatesFlowLoad(wrapper); |
| 755 |
} |
| 756 |
|
| 757 |
/** |
| 758 |
* Toggle dates and step filters. |
| 759 |
*/ |
| 760 |
function vboWidgetRatesFlowToggleFilters(wrapper) { |
| 761 |
var widget_instance = jQuery('#' + wrapper); |
| 762 |
if (!widget_instance.length) { |
| 763 |
return false; |
| 764 |
} |
| 765 |
|
| 766 |
widget_instance.find('.vbo-reportwidget-filters').toggle(); |
| 767 |
} |
| 768 |
|
| 769 |
/** |
| 770 |
* Datepicker dates selection. |
| 771 |
*/ |
| 772 |
function vboWidgetRatesFlowCheckDates(selectedDate, inst) { |
| 773 |
if (selectedDate === null || inst === null) { |
| 774 |
return; |
| 775 |
} |
| 776 |
var cur_from_date = jQuery(this).val(); |
| 777 |
if (jQuery(this).hasClass("vbo-ratesflow-dtpicker-from") && cur_from_date.length) { |
| 778 |
var nowstart = jQuery(this).datepicker("getDate"); |
| 779 |
var nowstartdate = new Date(nowstart.getTime()); |
| 780 |
jQuery(".vbo-ratesflow-dtpicker-to").datepicker("option", {minDate: nowstartdate}); |
| 781 |
} |
| 782 |
} |
| 783 |
|
| 784 |
/** |
| 785 |
* Navigation period cookie onchange. |
| 786 |
*/ |
| 787 |
function vboWidgetRatesFlowChangePeriod(period) { |
| 788 |
// update cookie for the step selected |
| 789 |
var nd = new Date(); |
| 790 |
nd.setTime(nd.getTime() + (365*24*60*60*1000)); |
| 791 |
document.cookie = "vbo_widget_ratesflow_step=" + period + "; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax"; |
| 792 |
} |
| 793 |
|
| 794 |
</script> |
| 795 |
<?php |
| 796 |
} |
| 797 |
?> |
| 798 |
|
| 799 |
<script type="text/javascript"> |
| 800 |
|
| 801 |
// declare widgets object history array |
| 802 |
if (typeof vbo_w_ratesflow_history === 'undefined') { |
| 803 |
vbo_w_ratesflow_history = { |
| 804 |
"<?php echo $wrapper_instance; ?>": [] |
| 805 |
}; |
| 806 |
} |
| 807 |
|
| 808 |
jQuery(function() { |
| 809 |
|
| 810 |
// render datepicker calendars for dates navigation |
| 811 |
jQuery('#<?php echo $wrapper_id; ?>').find('.vbo-ratesflow-dtpicker-from, .vbo-ratesflow-dtpicker-to').datepicker({ |
| 812 |
minDate: "<?php echo date($df, $mindate); ?>", |
| 813 |
maxDate: "<?php echo date($df, $maxdate); ?>", |
| 814 |
yearRange: "<?php echo date('Y', $mindate); ?>:<?php echo date('Y', $maxdate); ?>", |
| 815 |
changeMonth: true, |
| 816 |
changeYear: true, |
| 817 |
dateFormat: "<?php echo $dtpicker_df; ?>", |
| 818 |
onSelect: vboWidgetRatesFlowCheckDates |
| 819 |
}); |
| 820 |
|
| 821 |
// triggering for datepicker calendar icons |
| 822 |
jQuery('i.vbo-widget-ratesflow-caltrigger').click(function() { |
| 823 |
var jdp = jQuery(this).parent().find('input.hasDatepicker'); |
| 824 |
if (jdp.length) { |
| 825 |
jdp.focus(); |
| 826 |
} |
| 827 |
}); |
| 828 |
|
| 829 |
// when document is ready, load rates flow records for this widget's instance |
| 830 |
vboWidgetRatesFlowLoad('<?php echo $wrapper_id; ?>'); |
| 831 |
|
| 832 |
}); |
| 833 |
|
| 834 |
</script> |
| 835 |
|
| 836 |
<?php |
| 837 |
} |
| 838 |
|
| 839 |
/** |
| 840 |
* Checks if the given range of dates belong to a festivity. |
| 841 |
* Internal method for this widget only. |
| 842 |
* |
| 843 |
* @param array $next_fests the list of festivities to parse. |
| 844 |
* @param int $day_from_ts from date timestamp. |
| 845 |
* @param int $day_to_ts to date timestamp. |
| 846 |
* |
| 847 |
* @return mixed string fest name on success, or false. |
| 848 |
*/ |
| 849 |
protected function findDatesFestName($next_fests, $day_from_ts, $day_to_ts) |
| 850 |
{ |
| 851 |
if (!is_array($next_fests) || empty($day_from_ts) || empty($day_to_ts)) { |
| 852 |
return false; |
| 853 |
} |
| 854 |
|
| 855 |
// we also support closest matches |
| 856 |
$closest_matches = array(); |
| 857 |
|
| 858 |
foreach ($next_fests as $fest_data) { |
| 859 |
if (empty($fest_data['festinfo'])) { |
| 860 |
continue; |
| 861 |
} |
| 862 |
$fest = $fest_data['festinfo'][0]; |
| 863 |
$earliest_ts = 0; |
| 864 |
$latest_ts = 0; |
| 865 |
|
| 866 |
if ($fest->from_ts <= $day_from_ts && $fest->to_ts >= $day_to_ts) { |
| 867 |
// fest found wrapping all dates updated |
| 868 |
return $fest->trans_name; |
| 869 |
} |
| 870 |
|
| 871 |
if (isset($fest->all_timestamps) && count($fest->all_timestamps) > 1) { |
| 872 |
// check bridge-dates |
| 873 |
$earliest_ts = $fest->all_timestamps[0]; |
| 874 |
$latest_ts = $fest->all_timestamps[(count($fest->all_timestamps) - 1)] + 86399; |
| 875 |
if ($earliest_ts <= $day_from_ts && $latest_ts >= $day_to_ts) { |
| 876 |
// fest found with bridge-dates wrapping all dates updated |
| 877 |
return $fest->trans_name; |
| 878 |
} |
| 879 |
} |
| 880 |
|
| 881 |
// check if at least one date updated in the interval is matching |
| 882 |
$days_diff = floor(($day_to_ts - $day_from_ts) / 86400); |
| 883 |
$days_to_start = null; |
| 884 |
if ($fest->from_ts <= $day_from_ts && $fest->to_ts >= $day_from_ts) { |
| 885 |
// this is a closest match, because the first interval day updated is a fest |
| 886 |
$days_to_start = floor(($day_to_ts - $fest->to_ts) / 86400); |
| 887 |
} elseif ($fest->from_ts <= $day_to_ts && $fest->to_ts >= $day_to_ts) { |
| 888 |
// this is a closest match, because the last interval day updated is a fest |
| 889 |
$days_to_start = floor(($fest->from_ts - $day_from_ts) / 86400); |
| 890 |
} elseif ($earliest_ts > 0 && $earliest_ts <= $day_from_ts && $latest_ts >= $day_from_ts) { |
| 891 |
// this is a closest match, because the first interval day updated is a fest-bridge |
| 892 |
$days_to_start = floor(($day_to_ts - $latest_ts) / 86400); |
| 893 |
} elseif ($earliest_ts > 0 && $earliest_ts <= $day_to_ts && $latest_ts >= $day_to_ts) { |
| 894 |
// this is a closest match, because the last interval day updated is a fest-bridge |
| 895 |
$days_to_start = floor(($earliest_ts - $day_from_ts) / 86400); |
| 896 |
} |
| 897 |
if ($days_to_start !== null && abs($days_to_start) <= 4 && $days_diff < 15) { |
| 898 |
// push close match |
| 899 |
array_push($closest_matches, array( |
| 900 |
'fest_name' => $fest->trans_name, |
| 901 |
'days_diff' => $days_diff, |
| 902 |
'days_start' => abs($days_to_start), |
| 903 |
)); |
| 904 |
} |
| 905 |
} |
| 906 |
|
| 907 |
// count closest matches, if any |
| 908 |
$tot_matches = count($closest_matches); |
| 909 |
if ($tot_matches === 1) { |
| 910 |
// return the first closest match |
| 911 |
return $closest_matches[0]['fest_name']; |
| 912 |
} |
| 913 |
if ($tot_matches > 1) { |
| 914 |
// sort closest matches |
| 915 |
usort($closest_matches, function($a, $b) { |
| 916 |
// sort by days diff ASC |
| 917 |
$sort_diff = ($a['days_diff'] < $b['days_diff']) ? -1 : 1; |
| 918 |
// sort by days to start ASC |
| 919 |
$sort_diff += ($a['days_start'] < $b['days_start']) ? -1 : 1; |
| 920 |
return $sort_diff; |
| 921 |
}); |
| 922 |
// return the best close match |
| 923 |
return $closest_matches[0]['fest_name']; |
| 924 |
} |
| 925 |
|
| 926 |
return false; |
| 927 |
} |
| 928 |
|
| 929 |
/** |
| 930 |
* Calculates the new dates interval for navigation according to step. |
| 931 |
* Internal method for this widget only. |
| 932 |
* |
| 933 |
* @param int $from_ts current from date timestamp. |
| 934 |
* @param int $to_ts current end date timestamp. |
| 935 |
* @param string $step the navigation step to take (weekend, month or quarter). |
| 936 |
* @param int $date_dir less than 0 backward nav, more than 0 forward. |
| 937 |
* |
| 938 |
* @return array the new from and end date timestamps, plus the step name. |
| 939 |
*/ |
| 940 |
protected function calcDatesNavigation($from_ts, $to_ts, $step, $date_dir) |
| 941 |
{ |
| 942 |
if (empty($from_ts) || (!($date_dir > 0) && !($date_dir < 0))) { |
| 943 |
return array($from_ts, $to_ts, ''); |
| 944 |
} |
| 945 |
|
| 946 |
// start date information |
| 947 |
$from_info = getdate($from_ts); |
| 948 |
|
| 949 |
// next or prev weekend |
| 950 |
if ($step == 'weekend') { |
| 951 |
if ($date_dir > 0) { |
| 952 |
// forward dates navigation |
| 953 |
$from_ts = strtotime("next Friday", $from_info[0]); |
| 954 |
$to_ts = strtotime("next Sunday", $from_info[0]); |
| 955 |
if ($to_ts < $from_ts) { |
| 956 |
// week after |
| 957 |
$to_ts = strtotime("+1 week", $to_ts); |
| 958 |
} |
| 959 |
} elseif ($date_dir < 0) { |
| 960 |
// backward dates navigation |
| 961 |
$from_ts = strtotime("last Friday", $from_info[0]); |
| 962 |
$to_ts = strtotime("last Sunday", $from_info[0]); |
| 963 |
if ($to_ts < $from_ts) { |
| 964 |
// week before |
| 965 |
$from_ts = strtotime("-1 week", $from_ts); |
| 966 |
} |
| 967 |
} |
| 968 |
return array($from_ts, $to_ts, JText::translate('VBOWEEKND')); |
| 969 |
} |
| 970 |
|
| 971 |
// next or prev month |
| 972 |
if ($step == 'month') { |
| 973 |
if ($date_dir > 0) { |
| 974 |
// forward dates navigation |
| 975 |
$from_ts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']); |
| 976 |
$to_ts = mktime(0, 0, 0, ($from_info['mon'] + 1), date('t', $from_ts), $from_info['year']); |
| 977 |
} elseif ($date_dir < 0) { |
| 978 |
// backward dates navigation |
| 979 |
$from_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']); |
| 980 |
$to_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), date('t', $from_ts), $from_info['year']); |
| 981 |
} |
| 982 |
return array($from_ts, $to_ts, JText::translate('VBPVIEWRESTRICTIONSTWO')); |
| 983 |
} |
| 984 |
|
| 985 |
// next or prev quarter by default |
| 986 |
if ($date_dir > 0) { |
| 987 |
// forward dates navigation |
| 988 |
$from_ts = mktime(0, 0, 0, ($from_info['mon'] + 3), 1, $from_info['year']); |
| 989 |
$end_ts = mktime(0, 0, 0, ($from_info['mon'] + 5), 1, $from_info['year']); |
| 990 |
$to_ts = mktime(0, 0, 0, ($from_info['mon'] + 5), date('t', $end_ts), $from_info['year']); |
| 991 |
} elseif ($date_dir < 0) { |
| 992 |
// backward dates navigation |
| 993 |
$from_ts = mktime(0, 0, 0, ($from_info['mon'] - 3), 1, $from_info['year']); |
| 994 |
$end_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']); |
| 995 |
$to_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), date('t', $end_ts), $from_info['year']); |
| 996 |
} |
| 997 |
return array($from_ts, $to_ts, JText::translate('VBO_QUARTER')); |
| 998 |
} |
| 999 |
|
| 1000 |
/** |
| 1001 |
* Parse an interval of date timestamps into a readable interval of dates according to step. |
| 1002 |
* Internal method for this widget only. |
| 1003 |
* |
| 1004 |
* @param int $from_ts current from date timestamp. |
| 1005 |
* @param int $to_ts current end date timestamp. |
| 1006 |
* @param string $vbo_df the date format in VBO. |
| 1007 |
* @param string $step the navigation step to take (weekend, month or quarter). |
| 1008 |
* |
| 1009 |
* @return array the formatted from and to date interval strings. |
| 1010 |
*/ |
| 1011 |
protected function parseReadableDateInterval($from_ts, $to_ts, $vbo_df, $step) |
| 1012 |
{ |
| 1013 |
$from_info = getdate($from_ts); |
| 1014 |
$to_info = getdate($to_ts); |
| 1015 |
$format_from_date = date($vbo_df, $from_ts); |
| 1016 |
$format_to_date = date($vbo_df, $to_ts); |
| 1017 |
if ($step == 'weekend') { |
| 1018 |
// include day of week |
| 1019 |
$format_from_date = VikBooking::sayWeekDay($from_info['wday']) . ', ' . $format_from_date; |
| 1020 |
$format_to_date = VikBooking::sayWeekDay($to_info['wday']) . ', ' . $format_to_date; |
| 1021 |
} elseif ($step == 'month') { |
| 1022 |
// check if it's a full month to say its name |
| 1023 |
if ($from_info['mon'] == $to_info['mon'] && $from_info['year'] == $to_info['year']) { |
| 1024 |
if ($from_info['mday'] == 1 && $to_info['mday'] == date('t', $to_ts)) { |
| 1025 |
$format_from_date = VikBooking::sayMonth($from_info['mon']) . ' ' . $from_info['year']; |
| 1026 |
$format_to_date = VikBooking::sayMonth($to_info['mon']) . ' ' . $to_info['year']; |
| 1027 |
} |
| 1028 |
} |
| 1029 |
} elseif ($step == 'quarter') { |
| 1030 |
// check if it's a full quarter |
| 1031 |
if ($from_info['mday'] == 1 && $to_info['mday'] == date('t', $to_ts)) { |
| 1032 |
$format_from_date = VikBooking::sayMonth($from_info['mon']) . ' ' . $from_info['year']; |
| 1033 |
$format_to_date = VikBooking::sayMonth($to_info['mon']) . ' ' . $to_info['year']; |
| 1034 |
} |
| 1035 |
} |
| 1036 |
|
| 1037 |
return array($format_from_date, $format_to_date); |
| 1038 |
} |
| 1039 |
} |
| 1040 |
|