PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / helpers / libraries / adapter / platform / wordpress.php
vikappointments / site / helpers / libraries / adapter / platform Last commit date
index.html 5 days ago wordpress.php 5 days ago
wordpress.php
1513 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 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 // this should be already loaded from autoload.php
15 VAPLoader::import('libraries.adapter.version.listener');
16
17 /**
18 * Helper class used to adapt the application to the requirements
19 * of the installed WordPress version.
20 *
21 * @see VersionListener Used to evaluate the current WordPress version.
22 *
23 * @since 1.6.3
24 * @since 1.7 Renamed from UIApplicationWordpress
25 */
26 class VAPApplicationWordpress extends VAPApplication
27 {
28 /**
29 * Backward compatibility for WordPress admin list <table> class.
30 *
31 * @return string The class selector to use.
32 */
33 public function getAdminTableClass()
34 {
35 return 'wp-list-table widefat striped';
36 }
37
38 /**
39 * Backward compatibility for WordPress admin list <table> head opening.
40 *
41 * @return string The <thead> tag to use.
42 */
43 public function openTableHead()
44 {
45 return '<thead>';
46 }
47
48 /**
49 * Backward compatibility for WordPress admin list <table> head closing.
50 *
51 * @return string The </thead> tag to use.
52 */
53 public function closeTableHead()
54 {
55 return '</thead>';
56 }
57
58 /**
59 * Backward compatibility for WordPress admin list <th> class.
60 *
61 * @param string $align The additional class to use for horizontal alignment.
62 * Accepted rules should be: left, center or right.
63 *
64 * @return string The class selector to use.
65 */
66 public function getAdminThClass($align = 'center')
67 {
68 return 'manage-column ' . $align;
69 }
70
71 /**
72 * Backward compatibility for WordPress admin list checkAll JS event.
73 *
74 * @param integer The total count of rows in the table.
75 *
76 * @return string The check all checkbox input to use.
77 */
78 public function getAdminToggle($count)
79 {
80 return '<input type="checkbox" onclick="Joomla.checkAll(this)" value="" name="checkall-toggle" />';
81 }
82
83 /**
84 * Backward compatibility for WordPress admin list isChecked JS event.
85 *
86 * @return string The JS function to use.
87 */
88 public function checkboxOnClick()
89 {
90 return 'Joomla.isChecked(this.checked);';
91 }
92
93 /**
94 * Helper method to send e-mails.
95 *
96 * @param string $from_address The e-mail address of the sender.
97 * @param string $from_name The name of the sender.
98 * @param string $to The e-mail address of the receiver.
99 * @param string $reply_address The reply to e-mail address.
100 * @param string $subject The subject of the e-mail.
101 * @param string $hmess The body of the e-mail (HTML is supported).
102 * @param array $attachments The list of the attachments to include.
103 * @param boolean $is_html True to support HTML body, otherwise false for plain text.
104 * @param string $encoding The encoding to use.
105 *
106 * @return boolean True if the e-mail was sent successfully, otherwise false.
107 */
108 public function sendMail($from_address, $from_name, $to, $reply_address, $subject, $hmess, $attachments = null, $is_html = true, $encoding = 'base64')
109 {
110 $mailer = JFactory::getMailer();
111
112 if ($is_html)
113 {
114 $hmess = "<html>\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"></head>\n<body>$hmess</body>\n</html>";
115 }
116
117 $sender = array($from_address, $from_name);
118 $mailer->setSender($sender);
119 $mailer->addRecipient($to);
120
121 if ($reply_address)
122 {
123 $mailer->addReplyTo($reply_address);
124 }
125
126 $mailer->setSubject($subject);
127 $mailer->setBody($hmess);
128 $mailer->isHTML($is_html);
129
130 $mailer->Encoding = $encoding;
131
132 if ($attachments !== null && is_array($attachments))
133 {
134 foreach ($attachments as $attach)
135 {
136 if (!empty($attach) && file_exists($attach))
137 {
138 $mailer->addAttachment($attach);
139 }
140 }
141 }
142
143 return $mailer->Send();
144 }
145
146 /**
147 * Backward compatibility for WordPress framework loading.
148 *
149 * @param string $fw The framework to load.
150 */
151 public function loadFramework($fw = '')
152 {
153 JHtml::fetch($fw);
154 }
155
156 /**
157 * Backward compatibility for Joomla add stylesheet.
158 *
159 * @param string $url URL to the linked style sheet.
160 * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9').
161 * @param array $attribs Array of attributes. Example: array('id' => 'scriptid', 'async' => 'async', 'data-test' => 1).
162 *
163 * @return void
164 *
165 * @since 1.7
166 */
167 public function addStyleSheet($url = '', $options = array(), $attribs = array())
168 {
169 if (empty($url))
170 {
171 return;
172 }
173
174 /**
175 * Add versioning to options array in order
176 * to reset the cache every time a new update is released.
177 * Versioning is used only in case it wasn't specified.
178 */
179 if (!isset($options['version']))
180 {
181 // make sure the constant is defined
182 if (defined('VIKAPPOINTMENTS_SOFTWARE_VERSION'))
183 {
184 $options['version'] = VIKAPPOINTMENTS_SOFTWARE_VERSION;
185 }
186 }
187 else if (empty($options['version']) || $options['version'] == 'auto')
188 {
189 // unset versioning
190 unset($options['version']);
191 }
192
193 // use JHtml to load native dependencies when needed
194 JHtml::fetch('stylesheet', $url, $options, $attribs);
195 }
196
197 /**
198 * Backward compatibility for WordPress add script.
199 *
200 * @param string $file Path to file.
201 * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9').
202 * @param array $attribs Array of attributes. Example: array('id' => 'scriptid', 'async' => 'async', 'data-test' => 1).
203 *
204 * @return void
205 */
206 public function addScript($file = '', $options = array(), $attribs = array())
207 {
208 if (empty($file))
209 {
210 return;
211 }
212
213 /**
214 * Add versioning to options array in order
215 * to reset the cache every time a new update is released.
216 * Versioning is used only in case it wasn't specified.
217 *
218 * @since 1.7
219 */
220 if (!isset($options['version']))
221 {
222 // make sure the constant is defined
223 if (defined('VIKAPPOINTMENTS_SOFTWARE_VERSION'))
224 {
225 $options['version'] = VIKAPPOINTMENTS_SOFTWARE_VERSION;
226 }
227 }
228 else if (empty($options['version']) || $options['version'] == 'auto')
229 {
230 // unset versioning
231 unset($options['version']);
232 }
233
234 // use JHtml to load native dependencies when needed
235 JHtml::fetch('script', $file, $options, $attribs);
236 }
237
238 /**
239 * Backward compatibility for card/row opening.
240 *
241 * @param string $class The class attribute for the fieldset.
242 * @param string $id The ID attribute for the fieldset.
243 *
244 * @return string The html to display.
245 *
246 * @since 1.7
247 */
248 public function openCard($class = '', $id = '')
249 {
250 return '<div class="row-fluid' . ($class ? ' ' . $class : '') . '"' . ($id ? ' id="' . $id . '' : '') . '>';
251 }
252
253 /**
254 * Backward compatibility for card/row closing.
255 *
256 * @return string The html to display.
257 *
258 * @since 1.7
259 */
260 public function closeCard()
261 {
262 return '</div>';
263 }
264
265 /**
266 * Backward compatibility for WordPress fieldset opening.
267 *
268 * @param string $legend The title of the fieldset.
269 * @param string $class The class attribute for the fieldset.
270 * @param string $id The ID attribute for the fieldset.
271 *
272 * @return string The html to display.
273 *
274 * @since 1.3
275 */
276 public function openFieldset($legend, $class = '', $id = '')
277 {
278 static $incrementalId = 0;
279 static $useScript = true;
280
281 if (!$id)
282 {
283 // generate a unique ID if not supplied
284 $id = 'vap-postbox-' . (++$incrementalId);
285 }
286
287 $input = JFactory::getApplication()->input;
288
289 $data = array();
290 $data['name'] = $legend;
291 $data['class'] = $class;
292 $data['id'] = $id;
293 $data['tag'] = preg_replace("/[^a-z0-9_]+/i", '_', implode('.', [$input->get('option'), $input->get('view'), $id]));
294 $data['visible'] = $input->cookie->getBool($data['tag'], true);
295
296 if ($useScript)
297 {
298 // use script only once
299 $useScript = false;
300
301 // include script from layout
302 $script = JLayoutHelper::render('html.form.fieldset.script');
303 }
304 else
305 {
306 $script = '';
307 }
308
309 return JLayoutHelper::render('html.form.fieldset.open', $data) . $script;
310 }
311
312 /**
313 * Backward compatibility for WordPress fieldset closing.
314 *
315 * @return string The html to display.
316 *
317 * @since 1.3
318 */
319 public function closeFieldset()
320 {
321 return JLayoutHelper::render('html.form.fieldset.close');
322 }
323
324 /**
325 * Backward compatibility for WordPress empty fieldset opening.
326 *
327 * @param string $class An additional class to use for the fieldset.
328 * @param string $id The ID attribute for the fieldset.
329 *
330 * @return string The html to display.
331 *
332 * @since 1.3
333 */
334 public function openEmptyFieldset($class = '', $id = '')
335 {
336 return $this->openFieldset('', $class, $id);
337 }
338
339 /**
340 * Backward compatibility for WordPress empty fieldset opening.
341 *
342 * @return string The html to display.
343 *
344 * @since 1.3
345 */
346 public function closeEmptyFieldset()
347 {
348 return $this->closeFieldset();
349 }
350
351 /**
352 * Backward compatibility for WordPress control opening.
353 *
354 * @param string $label The label of the control field.
355 * @param string $class The class of the control field.
356 * @param mixed $attr The additional attributes to add (string or array).
357 *
358 * @return string The html to display.
359 *
360 * @since 1.3
361 */
362 public function openControl($label, $class = '', $attr = array())
363 {
364 $data = array();
365
366 if (is_string($attr))
367 {
368 // string is not supported on WordPress platform
369 trigger_error(sprintf('%s() expects parameter 3 to be array, %s given', __METHOD__, gettype($attr)), E_USER_NOTICE);
370
371 // use empty attributes
372 $attr = array();
373 }
374
375 foreach ($attr as $k => $v)
376 {
377 $data[$k] = $v;
378 }
379
380 $data['label'] = $label;
381 $data['class'] = $class;
382
383 return JLayoutHelper::render('html.form.control.open', $data);
384 }
385
386 /**
387 * Backward compatibility for WordPress control closing.
388 *
389 * @return string The html to display.
390 *
391 * @since 1.3
392 */
393 public function closeControl()
394 {
395 return JLayoutHelper::render('html.form.control.close');
396 }
397
398 /**
399 * Prepares the editor scripts for being used.
400 * Useful in case the editor is initialized via JavaScript/AJAX.
401 *
402 * @param string $name The name of the editor.
403 *
404 * @return void
405 *
406 * @since 1.6.3
407 */
408 public function prepareEditor($name)
409 {
410 switch (strtolower($name))
411 {
412 case 'tinymce':
413 JHtml::fetch('behavior.tinyMCE');
414 break;
415
416 case 'codemirror':
417 JHtml::fetch('behavior.codeMirror');
418 break;
419 }
420 }
421
422 /**
423 * Returns the specified editor.
424 *
425 * @param mixed $editor The editor to load.
426 * The default one if not specified.
427 *
428 * @return JEditor The editor instance.
429 *
430 * @since 1.7
431 */
432 public function getEditor($editor = null)
433 {
434 return JFactory::getEditor($editor);
435 }
436
437 /**
438 * Returns the codemirror editor in WordPress 4.9+, otherwise a simple textarea.
439 *
440 * @param string $name The name of the textarea.
441 * @param string $value The value of the textarea.
442 * @param array $params An array of options (@since 1.7).
443 *
444 * @return string The html to display.
445 *
446 * @since 1.4
447 */
448 public function getCodeMirror($name, $value, array $params = array())
449 {
450 if (VersionListener::isLowerThan('4.9'))
451 {
452 // CodeMirror is not supported on WP lower than 4.9, use a plain textarea
453 $editorName = 'none';
454 }
455 else
456 {
457 // use CoreMirror editor
458 $editorName = 'codemirror';
459 }
460
461 // display editor
462 return JEditor::getInstance($editorName)->display($name, $value, '100%', 600, 30, 30, $buttons = false, $id = null, $params);
463 }
464
465 /**
466 * Backward compatibility for WordPress Bootstrap tabset opening.
467 *
468 * @param string $group The group of the tabset.
469 * @param string $attr The attributes to use.
470 *
471 * @return string The html to display.
472 *
473 * @since 1.4
474 */
475 public function bootStartTabSet($group, $attr = array())
476 {
477 /**
478 * In case the cookie attribute was included within the,
479 * array, we can register the script that will be used to
480 * handle the tab changes. The last selected tab will
481 * be stored in a cookie in order to be pre-selected
482 * when refreshing the page.
483 *
484 * @since 1.7
485 */
486 if (isset($attr['cookie']))
487 {
488 $this->bootstrapTabSetCookie = '<script>' . JHtml::fetch('vaphtml.scripts.tabhandler', $group, $attr['cookie']) . '</script>';
489 }
490 else
491 {
492 $this->bootstrapTabSetCookie = '';
493 }
494
495 return JHtml::fetch('bootstrap.startTabSet', $group, $attr);
496 }
497
498 /**
499 * Backward compatibility for WordPress Bootstrap tabset closing.
500 *
501 * @return string The html to display.
502 *
503 * @since 1.4
504 */
505 public function bootEndTabSet()
506 {
507 /**
508 * Append the 'cookie' script after creating the tabset.
509 *
510 * @since 1.7
511 */
512
513 return JHtml::fetch('bootstrap.endTabSet') . $this->bootstrapTabSetCookie;
514 }
515
516 /**
517 * Backward compatibility for WordPress Bootstrap add tab.
518 *
519 * @param string $group The tabset parent group.
520 * @param string $id The id of the tab.
521 * @param string $label The title of the tab.
522 * @param array $options A list of options.
523 *
524 * @return string The html to display.
525 *
526 * @since 1.4
527 */
528 public function bootAddTab($group, $id, $label, array $options = array())
529 {
530 /**
531 * In case the badge option is specified, append a badge
532 * to the tab label, displaying the count of records.
533 *
534 * @since 1.7
535 */
536 if (isset($options['badge']))
537 {
538 $badge_class = isset($options['badge']['class']) ? $options['badge']['class'] : 'badge-info';
539 $badge_id = isset($options['badge']['id']) ? $options['badge']['id'] : $id . '_tab_badge';
540 $badge_count = isset($options['badge']['count']) ? $options['badge']['count'] : (int) $options['badge'];
541
542 $label .= "<span class=\"badge {$badge_class} tab-badge-count\" id=\"{$badge_id}\" data-count=\"{$badge_count}\"> </span>";
543 }
544
545 return JHtml::fetch('bootstrap.addTab', $group, $id, $label);
546 }
547
548 /**
549 * Backward compatibility for WordPress Bootstrap end tab.
550 *
551 * @return string The html to display.
552 *
553 * @since 1.4
554 */
555 public function bootEndTab()
556 {
557 return JHtml::fetch('bootstrap.endTab');
558 }
559
560 /**
561 * Backward compatibility for WordPress Bootstrap open modal JS event.
562 *
563 * @param string $onclose The javascript function to call on close event.
564 *
565 * @return string The javascript function.
566 *
567 * @since 1.5
568 */
569 public function bootOpenModalJS($onclose = '')
570 {
571 if ($onclose)
572 {
573 $onclose .= '();';
574 }
575
576 return
577 <<<JS
578 var on_hide = null;
579
580 if ("$onclose") {
581 on_hide = function() {
582 $onclose
583 }
584 }
585
586 wpOpenJModal(id, url, null, on_hide);
587
588 return false;
589 JS
590 ;
591 }
592
593 /**
594 * Backward compatibility for WordPress Bootstrap dismiss modal JS event.
595 *
596 * @param string $selector The selector to identify the modal box.
597 *
598 * @return string The javascript function.
599 *
600 * @since 1.5
601 */
602 public function bootDismissModalJS($selector = null)
603 {
604 if (!is_null($selector))
605 {
606 /**
607 * The static selector is no more supported due
608 * to the possibility of conflicts between 2 or more
609 * modals loaded within the same page.
610 *
611 * @deprecated 1.8
612 */
613 JFactory::getDocument()->addScriptDeclaration('console.warn("The selector is deprecated in ' . __METHOD__ . '()");');
614 }
615
616 return
617 <<<JS
618 if ('$selector') {
619 id = '$selector';
620 }
621
622 wpCloseJModal(id);
623 JS
624 ;
625
626 return
627 <<<JS
628 wpCloseJModal('$selector');
629 JS
630 ;
631 }
632
633 /**
634 * Adds javascript support for Bootstrap popovers.
635 *
636 * @param string $selector Selector for the popover.
637 * @param array $options An array of options for the popover.
638 * Options for the popover can be:
639 * animation boolean apply a css fade transition to the popover
640 * html boolean Insert HTML into the popover. If false, jQuery's text method will be used to insert
641 * content into the dom.
642 * placement string|function how to position the popover - top | bottom | left | right
643 * selector string If a selector is provided, popover objects will be delegated to the specified targets.
644 * trigger string how popover is triggered - hover | focus | manual
645 * title string|function default title value if `title` tag isn't present
646 * content string|function default content value if `data-content` attribute isn't present
647 * delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type
648 * If a number is supplied, delay is applied to both hide/show
649 * Object structure is: delay: { show: 500, hide: 100 }
650 * container string|boolean Appends the popover to a specific element: { container: 'body' }
651 */
652 public function attachPopover($selector = '.wpPopover', array $options = array())
653 {
654 static $loaded = array();
655
656 $sign = serialize(array($selector, $options));
657
658 if (!isset($loaded[$sign]))
659 {
660 // do not sanitize HTML contents
661 $options['sanitize'] = false;
662
663 /**
664 * In case the "container" attribute is not set,
665 * always place the popover within the body.
666 *
667 * @since 1.6.5
668 */
669 if (!isset($options['container']))
670 {
671 $options['container'] = 'body';
672 }
673
674 $data = $options ? json_encode($options) : '{}';
675 JFactory::getDocument()->addScriptDeclaration(
676 <<<JS
677 jQuery(function() {
678 jQuery('$selector').popover($data);
679 });
680 JS
681 );
682
683 $loaded[$sign] = 1;
684 }
685 }
686
687 /**
688 * Create a standard tag and attach a popover event.
689 * NOTE. FontAwesome framework MUST be loaded in order to work.
690 *
691 * @param array $options An array of options for the popover.
692 *
693 * @see VAPApplication::attachPopover() for further details about options keys.
694 *
695 * @since 1.6
696 */
697 public function createPopover(array $options = array())
698 {
699 $icon = isset($options['icon_class']) ? $options['icon_class'] : 'fas fa-question-circle';
700
701 $icon = isset($options['icon']) ? 'fas fa-' . $options['icon'] : $icon;
702
703 $template = "<i class=\"{$icon} vap-quest-popover\" {popover}></i>";
704
705 return $this->_popover($template, $options);
706 }
707
708 /**
709 * Create a text span and attach a popover event.
710 *
711 * @param array $options An array of options for the popover.
712 *
713 * @see VAPApplication::attachPopover() for further details about options keys.
714 *
715 * @since 1.6
716 */
717 public function textPopover(array $options = array())
718 {
719 $title = isset($options['title']) ? $options['title'] : '[MISSING TITLE]';
720 $template = "<span class=\"inline-popover vap-quest-popover\" {popover}>{$title}</span>";
721
722 return $this->_popover($template, $options);
723 }
724
725 /**
726 * Creates a popover using the provided template.
727 *
728 * @param string $template The popover template.
729 * @param array $options An array of options for the popover.
730 *
731 * @return string The popover HTML.
732 *
733 * @see VAPApplication::attachPopover() for further details about options keys.
734 */
735 protected function _popover($template, array $options)
736 {
737 $layout = new JLayoutFile('html.plugins.popover', null, array('component' => 'com_vikappointments'));
738
739 $options['html'] = true;
740 $options['title'] = isset($options['title']) ? $options['title'] : '';
741 $options['content'] = isset($options['content']) ? $options['content'] : '';
742 $options['trigger'] = isset($options['trigger']) ? $options['trigger'] : 'hover focus';
743 $options['placement'] = isset($options['placement']) ? $options['placement'] : 'right';
744 $options['template'] = isset($options['template']) ? $options['template'] : $layout->render();
745
746 // attach an empty array option so that the data will be recovered
747 // directly from the tag during the runtime
748 $this->attachPopover(".vap-quest-popover", array());
749
750 $attr = '';
751 foreach ($options as $k => $v)
752 {
753 $attr .= "data-{$k}=\"" . esc_attr($v) . "\" ";
754 }
755
756 return str_replace('{popover}', $attr, $template);
757 }
758
759 /**
760 * Return the WordPress date format specs.
761 *
762 * @param string $format The format to use.
763 * @param array &$attributes Some attributes to use.
764 *
765 * @return string The adapted date format.
766 *
767 * @since 1.6
768 */
769 public function jdateFormat($format = null, array &$attributes = array())
770 {
771 if ($format === null)
772 {
773 $format = VAPFactory::getConfig()->getString('dateformat');
774
775 if (!empty($attributes['showTime']))
776 {
777 // concat the time format (24 hours format only)
778 $format .= ' H:i';
779 }
780 }
781
782 // strip % from date format string, which was required in Joomla
783 return str_replace('%', '', $format);
784 }
785
786 /**
787 * Provides support to handle the WordPress calendar across different frameworks.
788 *
789 * @param mixed $value The date or the timestamp to fill.
790 * @param string $name The input name.
791 * @param string $id The input id attribute.
792 * @param string $format The date format.
793 * @param array $attributes Some attributes to use.
794 *
795 * @return string The calendar field.
796 *
797 * @since 1.6
798 */
799 public function calendar($value, $name, $id = null, $format = null, array $attributes = array())
800 {
801 $format = $this->jdateFormat($format, $attributes);
802
803 JHtml::fetch('behavior.calendar');
804
805 return JHtml::fetch('calendar', $value, $name, $id, $format, $attributes);
806 }
807
808 /**
809 * Method used to obtain a WordPress media form field.
810 *
811 * @return string The media in HTML.
812 *
813 * @since 1.6
814 */
815 public function getMediaField($name, $value = null, array $data = array())
816 {
817 // import form field class
818 JLoader::import('adapter.form.field');
819
820 // create XML field manifest
821 $xml = "<field name=\"$name\" type=\"media\" />";
822
823 // instantiate field
824 $field = JFormField::getInstance(simplexml_load_string($xml));
825
826 // overwrite name and value within data
827 $data['name'] = $name;
828 $data['value'] = $value;
829
830 // inject display data within field instance
831 foreach ($data as $k => $v)
832 {
833 $field->bind($v, $k);
834 }
835
836 // render field
837 return $field->render();
838 }
839
840 /**
841 * Method used to handle the reCAPTCHA events.
842 *
843 * @param string $event The reCAPTCHA event to trigger.
844 * Here's the list of the accepted events:
845 * - display Returns the HTML used to display the ReCAPTCHA input;
846 * - check Validates the POST data to make sure the ReCAPTCHA input was checked.
847 * @param array $options A configuration array.
848 *
849 * @return mixed The event response.
850 *
851 * @since 1.6
852 * @deprecated 1.8 Use captcha() instead.
853 */
854 public function reCaptcha($event = 'display', array $options = array())
855 {
856 return $this->captcha($event, $options);
857 }
858
859 /**
860 * Method used to handle the CAPTCHA events.
861 *
862 * @param string $event The CAPTCHA event to trigger.
863 * Here's the list of the accepted events:
864 * - display Returns the HTML used to
865 * display the CAPTCHA input.
866 * - check Validates the POST data to make sure
867 * the CAPTCHA input was checked.
868 * @param array $options A configuration array.
869 *
870 * @return mixed The event response.
871 *
872 * @since 1.7.10
873 */
874 public function captcha($event = 'display', array $options = [])
875 {
876 $response = null;
877
878 /**
879 * Trigger action to perform the specified ReCAPTCHA event.
880 *
881 * @param mixed $response The response to return.
882 * @param array $options A configuration array.
883 *
884 * @since 1.0
885 */
886 do_action_ref_array('vik_recaptcha_' . strtolower($event), array(&$response, $options));
887
888 return $response;
889 }
890
891 /**
892 * Checks if the com_user captcha is configured.
893 * In case the parameter is set to global, the default one
894 * will be retrieved.
895 *
896 * @param string $plugin The plugin name to check. Leave empty
897 * to use any type of captcha.
898 *
899 * @return boolean True if configured, otherwise false.
900 *
901 * @since 1.6
902 * @deprecated 1.8 Use getUserCaptcha() instead.
903 */
904 public function isCaptcha($plugin = null)
905 {
906 return $this->isGlobalCaptcha();
907 }
908
909
910 /**
911 * Returns the configured captcha plugin for com_users component.
912 * In case the parameter is set to global, the default one will be retrieved.
913 *
914 * @return string|null The configured captcha, NULL otherwise.
915 *
916 * @since 1.7.10
917 */
918 public function getUserCaptcha()
919 {
920 return $this->getGlobalCaptcha();
921 }
922
923 /**
924 * Checks if the global captcha is configured.
925 *
926 * @param string $plugin The plugin name to check. Leave empty
927 * to use any type of captcha.
928 *
929 * @return boolean True if configured, otherwise false.
930 *
931 * @since 1.6
932 * @deprecated 1.8 Use getGlobalCaptcha() instead.
933 */
934 public function isGlobalCaptcha($plugin = null)
935 {
936 return (bool) $this->getGlobalCaptcha();
937 }
938
939 /**
940 * Returns the configured captcha plugin for global use.
941 *
942 * @return string|null The configured captcha, NULL otherwise.
943 *
944 * @since 1.7.10
945 */
946 public function getGlobalCaptcha()
947 {
948 /**
949 * Trigger action to check whether the ReCAPTCHA plugin is supported.
950 *
951 * @param boolean $active True if active, false otherwise.
952 * @param mixed $plugin The requested plugin, if specified.
953 *
954 * @since 1.0
955 */
956 return apply_filters('vik_recaptcha_on', false, null) ? 'recaptcha' : null;
957 }
958
959 /**
960 * Rewrites an internal URI that needs to be used outside of the website.
961 * This means that the routed URI MUST start with the base path of the site.
962 *
963 * @param mixed $query The query string or an associative array of data.
964 * @param boolean $xhtml Replace & by &amp; for XML compliance.
965 * @param mixed $itemid The itemid to use. If null, the current one will be used.
966 *
967 * @return string The complete routed URI.
968 *
969 * @since 1.6
970 */
971 public function routeForExternalUse($query = '', $xhtml = true, $itemid = null)
972 {
973 // check if the query already specifies the Itemid
974 if (!preg_match("/&Itemid=[\d]*/", $query))
975 {
976 // try to extract view from query
977 if (preg_match("/&view=([a-z0-9_]+)(?:&|$)/", $query, $match))
978 {
979 $view = end($match);
980 }
981 else
982 {
983 $view = null;
984 }
985
986 // import shortcodes model
987 $model = JModel::getInstance('vikappointments', 'shortcodes', 'admin');
988 $itemid = $model->best($view);
989
990 if ($itemid)
991 {
992 // update query with Itemid found
993 $query .= (strpos($query, '?') === false ? '?' : '&') . 'Itemid=' . $itemid;
994 }
995 }
996
997 // route URL
998 return JRoute::rewrite($query, false);
999 }
1000
1001 /**
1002 * Routes an admin URL for being used outside from the website (complete URI).
1003 *
1004 * @param mixed $query The query string or an associative array of data.
1005 * @param boolean $xhtml Replace & by &amp; for XML compliance.
1006 *
1007 * @return string The complete routed URI.
1008 *
1009 * @since 1.6.3
1010 */
1011 public function adminUrl($query = '', $xhtml = true)
1012 {
1013 $app = JFactory::getApplication();
1014
1015 if (is_array($query))
1016 {
1017 // make sure the array is not empty
1018 if ($query)
1019 {
1020 $query = '?' . http_build_query($query);
1021 }
1022 else
1023 {
1024 $query = '';
1025 }
1026
1027 // the query is an array, build the query string
1028 $query = 'index.php' . $query;
1029 }
1030
1031 // replace initial index.php with admin.php
1032 $query = preg_replace("/^index\.php/", 'admin.php', $query);
1033 // replace option=com_vikappointments with page=vikappointments
1034 $query = preg_replace("/(&|\?)option=com_vikappointments/", '$1page=vikappointments', $query);
1035
1036 // finalise admin URI
1037 $uri = JUri::root() . 'wp-admin/' . $query;
1038
1039 if ($xhtml)
1040 {
1041 $uri = str_replace('&', '&amp;', $uri);
1042 }
1043
1044 return $uri;
1045 }
1046
1047 /**
1048 * Prepares a plain/routed URL to be used for an AJAX request.
1049 *
1050 * @param mixed $query The query string or a routed URL.
1051 * @param boolean $xhtml Replace & by &amp; for XML compliance.
1052 *
1053 * @return string The AJAX end-point URI.
1054 *
1055 * @since 1.7
1056 */
1057 public function ajaxUrl($query = '', $xhtml = false)
1058 {
1059 // instantiate path based on specified query
1060 $path = new JUri($query);
1061
1062 // delete option var from query
1063 $path->delVar('option');
1064
1065 // force action in query
1066 $path->setVar('action', 'vikappointments');
1067
1068 // force application client in case of front-end
1069 if (JFactory::getApplication()->isClient('site'))
1070 {
1071 $path->setVar('vik_ajax_client', 'site');
1072 }
1073
1074 // create AJAX URI
1075 $uri = admin_url('admin-ajax.php') . '?' . $path->getQuery();
1076
1077 if ($xhtml)
1078 {
1079 // try to make "&" XML safe
1080 $uri = preg_replace("/&(?!amp;)/", '&amp;', $uri);
1081 }
1082
1083 return $uri;
1084 }
1085
1086 /**
1087 * Includes the CSRF-proof token within the specified query string/URL.
1088 *
1089 * @param mixed $query The query string or a routed URL.
1090 * @param boolean $xhtml Replace & by &amp; for XML compliance.
1091 *
1092 * @return string The resulting path.
1093 *
1094 * @since 1.7
1095 */
1096 public function addUrlCSRF($query = '', $xhtml = false)
1097 {
1098 JLoader::import('adapter.session.session');
1099
1100 // safely append the CSRF token within the query string
1101 $uri = JUri::getInstance($query);
1102 $uri->setVar(JSession::getFormTokenName(), JSession::getFormToken());
1103
1104 if ($xhtml)
1105 {
1106 // try to make "&" XML safe
1107 $uri = preg_replace("/&(?!amp;)/", '&amp;', (string) $uri);
1108 }
1109
1110 return (string) $uri;
1111 }
1112
1113 /**
1114 * Returns the platform base path.
1115 *
1116 * @return string
1117 *
1118 * @since 1.7.1
1119 */
1120 public function getAbsolutePath()
1121 {
1122 return ABSPATH;
1123 }
1124
1125 /**
1126 * Helper method to pre-load the assets needed for the Employee Area.
1127 *
1128 * @return void
1129 *
1130 * @since 1.6
1131 */
1132 public function loadEmployeeAreaAssets()
1133 {
1134 parent::loadEmployeeAreaAssets();
1135
1136 // load bootstrap too
1137 $doc = JFactory::getDocument();
1138
1139 $internalFilesOptions = array('version' => VIKAPPOINTMENTS_SOFTWARE_VERSION);
1140
1141 $doc->addStyleSheet(VIKAPPOINTMENTS_CORE_MEDIA_URI . 'css/bootstrap.lite.css', $internalFilesOptions, array('id' => 'bootstrap-lite-style'));
1142 $doc->addScript(VIKAPPOINTMENTS_CORE_MEDIA_URI . 'js/bootstrap.min.js', $internalFilesOptions, array('id' => 'bootstrap-script'));
1143 }
1144
1145 /**
1146 * Returns a list of users that are currently logged-in.
1147 *
1148 * @param mixed $limit The query limit, if specified.
1149 * @param integer $offset The query offset, if specified.
1150 *
1151 * @return array A list of users.
1152 *
1153 * @since 1.6.3
1154 */
1155 public function getLoggedUsers($limit = null, $offset = 0)
1156 {
1157 // get record of currently logged-in users (get_users() returns always an array)
1158 $users = get_users([
1159 'meta_key' => 'session_tokens',
1160 'meta_compare' => 'EXISTS',
1161 ]);
1162
1163 // splice the users list following the specified limits
1164 $users = array_splice($users, $offset, $limit);
1165
1166 if (!$users)
1167 {
1168 // do not proceed in case of no users
1169 return array();
1170 }
1171
1172 $dbo = JFactory::getDbo();
1173
1174 $query = $dbo->getQuery(true)
1175 ->select($dbo->qn(array('id', 'country_code')))
1176 ->from($dbo->qn('#__vikappointments_users', 'c'));
1177
1178 /**
1179 * Here's a list of columns that have to be supported:
1180 *
1181 * @property string session_time The session last time.
1182 * @property integer jid The user ID.
1183 * @property integer id The customer ID.
1184 * @property string billing_name The user nominative.
1185 * @property string billing_mail The user e-mail address.
1186 * @property string country_code The user 2-letters country code.
1187 */
1188
1189 // map array to extract a valid response
1190 return array_map(function($user) use ($query, $dbo)
1191 {
1192 $sessions = get_user_meta($user->ID, 'session_tokens', true);
1193
1194 if (is_array($sessions) && $sessions)
1195 {
1196 // extract last session token
1197 $session = end($sessions);
1198 $last_login = $session['login'];
1199 }
1200 else
1201 {
1202 // impossible to fetch last login
1203 $last_login = null;
1204 }
1205
1206 $tmp = array();
1207 $tmp['jid'] = $user->ID;
1208 $tmp['session_time'] = $last_login;
1209 $tmp['billing_name'] = $user->display_name;
1210 $tmp['billing_mail'] = $user->user_email;
1211 $tmp['country_code'] = null;
1212 $tmp['id'] = null;
1213
1214 // complete query
1215 $query->clear('where')->where(array(
1216 $dbo->qn('c.jid') . ' = ' . (int) $user->ID,
1217 $dbo->qn('c.billing_mail') . ' = ' . $dbo->q($user->user_email),
1218 ), 'OR');
1219
1220 $dbo->setQuery($query, 0, 1);
1221
1222 // merge columns found
1223 $tmp = array_merge($tmp, (array) $dbo->loadAssoc());
1224
1225 return $tmp;
1226 }, $users);
1227 }
1228
1229 /**
1230 * Prepares the specified content before being displayed.
1231 *
1232 * @param mixed &$content The table content instance or a string to fetch.
1233 * @param mixed $params True to apply the full description, false to apply
1234 * the short description, if any. Any other non scalar
1235 * value to pass a configuration for plugins.
1236 *
1237 * @return void
1238 *
1239 * @since 1.6.3
1240 */
1241 public function onContentPrepare(&$content, $params = array())
1242 {
1243 $pattern = "/(?:<p>)?<!--\s*more\s*-->(?:<\/p>)?/i";
1244
1245 if (is_null($content))
1246 {
1247 $content = '';
1248 }
1249
1250 // create Content table in case a string was passed
1251 if (is_string($content))
1252 {
1253 $text = $content;
1254
1255 $content = JTable::getInstance('content');
1256 $content->text = $text;
1257 }
1258
1259 if (is_bool($params))
1260 {
1261 // BC: take the specified type of text (full or short)
1262 $full = $params;
1263
1264 // use an empty array
1265 $params = array();
1266 }
1267 else
1268 {
1269 /**
1270 * Extract type of text from parameters.
1271 * If not specified, the full text will be used.
1272 *
1273 * @since 1.7
1274 */
1275 $full = isset($params['fulltext']) ? (bool) $params['fulltext'] : true;
1276 }
1277
1278 // always replaces double line-breaks with paragraph elements
1279 $content->text = wpautop($content->text);
1280
1281 /**
1282 * Interprets shortcodes contained within the full text.
1283 *
1284 * @since 1.7
1285 */
1286 $content->text = do_shortcode($content->text);
1287
1288 // check if the description owns a readmore separator
1289 if (preg_match($pattern, $content->text))
1290 {
1291 // split the description in 2 chunks
1292 $chunks = preg_split($pattern, $content->text, 2);
1293
1294 // overwrite text with short (0) or full (1) description
1295 $content->text = $chunks[$full ? 1 : 0];
1296
1297 /**
1298 * Register intro and full texts too.
1299 *
1300 * @since 1.7
1301 */
1302 $content->introtext = $chunks[0];
1303 $content->fulltext = $chunks[1];
1304 }
1305 }
1306
1307 /**
1308 * Returns a list of supported payment gateways.
1309 *
1310 * @return array A list of paths.
1311 *
1312 * @since 1.6.3
1313 */
1314 public function getPaymentDrivers()
1315 {
1316 // import payment dispatcher
1317 JLoader::import('adapter.payment.dispatcher');
1318
1319 // get paths list of supported drivers
1320 return JPaymentDispatcher::getSupportedDrivers('vikappointments');
1321 }
1322
1323 /**
1324 * Returns the configuration form of a payment.
1325 *
1326 * @param string $payment The name of the payment.
1327 *
1328 * @return mixed The configuration array/object.
1329 *
1330 * @since 1.6.3
1331 *
1332 * @uses getPaymentInstance()
1333 */
1334 public function getPaymentConfig($payment)
1335 {
1336 // instantiate payment and access its configuration form
1337 return $this->getPaymentInstance($payment)->getAdminParameters();
1338 }
1339
1340 /**
1341 * Provides a new payment instance for the specified arguments.
1342 *
1343 * @param string $payment The name of the payment that should be instantiated.
1344 * @param mixed $order The details of the order that has to be paid.
1345 * @param mixed $config The payment configuration array or a JSON string.
1346 *
1347 * @return mixed The payment instance.
1348 *
1349 * @throws RuntimeException
1350 *
1351 * @since 1.6.3
1352 */
1353 public function getPaymentInstance($payment, $order = array(), $config = array())
1354 {
1355 // import payment dispatcher
1356 JLoader::import('adapter.payment.dispatcher');
1357
1358 // instantiate payment
1359 return JPaymentDispatcher::getInstance('vikappointments', $payment, $order, $config);
1360 }
1361
1362 /**
1363 * Returns the component manufacturer name or link.
1364 *
1365 * @param array $options An array of options:
1366 * - link (boolean) True to return a link, false to return the
1367 * name only (false by default);
1368 * - short (boolean) True to display the short manufacturer name,
1369 * false otherwise (false by default);
1370 * - long (boolean) True to display the long manufacturer name,
1371 * false otherwise (true by default);
1372 * - separator (string) A separator string to insert between the
1373 * names fetched ('-' by default).
1374 *
1375 * @return string The manufacturer name or link.
1376 *
1377 * @since 1.6.3
1378 */
1379 public function getManufacturer(array $options = array())
1380 {
1381 // add support for manufacturer default options
1382 $options['manufacturer'] = array(
1383 // specify a default URI
1384 'link' => 'https://vikwp.com',
1385 // specify the manufacturer short name
1386 'short' => 'VikWP',
1387 // specify the manufacturer long name
1388 'long' => 'vikwp.com',
1389 );
1390
1391 // invoke parent to complete name building
1392 return parent::getManufacturer($options);
1393 }
1394
1395 /**
1396 * Checks whether the reservation can be completed.
1397 *
1398 * @return boolean
1399 *
1400 * @since 1.6.3
1401 */
1402 public function checkAvailability()
1403 {
1404 // always return true in WordPress
1405 return true;
1406 }
1407
1408 /**
1409 * Displays the platform alert.
1410 *
1411 * @param array $options The alert display data.
1412 *
1413 * @return string The HTML of the alert.
1414 *
1415 * @see alert()
1416 *
1417 * @since 1.7
1418 */
1419 protected function displayAlert(array $data)
1420 {
1421 // register script to handle cookie alert
1422 JHtml::fetch('vaphtml.scripts.cookiealert');
1423
1424 // include INLINE class to keep the notice stuck on its position
1425 if (empty($data['attrs']['class']))
1426 {
1427 $data['attrs']['class'] = 'inline';
1428 }
1429 else
1430 {
1431 $data['attrs']['class'] .= ' inline';
1432 }
1433
1434 // instantiate layout file
1435 $layout = new JLayoutFile('html.system.notice');
1436
1437 // display layout
1438 return $layout->render($data);
1439 }
1440
1441 /**
1442 * Returns a list of supported SMS providers.
1443 *
1444 * @return array A list of paths.
1445 *
1446 * @since 1.7
1447 */
1448 public function getSmsDrivers()
1449 {
1450 // import sms dispatcher
1451 JLoader::import('adapter.sms.dispatcher');
1452
1453 // get paths list of supported drivers
1454 return JSmsDispatcher::getSupportedDrivers('vikappointments');
1455 }
1456
1457 /**
1458 * Returns the configuration form of a SMS provider.
1459 *
1460 * @param string $driver The name of the driver.
1461 *
1462 * @return mixed The configuration array/object.
1463 *
1464 * @since 1.7
1465 */
1466 public function getSmsConfig($driver)
1467 {
1468 // instantiate driver and access its configuration form
1469 return $this->getSmsInstance($driver)->getAdminParameters();
1470 }
1471
1472 /**
1473 * Provides a new SMS driver instance for the specified arguments.
1474 *
1475 * @param string $driver The name of the provider that should be instantiated.
1476 * If not specified, the default one will be used.
1477 * @param mixed $config The SMS configuration array or a JSON string.
1478 * @param mixed $order The details of the order that has to be notified.
1479 *
1480 * @return mixed The driver instance.
1481 *
1482 * @throws RuntimeException
1483 *
1484 * @since 1.7
1485 */
1486 public function getSmsInstance($driver = null, $config = null, $order = array())
1487 {
1488 if (is_null($driver))
1489 {
1490 // get default driver if not specified
1491 $driver = VAPFactory::getConfig()->get('smsapi');
1492 }
1493
1494 if (empty($driver))
1495 {
1496 // SMS API not configured
1497 throw new RuntimeException('SMS API framework not configured', 500);
1498 }
1499
1500 if (is_null($config))
1501 {
1502 // get default configuration if not specified
1503 $config = VAPFactory::getConfig()->get('smsapifields');
1504 }
1505
1506 // import sms dispatcher
1507 JLoader::import('adapter.sms.dispatcher');
1508
1509 // instantiate sms
1510 return JSmsDispatcher::getInstance('vikappointments', $driver, $order, $config);
1511 }
1512 }
1513