PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
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 / src / reminders / helper.php

helper.php in VikBooking Hotel Booking Engine & PMS trunk, at admin/helpers/src/reminders/helper.php

592 lines 15.7 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 core
5 * @author Alessio Gaggii - E4J s.r.l.
6 * @copyright Copyright (C) 2022 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Helper class to handle reminders.
16 *
17 * @since 1.15.0 (J) - 1.5.0 (WP)
18 */
19 final class VBORemindersHelper extends JObject
20 {
21 /**
22 * Proxy to construct the object.
23 *
24 * @param array|object $data optional data to bind.
25 *
26 * @return self
27 */
28 public static function getInstance($data = [])
29 {
30 return new static($data);
31 }
32
33 /**
34 * Loads records from the db table for the reminders.
35 *
36 * @param array $fetch optional query fetch options.
37 * @param int $offset optional query limit start.
38 * @param int $length optional query limit length.
39 *
40 * @return array list of record objects, if any.
41 *
42 * @since 1.16.5 (J) - 1.6.5 (WP) Query refactoring and support added
43 * for reminders not yet displayed. Implemented "important" flag
44 * for future usage in order to always display them until completed.
45 */
46 public function loadReminders(array $fetch = [], $offset = 0, $length = 0)
47 {
48 $dbo = JFactory::getDbo();
49
50 // build default fetch params
51 $params = [
52 'after' => 'NOW',
53 'before' => null,
54 'idorder' => 0,
55 'onlyorder' => 0,
56 'completed' => 0,
57 'expired' => 0,
58 'not_shown' => 0,
59 'important' => 0,
60 'missed' => 0,
61 ];
62
63 // merge fetch params
64 $params = array_merge($params, $fetch);
65
66 // build query
67 $q = $dbo->getQuery(true)
68 ->select('*')
69 ->from($dbo->qn('#__vikbooking_reminders'));
70
71 if ($params['missed'] && isset($params['after']) && $params['after'] != 'NOW') {
72 /**
73 * Fetch important reminders missed (expired after a date) or any imminent one.
74 * (
75 * (`duedate` >= 'Y-m-d' AND `important` = 1)
76 * OR
77 * (`duedate` >= NOW() AND `important` = 0)
78 * )
79 */
80 $where_clause = '((%1$s >= %2$s AND %3$s = 1) OR (%1$s >= NOW() AND %3$s = 0))' . "\n";
81 $q->where(sprintf($where_clause, $dbo->qn('duedate'), $dbo->q($params['after']), $dbo->qn('important')));
82 } else {
83 // regular date intervals
84 if (!$params['expired'] && !empty($params['after']) && is_string($params['after'])) {
85 if (!strcasecmp($params['after'], 'NOW')) {
86 $q->where($dbo->qn('duedate') . ' >= NOW()');
87 } else {
88 // datetime string is expected
89 $q->where($dbo->qn('duedate') . ' >= ' . $dbo->q($params['after']));
90 }
91 }
92 }
93
94 if ($params['before']) {
95 // set clause for max future date
96 $q->where($dbo->qn('duedate') . ' <= ' . $dbo->q($params['before']));
97 }
98
99 if ($params['idorder']) {
100 // exclude all reminders not for this booking
101 $res_filter = [(int)$params['idorder']];
102 if (!$params['onlyorder']) {
103 // take the ones for no bookings or for this booking only
104 array_unshift($res_filter, 0);
105 }
106 $q->where($dbo->qn('idorder') . ' IN (' . implode(', ', $res_filter) . ')');
107
108 // set ordering to display reminders for this booking on top
109 $q->order($dbo->qn('idorder') . ' DESC');
110 }
111
112 if (!$params['completed']) {
113 $q->where($dbo->qn('completed') . ' = 0');
114 }
115
116 if ($params['not_shown']) {
117 // exclude the reminders that were displayed
118 $q->where($dbo->qn('displayed') . ' = 0');
119 }
120
121 if ($params['important']) {
122 // fetch only those reminders with the "important" flag enabled
123 $q->where($dbo->qn('important') . ' = ' . (int)$params['important']);
124 }
125
126 // set general ordering
127 if ($params['expired']) {
128 // order by the time difference from now as an absolute value
129 $q->order('ABS(' . $dbo->qn('duedate') . ' - NOW()) ASC');
130 } else {
131 // due date ascending is useful when before/after date filters are given
132 $q->order($dbo->qn('duedate') . ' ASC');
133 }
134
135 $dbo->setQuery($q, $offset, $length);
136 $reminders = $dbo->loadObjectList();
137
138 if (!$reminders) {
139 return [];
140 }
141
142 // decode payload on all records, if needed
143 foreach ($reminders as $k => $reminder) {
144 if (!empty($reminder->payload)) {
145 $reminders[$k]->payload = json_decode($reminder->payload);
146 }
147 }
148
149 return $reminders;
150 }
151
152 /**
153 * Returns a list of imminent reminders that were not displayed yet.
154 *
155 * @param int $length the maximum records to fetch.
156 *
157 * @return array list of object records, if any.
158 */
159 public function getImminents($length = 10)
160 {
161 // imminent reminders are meant to not expire in more than 1 day
162 $now_info = getdate();
163 $lim_max_date = date('Y-m-d H:i:s', mktime(23, 59, 59, $now_info['mon'], ($now_info['mday'] + 1), $now_info['year']));
164
165 // we also grab what was not displayed (missed) of important within the last week
166 $lim_min_date = date('Y-m-d H:i:s', mktime(0, 0, 0, $now_info['mon'], ($now_info['mday'] - 7), $now_info['year']));
167
168 // build fetch instructions
169 $fetch = [
170 'after' => $lim_min_date,
171 'before' => $lim_max_date,
172 'idorder' => 0,
173 'completed' => 0,
174 'expired' => 0,
175 'not_shown' => 1,
176 'missed' => 1,
177 ];
178
179 return $this->loadReminders($fetch, 0, $length);
180 }
181
182 /**
183 * Gets a specific reminder by ID.
184 *
185 * @param int $rid the record ID.
186 *
187 * @return null|object
188 */
189 public function getReminder($rid)
190 {
191 if (empty($rid)) {
192 return null;
193 }
194
195 $dbo = JFactory::getDbo();
196
197 $q = $dbo->getQuery(true)
198 ->select('*')
199 ->from($dbo->qn('#__vikbooking_reminders'))
200 ->where($dbo->qn('id') . ' = ' . (int)$rid);
201
202 $dbo->setQuery($q, 0, 1);
203 $reminder = $dbo->loadObject();
204
205 if (!$reminder) {
206 return null;
207 }
208
209 if (!empty($reminder->payload)) {
210 $reminder->payload = json_decode($reminder->payload);
211 }
212
213 return $reminder;
214 }
215
216 /**
217 * Toggles the "displayed" property for a given reminder ID.
218 *
219 * @param int $id the reminder record ID.
220 * @param bool $displayed the status to set.
221 *
222 * @return bool
223 *
224 * @since 1.16.5 (J) - 1.6.5 (WP)
225 */
226 public function setDisplayed($id, $displayed = true)
227 {
228 $record = new stdClass;
229
230 $record->id = (int)$id;
231 $record->displayed = (int)$displayed;
232
233 return $this->updateReminder($record);
234 }
235
236 /**
237 * Inserts a new reminder record object.
238 *
239 * @param object $reminder the record object to insert.
240 *
241 * @return bool
242 */
243 public function saveReminder($reminder)
244 {
245 if (!is_object($reminder) || !count(get_object_vars($reminder))) {
246 $this->setError('Empty or invalid argument');
247 return false;
248 }
249
250 if (!empty($reminder->payload) && !is_scalar($reminder->payload)) {
251 // make sure to JSON encode the payload property
252 $reminder->payload = json_encode($reminder->payload);
253 }
254
255 $dbo = JFactory::getDbo();
256
257 try {
258 $dbo->insertObject('#__vikbooking_reminders', $reminder, 'id');
259 } catch (Exception $e) {
260 // do nothing
261 $this->setError('The query to insert the record failed');
262 }
263
264 return (!empty($reminder->id));
265 }
266
267 /**
268 * Updates an existing reminder record object.
269 *
270 * @param object $reminder the record object to update.
271 *
272 * @return bool
273 */
274 public function updateReminder($reminder)
275 {
276 if (!is_object($reminder) || !count(get_object_vars($reminder))) {
277 $this->setError('Empty or invalid argument');
278 return false;
279 }
280
281 if (empty($reminder->id)) {
282 $this->setError('Empty reminder id');
283 return false;
284 }
285
286 if (!empty($reminder->payload) && !is_scalar($reminder->payload)) {
287 // make sure to JSON encode the payload property
288 $reminder->payload = json_encode($reminder->payload);
289 }
290
291 $dbo = JFactory::getDbo();
292
293 try {
294 $res = $dbo->updateObject('#__vikbooking_reminders', $reminder, 'id');
295 } catch (Exception $e) {
296 $this->setError('The query to insert the record failed');
297 $res = false;
298 }
299
300 return $res;
301 }
302
303 /**
304 * Deletes an existing reminder record.
305 *
306 * @param int|object $reminder the record to remove.
307 *
308 * @return bool
309 */
310 public function deleteReminder($reminder)
311 {
312 if (!is_numeric($reminder) && !is_object($reminder)) {
313 return false;
314 }
315
316 $reminder_id = null;
317
318 if (is_object($reminder) && !empty($reminder->id)) {
319 $reminder_id = (int)$reminder->id;
320 } elseif (is_numeric($reminder)) {
321 $reminder_id = (int)$reminder;
322 }
323
324 if (empty($reminder_id)) {
325 return false;
326 }
327
328 $dbo = JFactory::getDbo();
329
330 $q = $dbo->getQuery(true)
331 ->delete($dbo->qn('#__vikbooking_reminders'))
332 ->where($dbo->qn('id') . ' = ' . $reminder_id);
333
334 $dbo->setQuery($q);
335 $dbo->execute();
336
337 return ($dbo->getAffectedRows() > 0);
338 }
339
340 /**
341 * Searches for a reminder according to the provided criteria.
342 *
343 * @param array $criteria Associative list of data to look for.
344 *
345 * @return object|null
346 *
347 * @since 1.16.10 (J) - 1.6.10 (WP)
348 */
349 public function searchReminder(array $criteria)
350 {
351 if (!$criteria) {
352 return null;
353 }
354
355 $dbo = JFactory::getDbo();
356
357 $q = $dbo->getQuery(true)
358 ->select('*')
359 ->from($dbo->qn('#__vikbooking_reminders'));
360
361 foreach ($criteria as $col => $val) {
362 if (is_array($val) || is_object($val)) {
363 $val = json_encode($val);
364 }
365
366 if (is_null($val)) {
367 $q->where($dbo->qn($col) . ' IS NULL');
368 } else {
369 $q->where($dbo->qn($col) . ' = ' . $dbo->q($val));
370 }
371 }
372
373 $dbo->setQuery($q);
374
375 return $dbo->loadObject();
376 }
377
378 /**
379 * Removes the reminders with a due date in the past.
380 *
381 * @return void
382 */
383 public function removeExpired()
384 {
385 $dbo = JFactory::getDbo();
386
387 $q = $dbo->getQuery(true)
388 ->delete($dbo->qn('#__vikbooking_reminders'))
389 ->where($dbo->qn('duedate') . ' < NOW()');
390
391 $dbo->setQuery($q);
392 $dbo->execute();
393
394 return;
395 }
396
397 /**
398 * Given two dates, compares the relative differences and returns the information.
399 * The language definitions are supposed to be loaded from the admin section.
400 *
401 * @param string|DateTime $date_a the date to compare from.
402 * @param string|DateTime $date_b the date to compare against.
403 *
404 * @return array false on failure, array with diff otherwise.
405 */
406 public function relativeDatesDiff($date_a, $date_b = null)
407 {
408 if (is_string($date_a)) {
409 $date_a = new DateTime($date_a);
410 }
411
412 if (!($date_a instanceof DateTime)) {
413 $date_a = new DateTime();
414 }
415
416 if (is_string($date_b) || empty($date_b)) {
417 // by default we compare against now
418 if (empty($date_b)) {
419 $date_b = new DateTime();
420 } else {
421 $date_b = new DateTime($date_b);
422 }
423 }
424
425 if (!($date_b instanceof DateTime)) {
426 $date_b = new DateTime();
427 }
428
429 // calculate close y-m-d dates
430 $fromd_ymd = $date_a->format('Y-m-d');
431 $today = date('Y-m-d');
432 $yesterday = date('Y-m-d', strtotime('-1 day'));
433 $tomorrow = date('Y-m-d', strtotime('+1 day'));
434
435 // get the date interval object of differences
436 $dt_interval = $date_a->diff($date_b);
437
438 // compose the associative data to be returned
439 $diff_data = [
440 'past' => ($date_a < $date_b),
441 'sameday' => ($fromd_ymd == $date_b->format('Y-m-d')),
442 'today' => ($fromd_ymd == $today),
443 'yesterday' => ($fromd_ymd == $yesterday),
444 'tomorrow' => ($fromd_ymd == $tomorrow),
445 'seconds' => $dt_interval->s,
446 'minutes' => $dt_interval->i,
447 'hours' => $dt_interval->h,
448 'days' => $dt_interval->d,
449 /**
450 * Rely on weeks only if less than a month for better precision.
451 * Weeks is the only value to not be calculated natively in DateInterval.
452 */
453 'weeks' => ($dt_interval->m > 0 ? 0 : floor($dt_interval->d / 7)),
454 //
455 'months' => $dt_interval->m,
456 'years' => $dt_interval->y,
457 // set the DateTime objects parsed
458 'date_a' => $date_a,
459 'date_b' => $date_b,
460 // prepare the formatted relative difference string
461 'relative' => $fromd_ymd,
462 ];
463
464 // build the relative difference string
465 if ($diff_data['today']) {
466 $diff_data['relative'] = JText::translate('VBTODAY');
467 } elseif ($diff_data['yesterday']) {
468 $diff_data['relative'] = JText::translate('VBOYESTERDAY');
469 } elseif ($diff_data['tomorrow']) {
470 $diff_data['relative'] = JText::translate('VBOTOMORROW');
471 } elseif ($diff_data['years'] > 0) {
472 // no translations available at the moment for the singular version of "year"
473 $diff_num = $diff_data['years'] . ' ' . JText::translate('VBCONFIGSEARCHPMAXDATEYEARS');
474 $diff_data['relative'] = JText::sprintf(($diff_data['past'] ? 'VBO_REL_EXP_PAST' : 'VBO_REL_EXP_FUTURE'), strtolower($diff_num));
475 } elseif ($diff_data['months'] > 0) {
476 $diff_num = $diff_data['months'] . ' ' . JText::translate(($diff_data['months'] > 1 ? 'VBCONFIGSEARCHPMAXDATEMONTHS' : 'VBPVIEWRESTRICTIONSTWO'));
477 $diff_data['relative'] = JText::sprintf(($diff_data['past'] ? 'VBO_REL_EXP_PAST' : 'VBO_REL_EXP_FUTURE'), strtolower($diff_num));
478 } elseif ($diff_data['weeks'] > 1) {
479 // use weeks only if more than one for a better precision
480 $diff_num = $diff_data['weeks'] . ' ' . JText::translate(($diff_data['weeks'] > 1 ? 'VBCONFIGSEARCHPMAXDATEWEEKS' : 'VBOWEEK'));
481 $diff_data['relative'] = JText::sprintf(($diff_data['past'] ? 'VBO_REL_EXP_PAST' : 'VBO_REL_EXP_FUTURE'), strtolower($diff_num));
482 } elseif ($diff_data['days'] > 0) {
483 $diff_num = $diff_data['days'] . ' ' . JText::translate(($diff_data['days'] > 1 ? 'VBCONFIGSEARCHPMAXDATEDAYS' : 'VBODAY'));
484 $diff_data['relative'] = JText::sprintf(($diff_data['past'] ? 'VBO_REL_EXP_PAST' : 'VBO_REL_EXP_FUTURE'), strtolower($diff_num));
485 }
486
487 return $diff_data;
488 }
489
490 /**
491 * Tells whether a specific booking ID has got a reminder assigned.
492 *
493 * @param int $booking_id the reservation ID to check.
494 * @param array $payload optional payload to compare.
495 *
496 * @return bool
497 *
498 * @since 1.16.3 (J) - 1.6.3 (WP)
499 * @since 1.16.5 (J) - 1.6.5 (WP) added argument $payload.
500 */
501 public function bookingHasReminder($booking_id, array $payload = [])
502 {
503 $dbo = JFactory::getDbo();
504
505 $q = $dbo->getQuery(true);
506
507 $q->select('COUNT(1)')
508 ->from($dbo->qn('#__vikbooking_reminders'))
509 ->where($dbo->qn('idorder') . ' = ' . (int)$booking_id);
510
511 if ($payload) {
512 $q->where($dbo->qn('payload') . ' = ' . $dbo->q(json_encode($payload)));
513 }
514
515 $dbo->setQuery($q);
516
517 return (bool)$dbo->loadResult();
518 }
519
520 /**
521 * Gathers a list of Airbnb reservations that may require a host-to-guest review.
522 *
523 * @param int $lim_start_ts the checkout timestamp to use as limit.
524 *
525 * @return array
526 *
527 * @since 1.16.3 (J) - 1.6.3 (WP)
528 */
529 public function gatherAirbnbReservationsCheckedOut($lim_start_ts = 0)
530 {
531 if (!$lim_start_ts) {
532 // default to checkout two weeks ago
533 $lim_start_ts = strtotime("-14 days", strtotime(date('Y-m-d')));
534 }
535
536 // maximum checkout date must be 14 days ahead from checkout
537 $lim_end_ts = strtotime("+14 days", $lim_start_ts);
538
539 $dbo = JFactory::getDbo();
540
541 $q = $dbo->getQuery(true)
542 ->select($dbo->qn([
543 'o.id',
544 'o.status',
545 'o.checkin',
546 'o.checkout',
547 'o.idorderota',
548 'o.channel',
549 ]))
550 ->from($dbo->qn('#__vikbooking_orders', 'o'))
551 ->where($dbo->qn('o.status') . ' = ' . $dbo->q('confirmed'))
552 ->where($dbo->qn('o.checkout') . ' >= ' . $lim_start_ts)
553 ->where($dbo->qn('o.checkout') . ' <= ' . $lim_end_ts)
554 ->where($dbo->qn('o.channel') . ' LIKE ' . $dbo->q('airbnbapi%'));
555
556 $dbo->setQuery($q);
557
558 return $dbo->loadAssocList();
559 }
560
561 /**
562 * Completes the reminder(s) of a specific booking with an optional payload type.
563 *
564 * @param int $booking_id The reservation ID to check.
565 * @param array $payload Optional payload to compare.
566 * @param int $lim Query update limit.
567 *
568 * @return bool
569 *
570 * @since 1.16.10 (J) - 1.6.10 (WP)
571 */
572 public function completeBookingReminders($booking_id, array $payload = [], $lim = 0)
573 {
574 $dbo = JFactory::getDbo();
575
576 $q = $dbo->getQuery(true);
577
578 $q->update($dbo->qn('#__vikbooking_reminders'))
579 ->set($dbo->qn('completed') . ' = 1')
580 ->where($dbo->qn('idorder') . ' = ' . (int) $booking_id);
581
582 if ($payload) {
583 $q->where($dbo->qn('payload') . ' = ' . $dbo->q(json_encode($payload)));
584 }
585
586 $dbo->setQuery($q, 0, $lim);
587 $dbo->execute();
588
589 return (bool) $dbo->getAffectedRows();
590 }
591 }
592