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