PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / libraries / system / builder.php
vikappointments / libraries / system Last commit date
assets.php 1 month ago body.php 4 years ago builder.php 1 month ago cron.php 4 years ago feedback.php 4 years ago gutenberg.php 2 years ago install.php 1 month ago mce.php 4 years ago rssfeeds.php 5 months ago screen.php 4 years ago
builder.php
833 lines
1 <?php
2 /**
3 * @package VikAppointments - Libraries
4 * @subpackage system
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 /**
15 * Helper class to setup the plugin.
16 *
17 * @since 1.0
18 */
19 class VikAppointmentsBuilder
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.3
34 */
35 if ($loaded)
36 {
37 return;
38 }
39
40 $loaded = true;
41
42 $app = JFactory::getApplication();
43
44 // the language file is located in /languages folder
45 $path = VIKAPPOINTMENTS_LANG;
46
47 $handler = VIKAPPOINTMENTS_LIBRARIES . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
48 $domain = 'vikappointments';
49
50 // init language
51 $lang = JFactory::getLanguage();
52
53 $lang->attachHandler($handler . 'system.php', $domain);
54
55 if ($app->isAdmin())
56 {
57 $lang->attachHandler($handler . 'adminsys.php', $domain);
58 $lang->attachHandler($handler . 'admin.php', $domain);
59 }
60
61 // always load site language handler as fallback
62 $lang->attachHandler($handler . 'site.php', $domain);
63
64 $lang->load($domain, $path);
65 }
66
67 /**
68 * Pushes the plugin pages into the WP admin menu.
69 *
70 * @return void
71 *
72 * @link https://developer.wordpress.org/resource/dashicons/#star-filled
73 */
74 public static function setupAdminMenu()
75 {
76 JLoader::import('adapter.acl.access');
77 $capability = JAccess::adjustCapability('core.manage', 'com_vikappointments');
78
79 // set default plugin menu title
80 $default_title = JText::translate('COM_VIKAPPOINTMENTS_MENU');
81 // let other plugins can filter the menu title
82 $title = apply_filters('vikappointments_menu_title', $default_title);
83
84 add_menu_page(
85 JText::translate('COM_VIKAPPOINTMENTS'), // page title
86 $title ? $title : $default_title, // menu title
87 $capability, // capability
88 'vikappointments', // slug
89 array('VikAppointmentsBody', 'getHtml'), // callback
90 'dashicons-calendar-alt', // icon
91 71 // ordering
92 );
93 }
94
95 /**
96 * Setup HTML helper classes.
97 * This method should be used to register custom function
98 * for example to render own layouts.
99 *
100 * @return void
101 */
102 public static function setupHtmlHelpers()
103 {
104 // helper method to render calendars layout
105 JHtml::register('renderCalendar', function($data)
106 {
107 JHtml::fetch('script', VAP_SITE_URI . 'assets/js/jquery-ui.min.js');
108 JHtml::fetch('stylesheet', VAP_SITE_URI . 'assets/css/jquery-ui.min.css');
109
110 $layout = new JLayoutFile('html.plugins.calendar', null, array('option' => 'com_vikappointments'));
111
112 return $layout->render($data);
113 });
114
115 // helper method to get the plugin layout file handler
116 JHtml::register('layoutfile', function($layoutId, $basePath = null, $options = array())
117 {
118 $input = JFactory::getApplication()->input;
119
120 if (!isset($options['component']) && !$input->getBool('option'))
121 {
122 // force layout file in case there is no active plugin
123 $options['component'] = 'com_vikappointments';
124 }
125
126 return new JLayoutFile($layoutId, $basePath, $options);
127 });
128
129 // helper method to include the system JS file
130 JHtml::register('system.js', function()
131 {
132 static $loaded = 0;
133
134 if (!$loaded)
135 {
136 // include only once
137 $loaded = 1;
138
139 $internalFilesOptions = array('version' => VIKAPPOINTMENTS_SOFTWARE_VERSION);
140
141 JHtml::fetch('script', VIKAPPOINTMENTS_CORE_MEDIA_URI . 'js/system.js', $internalFilesOptions, array('id' => 'vap-sys-script'));
142 JHtml::fetch('stylesheet', VIKAPPOINTMENTS_CORE_MEDIA_URI . 'css/system.css', $internalFilesOptions, array('id' => 'vap-sys-style'));
143
144 JHtml::fetch('script', VIKAPPOINTMENTS_CORE_MEDIA_URI . 'js/bootstrap.min.js', $internalFilesOptions, array('id' => 'bootstrap-script'));
145 JHtml::fetch('stylesheet', VIKAPPOINTMENTS_CORE_MEDIA_URI . 'css/bootstrap.lite.css', $internalFilesOptions, array('id' => 'bootstrap-lite-style'));
146 }
147 });
148
149 // helper method to register select2 plugin
150 JHtml::register('select2', function()
151 {
152 // stop loading select2 outside of VAP
153 // VikAppointments::load_complex_select();
154 });
155 }
156
157 /**
158 * This method is used to configure teh payments framework.
159 * Here should be registered all the default gateways supported
160 * by the plugin.
161 *
162 * @return void
163 */
164 public static function configurePaymentFramework()
165 {
166 // push the pre-installed gateways within the payment drivers list
167 add_filter('get_supported_payments_vikappointments', function($drivers)
168 {
169 $list = glob(VIKAPPOINTMENTS_LIBRARIES . DIRECTORY_SEPARATOR . 'payments' . DIRECTORY_SEPARATOR . '*.php');
170
171 return array_merge($drivers, $list);
172 });
173
174 // load payment handlers when dispatched
175 add_action('load_payment_gateway_vikappointments', function(&$drivers, $payment)
176 {
177 $classname = null;
178
179 VikAppointmentsLoader::import('payments.' . $payment);
180
181 switch ($payment)
182 {
183 case 'paypal':
184 $classname = 'VikAppointmentsPayPalPayment';
185 break;
186
187 case 'paypal_express_checkout':
188 $classname = 'VikAppointmentsPayPalExpressCheckoutPayment';
189 break;
190
191 case 'offline_credit_card':
192 $classname = 'VikAppointmentsOfflineCreditCardPayment';
193 break;
194
195 case 'bank_transfer':
196 $classname = 'VikAppointmentsBankTransferPayment';
197 break;
198 }
199
200 if ($classname)
201 {
202 $drivers[] = $classname;
203 }
204 }, 10, 2);
205
206 // echo directly the payment HTML as showPayment() only returns it
207 add_action('vikappointments_payment_after_begin_transaction', function(&$payment, &$html)
208 {
209 echo $html;
210 }, 10, 2);
211
212 // manipulate response to be compliant with notifypayment task
213 add_action('vikappointments_payment_after_validate_transaction', function(&$payment, &$status, &$response)
214 {
215 // manipulate the response to be compliant with the old payment system
216 $response = [
217 'verified' => (int) $status->isVerified(),
218 'tot_paid' => $status->amount,
219 'log' => $status->log,
220 'transaction' => $status->transaction,
221 ];
222 }, 10, 3);
223 }
224
225 /**
226 * This method is used to configure the sms drivers framework.
227 * Here should be registered all the default drivers supported
228 * by the plugin.
229 *
230 * @return void
231 *
232 * @since 1.2
233 */
234 public static function configureSmsFramework()
235 {
236 // push the pre-installed drivers within the sms drivers list
237 add_filter('get_supported_sms_drivers_vikappointments', function($drivers)
238 {
239 $list = glob(VIKAPPOINTMENTS_LIBRARIES . DIRECTORY_SEPARATOR . 'sms' . DIRECTORY_SEPARATOR . '*.php');
240
241 return array_merge($drivers, $list);
242 });
243
244 // load sms handlers when dispatched
245 add_action('load_sms_driver_vikappointments', function(&$drivers, $driver)
246 {
247 $classname = null;
248
249 VikAppointmentsLoader::import('sms.' . $driver);
250
251 switch ($driver)
252 {
253 case 'clickatell':
254 $classname = 'VikAppointmentsSmsClickatell';
255 break;
256
257 case 'clicksend':
258 $classname = 'VikAppointmentsSmsClicksend';
259 break;
260
261 case 'cmtelecom':
262 $classname = 'VikAppointmentsSmsCmtelecom';
263 break;
264
265 case 'smshosting':
266 $classname = 'VikAppointmentsSmsHosting';
267 break;
268
269 case 'tellustalk':
270 $classname = 'VikAppointmentsSmsTellustalk';
271 break;
272
273 case 'whatsapp':
274 $classname = 'VikAppointmentsSmsWhatsapp';
275 break;
276 }
277
278 if ($classname)
279 {
280 $drivers[] = $classname;
281 }
282 }, 10, 2);
283 }
284
285 /**
286 * Registers all the widget contained within the modules folder.
287 *
288 * @return void
289 */
290 public static function setupWidgets()
291 {
292 JLoader::import('adapter.module.factory');
293
294 // load all the modules
295 JModuleFactory::load(VIKAPPOINTMENTS_BASE . DIRECTORY_SEPARATOR . 'modules');
296
297 /**
298 * Loads also the widgets to display within the
299 * admin dashboard of WordPress.
300 *
301 * @since 1.1.9
302 */
303 add_action('wp_dashboard_setup', function()
304 {
305 JLoader::import('adapter.dashboard.admin');
306
307 // set up folder containing the widget to load
308 $path = VIKAPPOINTMENTS_LIBRARIES . DIRECTORY_SEPARATOR . 'dashboard';
309 // define the classname prefix
310 $prefix = 'JDashboardWidgetVikAppointments';
311
312 try
313 {
314 // load and register widgets
315 JDashboardAdmin::load($path, $prefix);
316 }
317 catch (Exception $e)
318 {
319 // silently suppress exception to avoid breaking the website
320
321 if (VIKAPPOINTMENTS_DEBUG)
322 {
323 // propagate error in case of debug enabled
324 throw $e;
325 }
326 }
327 });
328 }
329
330 /**
331 * Configures the RSS feeds reader.
332 *
333 * @return JRssReader
334 *
335 * @since 1.1.9
336 */
337 public static function setupRssReader()
338 {
339 // autoload RSS handler class
340 JLoader::import('adapter.rss.reader');
341
342 /**
343 * Hook used to manipulate the RSS channels to which the plugin is subscribed.
344 *
345 * @param array $channels A list of RSS permalinks.
346 * @param boolean $status True to return only the published channels.
347 *
348 * @return array A list of supported channels.
349 *
350 * @since 1.1.9
351 */
352 $channels = apply_filters('vikappointments_fetch_rss_channels', array(), true);
353
354 if (VIKAPPOINTMENTS_DEBUG)
355 {
356 /**
357 * Filters the transient lifetime of the feed cache.
358 *
359 * @since 2.8.0
360 *
361 * @param integer $lifetime Cache duration in seconds. Default is 43200 seconds (12 hours).
362 * @param string $filename Unique identifier for the cache object.
363 */
364 add_filter('wp_feed_cache_transient_lifetime', function($time, $url) use ($channels)
365 {
366 // in case of debug enabled, cache the feeds only for 60 seconds
367 if ($url == $channels || in_array($url, $channels))
368 {
369 $time = 60;
370 }
371
372 return $time;
373 }, 10, 2);
374 }
375
376 // instantiate RSS reader
377 $rss = JRssReader::getInstance($channels, 'vikappointments');
378
379 /**
380 * Hook used to apply some stuff before returning the RSS reader.
381 *
382 * @param JRssReader &$rss The RSS reader handler.
383 *
384 * @return void
385 *
386 * @since 1.1.9
387 */
388 do_action_ref_array('vikappointments_before_use_rss', array(&$rss));
389
390 return $rss;
391 }
392
393 /**
394 * Extends the backup framework.
395 *
396 * @return void
397 *
398 * @since 1.2.3
399 */
400 public static function setupBackupSystem()
401 {
402 /**
403 * Anonymous function used to check whether the manifest includes the shortcodes import.
404 *
405 * @param object $manifest The backup manifest.
406 *
407 * @return boolean
408 */
409 $hasShortcodes = function($manifest)
410 {
411 // look for the uninstall directive inside the manifest, which is mainly
412 // used during the sample data installation
413 if (isset($manifest->uninstall))
414 {
415 // iterate all uninstall queries
416 foreach ((array) $manifest->uninstall as $query)
417 {
418 // look for a table that uninstall the shortcodes
419 if (preg_match("/#__vikappointments_wpshortcodes\b/", $query))
420 {
421 return true;
422 }
423 }
424 }
425
426 // look for the directive containing the installation rules
427 if (isset($manifest->installers))
428 {
429 // iterate all install rules
430 foreach ((array) $manifest->installers as $rule)
431 {
432 // detect SQL File role
433 if ($rule->role === 'sqlfile')
434 {
435 // check shortcodes into the file path
436 $target = [$rule->data->path];
437 }
438 else if ($rule->role === 'sql')
439 {
440 // search into all the provided queries
441 $target = (array) $rule->data;
442 }
443 else
444 {
445 // nothing to check
446 $target = [];
447 }
448
449 foreach ($target as $tmp)
450 {
451 // check whether the current target mentions the shortcodes database table
452 if (preg_match("/#__vikappointments_wpshortcodes\b/", $tmp))
453 {
454 return true;
455 }
456 }
457 }
458 }
459
460 return false;
461 };
462
463 /**
464 * Trigger event to allow third party plugins to extend the backup import.
465 * This hook triggers before processing the import of an existing backup.
466 *
467 * It is possible to throw an exception to prevent the import process.
468 *
469 * Uninstalls all the pages that have been assigned to the existing shortcodes.
470 *
471 * @param mixed $status Internal environment variable.
472 * @param object $manifest The backup manifest.
473 * @param string $path The path of the backup archive (uncompressed).
474 *
475 * @since 1.7.1
476 *
477 * @throws Exception
478 */
479 add_action('vikappointments_before_import_backup', function($status, $manifest, $path) use ($hasShortcodes)
480 {
481 if (!function_exists('is_plugin_active')) {
482 require_once ABSPATH . 'wp-admin/includes/plugin.php';
483 }
484
485 /**
486 * Abort the backup import process in case Rank Math SEO plugin is installed and active.
487 *
488 * @since 1.2.18
489 */
490 if (is_plugin_active('seo-by-rank-math/rank-math.php')) {
491 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);
492 }
493
494 // check whether the manifest includes the shortcodes installation
495 $manifest->shortcodes = $hasShortcodes($manifest);
496
497 if (empty($manifest->shortcodes))
498 {
499 // shortcodes not included within the backup, do not uninstall
500 return;
501 }
502
503 // get shortcode admin model
504 $model = JModel::getInstance('vikappointments', 'shortcodes', 'admin');
505
506 // get all existing shortcodes
507 $shortcodes = $model->all(array('createdon', 'post_id'));
508
509 // iterate all shortcodes found
510 foreach ($shortcodes as $shortcode)
511 {
512 // make sure the shortcode has been assigned to a post
513 if ($shortcode->post_id)
514 {
515 // get post details
516 $post = get_post((int) $shortcode->post_id);
517
518 // convert shortcode creation date
519 $shortcode->createdon = JFactory::getDate($shortcode->createdon);
520 // convert post creation date
521 $post->post_date_gmt = JFactory::getDate($post->post_date_gmt);
522
523 // compare ephocs and make sure the post was not created before the shortcode
524 if ((int) $shortcode->createdon->format('U') <= (int) $post->post_date_gmt->format('U'))
525 {
526 // permanently delete post
527 wp_delete_post($post->ID, $force_delete = true);
528 }
529 }
530 }
531 }, 10, 3);
532
533 /**
534 * Trigger event to allow third party plugins to extend the backup import.
535 * This hook triggers after processing the import of an existing backup.
536 *
537 * It is possible to throw an exception to prevent the import process.
538 *
539 * Assigns all the newly created shortcodes to new pages.
540 *
541 * @param mixed $status Internal environment variable.
542 * @param object $manifest The backup manifest.
543 * @param string $path The path of the backup archive (uncompressed).
544 *
545 * @since 1.7.1
546 *
547 * @throws Exception
548 */
549 add_action('vikappointments_after_import_backup', function($status, $manifest, $path)
550 {
551 if (empty($manifest->shortcodes))
552 {
553 // shortcodes not included within the backup, do not install
554 return;
555 }
556
557 // get shortcodes admin model
558 $listModel = JModel::getInstance('vikappointments', 'shortcodes', 'admin');
559
560 // get all existing shortcodes
561 $shortcodes = $listModel->all('id');
562
563 // get shortcode admin model
564 $model = JModel::getInstance('vikappointments', 'shortcode', 'admin');
565
566 // iterate all shortcodes found
567 foreach ($shortcodes as $shortcode)
568 {
569 // assign the shortcode to a new page
570 $model->addPage($shortcode->id);
571 }
572 }, 10, 3);
573
574 /**
575 * Trigger event to allow third party plugins to choose what are the columns to dump
576 * and whether the table should be skipped or not.
577 *
578 * Fires while attaching a rule to dump some SQL statements.
579 *
580 * Used to avoid dumping the post ID to which the shortcodes are attached
581 *
582 * @param boolean $include False to avoid including the table into the backup.
583 * @param array &$columns An associative array of supported database table columns,
584 * where the key is the column name and the value is a nested
585 * array holding the column information.
586 * @param string $table The name of the database table.
587 *
588 * @since 1.7.1
589 */
590 add_filter('vikappointments_before_backup_dump_sql', function($include, &$columns, $table)
591 {
592 if (is_null($include))
593 {
594 $include = true;
595 }
596
597 // check if we are exporting the shortcodes
598 if ($table === '#__vikappointments_wpshortcodes')
599 {
600 // avoid dumping the post ID column
601 unset($columns['post_id'], $columns['tmp_post_id']);
602 }
603
604 return $include;
605 }, 10, 3);
606 }
607
608 /**
609 * Sets up the wizard.
610 *
611 * @return void
612 *
613 * @since 1.2.3
614 */
615 public static function setupWizard()
616 {
617 add_action('vikappointments_setup_wizard', function($ret, $wizard)
618 {
619 // include path to load additional steps
620 $wizard->addIncludePath(VIKAPPOINTMENTS_LIBRARIES . DIRECTORY_SEPARATOR . 'wizard');
621 }, 10, 2);
622
623 add_action('vikappointments_after_setup_wizard', function($ret, $wizard)
624 {
625 // add step to manage the shortcodes
626 $wizard->addStep(new VAPWizardStepShortcodes());
627
628 // add packages and subscriptions steps as dependency
629 $wizard['shortcodes']->addDependency($wizard['syspack'], $wizard['syssubscr']);
630
631 // add step to install sample data (after taxes widget)
632 $wizard->addStepAfter(new VAPWizardStepSampleData(), 'taxes');
633 }, 10, 2);
634 }
635
636 /**
637 * Implements the tools needed to manage the overrides
638 * without having to use a FTP client.
639 *
640 * @return void
641 *
642 * @since 1.2.13
643 */
644 public static function setupOverridesManager()
645 {
646 // manually append fieldset to manage page overrides
647 add_action('vikappointments_display_view_configapp_customizer', function($forms, $view, $setup)
648 {
649 if (!$forms)
650 {
651 // init forms array
652 $forms = [];
653 }
654
655 // render configuration layout
656 $html = JLayoutHelper::render('html.overrides.config', [
657 'view' => $view,
658 ]);
659
660 // add fieldset to forms
661 $forms[__('Page Overrides', 'vikappointments')] = $html;
662
663 // register an icon for this new fieldset
664 $setup->icons[__('Page Overrides', 'vikappointments')] = 'fas fa-cut';
665
666 return $forms;
667 }, 10, 3);
668 }
669
670 /**
671 * Registers all the events used to backup the extendable files when needed.
672 *
673 * @return void
674 */
675 public static function setupMirroring()
676 {
677 /**
678 * Backup all the files below before accessing the configuration page.
679 *
680 * - custom CSS
681 * - theme CSS
682 * - mail templates
683 * - mail attachments
684 * - SMS gateways
685 */
686 add_action('vikappointments_before_display_editconfig', function()
687 {
688 // import update manager
689 VikAppointmentsLoader::import('update.manager');
690
691 try
692 {
693 // backup CSS themes
694 VikAppointmentsUpdateManager::doBackup(
695 // target to backup
696 VAPBASE . '/assets/css/themes',
697 // destination folder
698 VAP_UPLOAD_DIR_PATH . '/css/themes'
699 );
700
701 // backup mail attachments
702 VikAppointmentsUpdateManager::doBackup(
703 // target to backup
704 VAPMAIL_ATTACHMENTS,
705 // destination folder
706 VAP_UPLOAD_DIR_PATH . '/mail/attachments'
707 );
708
709 // backup mail templates
710 VikAppointmentsUpdateManager::doBackup(
711 // target to backup
712 VAPBASE . '/helpers/mail_tmpls',
713 // destination folder
714 VAP_UPLOAD_DIR_PATH . '/mail/tmpl'
715 );
716 }
717 catch (Exception $e)
718 {
719 // raise error and avoid breaking the flow
720 JFactory::getApplication()->enqueueMessage("Impossible to complete back-up.\n" . $e->getMessage(), 'error');
721 }
722 });
723
724 /**
725 * Backup all the files below before accessing the configuration page.
726 *
727 * - custom CSS
728 * - customizer
729 */
730 add_action('vikappointments_before_display_editconfigapp', function()
731 {
732 // import update manager
733 VikAppointmentsLoader::import('update.manager');
734
735 try
736 {
737 // backup custom CSS file
738 VikAppointmentsUpdateManager::doBackup(
739 // target to backup
740 VAPBASE . '/assets/css/vap-custom.css',
741 // destination folder
742 VAP_UPLOAD_DIR_PATH . '/css'
743 );
744
745 // backup CSS customizer
746 VikAppointmentsUpdateManager::doBackup(
747 // target to backup
748 VAPBASE . '/assets/css/customizer',
749 // destination folder
750 VAP_UPLOAD_DIR_PATH . '/css/customizer'
751 );
752 }
753 catch (Exception $e)
754 {
755 // raise error and avoid breaking the flow
756 JFactory::getApplication()->enqueueMessage("Impossible to complete back-up.\n" . $e->getMessage(), 'error');
757 }
758 });
759
760 /**
761 * Declare an anonymous function for the back-up that should be applied
762 * within the dashboard, as the default view might be different.
763 * For this reason, we should apply the back-up when visitin the
764 * dashboard, the calendar and the weekly calendar.
765 *
766 * @since 1.1.6
767 */
768 $dashboard_backup = function()
769 {
770 // check if we are doing an AJAX request
771 if (!wp_doing_ajax())
772 {
773 // import update manager
774 VikAppointmentsLoader::import('update.manager');
775
776 try
777 {
778 // backup cron jobs
779 VikAppointmentsUpdateManager::doBackup(
780 // target to backup
781 VIKAPPOINTMENTS_BASE . '/languages',
782 // destination folder
783 VAP_UPLOAD_DIR_PATH . '/languages'
784 );
785 }
786 catch (Exception $e)
787 {
788 // raise error and avoid breaking the flow
789 JFactory::getApplication()->enqueueMessage("Impossible to complete back-up.\n" . $e->getMessage(), 'error');
790 }
791 }
792 };
793
794 /**
795 * Backup all the files below before accessing the dashboard page.
796 *
797 * - languages
798 */
799 add_action('vikappointments_before_display_vikappointments', $dashboard_backup);
800 add_action('vikappointments_before_display_calendar', $dashboard_backup);
801 add_action('vikappointments_before_display_caldays', $dashboard_backup);
802
803 /**
804 * Backup all the files below before accessing the service management page.
805 *
806 * - mail attachments
807 *
808 * @since 1.2
809 */
810 add_action('vikappointments_before_display_manageservice', function()
811 {
812 // import update manager
813 VikAppointmentsLoader::import('update.manager');
814
815 try
816 {
817 // backup mail attachments
818 VikAppointmentsUpdateManager::doBackup(
819 // target to backup
820 VAPMAIL_ATTACHMENTS,
821 // destination folder
822 VAP_UPLOAD_DIR_PATH . '/mail/attachments'
823 );
824 }
825 catch (Exception $e)
826 {
827 // raise error and avoid breaking the flow
828 JFactory::getApplication()->enqueueMessage("Impossible to complete back-up.\n" . $e->getMessage(), 'error');
829 }
830 });
831 }
832 }
833