PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.6.4
VikBooking Hotel Booking Engine & PMS v1.6.4
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
vikbooking / admin / helpers / admin_widgets.php

admin_widgets.php in VikBooking Hotel Booking Engine & PMS 1.6.4, at admin/helpers/admin_widgets.php

842 lines 20.9 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 - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 /**
14 * Helper class for the administrator widgets.
15 *
16 * @since 1.14 (J) - 1.4.0 (WP)
17 */
18 class VikBookingHelperAdminWidgets
19 {
20 /**
21 * The singleton instance of the class.
22 *
23 * @var VikBookingHelperAdminWidgets
24 */
25 protected static $instance = null;
26
27 /**
28 * An array to store some cached/static values.
29 *
30 * @var array
31 */
32 protected static $helper = null;
33
34 /**
35 * The database handler instance.
36 *
37 * @var object
38 */
39 protected $dbo;
40
41 /**
42 * The list of widget instances loaded.
43 *
44 * @var array
45 */
46 protected $widgets;
47
48 /**
49 * Class constructor is protected.
50 *
51 * @see getInstance()
52 */
53 protected function __construct()
54 {
55 static::$helper = [];
56 $this->dbo = JFactory::getDbo();
57 $this->widgets = [];
58 $this->load();
59 }
60
61 /**
62 * Returns the global object, either
63 * a new instance or the existing instance
64 * if the class was already instantiated.
65 *
66 * @return self A new instance of the class.
67 */
68 public static function getInstance()
69 {
70 if (is_null(static::$instance)) {
71 static::$instance = new static();
72 }
73
74 return static::$instance;
75 }
76
77 /**
78 * Loads a list of all available admin widgets.
79 *
80 * @return self
81 */
82 protected function load()
83 {
84 /** @var VBOPlatformDispatcherInterface */
85 $dispatcher = VBOFactory::getPlatform()->getDispatcher();
86
87 // require main/parent admin-widget class
88 require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'admin_widget.php');
89
90 $widgets_base = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'widgets' . DIRECTORY_SEPARATOR;
91 $widgets_files = glob($widgets_base . '*.php');
92 $widgets_banned = [];
93
94 /**
95 * Trigger event to let other plugins register additional widgets.
96 *
97 * @return array A list of supported widgets.
98 */
99 $list = $dispatcher->filter('onLoadAdminWidgets');
100
101 foreach ($list as $chunk) {
102 // merge default widget files with the returned ones
103 $widgets_files = array_merge($widgets_files, (array)$chunk);
104 }
105
106 /**
107 * Trigger event to let other plugins unregister specific widgets.
108 *
109 * @return array A list of widget identifiers to unload.
110 *
111 * @since 1.16.0 (J) - 1.6.0 (WP)
112 */
113 $unloaded = $dispatcher->filter('onUnloadAdminWidgets');
114
115 foreach ($unloaded as $chunk) {
116 // merge all the the returned ones
117 $widgets_banned = array_merge($widgets_banned, (array)$chunk);
118 }
119
120 // parse all admin widgets
121 foreach ($widgets_files as $wf) {
122 try {
123 /**
124 * Require widget class file only if available, to allow registering classes at
125 * runtime by third-party plugins without needing to have a dedicated PHP file.
126 * The widget class must obviously exist all the times, but the file must not.
127 */
128 if (is_file($wf)) {
129 require_once($wf);
130 }
131
132 // widget identifier
133 $widget_identif = basename($wf, '.php');
134
135 // check if the widget was unloaded
136 if (in_array($widget_identif, $widgets_banned)) {
137 continue;
138 }
139
140 // build widget class name
141 $classname = 'VikBookingAdminWidget' . str_replace(' ', '', ucwords(str_replace('_', ' ', $widget_identif)));
142
143 if (class_exists($classname)) {
144 // instantiate widget object
145 $widget = new $classname();
146
147 // push widget object
148 array_push($this->widgets, $widget);
149 }
150 } catch (Exception $e) {
151 // do nothing
152 }
153 }
154
155 return $this;
156 }
157
158 /**
159 * Gets the default map of admin widgets.
160 *
161 * @return object the associative map of sections,
162 * containers and widgets.
163 */
164 protected function getDefaultWidgetsMap()
165 {
166 // sections container
167 $sections = [];
168
169 // build default sections
170
171 // new section
172 $section = new stdClass;
173 $section->name = 'Top';
174 $section->containers = [];
175 // start container
176 $container = new stdClass;
177 $container->size = 'medium';
178 $container->widgets = [
179 'sticky_notes',
180 'arriving_today',
181 'departing_today',
182 'check_availability',
183 'latest_events',
184 ];
185 // push container
186 array_push($section->containers, $container);
187 // start container
188 $container = new stdClass;
189 $container->size = 'small';
190 $container->widgets = [
191 'guest_messages',
192 'latest_from_guests',
193 'visitors_counter',
194 ];
195 // push container
196 array_push($section->containers, $container);
197 // start container
198 $container = new stdClass;
199 $container->size = 'small';
200 $container->widgets = [
201 'forecast',
202 'bookings_calendar',
203 'reminders',
204 ];
205 // push container
206 array_push($section->containers, $container);
207 // push section
208 array_push($sections, $section);
209
210 // new section
211 $section = new stdClass;
212 $section->name = 'Middle';
213 $section->containers = [];
214 // start container
215 $container = new stdClass;
216 $container->size = 'full';
217 $container->widgets = [
218 'today_rooms_occupancy',
219 'weekly_bookings',
220 ];
221 // push container
222 array_push($section->containers, $container);
223 // push section
224 array_push($sections, $section);
225
226 // new section
227 $section = new stdClass;
228 $section->name = 'Middle 2';
229 $section->containers = [];
230 // start container
231 $container = new stdClass;
232 $container->size = 'small';
233 $container->widgets = [
234 'latest_events',
235 ];
236 // push container
237 array_push($section->containers, $container);
238 // start container
239 $container = new stdClass;
240 $container->size = 'small';
241 $container->widgets = [
242 'rates_flow',
243 ];
244 // push container
245 array_push($section->containers, $container);
246 // start container
247 $container = new stdClass;
248 $container->size = 'medium';
249 $container->widgets = [
250 'finance',
251 'currency_converter',
252 ];
253 // push container
254 array_push($section->containers, $container);
255 // push section
256 array_push($sections, $section);
257
258 // new section
259 $section = new stdClass;
260 $section->name = 'Bottom';
261 $section->containers = [];
262 // start container
263 $container = new stdClass;
264 $container->size = 'medium';
265 $container->widgets = [
266 'last_reservations',
267 ];
268 // push container
269 array_push($section->containers, $container);
270 // start container
271 $container = new stdClass;
272 $container->size = 'medium';
273 $container->widgets = [
274 'next_bookings',
275 ];
276 // push container
277 array_push($section->containers, $container);
278 // push section
279 array_push($sections, $section);
280
281 // new section
282 $section = new stdClass;
283 $section->name = 'Bottom 2';
284 $section->containers = [];
285 // start container
286 $container = new stdClass;
287 $container->size = 'medium';
288 $container->widgets = [
289 'orphan_dates',
290 'rooms_locked',
291 ];
292 // push container
293 array_push($section->containers, $container);
294 // start container
295 $container = new stdClass;
296 $container->size = 'medium';
297 $container->widgets = [
298 'sticky_notes',
299 ];
300 // push container
301 array_push($section->containers, $container);
302 // push section
303 array_push($sections, $section);
304
305 // compose the final map object
306 $map = new stdClass;
307 $map->sections = $sections;
308
309 return $map;
310 }
311
312 /**
313 * Gets the list of admin widgets instantiated.
314 *
315 * @return array list of admin widget objects.
316 */
317 public function getWidgets()
318 {
319 return $this->widgets;
320 }
321
322 /**
323 * Gets a single admin widget instantiated.
324 *
325 * @param string $id the widget identifier.
326 *
327 * @return mixed the admin widget object, false otherwise.
328 */
329 public function getWidget($id)
330 {
331 $id = $this->simplifyId($id);
332
333 foreach ($this->widgets as $widget) {
334 if ($widget->getIdentifier() != $id) {
335 continue;
336 }
337 return $widget;
338 }
339
340 return false;
341 }
342
343 /**
344 * Gets a list of sorted widget names, ids, descriptions, icons and styles.
345 * If called with pre-loading, widgets will be able to load their CSS and JS
346 * assets. Moreover, pre-loading a widget can return a watch-data object to
347 * implement the notifications scheduling of a specific widget.
348 *
349 * @param bool $preload whether to call the widget's preload method.
350 *
351 * @return array associative and sorted widgets list.
352 *
353 * @since 1.15.0 (J) - 1.5.0 (WP) added support for icons and style names.
354 * implemented widgets preloading for assets.
355 */
356 public function getWidgetNames($preload = false)
357 {
358 // containers
359 $names = [];
360 $pool = [];
361 $watch = [];
362
363 foreach ($this->widgets as $widget) {
364 // get widget details
365 $id = $widget->getIdentifier();
366 $name = $widget->getName();
367 $descr = $widget->getDescription();
368
369 if ($preload) {
370 // preload widget's CSS/JS assets (if any)
371 $watch_data = $widget->preload();
372 if ($watch_data) {
373 // assign watching data to this widget
374 $watch[$id] = $watch_data;
375 }
376 }
377
378 // build widget info object
379 $wtdata = new stdClass;
380 $wtdata->id = $id;
381 $wtdata->name = $name;
382 $wtdata->descr = $descr;
383 $wtdata->icon = $widget->getIcon();
384 $wtdata->style = str_replace(array(' ', '_'), '-', $widget->getStyleName());
385
386 // set object for sorting
387 $names[$name] = $wtdata;
388 }
389
390 // apply sorting by name
391 ksort($names);
392
393 // push sorted widgets to pool
394 foreach ($names as $wtdata) {
395 $pool[] = $wtdata;
396 }
397
398 /**
399 * In case some widgets were pre-loaded for watching certain
400 * events, enqueue the watch-data list to the document.
401 */
402 if (count($watch)) {
403 // schedule the browser notifications watching data
404 VBONotificationScheduler::getInstance()->registerWatchData($watch);
405 }
406
407 // return the list of admin widgets
408 return $pool;
409 }
410
411 /**
412 * Gets the current or default map of admin widgets.
413 * If no map currently set, stores the default map.
414 *
415 * @return array the associative map of sections,
416 * containers and widgets.
417 */
418 public function getWidgetsMap()
419 {
420 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='admin_widgets_map';";
421 $this->dbo->setQuery($q);
422 $this->dbo->execute();
423 if ($this->dbo->getNumRows()) {
424 $map = json_decode($this->dbo->loadResult());
425 return is_object($map) && !empty($map->sections) && is_array($map->sections) ? $map : $this->getDefaultWidgetsMap();
426 }
427
428 $default_map = $this->getDefaultWidgetsMap();
429 $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('admin_widgets_map', " . $this->dbo->quote(json_encode($default_map)) . ");";
430 $this->dbo->setQuery($q);
431 $this->dbo->execute();
432
433 return $default_map;
434 }
435
436 /**
437 * Updates the map of admin widgets.
438 *
439 * @param array $sections the list of sections for the map.
440 *
441 * @return bool True on success, false otherwise.
442 */
443 public function updateWidgetsMap($sections)
444 {
445 if (!is_array($sections) || !count($sections)) {
446 return false;
447 }
448
449 // prepare new map object
450 $map = new stdClass;
451 $map->sections = $sections;
452
453 $q = "UPDATE `#__vikbooking_config` SET `setting`=" . $this->dbo->quote(json_encode($map)) . " WHERE `param`='admin_widgets_map';";
454 $this->dbo->setQuery($q);
455 $this->dbo->execute();
456
457 return true;
458 }
459
460 /**
461 * Restores the default admin widgets map.
462 * First it resets the settings of each widget.
463 *
464 * @return bool True on success, false otherwise.
465 */
466 public function restoreDefaultWidgetsMap()
467 {
468 foreach ($this->widgets as $widget) {
469 $widget->resetSettings();
470 }
471
472 $default_map = $this->getDefaultWidgetsMap();
473
474 return $this->updateWidgetsMap($default_map->sections);
475 }
476
477 /**
478 * Forces the rendering of a specific widget identifier.
479 *
480 * @param string $id the widget identifier.
481 * @param mixed $data anything to pass to the widget.
482 *
483 * @return mixed void on success, false otherwise.
484 */
485 public function renderWidget($id, $data = null)
486 {
487 $id = $this->simplifyId($id);
488
489 foreach ($this->widgets as $widget) {
490 if ($widget->getIdentifier() != $id) {
491 continue;
492 }
493 return $widget->render($data);
494 }
495
496 return false;
497 }
498
499 /**
500 * Maps the size identifier to a CSS class.
501 *
502 * @param string $size the container size identifier.
503 *
504 * @return string the full CSS class for the container.
505 */
506 public function getContainerCssClass($size)
507 {
508 $css_size_map = [
509 'small' => 'vbo-admin-widgets-container-small',
510 'medium' => 'vbo-admin-widgets-container-medium',
511 'large' => 'vbo-admin-widgets-container-large',
512 'full' => 'vbo-admin-widgets-container-fullwidth',
513 ];
514
515 return isset($css_size_map[$size]) ? $css_size_map[$size] : $css_size_map['full'];
516 }
517
518 /**
519 * Returns an associative array with the class names for the containers.
520 *
521 * @return array a text representation list of all sizes.
522 */
523 public function getContainerClassNames()
524 {
525 return array(
526 'full' => array(
527 'name' => JText::translate('VBO_WIDGETS_CONTFULL'),
528 'css' => $this->getContainerCssClass('full'),
529 ),
530 'large' => array(
531 'name' => JText::translate('VBO_WIDGETS_CONTLARGE'),
532 'css' => $this->getContainerCssClass('large'),
533 ),
534 'medium' => array(
535 'name' => JText::translate('VBO_WIDGETS_CONTMEDIUM'),
536 'css' => $this->getContainerCssClass('medium'),
537 ),
538 'small' => array(
539 'name' => JText::translate('VBO_WIDGETS_CONTSMALL'),
540 'css' => $this->getContainerCssClass('small'),
541 ),
542 );
543 }
544
545 /**
546 * Maps the size identifier to the corresponding name.
547 *
548 * @param string $size the container size identifier.
549 *
550 * @return string the size name for the container.
551 */
552 public function getContainerName($size)
553 {
554 $names = $this->getContainerClassNames();
555
556 return isset($names[$size]) ? $names[$size]['name'] : $names['full']['name'];
557 }
558
559 /**
560 * Many widgets may need to know some values about the rooms.
561 * This method uses the static instance of the class to cache data.
562 *
563 * @return void
564 */
565 protected function loadRoomsData()
566 {
567 if (isset(static::$helper['all_rooms_ids'])) {
568 // do not execute the same queries again
569 return;
570 }
571
572 $all_rooms_ids = [];
573 $all_rooms_units = [];
574 $all_rooms_features = [];
575 $unpublished_rooms = [];
576 $tot_rooms_units = 0;
577
578 $q = "SELECT `id`,`name`,`units`,`params`,`avail` FROM `#__vikbooking_rooms`;";
579 $this->dbo->setQuery($q);
580 $this->dbo->execute();
581 if ($this->dbo->getNumRows() > 0) {
582 $all_rooms = $this->dbo->loadAssocList();
583 foreach ($all_rooms as $k => $r) {
584 if ($r['avail'] < 1) {
585 $unpublished_rooms[] = $r['id'];
586 } else {
587 $tot_rooms_units += $r['units'];
588 }
589 $all_rooms_ids[$r['id']] = $r['name'];
590 $all_rooms_units[$r['id']] = $r['units'];
591 $rparams = json_decode($r['params'], true);
592 $all_rooms_features[$r['id']] = is_array($rparams) && array_key_exists('features', $rparams) && is_array($rparams['features']) ? $rparams['features'] : [];
593 }
594 }
595
596 // update static values
597 static::$helper['all_rooms_ids'] = $all_rooms_ids;
598 static::$helper['all_rooms_units'] = $all_rooms_units;
599 static::$helper['all_rooms_features'] = $all_rooms_features;
600 static::$helper['unpublished_rooms'] = $unpublished_rooms;
601 static::$helper['tot_rooms_units'] = $tot_rooms_units;
602 }
603
604 /**
605 * Many widgets could use this method to access cached information.
606 *
607 * @param string $key the data key identifier.
608 *
609 * @return mixed array/int on success, false otherwise.
610 */
611 public function getRoomsData($key)
612 {
613 if (empty($key)) {
614 return false;
615 }
616
617 if (!count(static::$helper)) {
618 $this->loadRoomsData();
619 }
620
621 if (isset(static::$helper[$key])) {
622 return static::$helper[$key];
623 }
624
625 return false;
626 }
627
628 /**
629 * Helper method to load all busy real records for all rooms for dates near today.
630 * This is useful to avoid double queries in the various widgets and to get data
631 * about yesterday, today and the next week.
632 *
633 * @return array
634 *
635 * @since 1.15.2 (J) - 1.5.5 (WP) changed the way limit timestamps are calculated.
636 */
637 public function loadBusyRecordsUnclosed()
638 {
639 if (!isset(static::$helper['all_rooms_ids'])) {
640 $this->loadRoomsData();
641 }
642
643 if (isset(static::$helper['busy_records_unclosed'])) {
644 return static::$helper['busy_records_unclosed'];
645 }
646
647 // cache value and return it
648 $base_from_ts = mktime(0, 0, 0, date("n"), (date("j") - 1), date("Y"));
649 $base_end_ts = mktime(23, 59, 59, date("n"), (date("j") + 7), date("Y"));
650 static::$helper['busy_records_unclosed'] = VikBooking::loadBusyRecordsUnclosed(array_keys(static::$helper['all_rooms_ids']), $base_from_ts, $base_end_ts);
651
652 return static::$helper['busy_records_unclosed'];
653 }
654
655 /**
656 * The first time the widget's customizer is open, the welcome is displayed.
657 * Congig value >= 1 means hide the welcome text, 0 or lower means show it.
658 *
659 * @return bool
660 */
661 public function showWelcome()
662 {
663 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='admin_widgets_welcome';";
664 $this->dbo->setQuery($q);
665 $this->dbo->execute();
666 if ($this->dbo->getNumRows()) {
667 return ((int)$this->dbo->loadResult() < 1);
668 }
669
670 $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('admin_widgets_welcome', '0');";
671 $this->dbo->setQuery($q);
672 $this->dbo->execute();
673
674 return true;
675 }
676
677 /**
678 * Updates the status of the welcome message for the widget's customizer.
679 * Congig value >= 1 means hide the welcome text, 0 or lower means show it.
680 *
681 * @param int $val the new value to set in the configuration.
682 *
683 * @return void
684 */
685 public function updateWelcome($val)
686 {
687 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='admin_widgets_welcome';";
688 $this->dbo->setQuery($q);
689 $this->dbo->execute();
690 if ($this->dbo->getNumRows()) {
691 $q = "UPDATE `#__vikbooking_config` SET `setting`=" . $this->dbo->quote((int)$val) . " WHERE `param`='admin_widgets_welcome';";
692 $this->dbo->setQuery($q);
693 $this->dbo->execute();
694 return;
695 }
696
697 $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('admin_widgets_welcome', " . $this->dbo->quote((int)$val) . ");";
698 $this->dbo->setQuery($q);
699 $this->dbo->execute();
700 return;
701 }
702
703 /**
704 * Gets the default map for multitasking pages.
705 *
706 * @return object the associative map of widgets.
707 */
708 protected function getDefaultMultitaskingMap()
709 {
710 $map = new stdClass;
711
712 // booking details page
713 $map->editorder = [
714 'reminders',
715 'check_availability',
716 'bookings_calendar',
717 ];
718
719 // booking modifying page
720 $map->editbusy = [
721 'bookings_calendar',
722 'check_availability',
723 'reminders',
724 ];
725
726 // availability overview
727 $map->overv = [
728 'reminders',
729 'rates_flow',
730 'latest_events',
731 ];
732
733 return $map;
734 }
735
736 /**
737 * Gets the list of widgets assigned to the current page.
738 *
739 * @param string $page the name of the current View/task.
740 * @param bool $whole whether to get the entire map object.
741 *
742 * @return array|object the list of widgets for the given page,
743 * or entire map object if $whole is true.
744 */
745 public function getMultitaskingMap($page = null, $whole = false)
746 {
747 // get saved map
748 $saved_map = VBOFactory::getConfig()->get('multitasking_map');
749 if (!empty($saved_map)) {
750 // try decoding the map data
751 $saved_map = json_decode($saved_map);
752 }
753
754 if (!is_object($saved_map)) {
755 // revert to the default map
756 $saved_map = $this->getDefaultMultitaskingMap();
757 }
758
759 if ($whole) {
760 // do not proceed and return the entire map object
761 return $saved_map;
762 }
763
764 if (empty($page) || !is_string($page)) {
765 return [];
766 }
767
768 return isset($saved_map->$page) && is_array($saved_map->$page) ? $saved_map->$page : [];
769 }
770
771 /**
772 * Updates the map of multitasking widgets.
773 *
774 * @param string $page the name of the current View/task.
775 * @param array|string $widgets the list of or the widget(s) to set.
776 * @param int $action 0 = replace, -1 = prepend, 1 = append.
777 *
778 * @return bool true on success, false otherwise.
779 */
780 public function updateMultitaskingMap($page, $widgets, $action = 0)
781 {
782 if (empty($page) || !is_string($page)) {
783 return false;
784 }
785
786 // check if we have one widget or a list
787 $widgets = is_string($widgets) ? [$widgets] : $widgets;
788 if (!is_array($widgets)) {
789 return false;
790 }
791
792 // get the whole current map
793 $map = $this->getMultitaskingMap($page, true);
794
795 if (!isset($map->$page)) {
796 $map->$page = [];
797 }
798
799 if ($action > 0) {
800 // append widgets
801 foreach ($widgets as $widget_id) {
802 if (empty($widget_id)) {
803 continue;
804 }
805 array_push($map->$page, $widget_id);
806 }
807 } elseif ($action < 0) {
808 // prepend widgets
809 foreach ($widgets as $widget_id) {
810 if (empty($widget_id)) {
811 continue;
812 }
813 array_unshift($map->$page, $widget_id);
814 }
815 } else {
816 // replace current list when $action === 0
817 $map->$page = $widgets;
818 }
819
820 // update configuration record
821 VBOFactory::getConfig()->set('multitasking_map', json_encode($map));
822
823 return true;
824 }
825
826 /**
827 * Admin widgets used to be named as the entire file name,
828 * inclusive of the ".php" extension. For BC we make sure
829 * to evaluate the admin widget identifiers correclty.
830 *
831 * @param string $id the widget identifier to simplify.
832 *
833 * @return string the simplified widget identifier.
834 *
835 * @since 1.15.0 (J) - 1.5.0 (WP)
836 */
837 protected function simplifyId($id)
838 {
839 return str_replace('.php', '', $id);
840 }
841 }
842