Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
wp-content
/
plugins
/
vikbooking
/
admin
/
helpers
:
admin_widgets.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * @package VikBooking * @subpackage com_vikbooking * @author Alessio Gaggii - e4j - Extensionsforjoomla.com * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE * @link https://vikwp.com */ defined('ABSPATH') or die('No script kiddies please!'); /** * Helper class for the administrator widgets. * * @since 1.14 (J) - 1.4.0 (WP) */ class VikBookingHelperAdminWidgets { /** * The singleton instance of the class. * * @var VikBookingHelperAdminWidgets */ protected static $instance = null; /** * An array to store some cached/static values. * * @var array */ protected static $helper = null; /** * The database handler instance. * * @var object */ protected $dbo; /** * The list of widget instances loaded. * * @var array */ protected $widgets; /** * The list of widget positions in the multitask panel. * * @var array * * @since 1.16.10 (J) - 1.6.10 (WP) */ protected $widgets_pos_list; /** * Class constructor is protected. * * @see getInstance() */ protected function __construct() { static::$helper = []; $this->dbo = JFactory::getDbo(); $this->widgets = []; $this->widgets_pos_list = (array) VBOFactory::getConfig()->getArray('multitasking_wpos', []); $this->load(); } /** * Returns the global object, either * a new instance or the existing instance * if the class was already instantiated. * * @return self A new instance of the class. */ public static function getInstance() { if (is_null(static::$instance)) { static::$instance = new static(); } return static::$instance; } /** * Loads a list of all available admin widgets. * * @return self */ protected function load() { /** @var VBOPlatformDispatcherInterface */ $dispatcher = VBOFactory::getPlatform()->getDispatcher(); // require main/parent admin-widget class require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'admin_widget.php'); $widgets_base = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'widgets' . DIRECTORY_SEPARATOR; $widgets_files = glob($widgets_base . '*.php'); $widgets_banned = []; /** * Trigger event to let other plugins register additional widgets. * * @return array A list of supported widgets. */ $list = $dispatcher->filter('onLoadAdminWidgets'); foreach ($list as $chunk) { // merge default widget files with the returned ones $widgets_files = array_merge($widgets_files, (array)$chunk); } /** * Trigger event to let other plugins unregister specific widgets. * * @return array A list of widget identifiers to unload. * * @since 1.16.0 (J) - 1.6.0 (WP) */ $unloaded = $dispatcher->filter('onUnloadAdminWidgets'); foreach ($unloaded as $chunk) { // merge all the the returned ones $widgets_banned = array_merge($widgets_banned, (array)$chunk); } // parse all admin widgets foreach ($widgets_files as $wf) { try { /** * Require widget class file only if available, to allow registering classes at * runtime by third-party plugins without needing to have a dedicated PHP file. * The widget class must obviously exist all the times, but the file must not. */ if (is_file($wf)) { require_once($wf); } // widget identifier $widget_identif = basename($wf, '.php'); // check if the widget was unloaded if (in_array($widget_identif, $widgets_banned)) { continue; } // build widget class name $classname = 'VikBookingAdminWidget' . str_replace(' ', '', ucwords(str_replace('_', ' ', $widget_identif))); if (class_exists($classname)) { // instantiate widget object $widget = new $classname(); /** * Call the widget's preflight method to see if it's compatible with the current environment. * * @since 1.16.10 (J) - 1.6.10 (WP) */ if (!$widget->preflight()) { continue; } // push the installed widget object $this->widgets[] = $widget; } } catch (Exception $e) { // do nothing } } return $this; } /** * Gets the default map of admin widgets. * * @return object the associative map of sections, * containers and widgets. */ protected function getDefaultWidgetsMap() { // sections container $sections = []; // build default sections // new section $section = new stdClass; $section->name = 'Top'; $section->containers = []; // start container $container = new stdClass; $container->size = 'medium'; $container->widgets = [ 'sticky_notes', 'arriving_today', 'departing_today', 'check_availability', 'latest_events', ]; // push container array_push($section->containers, $container); // start container $container = new stdClass; $container->size = 'small'; $container->widgets = [ 'guest_messages', 'latest_from_guests', 'booking_details', 'visitors_counter', ]; // push container array_push($section->containers, $container); // start container $container = new stdClass; $container->size = 'small'; $container->widgets = [ 'aitools', 'forecast', 'bookings_calendar', 'reminders', ]; // push container array_push($section->containers, $container); // push section array_push($sections, $section); // new section $section = new stdClass; $section->name = 'Middle'; $section->containers = []; // start container $container = new stdClass; $container->size = 'full'; $container->widgets = [ 'today_rooms_occupancy', 'weekly_bookings', ]; // push container array_push($section->containers, $container); // push section array_push($sections, $section); // new section $section = new stdClass; $section->name = 'Middle 2'; $section->containers = []; // start container $container = new stdClass; $container->size = 'small'; $container->widgets = [ 'latest_events', ]; // push container array_push($section->containers, $container); // start container $container = new stdClass; $container->size = 'small'; $container->widgets = [ 'rates_flow', ]; // push container array_push($section->containers, $container); // start container $container = new stdClass; $container->size = 'medium'; $container->widgets = [ 'finance', 'currency_converter', ]; // push container array_push($section->containers, $container); // push section array_push($sections, $section); // new section $section = new stdClass; $section->name = 'Bottom'; $section->containers = []; // start container $container = new stdClass; $container->size = 'medium'; $container->widgets = [ 'last_reservations', ]; // push container array_push($section->containers, $container); // start container $container = new stdClass; $container->size = 'medium'; $container->widgets = [ 'next_bookings', ]; // push container array_push($section->containers, $container); // push section array_push($sections, $section); // new section $section = new stdClass; $section->name = 'Bottom 2'; $section->containers = []; // start container $container = new stdClass; $container->size = 'medium'; $container->widgets = [ 'orphan_dates', 'rooms_locked', ]; // push container array_push($section->containers, $container); // start container $container = new stdClass; $container->size = 'medium'; $container->widgets = [ 'sticky_notes', ]; // push container array_push($section->containers, $container); // push section array_push($sections, $section); // compose the final map object $map = new stdClass; $map->sections = $sections; return $map; } /** * Gets the list of admin widgets instantiated. * * @return array list of admin widget objects. */ public function getWidgets() { return $this->widgets; } /** * Gets a single admin widget instantiated. * * @param string $id the widget identifier. * * @return mixed the admin widget object, false otherwise. */ public function getWidget($id) { $id = $this->simplifyId($id); foreach ($this->widgets as $widget) { if ($widget->getIdentifier() == $id) { return $widget; } } return false; } /** * Gets a list of sorted widget names, ids, descriptions, icons and styles. * If called with pre-loading, widgets will be able to load their CSS and JS * assets. Moreover, pre-loading a widget can return a watch-data object to * implement the notifications scheduling of a specific widget. * * @param bool $preload whether to call the widget's preload method. * * @return array associative and sorted widgets list. * * @since 1.15.0 (J) - 1.5.0 (WP) added support for icons and style names. * implemented widgets preloading for assets. */ public function getWidgetNames($preload = false) { // containers $names = []; $pool = []; $watch = []; foreach ($this->widgets as $widget) { // get widget details $id = $widget->getIdentifier(); $name = $widget->getName(); $descr = $widget->getDescription(); if ($preload) { // preload widget's CSS/JS assets (if any) $watch_data = $widget->preload(); if ($watch_data) { // assign watching data to this widget $watch[$id] = $watch_data; } } // build widget info object $wtdata = new stdClass; $wtdata->id = $id; $wtdata->name = $name; $wtdata->descr = $descr; $wtdata->icon = $widget->getIcon(); $wtdata->style = str_replace(array(' ', '_'), '-', $widget->getStyleName()); $wtdata->priority = $widget->getPriority(); // set object for sorting $names[$name] = $wtdata; } // apply sorting by name ksort($names); // apply sorting by priority usort($names, function($a, $b) { // the higher the priority is, the higher the position will be return $b->priority - $a->priority; }); if ($this->widgets_pos_list) { // get the custom positions list $widgets_pos_list = $this->widgets_pos_list; // apply custom sorting usort($names, function($a, $b) use ($widgets_pos_list) { $wa_id = $a->id; $wb_id = $b->id; if (($widgets_pos_list[$wa_id] ?? null) && ($widgets_pos_list[$wb_id] ?? null)) { // the higher the custom position is, the lower the sorting position will be return $widgets_pos_list[$wa_id] > $widgets_pos_list[$wb_id] ? 1 : -1; } return 0; }); } // push sorted widgets to pool foreach ($names as $wtdata) { $pool[] = $wtdata; } /** * In case some widgets were pre-loaded for watching certain * events, enqueue the watch-data list to the document. */ if ($watch) { // schedule the browser notifications watching data VBONotificationScheduler::getInstance()->registerWatchData($watch); } // return the list of admin widgets return $pool; } /** * Gets the current or default map of admin widgets. * If no map currently set, stores the default map. * * @return array the associative map of sections, * containers and widgets. */ public function getWidgetsMap() { $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='admin_widgets_map';"; $this->dbo->setQuery($q); $map = $this->dbo->loadResult(); if ($map) { $map = json_decode($map); return is_object($map) && !empty($map->sections) && is_array($map->sections) ? $map : $this->getDefaultWidgetsMap(); } $default_map = $this->getDefaultWidgetsMap(); $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('admin_widgets_map', " . $this->dbo->quote(json_encode($default_map)) . ");"; $this->dbo->setQuery($q); $this->dbo->execute(); return $default_map; } /** * Updates the map of admin widgets. * * @param array $sections the list of sections for the map. * * @return bool True on success, false otherwise. */ public function updateWidgetsMap($sections) { if (!is_array($sections) || !$sections) { return false; } // prepare new map object $map = new stdClass; $map->sections = $sections; $q = "UPDATE `#__vikbooking_config` SET `setting`=" . $this->dbo->quote(json_encode($map)) . " WHERE `param`='admin_widgets_map';"; $this->dbo->setQuery($q); $this->dbo->execute(); return true; } /** * Restores the default admin widgets map. * First it resets the settings of each widget. * * @return bool True on success, false otherwise. */ public function restoreDefaultWidgetsMap() { foreach ($this->widgets as $widget) { $widget->resetSettings(); } $default_map = $this->getDefaultWidgetsMap(); return $this->updateWidgetsMap($default_map->sections); } /** * Forces the rendering of a specific widget identifier. * * @param string $id the widget identifier. * @param ?VBOMultitaskData $data optional multitask data for the widget. * * @return void */ public function renderWidget($id, ?VBOMultitaskData $data = null) { $id = $this->simplifyId($id); foreach ($this->widgets as $widget) { if ($widget->getIdentifier() == $id) { // render the requested widget $widget->render($data); // return void return; } } } /** * Maps the size identifier to a CSS class. * * @param string $size the container size identifier. * * @return string the full CSS class for the container. */ public function getContainerCssClass($size) { $css_size_map = [ 'small' => 'vbo-admin-widgets-container-small', 'medium' => 'vbo-admin-widgets-container-medium', 'large' => 'vbo-admin-widgets-container-large', 'full' => 'vbo-admin-widgets-container-fullwidth', ]; return isset($css_size_map[$size]) ? $css_size_map[$size] : $css_size_map['full']; } /** * Returns an associative array with the class names for the containers. * * @return array a text representation list of all sizes. */ public function getContainerClassNames() { return array( 'full' => array( 'name' => JText::translate('VBO_WIDGETS_CONTFULL'), 'css' => $this->getContainerCssClass('full'), ), 'large' => array( 'name' => JText::translate('VBO_WIDGETS_CONTLARGE'), 'css' => $this->getContainerCssClass('large'), ), 'medium' => array( 'name' => JText::translate('VBO_WIDGETS_CONTMEDIUM'), 'css' => $this->getContainerCssClass('medium'), ), 'small' => array( 'name' => JText::translate('VBO_WIDGETS_CONTSMALL'), 'css' => $this->getContainerCssClass('small'), ), ); } /** * Maps the size identifier to the corresponding name. * * @param string $size the container size identifier. * * @return string the size name for the container. */ public function getContainerName($size) { $names = $this->getContainerClassNames(); return isset($names[$size]) ? $names[$size]['name'] : $names['full']['name']; } /** * Many widgets may need to know some values about the rooms. * This method uses the static instance of the class to cache data. * * @return void */ protected function loadRoomsData() { if (isset(static::$helper['all_rooms_ids'])) { // do not execute the same queries again return; } $all_rooms_ids = []; $all_rooms_units = []; $all_rooms_features = []; $unpublished_rooms = []; $tot_rooms_units = 0; $q = "SELECT `id`,`name`,`units`,`params`,`avail` FROM `#__vikbooking_rooms`;"; $this->dbo->setQuery($q); $all_rooms = $this->dbo->loadAssocList(); if ($all_rooms) { foreach ($all_rooms as $k => $r) { if ($r['avail'] < 1) { $unpublished_rooms[] = $r['id']; } else { $tot_rooms_units += $r['units']; } $all_rooms_ids[$r['id']] = $r['name']; $all_rooms_units[$r['id']] = $r['units']; $rparams = json_decode((string) $r['params'], true); $all_rooms_features[$r['id']] = is_array($rparams) && array_key_exists('features', $rparams) && is_array($rparams['features']) ? $rparams['features'] : []; } } // update static values static::$helper['all_rooms_ids'] = $all_rooms_ids; static::$helper['all_rooms_units'] = $all_rooms_units; static::$helper['all_rooms_features'] = $all_rooms_features; static::$helper['unpublished_rooms'] = $unpublished_rooms; static::$helper['tot_rooms_units'] = $tot_rooms_units; } /** * Many widgets could use this method to access cached information. * * @param string $key the data key identifier. * * @return mixed array/int on success, false otherwise. */ public function getRoomsData($key) { if (empty($key)) { return false; } if (!static::$helper) { $this->loadRoomsData(); } if (isset(static::$helper[$key])) { return static::$helper[$key]; } return false; } /** * Helper method to load all busy real records for all rooms for dates near today. * This is useful to avoid double queries in the various widgets and to get data * about yesterday, today and the next week. * * @return array * * @since 1.15.2 (J) - 1.5.5 (WP) changed the way limit timestamps are calculated. */ public function loadBusyRecordsUnclosed() { if (!isset(static::$helper['all_rooms_ids'])) { $this->loadRoomsData(); } if (isset(static::$helper['busy_records_unclosed'])) { return static::$helper['busy_records_unclosed']; } // cache value and return it $base_from_ts = mktime(0, 0, 0, date("n"), (date("j") - 1), date("Y")); $base_end_ts = mktime(23, 59, 59, date("n"), (date("j") + 7), date("Y")); static::$helper['busy_records_unclosed'] = VikBooking::loadBusyRecordsUnclosed(array_keys(static::$helper['all_rooms_ids']), $base_from_ts, $base_end_ts); return static::$helper['busy_records_unclosed']; } /** * The first time the widget's customizer is open, the welcome is displayed. * Congig value >= 1 means hide the welcome text, 0 or lower means show it. * * @return bool */ public function showWelcome() { $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='admin_widgets_welcome';"; $this->dbo->setQuery($q); $show_welcome = $this->dbo->loadResult(); if ($show_welcome !== null) { return ((int)$show_welcome < 1); } $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('admin_widgets_welcome', '0');"; $this->dbo->setQuery($q); $this->dbo->execute(); return true; } /** * Updates the status of the welcome message for the widget's customizer. * Congig value >= 1 means hide the welcome text, 0 or lower means show it. * * @param int $val the new value to set in the configuration. * * @return void */ public function updateWelcome($val) { $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='admin_widgets_welcome';"; $this->dbo->setQuery($q); $this->dbo->execute(); if ($this->dbo->getNumRows()) { $q = "UPDATE `#__vikbooking_config` SET `setting`=" . $this->dbo->quote((int)$val) . " WHERE `param`='admin_widgets_welcome';"; $this->dbo->setQuery($q); $this->dbo->execute(); return; } $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('admin_widgets_welcome', " . $this->dbo->quote((int)$val) . ");"; $this->dbo->setQuery($q); $this->dbo->execute(); return; } /** * Gets the default map for multitasking pages. * * @return object the associative map of widgets. */ protected function getDefaultMultitaskingMap() { $map = new stdClass; // booking details page $map->editorder = [ 'reminders', 'check_availability', 'bookings_calendar', ]; // booking modifying page $map->editbusy = [ 'bookings_calendar', 'check_availability', 'reminders', ]; // availability overview $map->overv = [ 'reminders', 'rates_flow', 'latest_events', ]; return $map; } /** * Gets the list of widgets assigned to the current page. * * @param string $page the name of the current View/task. * @param bool $whole whether to get the entire map object. * * @return array|object the list of widgets for the given page, * or entire map object if $whole is true. */ public function getMultitaskingMap($page = null, $whole = false) { // get saved map $saved_map = VBOFactory::getConfig()->get('multitasking_map'); if (!empty($saved_map)) { // try decoding the map data $saved_map = json_decode($saved_map); } if (!is_object($saved_map)) { // revert to the default map $saved_map = $this->getDefaultMultitaskingMap(); } if ($whole) { // do not proceed and return the entire map object return $saved_map; } if (empty($page) || !is_string($page)) { return []; } return isset($saved_map->$page) && is_array($saved_map->$page) ? $saved_map->$page : []; } /** * Updates the map of multitasking widgets. * * @param string $page the name of the current View/task. * @param array|string $widgets the list of or the widget(s) to set. * @param int $action 0 = replace, -1 = prepend, 1 = append. * * @return bool true on success, false otherwise. */ public function updateMultitaskingMap($page, $widgets, $action = 0) { if (empty($page) || !is_string($page)) { return false; } // check if we have one widget or a list $widgets = is_string($widgets) ? [$widgets] : $widgets; if (!is_array($widgets)) { return false; } // get the whole current map $map = $this->getMultitaskingMap($page, true); if (!isset($map->$page)) { $map->$page = []; } if ($action > 0) { // append widgets foreach ($widgets as $widget_id) { if (empty($widget_id)) { continue; } array_push($map->$page, $widget_id); } } elseif ($action < 0) { // prepend widgets foreach ($widgets as $widget_id) { if (empty($widget_id)) { continue; } array_unshift($map->$page, $widget_id); } } else { // replace current list when $action === 0 $map->$page = $widgets; } // update configuration record VBOFactory::getConfig()->set('multitasking_map', json_encode($map)); return true; } /** * Sets the position for an admin widget in the multitask panel. * * @param array $pos_list Associative list of widget positions. * * @return array The updated widgets position list. * * @since 1.16.10 (J) - 1.6.10 (WP) */ public function setMultitaskingWidgetPos($pos_list) { if (!is_array($pos_list)) { return false; } // turn all position values into integers $pos_list = array_map(function($pos) { return (int) $pos; }, $pos_list); // update configuration record VBOFactory::getConfig()->set('multitasking_wpos', $pos_list); return $pos_list; } /** * Admin widgets used to be named as the entire file name, * inclusive of the ".php" extension. For BC we make sure * to evaluate the admin widget identifiers correclty. * * @param string $id the widget identifier to simplify. * * @return string the simplified widget identifier. * * @since 1.15.0 (J) - 1.5.0 (WP) */ protected function simplifyId($id) { return str_replace('.php', '', $id); } }