PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/helpers/widgets/reminders.php +1450 -0 1.6.3 → 1.8.16 View file →
@@ -1,0 +1,1450 @@
1 +<?php
2 +/**
3 + * @package VikBooking
4 + * @subpackage com_vikbooking
5 + * @author Alessio Gaggii - E4J srl
6 + * @copyright Copyright (C) 2022 E4J srl. 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 "reminders".
15 + *
16 + * @since 1.15.0 (J) - 1.5.0 (WP)
17 + */
18 +class VikBookingAdminWidgetReminders 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 reminders per page.
30 + *
31 + * @var int
32 + */
33 + protected $reminders_per_page = 6;
34 +
35 + /**
36 + * Tells whether VCM is installed.
37 + *
38 + * @var bool
39 + */
40 + protected $vcm_exists = false;
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('VBO_W_REMINDERS_TITLE');
51 + $this->widgetDescr = JText::translate('VBO_W_REMINDERS_DESCR');
52 + $this->widgetId = basename(__FILE__, '.php');
53 +
54 + // define widget and icon and style name
55 + $this->widgetIcon = '<i class="' . VikBookingIcons::i('bell') . '"></i>';
56 + $this->widgetStyleName = 'green';
57 +
58 + // load widget's settings
59 + $this->widgetSettings = $this->loadSettings();
60 + if (!is_object($this->widgetSettings)) {
61 + $this->widgetSettings = new stdClass;
62 + }
63 +
64 + // check if VCM is available
65 + if (is_file(VCM_SITE_PATH . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'lib.vikchannelmanager.php')) {
66 + $this->vcm_exists = true;
67 + }
68 + }
69 +
70 + /**
71 + * This widget does not actually need to preload data to be watched for
72 + * dispatching notifications, and the reminders due are automatically
73 + * scheduled for being dispatched as a browser notification. However,
74 + * we make use of this method to periodically check if some reminders
75 + * should be automatically created for specific reasons, such as the
76 + * Airbnb host-to-guest reviews to remind the host to review the guest.
77 + * We also preload the necessary CSS/JS assets and schedule the imminent
78 + * reminders for notifications, if any.
79 + *
80 + * @return void
81 + */
82 + public function preload()
83 + {
84 + // declare JS lang vars
85 + JText::script('VBO_PLEASE_FILL_FIELDS');
86 + JText::script('VBDELCONFIRM');
87 + JText::script('VBCONFIGCLOSINGDATEADD');
88 + JText::script('VBADMINNOTESUPD');
89 +
90 + // load assets
91 + $this->vbo_app->loadDatePicker();
92 +
93 + // check if VCM is available
94 + if ($this->vcm_exists) {
95 + $today_dt = date('Y-m-d');
96 + $yesterday_dt = date('Y-m-d', strtotime('yesterday'));
97 +
98 + // check the last time this check was made
99 + $last_check_dt = isset($this->widgetSettings->last_check_dt) ? $this->widgetSettings->last_check_dt : $yesterday_dt;
100 +
101 + if ($last_check_dt != $today_dt) {
102 + // update last check date
103 + $this->widgetSettings->last_check_dt = $today_dt;
104 + $this->updateSettings(json_encode($this->widgetSettings));
105 +
106 + // check for any Channel Manager reservation that requires an auto-reminder
107 + $this->scheduleChannelManagerReminders();
108 + }
109 + }
110 +
111 + // load the 10 next (imminent) reminders
112 + $overdue = VBORemindersHelper::getInstance()->getImminents(10);
113 + if ($overdue) {
114 + // schedule browser notifications for the next reminders
115 + VBONotificationScheduler::getInstance()->enqueueReminders($overdue);
116 + }
117 +
118 + // nothing should be actually watched, ever
119 + return null;
120 + }
121 +
122 + /**
123 + * Custom method for this widget only to load the reminder records.
124 + * The method is called by the admin controller through an AJAX request.
125 + * The visibility should be public, it should not exit the process, and
126 + * any content sent to output will be returned to the AJAX response.
127 + * In this case we return an array because this method requires "return":1.
128 + *
129 + * It's the actual rendering of the widget which also allows navigation.
130 + *
131 + * @return array
132 + */
133 + public function loadReminders()
134 + {
135 + $offset = VikRequest::getInt('offset', 0, 'request');
136 + $length = VikRequest::getInt('length', $this->reminders_per_page, 'request');
137 + $completed = VikRequest::getInt('completed', 0, 'request');
138 + $expired = VikRequest::getInt('expired', 0, 'request');
139 + $bid = VikRequest::getInt('bid', 0, 'request');
140 + $reminder_id = VikRequest::getInt('reminder_id', 0, 'request');
141 + $wrapper = VikRequest::getString('wrapper', '', 'request');
142 +
143 + // load reminders starting from today at 00:00:00
144 + $now_info = getdate();
145 + $lim_min_date = date('Y-m-d H:i:s', mktime(0, 0, 0, $now_info['mon'], $now_info['mday'], $now_info['year']));
146 +
147 + // prepare fetch options
148 + $fetch = [
149 + 'after' => $lim_min_date,
150 + 'completed' => $completed,
151 + 'expired' => $expired,
152 + 'idorder' => $bid,
153 + ];
154 +
155 + // load reminders
156 + $helper = VBORemindersHelper::getInstance();
157 + $reminders = $helper->loadReminders($fetch, $offset, $length);
158 +
159 + // check if a next page can be available
160 + $has_next_page = (count($reminders) >= $length);
161 +
162 + // compose bookings base URI
163 + $bookings_base_uri = VBOFactory::getPlatform()->getUri()->admin('index.php?option=com_vikbooking&task=editorder&cid[]=%d', $xhtml = false);
164 +
165 + // check if a specific reminder ID was requested
166 + if ($reminder_id) {
167 + // check if the requested ID was found among the most recent ones
168 + $reminder_found = false;
169 + foreach ($reminders as $reminder) {
170 + if ($reminder->id == $reminder_id) {
171 + $reminder_found = true;
172 + break;
173 + }
174 + }
175 +
176 + if (!$reminder_found) {
177 + // fetch the record by ID
178 + $reminder_record = $helper->getReminder($reminder_id);
179 +
180 + if ($reminder_record) {
181 + // prepend element to the list
182 + array_unshift($reminders, $reminder_record);
183 + }
184 + }
185 + }
186 +
187 + // start output buffering
188 + ob_start();
189 +
190 + if (!$reminders) {
191 + ?>
192 + <p class="info"><?php echo JText::translate('VBONORESULTS'); ?></p>
193 + <?php
194 + } else {
195 + // update widget's settings with the lastly used filters
196 + $this->widgetSettings->show_completed = $completed;
197 + $this->widgetSettings->show_expired = $expired;
198 + $this->updateSettings(json_encode($this->widgetSettings));
199 + }
200 +
201 + foreach ($reminders as $reminder) {
202 + $use_icn_status = $reminder->completed ? 'dot-circle' : 'far fa-circle';
203 + $hidden_date = '';
204 + $hidden_hours = '';
205 + $hidden_minutes = '';
206 + if (!empty($reminder->duedate)) {
207 + $date_parts = explode(' ', $reminder->duedate);
208 + $hidden_date = date($this->df, strtotime($date_parts[0]));
209 + if ($reminder->usetime) {
210 + $time_parts = explode(':', $date_parts[1]);
211 + $hidden_hours = (int)$time_parts[0];
212 + $hidden_minutes = (int)$time_parts[1];
213 + }
214 + }
215 + $is_past_reminder = false;
216 + if (!empty($reminder->duedate)) {
217 + // calculate distance to expiration date from today
218 + $diff_data = $helper->relativeDatesDiff($reminder->duedate);
219 + $is_past_reminder = $diff_data['past'];
220 + }
221 +
222 + // list of extra classes for the reminder
223 + $extra_classes = [];
224 + if ($is_past_reminder) {
225 + $extra_classes[] = 'vbo-reminder-past';
226 + } else {
227 + $extra_classes[] = 'vbo-reminder-future';
228 + }
229 + if ($reminder->completed) {
230 + $extra_classes[] = 'vbo-reminder-ok';
231 + } else {
232 + $extra_classes[] = 'vbo-reminder-nok';
233 + }
234 +
235 + ?>
236 + <div class="vbo-widget-reminders-record <?php echo implode(' ', $extra_classes); ?>" data-reminderid="<?php echo $reminder->id; ?>">
237 + <div class="vbo-widget-reminders-record-status">
238 + <button type="button" class="vbo-reminder-status-dot" onclick="vboWidgetRemindersComplete('<?php echo $wrapper; ?>', '<?php echo $reminder->id; ?>');"><?php VikBookingIcons::e($use_icn_status); ?></button>
239 + <?php
240 + if ($reminder->important) {
241 + ?>
242 + <div class="vbo-widget-reminders-record-important">
243 + <?php VikBookingIcons::e('exclamation-triangle'); ?>
244 + </div>
245 + <?php
246 + }
247 + ?>
248 + </div>
249 + <div class="vbo-widget-reminders-record-info">
250 + <div class="vbo-widget-reminders-record-txt">
251 + <span class="vbo-widget-reminder-title"><?php echo htmlspecialchars($reminder->title); ?></span>
252 + <?php
253 + if (!empty($reminder->descr)) {
254 + ?>
255 + <span class="vbo-widget-reminder-descr"><?php echo htmlspecialchars($reminder->descr); ?></span>
256 + <?php
257 + }
258 + ?>
259 + </div>
260 + <div class="vbo-widget-reminders-record-due">
261 + <span class="vbo-widget-reminder-date" style="display: none;"><?php echo $hidden_date; ?></span>
262 + <span class="vbo-widget-reminder-hours" style="display: none;"><?php echo $hidden_hours; ?></span>
263 + <span class="vbo-widget-reminder-minutes" style="display: none;"><?php echo $hidden_minutes; ?></span>
264 + <?php
265 + if (!empty($reminder->idorder) && $reminder->idorder > 0) {
266 + ?>
267 + <span class="vbo-widget-reminder-idorder" style="display: none;"><?php echo $reminder->idorder; ?></span>
268 + <div class="vbo-widget-reminders-record-due-booking">
269 + <a class="label label-info" href="<?php echo sprintf($bookings_base_uri, $reminder->idorder); ?>" target="_blank"><?php echo $reminder->idorder; ?></a>
270 + </div>
271 + <?php
272 + }
273 + if (!empty($reminder->duedate)) {
274 + ?>
275 + <div class="vbo-widget-reminders-record-due-datetime">
276 + <div class="vbo-widget-reminders-record-due-date">
277 + <span title="<?php echo $reminder->duedate; ?>"><?php echo $diff_data['relative']; ?></span>
278 + </div>
279 + <?php
280 + if ($reminder->usetime) {
281 + ?>
282 + <div class="vbo-widget-reminders-record-due-time">
283 + <span><?php echo $diff_data['date_a']->format('H:i'); ?></span>
284 + </div>
285 + <?php
286 + }
287 + ?>
288 + </div>
289 + <?php
290 + }
291 + ?>
292 + </div>
293 + </div>
294 + <div class="vbo-widget-reminders-record-edit">
295 + <button type="button" class="vbo-reminder-record-edit" onclick="vboWidgetRemindersEdit('<?php echo $wrapper; ?>', '<?php echo $reminder->id; ?>');"><?php VikBookingIcons::e('edit'); ?></button>
296 + </div>
297 + </div>
298 + <?php
299 + }
300 +
301 + // append navigation
302 + ?>
303 + <div class="vbo-widget-commands vbo-widget-commands-right">
304 + <div class="vbo-widget-commands-main">
305 + <?php
306 + if ($offset > 0) {
307 + // show backward navigation button
308 + ?>
309 + <div class="vbo-widget-command-chevron vbo-widget-command-prev">
310 + <span class="vbo-widget-command-chevron-prev" onclick="vboWidgetRemindersNavigate('<?php echo $wrapper; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span>
311 + </div>
312 + <?php
313 + }
314 + if ($has_next_page) {
315 + // show forward navigation button
316 + ?>
317 + <div class="vbo-widget-command-chevron vbo-widget-command-next">
318 + <span class="vbo-widget-command-chevron-next" onclick="vboWidgetRemindersNavigate('<?php echo $wrapper; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span>
319 + </div>
320 + <?php
321 + }
322 + ?>
323 + </div>
324 + </div>
325 + <?php
326 +
327 + if ($reminder_id) {
328 + ?>
329 + <script type="text/javascript">
330 + setTimeout(() => {
331 + vboWidgetRemindersEdit('<?php echo $wrapper; ?>', '<?php echo $reminder_id; ?>');
332 + }, 200);
333 + </script>
334 + <?php
335 + }
336 +
337 + // get the HTML buffer
338 + $html_content = ob_get_contents();
339 + ob_end_clean();
340 +
341 + // return an associative array of values
342 + return array(
343 + 'html' => $html_content,
344 + 'tot_records' => count($reminders),
345 + 'next_page' => (int)$has_next_page,
346 + );
347 + }
348 +
349 + /**
350 + * Custom method for this widget only.
351 + * Creates a new reminder.
352 + *
353 + * @return array
354 + */
355 + public function saveReminder()
356 + {
357 + $title = VikRequest::getString('title', '', 'request');
358 + $descr = VikRequest::getString('descr', '', 'request');
359 + $date = VikRequest::getString('date', '', 'request');
360 + $hours = VikRequest::getInt('hours', 0, 'request');
361 + $minutes = VikRequest::getInt('minutes', 0, 'request');
362 + $bid = VikRequest::getInt('bid', 0, 'request');
363 + $important = VikRequest::getInt('important', 0, 'request');
364 +
365 + // create reminder object
366 + $reminder = new stdClass;
367 + $reminder->title = $title;
368 + $reminder->descr = $descr;
369 + $use_time = 0;
370 + if (!empty($date)) {
371 + if ($hours >= 0) {
372 + $use_time = 1;
373 + $minutes = $minutes >= 0 ? $minutes : 0;
374 + $due_ts = VikBooking::getDateTimestamp($date, $hours, $minutes);
375 + } else {
376 + $due_ts = VikBooking::getDateTimestamp($date);
377 + }
378 + $reminder->duedate = date('Y-m-d H:i:s', $due_ts);
379 + }
380 + $reminder->usetime = $use_time;
381 + if (!empty($bid)) {
382 + $reminder->idorder = $bid;
383 + }
384 + $reminder->important = $important;
385 +
386 + if (!VBORemindersHelper::getInstance()->saveReminder($reminder)) {
387 + VBOHttpDocument::getInstance()->close(400, 'Could not create the reminder');
388 + }
389 +
390 + // compose the notification payload for the newly created reminder
391 + $notif_data = VBONotificationScheduler::getInstance()->getReminderDataObject($reminder, $max_future_hours = 12);
392 +
393 + return [
394 + 'status' => true,
395 + 'notification' => $notif_data,
396 + 'debug' => print_r($reminder, true)
397 + ];
398 + }
399 +
400 + /**
401 + * Custom method for this widget only.
402 + * Updates an existing reminder.
403 + *
404 + * @return array
405 + */
406 + public function updateReminder()
407 + {
408 + $rid = VikRequest::getInt('rid', 0, 'request');
409 + $title = VikRequest::getString('title', '', 'request');
410 + $descr = VikRequest::getString('descr', '', 'request');
411 + $date = VikRequest::getString('date', '', 'request');
412 + $hours = VikRequest::getInt('hours', 0, 'request');
413 + $minutes = VikRequest::getInt('minutes', 0, 'request');
414 + $bid = VikRequest::getInt('bid', 0, 'request');
415 + $important = VikRequest::getInt('important', 0, 'request');
416 +
417 + if (empty($rid)) {
418 + VBOHttpDocument::getInstance()->close(400, 'Missing reminder ID to update');
419 + }
420 +
421 + // create reminder object
422 + $reminder = new stdClass;
423 + $reminder->id = $rid;
424 + $reminder->title = $title;
425 + $reminder->descr = $descr;
426 + $use_time = 0;
427 + if (!empty($date)) {
428 + if ($hours >= 0) {
429 + $use_time = 1;
430 + $minutes = $minutes >= 0 ? $minutes : 0;
431 + $due_ts = VikBooking::getDateTimestamp($date, $hours, $minutes);
432 + } else {
433 + $due_ts = VikBooking::getDateTimestamp($date);
434 + }
435 + $reminder->duedate = date('Y-m-d H:i:s', $due_ts);
436 + }
437 + $reminder->usetime = $use_time;
438 + $reminder->idorder = !empty($bid) ? $bid : 0;
439 +
440 + if (!VBORemindersHelper::getInstance()->updateReminder($reminder)) {
441 + VBOHttpDocument::getInstance()->close(400, 'Could not update the reminder');
442 + }
443 +
444 + // compose the notification paylod for the newly created reminder
445 + $notif_data = VBONotificationScheduler::getInstance()->getReminderDataObject($reminder, $max_future_hours = 12);
446 +
447 + return [
448 + 'status' => true,
449 + 'notification' => $notif_data,
450 + ];
451 + }
452 +
453 + /**
454 + * Custom method for this widget only.
455 + * Toggles the completed status for an existing reminder.
456 + *
457 + * @return array
458 + */
459 + public function toggleReminderCompleted()
460 + {
461 + $rid = VikRequest::getInt('rid', 0, 'request');
462 +
463 + if (empty($rid)) {
464 + VBOHttpDocument::getInstance()->close(400, 'Missing reminder ID to update');
465 + }
466 +
467 + $helper = VBORemindersHelper::getInstance();
468 + $reminder = $helper->getReminder($rid);
469 + if (!$reminder) {
470 + VBOHttpDocument::getInstance()->close(400, 'Record not found');
471 + }
472 +
473 + // toggle completed status
474 + $reminder->completed = (int) !$reminder->completed;
475 +
476 + if (!$helper->updateReminder($reminder)) {
477 + VBOHttpDocument::getInstance()->close(400, 'Could not update the reminder');
478 + }
479 +
480 + $notif_data = null;
481 + if (!$reminder->completed) {
482 + // compose the notification paylod for the uncompleted reminder
483 + $notif_data = VBONotificationScheduler::getInstance()->getReminderDataObject($reminder, $max_future_hours = 12);
484 + }
485 +
486 + return [
487 + 'status' => $reminder->completed,
488 + 'notification' => $notif_data,
489 + ];
490 + }
491 +
492 + /**
493 + * Custom method for this widget only.
494 + * Deletes an existing reminder.
495 + *
496 + * @return bool
497 + */
498 + public function deleteReminder()
499 + {
500 + $rid = VikRequest::getInt('rid', 0, 'request');
501 +
502 + if (empty($rid)) {
503 + VBOHttpDocument::getInstance()->close(400, 'Missing reminder ID to delete');
504 + }
505 +
506 + $helper = VBORemindersHelper::getInstance();
507 + $reminder = $helper->getReminder($rid);
508 + if (!$reminder) {
509 + VBOHttpDocument::getInstance()->close(400, 'Record not found');
510 + }
511 +
512 + if (!$helper->deleteReminder($reminder)) {
513 + VBOHttpDocument::getInstance()->close(400, 'Could not delete the reminder');
514 + }
515 +
516 + return true;
517 + }
518 +
519 + /**
520 + * Main method to invoke the widget. Contents will be loaded
521 + * through AJAX requests, not via PHP when the page loads.
522 + *
523 + * @param ?VBOMultitaskData $data multitask data object.
524 + *
525 + * @return void
526 + */
527 + public function render(?VBOMultitaskData $data = null)
528 + {
529 + // increase widget's instance counter
530 + static::$instance_counter++;
531 +
532 + // check whether the widget is being rendered via AJAX when adding it through the customizer
533 + $is_ajax = $this->isAjaxRendering();
534 +
535 + // generate a unique ID for the sticky notes wrapper instance
536 + $wrapper_instance = !$is_ajax ? static::$instance_counter : rand();
537 + $wrapper_id = 'vbo-widget-reminders-' . $wrapper_instance;
538 +
539 + // default filters
540 + $reminder_id = 0;
541 + $def_completed = 0;
542 + $def_expired = 0;
543 + $def_duedate = time();
544 + $def_duehours = -1;
545 + $def_duemins = -1;
546 +
547 + // check multitask data
548 + $page_bid = '';
549 + $auto_action = '';
550 + $js_modal_id = '';
551 + if ($data) {
552 + // access Multitask data
553 + $page_bid = $data->getBookingID();
554 + if ($data->isModalRendering()) {
555 + // get modal JS identifier
556 + $js_modal_id = $data->getModalJsIdentifier();
557 + }
558 +
559 + // we allow the page booking ID to be passed as an option as well
560 + $page_bid = !$page_bid ? $this->options()->fetchBookingId() : $page_bid;
561 +
562 + // check for options
563 + $reminder_id = (int) $this->getOption('reminder_id', 0);
564 + $auto_action = $this->getOption('action', '');
565 + $def_completed = (int) $this->getOption('completed', $def_completed);
566 + $def_expired = (int) $this->getOption('expired', $def_expired);
567 +
568 + // attempt to define values for this precise booking
569 + $booking_info = $page_bid ? VikBooking::getBookingInfoFromID($page_bid) : [];
570 + if ($booking_info) {
571 + $def_time_info = [];
572 + if ($booking_info['checkin'] > $def_duedate) {
573 + $def_duedate = $booking_info['checkin'];
574 + $def_time_info = getdate($def_duedate);
575 + } elseif ($booking_info['checkout'] > $def_duedate) {
576 + $def_duedate = $booking_info['checkout'];
577 + $def_time_info = getdate($def_duedate);
578 + }
579 + if ($def_time_info) {
580 + $def_duehours = (int)$def_time_info['hours'];
581 + $def_duemins = (int)$def_time_info['minutes'];
582 + }
583 + }
584 + } else {
585 + // load settings
586 + $def_completed = isset($this->widgetSettings->show_completed) ? (int)$this->widgetSettings->show_completed : $def_completed;
587 + $def_expired = isset($this->widgetSettings->show_expired) ? (int)$this->widgetSettings->show_expired : $def_expired;
588 + }
589 +
590 + ?>
591 + <div id="<?php echo $wrapper_id; ?>" class="vbo-admin-widget-wrapper" data-instance="<?php echo $wrapper_instance; ?>" data-offset="0" data-pagebid="<?php echo $page_bid; ?>" data-length="<?php echo $this->reminders_per_page; ?>">
592 + <div class="vbo-admin-widget-head">
593 + <div class="vbo-admin-widget-head-inline">
594 + <h4><?php echo $this->widgetIcon; ?> <span><?php echo $this->widgetName; ?></span></h4>
595 + <div class="vbo-admin-widget-head-commands">
596 +
597 + <div class="vbo-reportwidget-commands">
598 + <div class="vbo-reportwidget-commands-main">
599 + <div class="vbo-reportwidget-command-add">
600 + <span class="vbo-widget-command-addnew" onclick="vboWidgetRemindersAdd('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('plus-circle'); ?></span>
601 + </div>
602 + </div>
603 + <div class="vbo-reportwidget-command-dots">
604 + <span class="vbo-widget-command-togglefilters vbo-widget-reminders-togglefilters" onclick="vboWidgetRemindersToggleFilters('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
605 + </div>
606 + </div>
607 + <div class="vbo-reportwidget-filters">
608 + <div class="vbo-reportwidget-filter">
609 + <label for="show_completed<?php echo $wrapper_instance; ?>-on"><?php echo JText::translate('VBO_SHOW_COMPLETED'); ?></label>
610 + <?php echo $this->vbo_app->printYesNoButtons('show_completed' . $wrapper_instance, JText::translate('VBYES'), JText::translate('VBNO'), $def_completed, 1, 0); ?>
611 + </div>
612 + <div class="vbo-reportwidget-filter">
613 + <label for="show_expired<?php echo $wrapper_instance; ?>-on"><?php echo JText::translate('VBO_SHOW_EXPIRED'); ?></label>
614 + <?php echo $this->vbo_app->printYesNoButtons('show_expired' . $wrapper_instance, JText::translate('VBYES'), JText::translate('VBNO'), $def_expired, 1, 0); ?>
615 + </div>
616 + <div class="vbo-reportwidget-filter vbo-reportwidget-filter-confirm">
617 + <button type="button" class="btn vbo-config-btn" onclick="vboWidgetRemindersChangeParams('<?php echo $wrapper_id; ?>');"><?php echo JText::translate('VBADMINNOTESUPD'); ?></button>
618 + </div>
619 + </div>
620 +
621 + </div>
622 + </div>
623 + </div>
624 + <div class="vbo-widget-reminders-wrap">
625 + <div class="vbo-widget-reminders-inner">
626 + <div class="vbo-widget-reminders-manage" style="display: none;">
627 +
628 + <div class="vbo-widget-reminders-goback">
629 + <a href="JavaScript: void(0);" onclick="vboWidgetRemindersToggleManage('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('chevron-left'); ?> <?php echo JText::translate('VBBACK'); ?></a>
630 + </div>
631 +
632 + <div class="vbo-widget-reminders-manage-wrap">
633 + <div class="vbo-widget-reminders-filter vbo-widget-reminders-filter-title">
634 + <input type="text" class="vbo-widget-reminders-ftitle" value="" placeholder="<?php echo htmlspecialchars(JText::translate('VBO_REMINDER_TITLE')); ?>" />
635 + </div>
636 + <div class="vbo-widget-reminders-filter vbo-widget-reminders-filter-descr">
637 + <textarea class="vbo-widget-reminders-fdescr" placeholder="<?php echo htmlspecialchars(JText::translate('VBPVIEWOPTIONALSTWO')); ?>" maxlength="2000"></textarea>
638 + </div>
639 + <div class="vbo-widget-reminders-filter vbo-widget-reminders-filter-booking vbo-toggle-small" style="<?php echo empty($page_bid) ? 'display: none;' : ''; ?>">
640 + <span class="vbo-widget-reminders-filter-tobooking">
641 + <label for="assign_booking<?php echo $wrapper_instance; ?>-on"><span class="label label-info vbo-widget-reminders-filter-booking-id"><?php echo $page_bid; ?></span> <?php echo JText::translate('VBO_ASSIGN_TO_BOOKING'); ?></label>
642 + <?php echo $this->vbo_app->printYesNoButtons('assign_booking' . $wrapper_instance, JText::translate('VBYES'), JText::translate('VBNO'), (!empty($page_bid) ? 1 : 0), 1, 0); ?>
643 + </span>
644 + <span class="vbo-widget-reminders-filter-importance">
645 + <label for="is_important<?php echo $wrapper_instance; ?>-on"><?php VikBookingIcons::e('exclamation-triangle'); ?> <?php echo JText::translate('VBO_IMPORTANT'); ?></label>
646 + <?php echo $this->vbo_app->printYesNoButtons('is_important' . $wrapper_instance, JText::translate('VBYES'), JText::translate('VBNO'), 0, 1, 0); ?>
647 + </span>
648 + </div>
649 + <div class="vbo-widget-reminders-filter-datetime">
650 + <div class="vbo-widget-reminders-filter vbo-widget-reminders-filter-date">
651 + <div class="vbo-field-calendar">
652 + <div class="input-append">
653 + <input type="text" class="vbo-reminders-dtpicker" value="<?php echo date($this->df, $def_duedate); ?>" placeholder="<?php echo htmlspecialchars(JText::translate('VBO_REMINDER_DUE_DATE')); ?>" />
654 + <button type="button" class="btn btn-secondary vbo-widget-reminders-caltrigger"><?php VikBookingIcons::e('calendar'); ?></button>
655 + </div>
656 + </div>
657 + </div>
658 + <div class="vbo-widget-reminders-filter vbo-widget-reminders-filter-time">
659 + <select class="vbo-widget-reminders-fh">
660 + <option value=""><?php echo ucfirst(JText::translate('VBCONFIGONETENEIGHT')); ?></option>
661 + <?php
662 + for ($i = 0; $i < 24; $i++) {
663 + ?>
664 + <option value="<?php echo $i; ?>"<?php echo $i == $def_duehours ? ' selected="selected"' : ''; ?>><?php echo $i < 10 ? "0{$i}" : $i; ?></option>
665 + <?php
666 + }
667 + ?>
668 + </select>
669 + <select class="vbo-widget-reminders-fm">
670 + <option value=""><?php echo ucfirst(JText::translate('VBTRKDIFFMINS')); ?></option>
671 + <?php
672 + for ($i = 0; $i < 60; $i++) {
673 + ?>
674 + <option value="<?php echo $i; ?>"<?php echo $i == $def_duemins ? ' selected="selected"' : ''; ?>><?php echo $i < 10 ? "0{$i}" : $i; ?></option>
675 + <?php
676 + }
677 + ?>
678 + </select>
679 + </div>
680 + </div>
681 + <div class="vbo-widget-reminders-filter vbo-widget-reminders-filter-save">
682 + <input type="hidden" class="vbo-widget-reminders-rid" value="" />
683 + <button type="button" class="btn btn-success vbo-widget-reminder-confirm" onclick="vboWidgetRemindersSaveNew('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('bell'); ?> <span><?php echo JText::translate('VBCONFIGCLOSINGDATEADD'); ?></span></button>
684 + <button type="button" class="btn btn-danger vbo-widget-reminder-delete" onclick="vboWidgetRemindersDelete('<?php echo $wrapper_id; ?>');" style="display: none;"><?php VikBookingIcons::e('trash'); ?> <?php echo JText::translate('VBELIMINA'); ?></button>
685 + </div>
686 + </div>
687 +
688 + </div>
689 + <div class="vbo-widget-reminders-list">
690 + <?php
691 + for ($i = 0; $i < floor($this->reminders_per_page / 2); $i++) {
692 + ?>
693 + <div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">
694 + <div class="vbo-dashboard-guest-activity-avatar">
695 + <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>
696 + </div>
697 + <div class="vbo-dashboard-guest-activity-content">
698 + <div class="vbo-dashboard-guest-activity-content-head">
699 + <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>
700 + </div>
701 + <div class="vbo-dashboard-guest-activity-content-info-msg">
702 + <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>
703 + </div>
704 + </div>
705 + </div>
706 + <?php
707 + }
708 + ?>
709 + </div>
710 + </div>
711 + </div>
712 + </div>
713 + <?php
714 +
715 + if (static::$instance_counter === 0 || $is_ajax) {
716 + /**
717 + * Print the JS code only once for all instances of this widget.
718 + * The real rendering is made through AJAX, not when the page loads.
719 + */
720 + ?>
721 + <script type="text/javascript">
722 +
723 + /**
724 + * Display the loading skeletons.
725 + */
726 + function vboWidgetRemindersSkeletons(wrapper) {
727 + var widget_instance = jQuery('#' + wrapper);
728 + if (!widget_instance.length) {
729 + return false;
730 + }
731 + widget_instance.find('.vbo-widget-reminders-list').html('');
732 + for (var i = 0; i < <?php echo floor($this->reminders_per_page / 2); ?>; i++) {
733 + var skeleton = '';
734 + skeleton += '<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton">';
735 + skeleton += ' <div class="vbo-dashboard-guest-activity-avatar">';
736 + skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>';
737 + skeleton += ' </div>';
738 + skeleton += ' <div class="vbo-dashboard-guest-activity-content">';
739 + skeleton += ' <div class="vbo-dashboard-guest-activity-content-head">';
740 + skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>';
741 + skeleton += ' </div>';
742 + skeleton += ' <div class="vbo-dashboard-guest-activity-content-info-msg">';
743 + skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>';
744 + skeleton += ' </div>';
745 + skeleton += ' </div>';
746 + skeleton += '</div>';
747 + // append skeleton
748 + jQuery(skeleton).appendTo(widget_instance.find('.vbo-widget-reminders-list'));
749 + }
750 + }
751 +
752 + /**
753 + * Toggles the layout to add or edit a reminder.
754 + */
755 + function vboWidgetRemindersToggleManage(wrapper) {
756 + var widget_instance = jQuery('#' + wrapper);
757 + if (!widget_instance.length) {
758 + return false;
759 + }
760 +
761 + var mng_cont = widget_instance.find('.vbo-widget-reminders-manage');
762 + if (mng_cont.is(':visible')) {
763 + mng_cont.hide();
764 + widget_instance.find('.vbo-widget-reminders-list').show();
765 + } else {
766 + widget_instance.find('.vbo-widget-reminders-list').hide();
767 + mng_cont.show();
768 + }
769 + }
770 +
771 + /**
772 + * Toggle filters.
773 + */
774 + function vboWidgetRemindersToggleFilters(wrapper) {
775 + var widget_instance = jQuery('#' + wrapper);
776 + if (!widget_instance.length) {
777 + return false;
778 + }
779 +
780 + widget_instance.find('.vbo-reportwidget-filters').toggle();
781 + }
782 +
783 + /**
784 + * Reload records with new params.
785 + */
786 + function vboWidgetRemindersChangeParams(wrapper) {
787 + var widget_instance = jQuery('#' + wrapper);
788 + if (!widget_instance.length) {
789 + return false;
790 + }
791 +
792 + // hide filters when making a new request
793 + widget_instance.find('.vbo-reportwidget-filters').hide();
794 +
795 + // reset offset to 0
796 + widget_instance.attr('data-offset', 0);
797 +
798 + // show loading skeletons
799 + vboWidgetRemindersSkeletons(wrapper);
800 +
801 + // reload the first page
802 + vboWidgetRemindersLoad(wrapper);
803 + }
804 +
805 + /**
806 + * Prepare the layout to add a new reminder.
807 + */
808 + function vboWidgetRemindersAdd(wrapper) {
809 + var widget_instance = jQuery('#' + wrapper);
810 + if (!widget_instance.length) {
811 + return false;
812 + }
813 +
814 + // always reset the reminder id in the hidden input and hide delete
815 + widget_instance.find('.vbo-widget-reminders-rid').val('');
816 + widget_instance.find('.vbo-widget-reminder-delete').hide();
817 + widget_instance.find('.vbo-widget-reminder-confirm span').text(Joomla.JText._('VBCONFIGCLOSINGDATEADD'));
818 +
819 + // check if we need to hide the assign-to-booking option
820 + var page_bid = widget_instance.attr('data-pagebid');
821 + if (page_bid.length && page_bid > 0) {
822 + // show assign-to-booking option and important reminder
823 + widget_instance.find('.vbo-widget-reminders-filter-booking-id').text(page_bid);
824 + widget_instance.find('input[name^="assign_booking"]').prop('checked', true);
825 + widget_instance.find('input[name^="is_important"]').prop('checked', false);
826 + widget_instance.find('.vbo-widget-reminders-filter-booking').show();
827 + } else {
828 + // hide assign-to-booking option and important reminder
829 + widget_instance.find('input[name^="assign_booking"]').prop('checked', false);
830 + widget_instance.find('input[name^="is_important"]').prop('checked', false);
831 + widget_instance.find('.vbo-widget-reminders-filter-booking').hide();
832 + }
833 +
834 + // reset the rest of the fields in case of previous editing
835 + if (widget_instance.find('.vbo-widget-reminders-ftitle').val() || widget_instance.find('.vbo-widget-reminders-fdescr').val()) {
836 + widget_instance.find('.vbo-widget-reminders-ftitle').val('');
837 + widget_instance.find('.vbo-widget-reminders-fdescr').val('');
838 + widget_instance.find('.vbo-reminders-dtpicker').val('');
839 + widget_instance.find('.vbo-widget-reminders-fh').val('');
840 + widget_instance.find('.vbo-widget-reminders-fm').val('');
841 + }
842 +
843 + // toggle edit mode
844 + vboWidgetRemindersToggleManage(wrapper);
845 + }
846 +
847 + /**
848 + * Prepare the reminder edit mode by populating data.
849 + */
850 + function vboWidgetRemindersEdit(wrapper, rid) {
851 + var widget_instance = jQuery('#' + wrapper);
852 + if (!widget_instance.length) {
853 + return false;
854 + }
855 + var reminder_instance = widget_instance.find('[data-reminderid="' + rid + '"]');
856 + if (!reminder_instance.length) {
857 + return false;
858 + }
859 +
860 + // populate fields for edit mode
861 + widget_instance.find('.vbo-widget-reminder-confirm span').text(Joomla.JText._('VBADMINNOTESUPD'));
862 + widget_instance.find('.vbo-widget-reminder-delete').show();
863 + widget_instance.find('.vbo-widget-reminders-rid').val(rid);
864 + widget_instance.find('.vbo-widget-reminders-ftitle').val(reminder_instance.find('.vbo-widget-reminder-title').text());
865 + widget_instance.find('.vbo-widget-reminders-fdescr').val(reminder_instance.find('.vbo-widget-reminder-descr').text());
866 + widget_instance.find('.vbo-reminders-dtpicker').val(reminder_instance.find('.vbo-widget-reminder-date').text());
867 + widget_instance.find('.vbo-widget-reminders-fh').val(reminder_instance.find('.vbo-widget-reminder-hours').text());
868 + widget_instance.find('.vbo-widget-reminders-fm').val(reminder_instance.find('.vbo-widget-reminder-minutes').text());
869 + // handle booking ID assignment
870 + var booking_assigned = null;
871 + var booking_container = reminder_instance.find('.vbo-widget-reminder-idorder');
872 + if (booking_container.length) {
873 + booking_assigned = booking_container.text();
874 + }
875 + if (booking_assigned) {
876 + // show assign-to-booking option
877 + widget_instance.find('.vbo-widget-reminders-filter-booking-id').text(booking_assigned);
878 + widget_instance.find('input[name^="assign_booking"]').prop('checked', true);
879 + widget_instance.find('.vbo-widget-reminders-filter-booking').show();
880 + } else {
881 + // hide assign-to-booking option
882 + widget_instance.find('input[name^="assign_booking"]').prop('checked', false);
883 + widget_instance.find('.vbo-widget-reminders-filter-booking').hide();
884 + }
885 +
886 + // handle important reminder flag
887 + if (booking_assigned && reminder_instance.find('.vbo-widget-reminders-record-important').length) {
888 + widget_instance.find('input[name^="is_important"]').prop('checked', true);
889 + } else {
890 + widget_instance.find('input[name^="is_important"]').prop('checked', false);
891 + }
892 +
893 + // enter edit mode
894 + vboWidgetRemindersToggleManage(wrapper);
895 + }
896 +
897 + /**
898 + * Toggle reminder completed status.
899 + */
900 + function vboWidgetRemindersComplete(wrapper, rid) {
901 + var widget_instance = jQuery('#' + wrapper);
902 + if (!widget_instance.length) {
903 + return false;
904 + }
905 + var reminder_instance = widget_instance.find('[data-reminderid="' + rid + '"]');
906 + if (!reminder_instance.length) {
907 + return false;
908 + }
909 +
910 + // the widget method to call
911 + var call_method = 'toggleReminderCompleted';
912 +
913 + // make a request to update the reminder
914 + VBOCore.doAjax(
915 + "<?php echo $this->getExecWidgetAjaxUri(); ?>",
916 + {
917 + widget_id: "<?php echo $this->getIdentifier(); ?>",
918 + call: call_method,
919 + return: 1,
920 + rid: rid,
921 + wrapper: wrapper,
922 + tmpl: "component"
923 + },
924 + function(response) {
925 + try {
926 + var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
927 + if (!obj_res.hasOwnProperty(call_method)) {
928 + console.error('Unexpected or invalid JSON response', obj_res);
929 + return false;
930 + }
931 +
932 + if (obj_res[call_method]['status'] > 0) {
933 + // new status is completed
934 + reminder_instance
935 + .removeClass('vbo-reminder-nok')
936 + .addClass('vbo-reminder-ok')
937 + .find('.vbo-reminder-status-dot').html('<?php VikBookingIcons::e('dot-circle'); ?>');
938 + // check current params
939 + var show_completed = widget_instance.find('input[name^="show_completed"]').prop('checked');
940 + if (!show_completed) {
941 + reminder_instance.fadeOut();
942 + }
943 + // attempt to unset any scheduled notification for this reminder
944 + VBOCore.updateNotification({
945 + id: rid,
946 + type: "reminder"
947 + }, 0);
948 + } else {
949 + // new status is un-completed
950 + reminder_instance
951 + .removeClass('vbo-reminder-ok')
952 + .addClass('vbo-reminder-nok')
953 + .find('.vbo-reminder-status-dot').html('<?php VikBookingIcons::e('far fa-circle'); ?>');
954 + if (obj_res[call_method]['notification'] != null) {
955 + // attempt to schedule a notification for this reminder
956 + VBOCore.enqueueNotifications(obj_res[call_method]['notification']);
957 + }
958 + }
959 + } catch(err) {
960 + console.error('could not parse JSON response', err, response);
961 + }
962 + },
963 + function(error) {
964 + // log the error and display the message
965 + console.error(error);
966 + alert(error.responseText);
967 + }
968 + );
969 + }
970 +
971 + /**
972 + * Delete one reminder record.
973 + */
974 + function vboWidgetRemindersDelete(wrapper) {
975 + var widget_instance = jQuery('#' + wrapper);
976 + if (!widget_instance.length) {
977 + return false;
978 + }
979 +
980 + var rid = widget_instance.find('.vbo-widget-reminders-rid').val();
981 + if (!rid || !rid.length) {
982 + return false;
983 + }
984 +
985 + if (!confirm(Joomla.JText._('VBDELCONFIRM'))) {
986 + return false;
987 + }
988 +
989 + // disable delete button
990 + var del_btn = widget_instance.find('button.vbo-widget-reminder-delete');
991 + del_btn.prop('disabled', true);
992 +
993 + // the widget method to call
994 + var call_method = 'deleteReminder';
995 +
996 + // make a request to update the reminder
997 + VBOCore.doAjax(
998 + "<?php echo $this->getExecWidgetAjaxUri(); ?>",
999 + {
1000 + widget_id: "<?php echo $this->getIdentifier(); ?>",
1001 + call: call_method,
1002 + return: 1,
1003 + rid: rid,
1004 + wrapper: wrapper,
1005 + tmpl: "component"
1006 + },
1007 + function(response) {
1008 + try {
1009 + var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
1010 + if (!obj_res.hasOwnProperty(call_method) || !obj_res[call_method]) {
1011 + console.error('Unexpected or invalid JSON response', obj_res);
1012 + return false;
1013 + }
1014 +
1015 + // turn flag on
1016 + vbo_reminders_changed = true;
1017 +
1018 + // toggle edit mode
1019 + vboWidgetRemindersToggleManage(wrapper);
1020 +
1021 + // enable delete button
1022 + del_btn.prop('disabled', false);
1023 +
1024 + // reset offset to 0
1025 + widget_instance.attr('data-offset', 0);
1026 +
1027 + // show loading skeletons
1028 + vboWidgetRemindersSkeletons(wrapper);
1029 +
1030 + // reload the first page
1031 + vboWidgetRemindersLoad(wrapper);
1032 +
1033 + // attempt to unset any scheduled notification for this reminder
1034 + VBOCore.updateNotification({
1035 + id: rid,
1036 + type: "reminder"
1037 + }, 0);
1038 + } catch(err) {
1039 + console.error('could not parse JSON response', err, response);
1040 + }
1041 + },
1042 + function(error) {
1043 + // log the error and display the message
1044 + console.error(error);
1045 + alert(error.responseText);
1046 + // enable delete button
1047 + del_btn.prop('disabled', false);
1048 + }
1049 + );
1050 + }
1051 +
1052 + /**
1053 + * Perform the request to load the reminders.
1054 + */
1055 + function vboWidgetRemindersLoad(wrapper, reminder_id) {
1056 + var widget_instance = jQuery('#' + wrapper);
1057 + if (!widget_instance.length) {
1058 + return false;
1059 + }
1060 +
1061 + // get vars for making the request
1062 + var current_offset = parseInt(widget_instance.attr('data-offset'));
1063 + var length_per_page = parseInt(widget_instance.attr('data-length'));
1064 + var show_completed = widget_instance.find('input[name^="show_completed"]').prop('checked');
1065 + var show_expired = widget_instance.find('input[name^="show_expired"]').prop('checked');
1066 + show_completed = show_completed ? 1 : 0;
1067 + show_expired = show_expired ? 1 : 0;
1068 + var page_bid = widget_instance.attr('data-pagebid');
1069 +
1070 + // the widget method to call
1071 + var call_method = 'loadReminders';
1072 +
1073 + // make a request to load the reminder records
1074 + VBOCore.doAjax(
1075 + "<?php echo $this->getExecWidgetAjaxUri(); ?>",
1076 + {
1077 + widget_id: "<?php echo $this->getIdentifier(); ?>",
1078 + call: call_method,
1079 + return: 1,
1080 + offset: current_offset,
1081 + length: length_per_page,
1082 + completed: show_completed,
1083 + expired: show_expired,
1084 + bid: page_bid,
1085 + reminder_id: reminder_id,
1086 + wrapper: wrapper,
1087 + tmpl: "component"
1088 + },
1089 + (response) => {
1090 + try {
1091 + var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
1092 + if (!obj_res.hasOwnProperty(call_method)) {
1093 + console.error('Unexpected JSON response', obj_res);
1094 + return false;
1095 + }
1096 +
1097 + // replace HTML with month-day reservations
1098 + widget_instance.find('.vbo-widget-reminders-list').html(obj_res[call_method]['html']);
1099 +
1100 + // check results
1101 + var tot_records = obj_res[call_method]['tot_records'] || 0;
1102 + if (!isNaN(tot_records) && parseInt(tot_records) < 1) {
1103 + // no results can indicate the offset is invalid or too high
1104 + if (!isNaN(current_offset) && current_offset > 0) {
1105 + // reset offset to 0
1106 + widget_instance.attr('data-offset', 0);
1107 + // show loading skeletons
1108 + vboWidgetRemindersSkeletons(wrapper);
1109 + // reload the first page
1110 + vboWidgetRemindersLoad(wrapper);
1111 + }
1112 + }
1113 + } catch(err) {
1114 + console.error('could not parse JSON response', err, response);
1115 + }
1116 + },
1117 + (error) => {
1118 + // remove the skeleton loading
1119 + widget_instance.find('.vbo-widget-reminders-list').find('.vbo-dashboard-guest-activity-skeleton').remove();
1120 + console.error(error);
1121 + }
1122 + );
1123 + }
1124 +
1125 + /**
1126 + * Save a new reminder or update an existing one.
1127 + */
1128 + function vboWidgetRemindersSaveNew(wrapper) {
1129 + var widget_instance = jQuery('#' + wrapper);
1130 + if (!widget_instance.length) {
1131 + return false;
1132 + }
1133 +
1134 + // either save or update
1135 + var mode = 'saveReminder';
1136 +
1137 + // collect new reminder details
1138 + var r_title = widget_instance.find('.vbo-widget-reminders-ftitle').val();
1139 + var r_descr = widget_instance.find('.vbo-widget-reminders-fdescr').val();
1140 + var r_date = widget_instance.find('.vbo-reminders-dtpicker').val();
1141 + var r_hours = widget_instance.find('.vbo-widget-reminders-fh').val();
1142 + var r_mins = widget_instance.find('.vbo-widget-reminders-fm').val();
1143 + // adjust time
1144 + if (!r_hours.length) {
1145 + r_hours = -1;
1146 + r_mins = 0;
1147 + }
1148 + // check if we are editing an existing reminder id
1149 + var r_id = widget_instance.find('.vbo-widget-reminders-rid').val();
1150 + if (r_id && r_id.length) {
1151 + mode = 'updateReminder';
1152 + } else {
1153 + r_id = 0;
1154 + }
1155 +
1156 + if ((!r_title.length && !r_descr.length) || !r_date.length) {
1157 + alert(Joomla.JText._('VBO_PLEASE_FILL_FIELDS'));
1158 + return false;
1159 + }
1160 +
1161 + // check if we are saving/updating a reminder for a booking ID
1162 + var r_bid = null;
1163 + var assign_to_bid = widget_instance.find('input[name^="assign_booking"]').prop('checked');
1164 + if (assign_to_bid) {
1165 + // validate the booking id depending on save/update mode
1166 + if (mode == 'saveReminder') {
1167 + // the booking id must have been set from the current page
1168 + var validate_bid = widget_instance.attr('data-pagebid');
1169 + if (validate_bid && validate_bid.length) {
1170 + // valid
1171 + r_bid = validate_bid;
1172 + }
1173 + } else {
1174 + // the booking id must be part of a hidden field
1175 + var reminder_instance = widget_instance.find('[data-reminderid="' + r_id + '"]');
1176 + if (reminder_instance.length) {
1177 + var validate_bid = reminder_instance.find('.vbo-widget-reminder-idorder').text();
1178 + if (validate_bid && validate_bid.length) {
1179 + // valid
1180 + r_bid = validate_bid;
1181 + }
1182 + }
1183 + }
1184 + }
1185 +
1186 + // check importance
1187 + var is_important = 0;
1188 + if (r_bid && widget_instance.find('input[name^="is_important"]').prop('checked')) {
1189 + is_important = 1;
1190 + }
1191 +
1192 + // disable save button
1193 + var save_btn = widget_instance.find('.vbo-widget-reminders-filter-save button');
1194 + save_btn.prop('disabled', true);
1195 +
1196 + // the widget method to call
1197 + var call_method = mode;
1198 +
1199 + // make a request to save a new reminder or to update it
1200 + VBOCore.doAjax(
1201 + "<?php echo $this->getExecWidgetAjaxUri(); ?>",
1202 + {
1203 + widget_id: "<?php echo $this->getIdentifier(); ?>",
1204 + call: call_method,
1205 + return: 1,
1206 + rid: r_id,
1207 + bid: r_bid,
1208 + title: r_title,
1209 + descr: r_descr,
1210 + date: r_date,
1211 + hours: r_hours,
1212 + minutes: r_mins,
1213 + important: is_important,
1214 + wrapper: wrapper,
1215 + tmpl: "component"
1216 + },
1217 + (response) => {
1218 + try {
1219 + var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
1220 + if (!obj_res.hasOwnProperty(call_method) || !obj_res[call_method]['status']) {
1221 + console.error('Unexpected or invalid JSON response', obj_res);
1222 + return false;
1223 + }
1224 +
1225 + // turn flag on & store booking involved, if any
1226 + vbo_reminders_changed = true;
1227 + vbo_reminders_changed_bid = r_bid;
1228 +
1229 + // toggle edit mode
1230 + vboWidgetRemindersToggleManage(wrapper);
1231 +
1232 + // enable save button
1233 + save_btn.prop('disabled', false);
1234 +
1235 + // reset offset to 0
1236 + widget_instance.attr('data-offset', 0);
1237 +
1238 + // show loading skeletons
1239 + vboWidgetRemindersSkeletons(wrapper);
1240 +
1241 + // reload the first page
1242 + vboWidgetRemindersLoad(wrapper);
1243 +
1244 + // handle browser notifications
1245 + if (call_method == 'saveReminder') {
1246 + if (obj_res[call_method]['notification'] != null) {
1247 + // attempt to schedule a notification for the new reminder
1248 + VBOCore.enqueueNotifications(obj_res[call_method]['notification']);
1249 + }
1250 + } else {
1251 + if (obj_res[call_method]['notification'] != null) {
1252 + // attempt to update the notification scheduling time for this reminder
1253 + let updated = VBOCore.updateNotification({
1254 + id: r_id,
1255 + type: "reminder"
1256 + }, obj_res[call_method]['notification']['dtime']);
1257 + if (updated !== true) {
1258 + // attempt to schedule this anticipated reminder as a new notification
1259 + VBOCore.enqueueNotifications(obj_res[call_method]['notification']);
1260 + }
1261 + } else {
1262 + // the notification may have been set to a far-ahead date, delete it
1263 + VBOCore.updateNotification({
1264 + id: r_id,
1265 + type: "reminder"
1266 + }, 0);
1267 + }
1268 + }
1269 + } catch(err) {
1270 + console.error('could not parse JSON response', err, response);
1271 + }
1272 + },
1273 + (error) => {
1274 + // log the error and display the message
1275 + console.error(error);
1276 + alert(error.responseText);
1277 + // enable save button
1278 + save_btn.prop('disabled', false);
1279 + }
1280 + );
1281 + }
1282 +
1283 + /**
1284 + * Navigate between the various pages of the reminders.
1285 + */
1286 + function vboWidgetRemindersNavigate(wrapper, direction) {
1287 + var widget_instance = jQuery('#' + wrapper);
1288 + if (!widget_instance.length) {
1289 + return false;
1290 + }
1291 +
1292 + // show loading skeletons
1293 + vboWidgetRemindersSkeletons(wrapper);
1294 +
1295 + // current offset
1296 + var current_offset = parseInt(widget_instance.attr('data-offset'));
1297 +
1298 + // records per page
1299 + var steps = parseInt(widget_instance.attr('data-length'));
1300 +
1301 + // check direction and update offsets for nav
1302 + if (direction > 0) {
1303 + // navigate forward
1304 + widget_instance.attr('data-offset', (current_offset + steps));
1305 + } else {
1306 + // navigate backward
1307 + var new_offset = current_offset - steps;
1308 + new_offset = new_offset >= 0 ? new_offset : 0;
1309 + widget_instance.attr('data-offset', new_offset);
1310 + }
1311 +
1312 + // launch navigation
1313 + vboWidgetRemindersLoad(wrapper);
1314 + }
1315 +
1316 + </script>
1317 + <?php
1318 + }
1319 + ?>
1320 +
1321 + <script type="text/javascript">
1322 +
1323 + // keep track of any changes related to reminders
1324 + var vbo_reminders_changed = false;
1325 +
1326 + // keep track of changes to reminders related to a bookind ID
1327 + var vbo_reminders_changed_bid = null;
1328 +
1329 + jQuery(function() {
1330 +
1331 + // render datepicker calendar
1332 + jQuery('#<?php echo $wrapper_id; ?>').find('.vbo-reminders-dtpicker').datepicker({
1333 + minDate: "0",
1334 + maxDate: "+5y",
1335 + yearRange: "<?php echo date('Y'); ?>:<?php echo (date('Y') + 5); ?>",
1336 + changeMonth: true,
1337 + changeYear: true,
1338 + dateFormat: "<?php echo $this->getDateFormat('jui'); ?>",
1339 + defaultDate: "<?php echo date($this->df, $def_duedate); ?>"
1340 + });
1341 +
1342 + // triggering for datepicker calendar icons
1343 + jQuery('#<?php echo $wrapper_id; ?>').find('.vbo-widget-reminders-caltrigger').click(function() {
1344 + var jdp = jQuery(this).parent().find('input.hasDatepicker');
1345 + if (jdp.length) {
1346 + jdp.focus();
1347 + }
1348 + });
1349 +
1350 + // when document is ready, load reminders for this widget's instance
1351 + vboWidgetRemindersLoad('<?php echo $wrapper_id; ?>', <?php echo $reminder_id; ?>);
1352 +
1353 + // subscribe to the multitask-panel-close event to emit the event for the reminders changed
1354 + document.addEventListener(VBOCore.multitask_close_event, function() {
1355 + if (vbo_reminders_changed) {
1356 + // emit the event with data for anyone who is listening to it
1357 + VBOCore.emitEvent('vbo_reminders_changed', {
1358 + bid: vbo_reminders_changed_bid
1359 + });
1360 + }
1361 + });
1362 +
1363 + <?php
1364 + if ($js_modal_id) {
1365 + // widget can be dismissed through the modal
1366 + ?>
1367 + // subscribe to the modal-dismissed event to emit the event for the reminders changed
1368 + document.addEventListener(VBOCore.widget_modal_dismissed + '<?php echo $js_modal_id; ?>', function() {
1369 + if (vbo_reminders_changed) {
1370 + // emit the event with data for anyone who is listening to it
1371 + VBOCore.emitEvent('vbo_reminders_changed', {
1372 + bid: vbo_reminders_changed_bid
1373 + });
1374 + }
1375 + });
1376 + <?php
1377 + }
1378 +
1379 + if ($auto_action == 'add_new') {
1380 + // injected optinos require to add a new reminder
1381 + ?>
1382 + setTimeout(() => {
1383 + vboWidgetRemindersAdd('<?php echo $wrapper_id; ?>');
1384 + }, 300);
1385 + <?php
1386 + }
1387 + ?>
1388 +
1389 + });
1390 +
1391 + </script>
1392 +
1393 + <?php
1394 + }
1395 +
1396 + /**
1397 + * Checks if any reminders should be automatically scheduled due to
1398 + * important events related to reservations downloaded through VCM.
1399 + *
1400 + * @return int number of reminders that were automatically scheduled.
1401 + *
1402 + * @since 1.16.3 (J) - 1.6.3 (WP)
1403 + */
1404 + protected function scheduleChannelManagerReminders()
1405 + {
1406 + $scheduled = 0;
1407 +
1408 + if (!method_exists('VikChannelManager', 'hostToGuestReviewSupported')) {
1409 + // prevent server errors
1410 + return $scheduled;
1411 + }
1412 +
1413 + // get the reminders helper object
1414 + $helper = VBORemindersHelper::getInstance();
1415 +
1416 + // get a list of Airbnb reservations with a proper checkout date
1417 + $airbnb_checkouts = $helper->gatherAirbnbReservationsCheckedOut();
1418 +
1419 + // default due-date for the newly added reminders
1420 + $def_due_date = date('Y-m-d H:i:s', strtotime("+1 minute"));
1421 +
1422 + // default reminder payload
1423 + $payload = [
1424 + 'airbnb_host_guest_review' => 1,
1425 + ];
1426 +
1427 + // parse all eligible reservations
1428 + foreach ($airbnb_checkouts as $airbnb_res) {
1429 + // make sure this airbnb reservation requires a host-to-guest review
1430 + if (!$helper->bookingHasReminder($airbnb_res['id'], $payload) && VikChannelManager::hostToGuestReviewSupported($airbnb_res)) {
1431 + // build and schedule an automatic reminder
1432 + $reminder = new stdClass;
1433 + $reminder->title = JText::translate('VBO_REVIEW_YOUR_GUEST');
1434 + $reminder->descr = 'Airbnb - ' . JText::translate('VBDASHBOOKINGID') . ' ' . $airbnb_res['id'];
1435 + $reminder->duedate = $def_due_date;
1436 + $reminder->usetime = 1;
1437 + $reminder->idorder = $airbnb_res['id'];
1438 + $reminder->payload = $payload;
1439 + $reminder->important = 1;
1440 +
1441 + if ($helper->saveReminder($reminder)) {
1442 + // increase counter
1443 + $scheduled++;
1444 + }
1445 + }
1446 + }
1447 +
1448 + return $scheduled;
1449 + }
1450 +}