| @@ -31,8 +31,17 @@ | ||
| 31 | 31 | */ |
| 32 | 32 | protected static $helper = null; |
| 33 | 33 | |
| 34 | 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 | + /** | |
| 35 | 44 | * The database handler instance. |
| 36 | 45 | * |
| 37 | 46 | * @var object |
| 38 | 47 | */ |
| @@ -45,8 +54,17 @@ | ||
| 45 | 54 | */ |
| 46 | 55 | protected $widgets; |
| 47 | 56 | |
| 48 | 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 | + /** | |
| 49 | 67 | * Class constructor is protected. |
| 50 | 68 | * |
| 51 | 69 | * @see getInstance() |
| 52 | 70 | */ |
| @@ -54,8 +72,9 @@ | ||
| 54 | 72 | { |
| 55 | 73 | static::$helper = []; |
| 56 | 74 | $this->dbo = JFactory::getDbo(); |
| 57 | 75 | $this->widgets = []; |
| 76 | + $this->widgets_pos_list = (array) VBOFactory::getConfig()->getArray('multitasking_wpos', []); | |
| 58 | 77 | $this->load(); |
| 59 | 78 | } |
| 60 | 79 | |
| 61 | 80 | /** |
| @@ -143,10 +162,19 @@ | ||
| 143 | 162 | if (class_exists($classname)) { |
| 144 | 163 | // instantiate widget object |
| 145 | 164 | $widget = new $classname(); |
| 146 | 165 | |
| 147 | - // push widget object | |
| 148 | - array_push($this->widgets, $widget); | |
| 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; | |
| 149 | 177 | } |
| 150 | 178 | } catch (Exception $e) { |
| 151 | 179 | // do nothing |
| 152 | 180 | } |
| @@ -198,8 +226,9 @@ | ||
| 198 | 226 | // start container |
| 199 | 227 | $container = new stdClass; |
| 200 | 228 | $container->size = 'small'; |
| 201 | 229 | $container->widgets = [ |
| 230 | + 'aitools', | |
| 202 | 231 | 'forecast', |
| 203 | 232 | 'bookings_calendar', |
| 204 | 233 | 'reminders', |
| 205 | 234 | ]; |
| @@ -248,8 +277,9 @@ | ||
| 248 | 277 | $container = new stdClass; |
| 249 | 278 | $container->size = 'medium'; |
| 250 | 279 | $container->widgets = [ |
| 251 | 280 | 'finance', |
| 281 | + 'inquiries_chat', | |
| 252 | 282 | 'currency_converter', |
| 253 | 283 | ]; |
| 254 | 284 | // push container |
| 255 | 285 | array_push($section->containers, $container); |
| @@ -367,30 +397,58 @@ | ||
| 367 | 397 | $descr = $widget->getDescription(); |
| 368 | 398 | |
| 369 | 399 | if ($preload) { |
| 370 | 400 | // preload widget's CSS/JS assets (if any) |
| 371 | - $watch_data = $widget->preload(); | |
| 401 | + $watch_data = array_key_exists($id, static::$preloaded) ? static::$preloaded[$id] : $widget->preload(); | |
| 402 | + | |
| 372 | 403 | if ($watch_data) { |
| 373 | 404 | // assign watching data to this widget |
| 374 | 405 | $watch[$id] = $watch_data; |
| 375 | 406 | } |
| 407 | + | |
| 408 | + // overwrite cache for the preloaded widget result | |
| 409 | + static::$preloaded[$id] = $watch_data; | |
| 376 | 410 | } |
| 377 | 411 | |
| 378 | 412 | // build widget info object |
| 379 | 413 | $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()); | |
| 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(); | |
| 385 | 420 | |
| 386 | 421 | // set object for sorting |
| 387 | - $names[$name] = $wtdata; | |
| 422 | + $names[$name] = $wtdata; | |
| 388 | 423 | } |
| 389 | 424 | |
| 390 | 425 | // apply sorting by name |
| 391 | 426 | ksort($names); |
| 392 | 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 | + | |
| 393 | 451 | // push sorted widgets to pool |
| 394 | 452 | foreach ($names as $wtdata) { |
| 395 | 453 | $pool[] = $wtdata; |
| 396 | 454 | } |
| @@ -398,9 +456,9 @@ | ||
| 398 | 456 | /** |
| 399 | 457 | * In case some widgets were pre-loaded for watching certain |
| 400 | 458 | * events, enqueue the watch-data list to the document. |
| 401 | 459 | */ |
| 402 | - if (count($watch)) { | |
| 460 | + if ($watch) { | |
| 403 | 461 | // schedule the browser notifications watching data |
| 404 | 462 | VBONotificationScheduler::getInstance()->registerWatchData($watch); |
| 405 | 463 | } |
| 406 | 464 | |
| @@ -416,11 +474,12 @@ | ||
| 416 | 474 | * containers and widgets. |
| 417 | 475 | */ |
| 418 | 476 | public function getWidgetsMap() |
| 419 | 477 | { |
| 420 | - $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='admin_widgets_map';"; | |
| 421 | - $this->dbo->setQuery($q); | |
| 422 | - $map = $this->dbo->loadResult(); | |
| 478 | + $config = VBOFactory::getConfig(); | |
| 479 | + | |
| 480 | + $map = $config->get('admin_widgets_map', null); | |
| 481 | + | |
| 423 | 482 | if ($map) { |
| 424 | 483 | $map = json_decode($map); |
| 425 | 484 | return is_object($map) && !empty($map->sections) && is_array($map->sections) ? $map : $this->getDefaultWidgetsMap(); |
| 426 | 485 | } |
| @@ -425,12 +484,11 @@ | ||
| 425 | 484 | return is_object($map) && !empty($map->sections) && is_array($map->sections) ? $map : $this->getDefaultWidgetsMap(); |
| 426 | 485 | } |
| 427 | 486 | |
| 428 | 487 | $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 | 488 | |
| 489 | + $config->set('admin_widgets_map', $default_map); | |
| 490 | + | |
| 433 | 491 | return $default_map; |
| 434 | 492 | } |
| 435 | 493 | |
| 436 | 494 | /** |
| @@ -441,9 +499,9 @@ | ||
| 441 | 499 | * @return bool True on success, false otherwise. |
| 442 | 500 | */ |
| 443 | 501 | public function updateWidgetsMap($sections) |
| 444 | 502 | { |
| 445 | - if (!is_array($sections) || !count($sections)) { | |
| 503 | + if (!is_array($sections) || !$sections) { | |
| 446 | 504 | return false; |
| 447 | 505 | } |
| 448 | 506 | |
| 449 | 507 | // prepare new map object |
| @@ -449,11 +507,9 @@ | ||
| 449 | 507 | // prepare new map object |
| 450 | 508 | $map = new stdClass; |
| 451 | 509 | $map->sections = $sections; |
| 452 | 510 | |
| 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(); | |
| 511 | + VBOFactory::getConfig()->set('admin_widgets_map', $map); | |
| 456 | 512 | |
| 457 | 513 | return true; |
| 458 | 514 | } |
| 459 | 515 | |
| @@ -477,13 +533,13 @@ | ||
| 477 | 533 | /** |
| 478 | 534 | * Forces the rendering of a specific widget identifier. |
| 479 | 535 | * |
| 480 | 536 | * @param string $id the widget identifier. |
| 481 | - * @param VBOMultitaskData $data optional multitask data for the widget. | |
| 537 | + * @param ?VBOMultitaskData $data optional multitask data for the widget. | |
| 482 | 538 | * |
| 483 | 539 | * @return void |
| 484 | 540 | */ |
| 485 | - public function renderWidget($id, VBOMultitaskData $data = null) | |
| 541 | + public function renderWidget($id, ?VBOMultitaskData $data = null) | |
| 486 | 542 | { |
| 487 | 543 | $id = $this->simplifyId($id); |
| 488 | 544 | |
| 489 | 545 | foreach ($this->widgets as $widget) { |
| @@ -587,9 +643,9 @@ | ||
| 587 | 643 | $tot_rooms_units += $r['units']; |
| 588 | 644 | } |
| 589 | 645 | $all_rooms_ids[$r['id']] = $r['name']; |
| 590 | 646 | $all_rooms_units[$r['id']] = $r['units']; |
| 591 | - $rparams = json_decode($r['params'], true); | |
| 647 | + $rparams = json_decode((string) $r['params'], true); | |
| 592 | 648 | $all_rooms_features[$r['id']] = is_array($rparams) && array_key_exists('features', $rparams) && is_array($rparams['features']) ? $rparams['features'] : []; |
| 593 | 649 | } |
| 594 | 650 | } |
| 595 | 651 | |
| @@ -613,9 +669,9 @@ | ||
| 613 | 669 | if (empty($key)) { |
| 614 | 670 | return false; |
| 615 | 671 | } |
| 616 | 672 | |
| 617 | - if (!count(static::$helper)) { | |
| 673 | + if (!static::$helper) { | |
| 618 | 674 | $this->loadRoomsData(); |
| 619 | 675 | } |
| 620 | 676 | |
| 621 | 677 | if (isset(static::$helper[$key])) { |
| @@ -820,8 +876,34 @@ | ||
| 820 | 876 | // update configuration record |
| 821 | 877 | VBOFactory::getConfig()->set('multitasking_map', json_encode($map)); |
| 822 | 878 | |
| 823 | 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; | |
| 824 | 906 | } |
| 825 | 907 | |
| 826 | 908 | /** |
| 827 | 909 | * Admin widgets used to be named as the entire file name, |