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 | libraries/system/builder.php +751 -0 1.6.0 → 1.8.16 View file →
@@ -1,0 +1,751 @@
1 +<?php
2 +/**
3 + * @package VikBooking - Libraries
4 + * @subpackage system
5 + * @author E4J s.r.l.
6 + * @copyright Copyright (C) 2018 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 setup the plugin.
16 + *
17 + * @since 1.0
18 + */
19 +class VikBookingBuilder
20 +{
21 + /**
22 + * Loads the .mo language related to the current locale.
23 + *
24 + * @return void
25 + */
26 + public static function loadLanguage()
27 + {
28 + static $loaded = false;
29 +
30 + /**
31 + * Do not load the languages more than once.
32 + *
33 + * @since 1.8.4
34 + */
35 + if ($loaded)
36 + {
37 + return;
38 + }
39 +
40 + $loaded = true;
41 +
42 + $app = JFactory::getApplication();
43 +
44 + /**
45 + * @since 1.0.2 All the language files have been merged
46 + * within a single file to be compliant with
47 + * the Worpdress Translation Standards.
48 + * The language file is located in /languages folder.
49 + */
50 + $path = VIKBOOKING_LANG;
51 +
52 + $handler = VIKBOOKING_LIBRARIES . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
53 + $domain = 'vikbooking';
54 +
55 + // init language
56 + $lang = JFactory::getLanguage();
57 +
58 + $lang->attachHandler($handler . 'system.php', $domain);
59 +
60 + if ($app->isAdmin())
61 + {
62 + $lang->attachHandler($handler . 'adminsys.php', $domain);
63 + $lang->attachHandler($handler . 'admin.php', $domain);
64 + }
65 + else
66 + {
67 + $lang->attachHandler($handler . 'site.php', $domain);
68 + }
69 +
70 + $lang->load($domain, $path);
71 + }
72 +
73 + /**
74 + * Setup the pagination layout to use.
75 + *
76 + * @return void
77 + */
78 + public static function setupPaginationLayout()
79 + {
80 + $layout = new JLayoutFile('html.system.pagination', null, array('component' => 'com_vikbooking'));
81 +
82 + JLoader::import('adapter.pagination.pagination');
83 + JPagination::setLayout($layout);
84 + }
85 +
86 + /**
87 + * Pushes the plugin pages into the WP admin menu.
88 + *
89 + * @return void
90 + *
91 + * @link https://developer.wordpress.org/resource/dashicons/#star-filled
92 + */
93 + public static function setupAdminMenu()
94 + {
95 + JLoader::import('adapter.acl.access');
96 + $capability = JAccess::adjustCapability('core.manage', 'com_vikbooking');
97 +
98 + add_menu_page(
99 + JText::translate('COM_VIKBOOKING'), // page title
100 + JText::translate('COM_VIKBOOKING_MENU'), // menu title
101 + $capability, // capability
102 + 'vikbooking', // slug
103 + array('VikBookingBody', 'getHtml'), // callback
104 + 'dashicons-building', // icon
105 + 71 // ordering
106 + );
107 + }
108 +
109 + /**
110 + * Setup HTML helper classes.
111 + * This method should be used to register custom function
112 + * for example to render own layouts.
113 + *
114 + * @return void
115 + */
116 + public static function setupHtmlHelpers()
117 + {
118 + // helper method to render calendars layout
119 + JHtml::register('renderCalendar', function($data)
120 + {
121 + JHtml::fetch('script', VBO_SITE_URI . 'resources/jquery-ui.min.js');
122 + JHtml::fetch('stylesheet', VBO_SITE_URI . 'resources/jquery-ui.min.css');
123 +
124 + $layout = new JLayoutFile('html.plugins.calendar', null, array('component' => 'com_vikbooking'));
125 +
126 + return $layout->render($data);
127 + });
128 +
129 + // helper method to get the plugin layout file handler
130 + JHtml::register('layoutfile', function($layoutId, $basePath = null, $options = array())
131 + {
132 + $input = JFactory::getApplication()->input;
133 +
134 + if (!isset($options['component']) && !$input->getBool('option'))
135 + {
136 + // force layout file in case there is no active plugin
137 + $options['component'] = 'com_vikbooking';
138 + }
139 +
140 + return new JLayoutFile($layoutId, $basePath, $options);
141 + });
142 +
143 + // helper method to include the system JS file
144 + JHtml::register('system.js', function()
145 + {
146 + static $loaded = 0;
147 +
148 + if (!$loaded)
149 + {
150 + // include only once
151 + $loaded = 1;
152 +
153 + $internalFilesOptions = array('version' => VIKBOOKING_SOFTWARE_VERSION);
154 +
155 + JHtml::fetch('script', VBO_ADMIN_URI . 'resources/js/system.js', $internalFilesOptions, array('id' => 'vbo-sys-script'));
156 + JHtml::fetch('stylesheet', VBO_ADMIN_URI . 'resources/css/system.css', $internalFilesOptions, array('id' => 'vbo-sys-style'));
157 +
158 + /**
159 + * The CSS/JS files of Bootstrap may disturb the styles of the Theme, and so
160 + * we load it only within the back-end, or if the configuration setting is on.
161 + *
162 + * @since 1.3.0
163 + */
164 + if (JFactory::getApplication()->isAdmin() || (class_exists('VikBooking') && VikBooking::loadBootstrap()))
165 + {
166 + /**
167 + * Prior the version 1.3.5 the file bootstrap.min.js was always loaded above and outside
168 + * this IF statement. We now wrap all Bootstrap assets within the admin or setting enabled.
169 + *
170 + * @since 1.3.5
171 + */
172 + JHtml::fetch('script', VBO_ADMIN_URI . 'resources/js/bootstrap.min.js', $internalFilesOptions, array('id' => 'bootstrap-script'));
173 +
174 + JHtml::fetch('stylesheet', VBO_ADMIN_URI . 'resources/css/bootstrap.lite.css', $internalFilesOptions, array('id' => 'bootstrap-lite-style'));
175 + }
176 + }
177 + });
178 +
179 + // helper method to include the select2 JS file
180 + JHtml::register('select2', function()
181 + {
182 + /**
183 + * Select2 is now loaded only when requested.
184 + *
185 + * @since 1.2.5
186 + */
187 + JHtml::fetch('script', VBO_ADMIN_URI . 'resources/select2.min.js');
188 + JHtml::fetch('stylesheet', VBO_ADMIN_URI . 'resources/select2.min.css');
189 + });
190 +
191 + /**
192 + * Register helper methods to sanitize attributes, html, JS and other elements.
193 + */
194 + JHtml::register('esc_attr', function($str)
195 + {
196 + return esc_attr($str);
197 + });
198 +
199 + JHtml::register('esc_html', function($str)
200 + {
201 + return esc_html($str);
202 + });
203 +
204 + JHtml::register('esc_js', function($str)
205 + {
206 + return esc_js($str);
207 + });
208 +
209 + JHtml::register('esc_textarea', function($str)
210 + {
211 + return esc_textarea($str);
212 + });
213 +
214 + /**
215 + * Attempt to turn on the SQL_BIG_SELECTS setting at runtime, to avoid
216 + * SQL errors like "The SELECT would examine more than MAX_JOIN_SIZE rows;".
217 + * This has affected several clients with the Channel Manager for the Guest Messages
218 + * downloaded by OTAs like Booking.com or Airbnb. Since we do this operation at runtime,
219 + * we attempt to suppress the DB errors in case the user does not have enough permissions
220 + * to run queries of type "SET". Once executed, we restore the original value for DB errors.
221 + *
222 + * @since 1.16.0 (J) - 1.6.0 (WP)
223 + */
224 + add_action('plugins_loaded', function()
225 + {
226 + $dbo = JFactory::getDbo();
227 +
228 + // suppress temporarily any database error
229 + $dbo->suppress_errors(true);
230 +
231 + // turn on the required SQL setting
232 + $dbo->setQuery('SET SQL_BIG_SELECTS=1');
233 + $dbo->execute();
234 +
235 + // restore the default SQL display errors setting
236 + $dbo->suppress_errors(false);
237 + });
238 + }
239 +
240 + /**
241 + * This method is used to configure teh payments framework.
242 + * Here should be registered all the default gateways supported
243 + * by the plugin.
244 + *
245 + * @return void
246 + *
247 + * @since 1.0.5
248 + */
249 + public static function configurePaymentFramework()
250 + {
251 + // push the pre-installed gateways within the payment drivers list
252 + add_filter('get_supported_payments_vikbooking', function($drivers)
253 + {
254 + $list = glob(VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'payments' . DIRECTORY_SEPARATOR . '*.php');
255 +
256 + return array_merge($drivers, $list);
257 + });
258 +
259 + // load payment handlers when dispatched
260 + add_action('load_payment_gateway_vikbooking', function(&$drivers, $payment)
261 + {
262 + $classname = null;
263 +
264 + VikBookingLoader::import('admin.payments.' . $payment, VIKBOOKING_BASE);
265 +
266 + switch ($payment)
267 + {
268 + case 'paypal':
269 + $classname = 'VikBookingPayPalPayment';
270 + break;
271 +
272 + case 'paypal_checkout':
273 + $classname = 'VikBookingPayPalCheckoutPayment';
274 + break;
275 +
276 + case 'offline_credit_card':
277 + $classname = 'VikBookingOfflineCreditCardPayment';
278 + break;
279 +
280 + case 'bank_transfer':
281 + $classname = 'VikBookingBankTransferPayment';
282 + break;
283 + }
284 +
285 + if ($classname)
286 + {
287 + $drivers[] = $classname;
288 + }
289 + }, 10, 2);
290 +
291 + // manipulate response to be compliant with notifypayment task
292 + add_action('payment_after_validate_transaction_vikbooking', function(&$payment, &$status, &$response)
293 + {
294 + /**
295 + * Transaction property can be used to perform refunds, and it's collected
296 + * and returned during charge/capture transactions only.
297 + *
298 + * @since 1.4.0
299 + * @since 1.6.9 added support for "tot_fees".
300 + */
301 +
302 + // manipulate the response to be compliant with the old payment system
303 + $response = array(
304 + 'verified' => (int) $status->isVerified(),
305 + 'tot_paid' => $status->amount,
306 + 'tot_fees' => $status->fees,
307 + 'log' => $status->log,
308 + 'transaction' => $status->transaction,
309 + );
310 +
311 + if ($status->skip_email)
312 + {
313 + $response['skip_email'] = $status->skip_email;
314 + }
315 + }, 10, 3);
316 +
317 + // manipulate response to be compliant with dorefund task
318 + add_action('payment_after_refund_transaction_vikbooking', function(&$payment, &$status, &$response)
319 + {
320 + /**
321 + * Transactions of type refund need to unify the response
322 + * to be compliant with all platforms.
323 + *
324 + * @since 1.4.0
325 + */
326 +
327 + // manipulate the response to be compliant with the old payment system
328 + $response = array(
329 + 'verified' => (int) $status->isVerified(),
330 + 'tot_refunded' => $status->amount,
331 + 'log' => $status->log,
332 + );
333 + }, 10, 3);
334 +
335 + // manipulate response to be compliant with direct charge transaction
336 + add_action('payment_after_direct_charge_vikbooking', function($payment, $status, &$response)
337 + {
338 + /**
339 + * Transactions of type direct charge need to unify the response
340 + * to be compliant with all platforms.
341 + *
342 + * @since 1.6.4
343 + */
344 +
345 + // manipulate the response to be compliant with any platform
346 + $response = array(
347 + 'verified' => (int) $status->isVerified(),
348 + 'tot_paid' => $status->amount,
349 + 'log' => $status->log,
350 + 'transaction' => $status->transaction,
351 + );
352 + }, 10, 3);
353 +
354 + // manipulate response to be compliant with the off-session capturing requests
355 + add_action('payment_after_offsession_capture_vikbooking', function($payment, $status, &$response)
356 + {
357 + /**
358 + * Transaction property can be used to perform refunds, and it's collected
359 + * and returned during charge/capture transactions only.
360 + *
361 + * @since 1.8.0
362 + */
363 +
364 + // manipulate the response to be compliant with any platform
365 + $response = array(
366 + 'verified' => (int) $status->isVerified(),
367 + 'tot_paid' => $status->amount,
368 + 'tot_fees' => $status->fees ?? null,
369 + 'log' => $status->log ?? null,
370 + 'transaction' => $status->transaction ?? null,
371 + );
372 + }, 10, 3);
373 + }
374 +
375 + /**
376 + * Registers all the widget contained within the modules folder.
377 + *
378 + * @return void
379 + */
380 + public static function setupWidgets()
381 + {
382 + JLoader::import('adapter.module.factory');
383 +
384 + // load all the modules
385 + JModuleFactory::load(VIKBOOKING_BASE . DIRECTORY_SEPARATOR . 'modules');
386 +
387 + /**
388 + * Loads also the widgets to display within the
389 + * admin dashboard of WordPress.
390 + *
391 + * @since 1.3.9
392 + */
393 + add_action('wp_dashboard_setup', function()
394 + {
395 + JLoader::import('adapter.dashboard.admin');
396 +
397 + // set up folder containing the widget to load
398 + $path = VIKBOOKING_LIBRARIES . DIRECTORY_SEPARATOR . 'dashboard';
399 + // define the classname prefix
400 + $prefix = 'JDashboardWidgetVikBooking';
401 +
402 + try
403 + {
404 + // load and register widgets
405 + JDashboardAdmin::load($path, $prefix);
406 + }
407 + catch (Exception $e)
408 + {
409 + // silently suppress exception to avoid breaking the website
410 +
411 + if (VIKBOOKING_DEBUG)
412 + {
413 + // propagate error in case of debug enabled
414 + throw $e;
415 + }
416 + }
417 + });
418 + }
419 +
420 + /**
421 + * Configures the RSS feeds reader.
422 + *
423 + * @return JRssReader
424 + *
425 + * @since 1.3.9
426 + */
427 + public static function setupRssReader()
428 + {
429 + // autoload RSS handler class
430 + JLoader::import('adapter.rss.reader');
431 +
432 + /**
433 + * Hook used to manipulate the RSS channels to which the plugin is subscribed.
434 + *
435 + * @param array $channels A list of RSS permalinks.
436 + * @param boolean $status True to return only the published channels.
437 + *
438 + * @return array A list of supported channels.
439 + *
440 + * @since 1.3.9
441 + */
442 + $channels = apply_filters('vikbooking_fetch_rss_channels', array(), true);
443 +
444 + if (VIKBOOKING_DEBUG)
445 + {
446 + /**
447 + * Filters the transient lifetime of the feed cache.
448 + *
449 + * @since 2.8.0
450 + *
451 + * @param integer $lifetime Cache duration in seconds. Default is 43200 seconds (12 hours).
452 + * @param string $filename Unique identifier for the cache object.
453 + */
454 + add_filter('wp_feed_cache_transient_lifetime', function($time, $url) use ($channels)
455 + {
456 + // in case of debug enabled, cache the feeds only for 60 seconds
457 + if ($url == $channels || in_array($url, $channels))
458 + {
459 + $time = 60;
460 + }
461 +
462 + return $time;
463 + }, 10, 2);
464 + }
465 +
466 + // instantiate RSS reader
467 + $rss = JRssReader::getInstance($channels, 'vikbooking');
468 +
469 + /**
470 + * Hook used to apply some stuff before returning the RSS reader.
471 + *
472 + * @param JRssReader &$rss The RSS reader handler.
473 + *
474 + * @return void
475 + *
476 + * @since 1.3.9
477 + */
478 + do_action_ref_array('vikbooking_before_use_rss', array(&$rss));
479 +
480 + return $rss;
481 + }
482 +
483 + /**
484 + * Extends the backup framework.
485 + *
486 + * @return void
487 + *
488 + * @since 1.5
489 + */
490 + public static function setupBackupSystem()
491 + {
492 + /**
493 + * Anonymous function used to check whether the manifest includes the shortcodes import.
494 + *
495 + * @param object $manifest The backup manifest.
496 + *
497 + * @return boolean
498 + */
499 + $hasShortcodes = function($manifest)
500 + {
501 + // look for the uninstall directive inside the manifest, which is mainly
502 + // used during the sample data installation
503 + if (isset($manifest->uninstall))
504 + {
505 + // iterate all uninstall queries
506 + foreach ((array) $manifest->uninstall as $query)
507 + {
508 + // look for a table that uninstall the shortcodes
509 + if (preg_match("/#__vikbooking_wpshortcodes\b/", $query))
510 + {
511 + return true;
512 + }
513 + }
514 + }
515 +
516 + // look for the directive containing the installation rules
517 + if (isset($manifest->installers))
518 + {
519 + // iterate all install rules
520 + foreach ((array) $manifest->installers as $rule)
521 + {
522 + // detect SQL File role
523 + if ($rule->role === 'sqlfile')
524 + {
525 + // check shortcodes into the file path
526 + $target = [$rule->data->path];
527 + }
528 + else if ($rule->role === 'sql')
529 + {
530 + // search into all the provided queries
531 + $target = (array) $rule->data;
532 + }
533 + else
534 + {
535 + // nothing to check
536 + $target = [];
537 + }
538 +
539 + foreach ($target as $tmp)
540 + {
541 + // check whether the current target mentions the shortcodes database table
542 + if (preg_match("/#__vikbooking_wpshortcodes\b/", $tmp))
543 + {
544 + return true;
545 + }
546 + }
547 + }
548 + }
549 +
550 + return false;
551 + };
552 +
553 + /**
554 + * Trigger event to allow third party plugins to extend the backup import.
555 + * This hook triggers before processing the import of an existing backup.
556 + *
557 + * It is possible to throw an exception to prevent the import process.
558 + *
559 + * Uninstalls all the pages that have been assigned to the existing shortcodes.
560 + *
561 + * @param object $manifest The backup manifest.
562 + * @param string $path The path of the backup archive (uncompressed).
563 + *
564 + * @since 1.5
565 + *
566 + * @throws Exception
567 + */
568 + add_action('vikbooking_before_import_backup', function($manifest, $path) use ($hasShortcodes)
569 + {
570 + if (!function_exists('is_plugin_active')) {
571 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
572 + }
573 +
574 + /**
575 + * Abort the backup import process in case Rank Math SEO plugin is installed and active.
576 + *
577 + * @since 1.8
578 + */
579 + if (is_plugin_active('seo-by-rank-math/rank-math.php')) {
580 + throw new Exception('The <strong>Rank Math SEO</strong> plugin appears to be installed on your website. Before proceeding with the restoration, it is necessary to deactivate this plugin. Once the data installation is complete, you will be able to re-enable it.', 409);
581 + }
582 +
583 + // check whether the manifest includes the shortcodes installation
584 + $manifest->shortcodes = $hasShortcodes($manifest);
585 +
586 + if (empty($manifest->shortcodes))
587 + {
588 + // shortcodes not included within the backup, do not uninstall
589 + return;
590 + }
591 +
592 + // get shortcode admin model
593 + $model = JModel::getInstance('vikbooking', 'shortcodes', 'admin');
594 +
595 + // get all existing shortcodes
596 + $shortcodes = $model->all(array('createdon', 'post_id'));
597 +
598 + // iterate all shortcodes found
599 + foreach ($shortcodes as $shortcode)
600 + {
601 + // make sure the shortcode has been assigned to a post
602 + if ($shortcode->post_id)
603 + {
604 + // get post details
605 + $post = get_post((int) $shortcode->post_id);
606 +
607 + if (!$post)
608 + {
609 + continue;
610 + }
611 +
612 + // convert shortcode creation date
613 + $shortcode->createdon = new JDate($shortcode->createdon);
614 + // convert post creation date
615 + $post->post_date_gmt = new JDate($post->post_date_gmt);
616 +
617 + // compare ephocs and make sure the post was not created before the shortcode
618 + if ((int) $shortcode->createdon->format('U') <= (int) $post->post_date_gmt->format('U'))
619 + {
620 + // permanently delete post
621 + wp_delete_post($post->ID, $force_delete = true);
622 + }
623 + }
624 + }
625 + }, 10, 2);
626 +
627 + /**
628 + * Trigger event to allow third party plugins to extend the backup import.
629 + * This hook triggers after processing the import of an existing backup.
630 + *
631 + * It is possible to throw an exception to prevent the import process.
632 + *
633 + * Assigns all the newly created shortcodes to new pages.
634 + *
635 + * @param object $manifest The backup manifest.
636 + * @param string $path The path of the backup archive (uncompressed).
637 + *
638 + * @since 1.5
639 + *
640 + * @throws Exception
641 + */
642 + add_action('vikbooking_after_import_backup', function($manifest, $path)
643 + {
644 + if (!empty($manifest->shortcodes))
645 + {
646 + // get shortcodes admin model
647 + $listModel = JModel::getInstance('vikbooking', 'shortcodes', 'admin');
648 +
649 + // get all existing shortcodes
650 + $shortcodes = $listModel->all('id');
651 +
652 + // get shortcode admin model
653 + $model = JModel::getInstance('vikbooking', 'shortcode', 'admin');
654 +
655 + // iterate all shortcodes found
656 + foreach ($shortcodes as $shortcode)
657 + {
658 + // assign the shortcode to a new page
659 + $model->addPage($shortcode->id);
660 + }
661 + }
662 +
663 + // trigger full files mirroring
664 + VikBookingUpdateManager::triggerUploadFullMirroring();
665 + }, 10, 2);
666 +
667 + /**
668 + * Trigger event to allow third party plugins to choose what are the columns to dump
669 + * and whether the table should be skipped or not.
670 + *
671 + * Fires while attaching a rule to dump some SQL statements.
672 + *
673 + * Used to avoid dumping the post ID to which the shortcodes are attached
674 + *
675 + * @param boolean $include False to avoid including the table into the backup.
676 + * @param array &$columns An associative array of supported database table columns,
677 + * where the key is the column name and the value is a nested
678 + * array holding the column information.
679 + * @param string $table The name of the database table.
680 + *
681 + * @since 1.5
682 + */
683 + add_filter('vikbooking_before_backup_dump_sql', function($include, &$columns, $table)
684 + {
685 + if (is_null($include))
686 + {
687 + $include = true;
688 + }
689 +
690 + // check if we are exporting the shortcodes
691 + if ($table === '#__vikbooking_wpshortcodes')
692 + {
693 + // avoid dumping the post ID column
694 + unset($columns['post_id'], $columns['tmp_post_id']);
695 + }
696 +
697 + return $include;
698 + }, 10, 3);
699 + }
700 +
701 + /**
702 + * Implements the tools needed to manage the overrides
703 + * without having to use a FTP client.
704 + *
705 + * @return void
706 + *
707 + * @since 1.6.5
708 + */
709 + public static function setupOverridesManager()
710 + {
711 + /**
712 + * Trigger event to allow the plugins to include custom HTML within the view.
713 + * It is possible to return an associative array to group the HTML strings
714 + * under different fieldsets. Plain/html string will be always pushed within
715 + * the "custom" fieldset instead.
716 + *
717 + * Displays the overrides configuration.
718 + *
719 + * @param mixed $forms The HTML to display.
720 + * @param mixed $view The current view instance.
721 + *
722 + * @since 1.6.5
723 + */
724 + add_filter('vikbooking_display_view_config_global', function($forms, $view)
725 + {
726 + if (!$forms)
727 + {
728 + // init forms array
729 + $forms = [];
730 + }
731 +
732 + // render configuration layout
733 + $html = JLayoutHelper::render('html.overrides.config', [
734 + 'view' => $view,
735 + ]);
736 +
737 + // add fieldset to forms
738 + $forms[__('Page Overrides', 'vikbooking')] = $html;
739 +
740 + return $forms;
741 + }, 8, 2);
742 +
743 + /**
744 + * Hook used to display a list of breaking changes after completing an update of the plugin.
745 + * The message will be displayed only once within the dashboard of VikBooking.
746 + *
747 + * @since 1.6.5
748 + */
749 + add_action('vikbooking_before_display_dashboard', array('VikBookingInstaller', 'showBreakingChanges'));
750 + }
751 +}