PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / vikappointments.php
vikappointments Last commit date
admin 1 month ago languages 4 months ago libraries 1 month ago media 1 month ago modules 1 month ago site 1 month ago sql 1 month ago LICENSE 6 years ago autoload.php 3 years ago changelog.md 1 month ago defines.php 1 month ago readme.txt 1 month ago vikappointments.php 1 month ago
vikappointments.php
977 lines
1 <?php
2 /*
3 Plugin Name: VikAppointments
4 Plugin URI: https://vikwp.com/plugin/vikappointments
5 Description: A professional tool for managing any kind of appointments.
6 Version: 1.2.19
7 Author: E4J s.r.l.
8 Author URI: https://vikwp.com
9 License: GPL2
10 License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 Text Domain: vikappointments
12 Domain Path: /languages
13 */
14
15 // No direct access
16 defined('ABSPATH') or die('No script kiddies please!');
17
18 // autoload dependencies
19 require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'autoload.php';
20
21 // handle install/uninstall
22 register_activation_hook(__FILE__, array('VikAppointmentsInstaller', 'activate'));
23 register_deactivation_hook(__FILE__, array('VikAppointmentsInstaller', 'deactivate'));
24 register_uninstall_hook(__FILE__, array('VikAppointmentsInstaller', 'delete'));
25
26 // init Installer
27 add_action('init', array('VikAppointmentsInstaller', 'update'));
28 add_action('init', array('VikAppointmentsInstaller', 'onInit'));
29
30 /**
31 * Fires after all automatic updates have run.
32 * Completes the update scheduled in background.
33 *
34 * @param array $results The results of all attempted updates.
35 *
36 * @since 1.1.11
37 */
38 add_action('automatic_updates_complete', array('VikAppointmentsInstaller', 'automaticUpdate'));
39
40 /**
41 * Filters whether to automatically update core, a plugin, a theme, or a language.
42 * Used to automatically turn off the update in case a PRO version expired.
43 *
44 * @param bool|null $update Whether to update. The value of null is internally used
45 * to detect whether nothing has hooked into this filter.
46 * @param object $item The update offer.
47 *
48 * @since 1.1.11
49 */
50 add_filter('auto_update_plugin', array('VikAppointmentsInstaller', 'useAutoUpdate'), 10, 2);
51
52 /**
53 * Fires at the end of the update message container in each
54 * row of the plugins list table.
55 *
56 * The dynamic portion of the hook name, `$file`, refers to the path
57 * of the plugin's primary file relative to the plugins directory.
58 *
59 * @link https://developer.wordpress.org/reference/hooks/in_plugin_update_message-file/
60 *
61 * @param array $data An array of plugin metadata.
62 * @param array $response An array of metadata about the available plugin update.
63 *
64 * @since 1.1.11
65 */
66 add_action('in_plugin_update_message-vikappointments/vikappointments.php', array('VikAppointmentsInstaller', 'getUpdateMessage'), 10, 2);
67
68 /**
69 * Hook used to display a list of breaking changes after completing an update of the plugin.
70 * The message will be displayed only once within the dashboard of VikAppointments.
71 *
72 * @since 1.2.13
73 */
74 add_action('vikappointments_before_display_vikappointments', array('VikAppointmentsInstaller', 'showBreakingChanges'));
75
76 /**
77 * Load plugin language only once all the plugins
78 * have been loaded, so that they are able to use
79 * the filters to extend the language functionalities.
80 *
81 * @since 1.1.6
82 * @since 1.2.18 Load language on init to prevent a WP warning.
83 */
84 add_action('init', array('VikAppointmentsBuilder', 'loadLanguage'));
85
86 // init html helpers
87 VikAppointmentsBuilder::setupHtmlHelpers();
88 // init payment framework
89 VikAppointmentsBuilder::configurePaymentFramework();
90 // init sms framework
91 VikAppointmentsBuilder::configureSmsFramework();
92 // init mirroring functions for extendable files
93 VikAppointmentsBuilder::setupMirroring();
94 // setup hooks to extend the backup functionalities
95 VikAppointmentsBuilder::setupBackupSystem();
96 // setup hooks to be used for the configuration of the wizard
97 VikAppointmentsBuilder::setupWizard();
98 // fix scripts loading via AJAX
99 VikAppointmentsAssets::fixAjax();
100
101 // setup plugin overrides management
102 add_action('plugins_loaded', array('VikAppointmentsBuilder', 'setupOverridesManager'));
103
104 // setup lite system
105 add_action('plugins_loaded', array('VikAppointmentsLiteManager', 'setup'));
106
107 // add support for Help tabs
108 add_action('current_screen', array('VikAppointmentsScreen', 'help'));
109 // add support for screen options
110 add_action('current_screen', array('VikAppointmentsScreen', 'options'));
111 // always attempt to save screen options
112 add_filter('set-screen-option', array('VikAppointmentsScreen', 'saveOption'), 10, 3);
113 /**
114 * Due to WordPress 5.4.2 changes, we need to attach
115 * VikAppointments to a dedicated hook in order to
116 * allow the update of the list limit.
117 *
118 * @since 1.1.7
119 */
120 add_filter('set_screen_option_vikappointments_list_limit', array('VikAppointmentsScreen', 'saveOption'), 10, 3);
121
122 // init Session
123 add_action('init', array('JSessionHandler', 'start'), 1);
124 add_action('wp_logout', array('JSessionHandler', 'destroy'));
125
126 // filter page link to rewrite URI
127 add_action('plugins_loaded', function()
128 {
129 global $pagenow;
130
131 $app = JFactory::getApplication();
132 $input = $app->input;
133
134 // check if the URI contains option=com_vikappointments
135 if ($input->get('option') == 'com_vikappointments')
136 {
137 // make sure we are not contacting the AJAX and POST end-points
138 if (!wp_doing_ajax() && $pagenow != 'admin-post.php')
139 {
140 /**
141 * Include page in query string only if we are in the back-end,
142 * because WordPress 5.5 seems to break the page loading in case
143 * that argument has been included in query string.
144 *
145 * It is not needed to include this argument in the front-end
146 * as the page should lean on the reached shortcode only.
147 *
148 * @since 1.1.7
149 */
150 if ($app->isAdmin())
151 {
152 // inject page=vikappointments in GET superglobal
153 $input->get->set('page', 'vikappointments');
154 }
155 }
156 else
157 {
158 // inject action=vikappointments in GET superglobal for AJAX and POST requests
159 $input->get->set('action', 'vikappointments');
160 }
161 }
162 else if ($input->get('page') == 'vikappointments' || $input->get('action') == 'vikappointments')
163 {
164 // inject option=com_vikappointments in GET superglobal for internal component detection
165 $input->get->set('option', 'com_vikappointments');
166 }
167 });
168
169 // process the request and obtain the response
170 add_action('init', function()
171 {
172 $app = JFactory::getApplication();
173 $input = $app->input;
174
175 /**
176 * Hook used to fetch the site pre-processing flag.
177 * When this flag is enabled, the plugin will try to dispatch the
178 * site controller within the "init" action. This is made by
179 * fetching the shortcode assigned to the current URI.
180 *
181 * By disabling this flag, the site controller will be dispatched
182 * with the headers already sent.
183 *
184 * @param boolean $preprocess The default preprocess flag.
185 *
186 * @since 1.1.10
187 */
188 $preprocess = apply_filters('vikappointments_site_preprocess', VIKAPPOINTMENTS_SITE_PREPROCESS);
189
190 // if we are in the front-end, try to parse the URL to inject
191 // option, view and args in the input request
192 if ($app->isSite() && $preprocess)
193 {
194 // get post ID from current URL
195 $id = url_to_postid(JUri::current());
196
197 if ($id)
198 {
199 // get shortcode admin model
200 $model = JModel::getInstance('vikappointments', 'shortcode', 'admin');
201 // get shortcode searching by post ID (false to avoid returning a new item)
202 $shortcode = $model->getItem(array('post_id' => $id), false);
203
204 if ($shortcode)
205 {
206 // build args array using the shortcode attributes
207 $args = (array) json_decode($shortcode->json, true);
208 $args['view'] = $shortcode->type;
209 $args['option'] = 'com_vikappointments';
210
211 // inject the shortcode args into the input request
212 foreach ($args as $k => $v)
213 {
214 // inject only if not defined
215 $input->def($k, $v);
216 }
217 }
218 }
219 }
220
221 /**
222 * Process VikAppointments only if it has been requested via GET or POST.
223 *
224 * The pre-process should occur only if we are in the back-end or in case
225 * the related flag is turned on, otherwise the pre-processing technique
226 * would still take effect for those URLs that own "com_vikappointments"
227 * set in request (@since 1.2).
228 */
229 if (($app->isAdmin() || $preprocess) && ($input->get('option') == 'com_vikappointments' || $input->get('page') == 'vikappointments'))
230 {
231 VikAppointmentsBody::process();
232 }
233 });
234
235 // handle AJAX requests for both logged and guest users
236 add_action('wp_ajax_vikappointments', 'handle_vikappointments_ajax');
237 add_action('wp_ajax_nopriv_vikappointments', 'handle_vikappointments_ajax');
238
239 /**
240 * Callback used to handle AJAX requests coming
241 * from both the front-end and back-end sections.
242 *
243 * @since 1.1.9
244 */
245 function handle_vikappointments_ajax()
246 {
247 // process controller request
248 VikAppointmentsBody::getHtml();
249
250 // die to get a valid response
251 wp_die();
252 }
253
254 // setup admin menu
255 add_action('admin_menu', array('VikAppointmentsBuilder', 'setupAdminMenu'));
256
257 // register widgets
258 add_action('widgets_init', array('VikAppointmentsBuilder', 'setupWidgets'));
259
260 /**
261 * Load plugin language when registering the widgets as well.
262 * Since translations are now loaded at "init", they might not be available yet.
263 *
264 * @since 1.3
265 */
266 add_action('widgets_init', ['VikAppointmentsBuilder', 'loadLanguage'], 1);
267
268 // handle shortcodes (SITE controller dispatcher)
269 add_shortcode('vikappointments', function($atts, $content = null)
270 {
271 $app = JFactory::getApplication();
272
273 /**
274 * Force the application client to "site" every time a shortcode is executed.
275 *
276 * @since 1.2.6
277 */
278 $app->setClient('site');
279
280 // wrap attributes in a registry
281 $args = new JObject($atts);
282
283 // get the VIEW (empty if not set)
284 $view = $args->get('view', '');
285
286 // load the FORM of the view
287 JLoader::import('adapter.form.form');
288 $path = implode(DIRECTORY_SEPARATOR, array(VAPBASE, 'views', $view, 'tmpl', 'default.xml'));
289 // raises an exception if the VIEW is not set
290 $form = JForm::getInstance($view, $path);
291
292 // get all the XML form fields
293 $fields = $form->getFields();
294
295 // filter the fields to get a list of allowed names
296 $fields = array_map(function($f)
297 {
298 return (string) $f->attributes()->name;
299 }, $fields);
300
301 // inject query vars
302 $input = $app->input;
303
304 // since we are going to render the controller manually,
305 // we need to push the option into $_REQUEST pool only
306 // whether it hasn't been added yet
307 $input->def('option', 'com_vikappointments');
308
309 // Inject shortcode vars only if they are not set
310 // in the request. This is used to allow the navigation
311 // between the pages.
312 $input->def('view', $view);
313
314 foreach ($fields as $k)
315 {
316 $input->def($k, $args->get($k));
317 }
318
319 /**
320 * When saving a shortcode block through Gutenberg,
321 * WordPress tries to reach the page to check what happens.
322 * The "confirmapp" view of VikAppointments, in case of
323 * no selected appointments, immediately redirects the
324 * users to the "serviceslist" page.
325 * This redirect breaks the request made by WordPress for the
326 * validation of the page, which follows the new location.
327 * The code below prevents the execution of the controller
328 * in case the URI matches the REST API end-point.
329 *
330 * @since 1.1.5
331 *
332 * The rest_get_url_prefix(), which should be equals to /wp-json is
333 * available only on websites with pretty permalinks enabled.
334 * On sites without pretty permalinks, the route is instead added
335 * to the URL as the rest_route parameter.
336 *
337 * @since 1.2.9
338 */
339 $rest_prefix = trailingslashit(rest_get_url_prefix());
340 $is_rest_api = strpos($input->server->getString('REQUEST_URI', ''), $rest_prefix) !== false
341 || JUri::getInstance($input->server->getString('REQUEST_URI', ''))->hasVar('rest_route')
342 || strpos($input->server->getString('REQUEST_URI', ''), '/wp-admin/') !== false;
343
344 if ($is_rest_api && in_array($input->get('view'), ['confirmapp']))
345 {
346 // return an empty string to prevent any redirects
347 return '';
348 }
349
350 // dispatch the controller
351 return VikAppointmentsBody::getHtml(true);
352 });
353
354 // the callback is fired before the VAP controller is dispatched
355 add_action('vikappointments_before_dispatch', function()
356 {
357 $app = JFactory::getApplication();
358 $user = Jfactory::getUser();
359
360 // initialize timezone handler
361 JDate::getDefaultTimezone();
362 // date_default_timezone_set($app->get('offset', 'UTC'));
363
364 // check if the user is authorised to access the back-end (only if the client is 'admin')
365 if ($app->isAdmin() && !$user->authorise('core.manage', 'com_vikappointments'))
366 {
367 if ($user->guest)
368 {
369 // if the user is not logged, redirect to login page
370 $app->redirect('index.php');
371 exit;
372 }
373 else
374 {
375 // otherwise raise an exception
376 wp_die(
377 '<h1>' . JText::translate('FATAL_ERROR') . '</h1>' .
378 '<p>' . JText::translate('RESOURCE_AUTH_ERROR') . '</p>',
379 403
380 );
381 }
382 }
383
384 if ($app->isAdmin())
385 {
386 // require helper files
387 require_once implode(DIRECTORY_SEPARATOR, [VAPADMIN, 'helpers', 'vikappointments.php']);
388
389 // remove expired credit cards
390 AppointmentsHelper::removeExpiredCreditCards();
391 }
392
393 /**
394 * Prevent the confirmation pages from performing an auto-redirect when running
395 * the Bricks plugin preview.
396 *
397 * @since 1.2.16
398 */
399 if ($app->input->get('bricks') === 'run' && in_array($app->input->get('view'), ['confirmapp']))
400 {
401 $app->input->set('view', '');
402 }
403 });
404
405 // the callback is fired before displaying DASHBOARD view
406 add_action('vikappointments_before_display_vikappointments', function()
407 {
408 $app = JFactory::getApplication();
409 $user = JFactory::getUser();
410
411 // make sure we are not doing AJAX, we are in the back-end and the user is an administrator
412 if (!wp_doing_ajax() && $app->isClient('administrator') && $user->authorise('core.admin', 'com_vikappointments'))
413 {
414 // push shortcodes button within the toolbar
415 JToolbarHelper::shortcodes('com_vikappointments');
416 }
417 });
418
419 /**
420 * Before displaying the Calendar view, check if we should
421 * introduce the toolbar button used to manage the ACL and
422 * the shortcode of the program.
423 *
424 * @since 1.1.5
425 */
426 add_action('vikappointments_before_display_calendar', function()
427 {
428 $app = JFactory::getApplication();
429 $user = JFactory::getUser();
430 $config = VAPFactory::getConfig();
431
432 // if we are not doing AJAX, push shortcodes button within the toolbar
433 if (!wp_doing_ajax() && $config->get('deftask') == 'calendar' && $app->isClient('administrator') && $user->authorise('core.admin', 'com_vikappointments'))
434 {
435 // add support for shortcodes management
436 JToolbarHelper::shortcodes('com_vikappointments');
437 // add support for ACL management
438 JToolBarHelper::preferences('com_vikappointments');
439 }
440 });
441
442 // instead using the default server timezone, try to use the one
443 // specified within the WordPress configuration
444 add_filter('vik_date_default_timezone', function($timezone)
445 {
446 // return JFactory::getApplication()->get('offset', $timezone);
447 return $timezone;
448 });
449
450 // the callback is fired once the VAP controller has been dispatched
451 add_action('vikappointments_after_dispatch', function()
452 {
453 // load assets after dispatching the controller to avoid
454 // including JS and CSS when an AJAX function exits or dies
455 VikAppointmentsAssets::load();
456
457 // load javascript core
458 JHtml::fetch('behavior.core');
459
460 // restore standard timezone
461 // date_default_timezone_set(JDate::getDefaultTimezone());
462
463 /**
464 * WordPress has some reserved values that shouldn't be used
465 * in query string or within the forms, otherwise they could
466 * be used to rewrite the URLs of the website.
467 *
468 * In example, by using the 'day' argument in query string,
469 * WordPress will start searching for POSTS that were created
470 * on the specified day (of the month), completely ignoring
471 * whether the current URL is used by a shortcode.
472 *
473 * For this reason, within the site section of VikAppointments,
474 * we should unset all the reserved arguments from the superglobals
475 * once the plugin finished using them.
476 *
477 * @link https://codex.wordpress.org/WordPress_Query_Vars
478 */
479
480 $app = JFactory::getApplication();
481
482 if ($app->isSite())
483 {
484 // define here the list of all the reserved arguments
485 // that are used by VikAppointments
486 $reserved_args_for_date_query = array(
487 // reserved by WordPress
488 'year',
489 'day',
490 'hour',
491 // reserved by third-party plugins
492 'people',
493 );
494
495 foreach ($reserved_args_for_date_query as $arg)
496 {
497 // unset argument from REQUEST
498 $app->input->delete($arg);
499 // unset argument from GET
500 $app->input->get->delete($arg);
501 // unset argument from POST
502 $app->input->post->delete($arg);
503 }
504 }
505
506 /**
507 * When the headers have been sent or when the request is AJAX
508 * the assets (CSS and JS) are appended into the document after
509 * the response is dispatched by the controller.
510 * Obviously only in case the controller doesn't manually exit.
511 */
512 });
513
514 // End-point for front-end post actions.
515 // The end-point URL must be built as .../wp-admin/admin-post.php
516 // and requires $_POST['action'] == 'vikappointments' to be submitted through a form or GET.
517 add_action('admin_post_vikappointments', 'handle_vikappointments_endpoint'); // if the user is logged in
518 add_action('admin_post_nopriv_vikappointments', 'handle_vikappointments_endpoint'); // if the user in not logged in
519
520 // handle POST end-point
521 function handle_vikappointments_endpoint()
522 {
523 // get PLAIN response
524 echo VikAppointmentsBody::getResponse();
525 }
526
527 // Hook used to access the PAGE details when a user is
528 // creating or updating it. This is helpful to make a relation
529 // between the page and the injected shortcode.
530 add_action('save_post', function($post_id)
531 {
532 // get model to access all the existing shortcodes
533 $model = JModel::getInstance('vikappointments', 'shortcodes', 'admin');
534 $shortcodes = $model->all(array('id', 'shortcode', 'post_id'));
535
536 // get post data
537 $post = get_post($post_id);
538
539 /**
540 * Added support for private posts and future schedules.
541 *
542 * @since 1.1.2
543 */
544 $accepted = array(
545 'publish',
546 'private',
547 'future',
548 );
549
550 /**
551 * Check if we are editing a child post as Gutenberg
552 * seems to use always the inherit status, which
553 * refers to a post parent.
554 */
555 if (!in_array($post->post_status, $accepted) && !empty($post->post_parent) && $post->post_parent != $post_id)
556 {
557 // fallback to obtain parent post data
558 $post = get_post($post->post_parent);
559
560 /**
561 * Use new post ID.
562 *
563 * @since 1.1 Fixed post ID property name.
564 */
565 $post_id = $post->ID;
566 }
567
568 if (!in_array($post->post_status, $accepted))
569 {
570 // ignore drafts auto-save
571 return;
572 }
573
574 // get shortcode model
575 $shortcodeModel = JModel::getInstance('vikappointments', 'shortcode', 'admin');
576
577 /**
578 * Since we need unique post IDs, all the shortcodes
579 * that are assigned to the specified $post_id should
580 * be detached.
581 */
582 foreach ($shortcodes as $data)
583 {
584 if ($data->post_id == $post_id)
585 {
586 // The post is already assigned to a shortcode.
587 // Unset it to avoid duplicates.
588 $data->post_id = 0;
589 $shortcodeModel->save($data);
590 }
591 }
592
593 // iterate the shortcodes
594 foreach ($shortcodes as $data)
595 {
596 // check if the content of the post contains the shortcode
597 // if (strpos($post->post_content, html_entity_decode($data->shortcode)) !== false)
598 if ($shortcodeModel->postContainsShortcode($post, $data->shortcode))
599 {
600 // inject the POST ID
601 $data->post_id = $post_id;
602
603 // update shortcode
604 $shortcodeModel->save($data);
605
606 // stop iterating
607 return;
608 }
609 }
610 });
611
612 // Hook used to unset temporarily the relationship
613 // between the trashed post and the shortcode.
614 add_action('trashed_post', function($post_id)
615 {
616 // get shortcode model
617 $model = JModel::getInstance('vikappointments', 'shortcode', 'admin');
618
619 // get the shortcode attached to the trashed post ID
620 $item = $model->getItem(array('post_id' => $post_id), false);
621
622 // if the item exists, temporarily detach the relationship
623 if ($item)
624 {
625 $item->post_id = 0;
626 $item->tmp_post_id = $post_id;
627
628 $model->save($item);
629 }
630 });
631
632 // Hook used to restore permanently the relationship
633 // between the untrashed post and the shortcode.
634 add_action('untrashed_post', function($post_id)
635 {
636 // get shortcode model
637 $model = JModel::getInstance('vikappointments', 'shortcode', 'admin');
638
639 // get the shortcode attached to the untrashed post ID
640 $item = $model->getItem(array('tmp_post_id' => $post_id), false);
641
642 // if the item exists, re-attach the relationship
643 if ($item)
644 {
645 $item->post_id = $post_id;
646 $item->tmp_post_id = 0;
647
648 $model->save($item);
649 }
650 });
651
652 // Hook used to temporarily detach the relationship
653 // between the deleted post and the shortcode.
654 add_action('deleted_post', function($post_id)
655 {
656 // get shortcode model
657 $model = JModel::getInstance('vikappointments', 'shortcode', 'admin');
658
659 // get the shortcode attached to the trashed post ID
660 $item = $model->getItem(array('tmp_post_id' => $post_id), false);
661
662 // If no item found, the "trash" feature is probably disabled.
663 // Try to take a look for a shortcode with an active relationship.
664 if (!$item)
665 {
666 $item = $model->getItem(array('post_id' => $post_id), false);
667 }
668
669 // if the item exists, permanently detach the relationship
670 if ($item)
671 {
672 $item->post_id = 0;
673 $item->tmp_post_id = 0;
674
675 $model->save($item);
676 }
677 });
678
679 if (JFactory::getApplication()->isAdmin() && !wp_doing_ajax())
680 {
681 VikAppointmentsLoader::import('system.mce');
682
683 // add new buttons
684 add_filter('mce_buttons', ['VikAppointmentsTinyMCE', 'addShortcodesButton']);
685
686 // load the button handlers
687 add_filter('mce_external_plugins', ['VikAppointmentsTinyMCE', 'registerShortcodesScript']);
688 }
689
690 /**
691 * Always load the Gutenberg shortcodes block to support the preview rendering.
692 *
693 * @since 1.2.12
694 */
695 VikAppointmentsLoader::import('system.gutenberg');
696 add_action('init', ['VikAppointmentsGutenberg', 'registerShortcodesScript']);
697
698 /**
699 * Dispatch the uninstallation of VikAppointments
700 * every time a new blog (multisite) is deleted.
701 *
702 * Fires after the site is deleted from the network (WP 4.8.0 or higher).
703 *
704 * @param integer $blog_id The site ID.
705 * @param boolean $drop True if site's tables should be dropped. Default is false.
706 */
707 add_action('deleted_blog', function($blog_id, $drop)
708 {
709 VikAppointmentsInstaller::uninstall($drop);
710 }, 10, 2);
711
712 /**
713 * Suppress WP Date Query warnings when visiting the views of VikAppointments as
714 * they might use reserved arguments in query string with wrong values, such
715 * as 'day' with UNIX timestamps.
716 *
717 * @param boolean $trigger Whether to trigger the error for _doing_it_wrong() calls. Default true.
718 * @param string $function The function that was called.
719 * @param string $message A message explaining what has been done incorrectly.
720 * @param string $version The version of WordPress where the message was added.
721 */
722 add_filter('doing_it_wrong_trigger_error', function($show, $function, $message, $version)
723 {
724 $input = JFactory::getApplication()->input;
725
726 // suppress any WP_Date_Query error messages in VikAppointments
727 if ($function == 'WP_Date_Query' && $input->get('option') == 'com_vikappointments')
728 {
729 // suppress the error
730 return false;
731 }
732
733 // keep the current value otherwise
734 return $show;
735 }, 10, 4);
736
737 /**
738 * Once the plugins have been loaded, evaluates to execute the
739 * scheduled cron jobs.
740 *
741 * Scheduling is processed in case a cron job is hitting wp-cron file
742 * or in case a user is visiting the website.
743 *
744 * @since 1.2.3 Schedules a different hook for each cron.
745 * @since 1.2.11 Extremely decreased the priority to make sure any other plugin ended
746 * the registration of custom schedule intervals.
747 */
748 add_action('plugins_loaded', array('VikAppointmentsCron', 'setup'), PHP_INT_MAX);
749
750 /**
751 * Filters the action links displayed for each plugin in the Plugins list table.
752 * Hook used to filter the "deactivation" link and ask a feedback every time that
753 * button is clicked.
754 *
755 * @param array $actions An array of plugin action links. By default this can include 'activate',
756 * 'deactivate', and 'delete'. With Multisite active this can also include
757 * 'network_active' and 'network_only' items.
758 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
759 * @param array $plugin_data An array of plugin data. See `get_plugin_data()`.
760 * @param string $context The plugin context. By default this can include 'all', 'active', 'inactive',
761 * 'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
762 *
763 * @since 1.1.2
764 */
765 add_filter('plugin_action_links', array('VikAppointmentsFeedback', 'deactivate'), 10, 4);
766
767 /**
768 * Adjusts the timezone of the website before dispatching
769 * a widget as we are currently outside of the main plugin and
770 * the timezone have probably been restored to the default one.
771 *
772 * @param string $id The widget ID (path name).
773 * @param JObject &$params The widget configuration registry.
774 *
775 * @since 1.1.3
776 */
777 add_action('vik_widget_before_dispatch_site', function($id, &$params)
778 {
779 // initialize timezone handler
780 JDate::getDefaultTimezone();
781 // date_default_timezone_set(JFactory::getApplication()->get('offset', 'UTC'));
782 }, 10, 2);
783
784 /**
785 * Restores the timezone of the website after dispatching
786 * a widget in order to avoid strange behaviors with other plugins.
787 *
788 * @param string $id The widget ID (path name).
789 * @param string &$html The HTML of the widget to display.
790 *
791 * @since 1.1.3
792 */
793 add_action('vik_widget_after_dispatch_site', function($id, &$html)
794 {
795 // restore standard timezone
796 // date_default_timezone_set(JDate::getDefaultTimezone());
797 }, 10, 2);
798
799 /**
800 * Action triggered before loading the text domain.
801 * Loads the language handlers when needed from a
802 * different application client.
803 *
804 * @param string $domain The plugin text domain to look for.
805 * @param string $basePath The base path containing the languages.
806 * @param mixed $langtag An optional language tag to use.
807 *
808 * @since 1.2
809 */
810 add_action('vik_plugin_before_load_language', function($domain, $basePath, $langtag)
811 {
812 if ($domain != 'vikappointments')
813 {
814 // do not go ahead
815 return;
816 }
817
818 $app = JFactory::getApplication();
819 $lang = JFactory::getLanguage();
820
821 $handler = VIKAPPOINTMENTS_LIBRARIES . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
822
823 // check if we are in the site client and the system
824 // needs to load the language used in the back-end
825 if ($app->isSite() && $basePath == JPATH_ADMINISTRATOR)
826 {
827 // load back-end language handler
828 $lang->attachHandler($handler . 'admin.php', $domain);
829 }
830 // check if we are in the admin client and the system
831 // needs to load the language used in the front-end
832 else if ($app->isAdmin() && $basePath == JPATH_SITE)
833 {
834 // load front-end language handler
835 $lang->attachHandler($handler . 'site.php', $domain);
836 }
837 }, 10, 3);
838
839 /**
840 * Added support for Loco Translate.
841 * In case some translations have been edited by using this plugin,
842 * we should look within the Loco Translate folder to check whether
843 * the requested translation is available.
844 *
845 * @param boolean $loaded True if the translation has been already loaded.
846 * @param string $domain The plugin text domain to load.
847 *
848 * @return boolean True if a new translation is loaded.
849 *
850 * @since 1.1.6
851 */
852 add_filter('vik_plugin_load_language', function($loaded, $domain)
853 {
854 // proceed only in case the translation hasn't been loaded
855 // and Loco Translate plugin is installed
856 if (!$loaded && is_dir(WP_LANG_DIR . DIRECTORY_SEPARATOR . 'loco'))
857 {
858 // Build LOCO path.
859 // Since load_plugin_textdomain accepts only relative paths,
860 // we should go back to the /wp-contents/ folder first.
861 $loco = implode(DIRECTORY_SEPARATOR, array('..', 'languages', 'loco', 'plugins'));
862
863 // try to load the plugin translation from Loco folder
864 $loaded = load_plugin_textdomain($domain, false, $loco);
865 }
866
867 return $loaded;
868 }, 10, 2);
869
870 /**
871 * Downloads the RSS feeds after loading the dashboard of VikAppointments.
872 *
873 * @since 1.1.9
874 */
875 add_action('vikappointments_after_display_vikappointments', array('VikAppointmentsRssFeeds', 'download'));
876
877 /**
878 * Trigger event to allow the plugins to include custom HTML within the view.
879 * It is possible to return an associative array to group the HTML strings
880 * under different fieldsets. Plain/html string will be always pushed within
881 * the "custom" fieldset instead.
882 *
883 * Displays the RSS configuration.
884 *
885 * @param mixed $forms The HTML to display.
886 * @param mixed $view The current view instance.
887 *
888 * @return mixed The HTML to display.
889 *
890 * @since 1.1.9
891 */
892 add_filter('vikappointments_display_view_config_global', array('VikAppointmentsRssFeeds', 'config'), 10, 2);
893
894 /**
895 * Trigger event to allow the plugins to make something after saving
896 * a record in the database.
897 *
898 * @param mixed $dummy A dummy argument for BC.
899 * @param array $args The saved record.
900 *
901 * @since 1.1.9
902 */
903 add_action('vikappointments_after_save_config', array('VikAppointmentsRssFeeds', 'save'), 10, 2);
904
905 /**
906 * Hook used to manipulate the RSS channels to which the plugin is subscribed.
907 *
908 * @param array $channels A list of RSS permalinks.
909 * @param boolean $status True to return only the published channels.
910 *
911 * @since 1.1.9
912 */
913 add_filter('vikappointments_fetch_rss_channels', array('VikAppointmentsRssFeeds', 'getChannels'), 10, 2);
914
915 /**
916 * Hook used to apply some stuff before returning the RSS reader.
917 *
918 * @param JRssReader &$rss The RSS reader handler.
919 *
920 * @since 1.1.9
921 */
922 add_action('vikappointments_before_use_rss', array('VikAppointmentsRssFeeds', 'ready'));
923
924 /**
925 * Fixed the issue with wptexturize() function, which might convert special characters contained
926 * within <script> tags into an HTML-encoded version (e.g. "&" became "&#038;").
927 *
928 * Short-circuit the usage of wptexturize for the whole front-end.
929 *
930 * @param bool $run Whether to short-circuit wptexturize().
931 *
932 * @since 1.2.13
933 */
934 add_filter('run_wptexturize', function($run)
935 {
936 return is_admin() ? $run : false;
937 }, PHP_INT_MAX);
938
939 /**
940 * Filters the login page errors.
941 *
942 * Hijack the default redirect to the native WP login page.
943 *
944 * @since 1.2.18
945 *
946 * @param WP_Error $error WP Error object.
947 * @param string $redirectUrl Redirect destination URL.
948 */
949 add_filter('wp_login_errors', function($error, $redirectUrl)
950 {
951 $app = JFactory::getApplication();
952
953 // make sure the login process started from VikAppointments
954 if (strcasecmp($app->input->getString('referer', ''), 'vikappointments') || !$redirectUrl)
955 {
956 // login started by someone else
957 return $error;
958 }
959
960 if (is_wp_error($error))
961 {
962 foreach ($error->get_error_messages() as $errorMessage)
963 {
964 // custom error set, enqueue the message for the user
965 $app->enqueueMessage($errorMessage, 'error');
966 }
967 }
968
969 // attach an argument in query string to easily identify an error
970 $redirectUrl = new JUri($redirectUrl);
971 $redirectUrl->setVar('login_failed', 1);
972
973 // hijack default behavior by redirecting the user to our custom page
974 $app->redirect((string) $redirectUrl);
975 $app->close();
976 }, 10, 2);
977