PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.6
VikBooking Hotel Booking Engine & PMS v1.8.6
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 1.7.3 All 36 releases
vikbooking / admin / helpers / widgets / reminders.php

reminders.php in VikBooking Hotel Booking Engine & PMS 1.8.6, at admin/helpers/widgets/reminders.php

1,451 lines 49.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
1451