PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / libraries / system / install.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
install.php
657 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 VikAppointmentsLoader::import('update.manager');
15 VikAppointmentsLoader::import('update.license');
16
17 /**
18 * Class used to handle the activation, deactivation and
19 * uninstallation of VikAppointments plugin.
20 *
21 * @since 1.0
22 */
23 class VikAppointmentsInstaller
24 {
25 /**
26 * Flag used to init the class only once.
27 *
28 * @var boolean
29 */
30 protected static $init = false;
31
32 /**
33 * Initialize the class attaching wp actions.
34 *
35 * @return void
36 */
37 public static function onInit()
38 {
39 // init only if not done yet
40 if (static::$init === false)
41 {
42 // handle installation message
43 add_action('admin_notices', array('VikAppointmentsInstaller', 'handleMessage'));
44
45 /**
46 * Register hooks and actions here
47 */
48
49 // mark flag as true to avoid init it again
50 static::$init = true;
51 }
52 }
53
54 /**
55 * Handles the activation of the plugin.
56 *
57 * @param boolean $message True to display the activation message,
58 * false to ignore it.
59 *
60 * @return void
61 */
62 public static function activate($message = true)
63 {
64 // get installed software version
65 $version = get_option('vikappointments_software_version', null);
66
67 // check if the plugin has been already installed
68 if (is_null($version))
69 {
70 // dispatch UPDATER to launch installation queries
71 VikAppointmentsUpdateManager::install();
72
73 // mark the plugin has installed to avoid duplicated installation queries
74 update_option('vikappointments_software_version', VIKAPPOINTMENTS_SOFTWARE_VERSION);
75 }
76
77 if ($message)
78 {
79 // set activation flag to display a message
80 add_option('vikappointments_onactivate', 1);
81 }
82 }
83
84 /**
85 * Handles the deactivation of the plugin.
86 *
87 * @return void
88 */
89 public static function deactivate()
90 {
91 // do nothing for the moment
92 }
93
94 /**
95 * Handles the uninstallation of the plugin.
96 *
97 * @param boolean $drop True to drop the tables of VikAppointments from the database.
98 *
99 * @return void
100 */
101 public static function uninstall($drop = true)
102 {
103 // dispatch UPDATER to drop database tables
104 VikAppointmentsUpdateManager::uninstall($drop);
105
106 // delete installation flag
107 delete_option('vikappointments_software_version');
108 }
109
110 /**
111 * Handles the uninstallation of the plugin.
112 * Proxy for uninstall method which always force database drop.
113 *
114 * @return void
115 *
116 * @uses uninstall()
117 */
118 public static function delete()
119 {
120 // complete uninstallation by dropping the database
121 static::uninstall(true);
122 }
123
124 /**
125 * Checks if the current version should be updated
126 * and, eventually, processes it.
127 *
128 * @return void
129 */
130 public static function update()
131 {
132 // get installed software version
133 $version = get_option('vikappointments_software_version', null);
134
135 $app = JFactory::getApplication();
136
137 // check if we are running an older version
138 if (VikAppointmentsUpdateManager::shouldUpdate($version))
139 {
140 // avoid useless redirections if doing ajax
141 if (!wp_doing_ajax() && $app->isAdmin() && JFactory::getUser()->authorise('core.admin', 'com_vikappointments'))
142 {
143 // Turn on maintenance mode before running the update.
144 // In case the maintenance mode was already active, then
145 // an error message will be thrown.
146 static::setMaintenance(true);
147
148 // process the update (we don't need to raise an error)
149 VikAppointmentsUpdateManager::update($version);
150
151 // update cached plugin version
152 update_option('vikappointments_software_version', VIKAPPOINTMENTS_SOFTWARE_VERSION);
153
154 // update internal configuration version
155 VAPFactory::getConfig()->set('version', VIKAPPOINTMENTS_SOFTWARE_VERSION);
156
157 // deactivate the maintenance mode on update completion
158 static::setMaintenance(false);
159
160 /**
161 * Check if pro version, but attempt to re-download the Pro settings
162 * within the current loading flow rather than redirecting. In case
163 * something goes wrong, fallback to the old "get pro" redirect method.
164 *
165 * @since 1.2.14
166 */
167 if (VikAppointmentsLicense::isPro())
168 {
169 // load license model
170 $model = JModel::getInstance('vikappointments', 'license', 'admin');
171
172 // download PRO version hoping that all will go fine
173 $result = $model->download(VikAppointmentsLicense::getKey());
174
175 if ($result === false)
176 {
177 // an error occurred, retrieve it as exception
178 $error = $model->getError(null, $toString = true);
179
180 // display exception error
181 $app->enqueueMessage($error, 'error');
182
183 // fallback to the pro-package download page (old method)
184 $app->redirect('index.php?option=com_vikappointments&view=getpro&version=' . $version);
185 $app->close();
186 }
187 }
188 }
189 }
190 // check if the current instance is a new blog of a network
191 else if (is_null($version))
192 {
193 /**
194 * The version is NULL, vikappointments_software_version doesn't
195 * exist as an option of this blog.
196 * We need to launch the installation manually.
197 *
198 * @see activate()
199 */
200
201 // Use FALSE to ignore the activation message
202 static::activate(false);
203 }
204 }
205
206 /**
207 * Callback used to complete the update of the plugin
208 * made after a scheduled event.
209 *
210 * @param array $results The results of all attempted updates.
211 *
212 * @return void
213 *
214 * @since 1.1.11
215 */
216 public static function automaticUpdate($results)
217 {
218 // create log trace
219 $trace = '### VikAppointments Automatic Update | ' . JHtml::fetch('date', 'now', 'Y-m-d H:i:s') . "\n\n";
220 $trace .= "```json\n" . json_encode($results, JSON_PRETTY_PRINT) . "\n```\n\n";
221
222 // iterate all plugins
223 foreach ($results['plugin'] as $plugin)
224 {
225 if (!empty($plugin->item->slug))
226 {
227 // register check trace
228 $trace .= "Does `{$plugin->item->slug}` match `vikappointments`?\n\n";
229
230 // make sure the plugin slug matches this one
231 if ($plugin->item->slug == 'vikappointments')
232 {
233 // register status trace
234 $trace .= "Did WP complete the update without errors? [" . ($plugin->result ? 'Y' : 'N') . "]\n\n";
235
236 // plugin found, make sure the update was successful
237 if ($plugin->result)
238 {
239 try
240 {
241 // register version trace
242 $trace .= sprintf("Updating from [%s] to [%s]...\n\n", VIKAPPOINTMENTS_SOFTWARE_VERSION, $plugin->item->new_version);
243
244 // complete the update in background
245 static::backgroundUpdate($plugin->item->new_version);
246
247 // update completed without errors
248 $trace .= "Background update completed\n\n";
249 }
250 catch (Exception $e)
251 {
252 // something went wrong, register error within the trace
253 $trace .= sprintf(
254 "An error occurred while trying to finalize the update (%d):\n> %s\n\n",
255 $e->getCode(),
256 $e->getMessage()
257 );
258
259 /**
260 * @todo An error occurred while trying to download the PRO version,
261 * evaluate to send an e-mail to the administrator.
262 */
263 }
264 }
265 }
266 }
267 }
268
269 // register debug trace within a log file
270 JLoader::import('adapter.filesystem.file');
271 JFile::write(VIKAPPOINTMENTS_BASE . DIRECTORY_SEPARATOR . 'au-log.md', $trace . "---\n\n");
272 }
273
274 /**
275 * Same as update task, but all made in background.
276 *
277 * @param string $new_version The new version of the plugin.
278 *
279 * @return void
280 *
281 * @since 1.1.11
282 */
283 protected static function backgroundUpdate($new_version)
284 {
285 // get installed software version
286 $version = get_option('vikappointments_software_version', null);
287
288 // DO NOT use shouldUpdate method because, since we are always within
289 // the same flow, the version constant is still referring to the previous
290 // version. So, always assume to proceed with the update of the plugin.
291
292 // Turn on maintenance mode before running the update.
293 // In case the maintenance mode was already active, then
294 // an error message will be thrown.
295 static::setMaintenance(true);
296
297 // process the update (we don't need to raise an error)
298 VikAppointmentsUpdateManager::update($version);
299
300 // update cached plugin version
301 update_option('vikappointments_software_version', $new_version);
302
303 // update internal configuration version
304 VAPFactory::getConfig()->set('version', $new_version);
305
306 // deactivate the maintenance mode on update completion
307 static::setMaintenance(false);
308
309 // check if pro version
310 if (VikAppointmentsLicense::isPro())
311 {
312 // load license model
313 $model = JModel::getInstance('vikappointments', 'license', 'admin');
314
315 // download PRO version hoping that all will go fine
316 $result = $model->download(VikAppointmentsLicense::getKey());
317
318 if ($result === false)
319 {
320 // an error occurred, retrieve it as exception
321 $error = $model->getError(null, $toString = false);
322
323 // propagate exception
324 throw $error;
325 }
326 }
327 }
328
329 /**
330 * Checks whether the automatic updates should be turned off.
331 * This is useful to prevent auto-updates for those customers
332 * that are running an expired PRO version. This will avoid
333 * losing the files after an unexpected update.
334 *
335 * @param boolean $update The current auto-update choice.
336 * @param object $item The plugin offer.
337 *
338 * @return mixed Null to let WP decides, false to always deny it.
339 *
340 * @since 1.1.11
341 */
342 public static function useAutoUpdate($update, $item)
343 {
344 // make sure we are fetching VikAppointments
345 if (!empty($item->slug) && $item->slug == 'vikappointments')
346 {
347 // plugin found, lets check whether the user is
348 // not running the PRO version
349 if (!VikAppointmentsLicense::isPro())
350 {
351 // not a PRO version, check whether a license
352 // key was registered
353 if (VikAppointmentsLicense::getKey())
354 {
355 // The plugin registered a key; the customer
356 // chose to let the license expires...
357 // We need to prevent auto-updates.
358 $update = false;
359 }
360 }
361 }
362
363 return $update;
364 }
365
366 /**
367 * Toggle maintenance mode for the site.
368 * Creates/deletes the maintenance file to enable/disable maintenance mode.
369 *
370 * @param boolean $enable True to enable maintenance mode, false to disable.
371 *
372 * @return void
373 *
374 * @since 1.2
375 */
376 protected static function setMaintenance($enable)
377 {
378 $maintenance_file_path = VIKAPPOINTMENTS_BASE . '/maintenance.txt';
379
380 if ($enable)
381 {
382 /**
383 * Check if we are in maintenance mode.
384 *
385 * @since 1.2.14 Go ahead in case the user clicked the "Retry" link.
386 */
387 if (JFile::exists($maintenance_file_path) && JFactory::getApplication()->input->getBool('disable_maintenance_mode', false) == false)
388 {
389 // default die message
390 $message = sprintf(
391 '<h1>%s</h1><p>%s</p>',
392 __('Maintenance'),
393 __('VikAppointments plugin is in maintenance mode. Please wait for the update completion.', 'vikappointments')
394 );
395
396 $args = [
397 // HTTP error code "locked"
398 'code' => 423,
399 ];
400
401 /**
402 * In case the update process is taking more than 5 minutes, warn the user
403 * and add the possibility to retry.
404 *
405 * @since 1.2.14
406 */
407 if (filemtime($maintenance_file_path) < strtotime('-5 minutes'))
408 {
409 $uri = new JUri(JUri::current());
410 $uri->setVar('disable_maintenance_mode', 1);
411
412 // set link for the message to be displayed
413 $args['link_url'] = (string) $uri;
414 $args['link_text'] = __('Retry');
415
416 // warn the user
417 $message .= sprintf(
418 '<p>%s<br />%s</p>',
419 __('The update process is taking too long...', 'vikappointments'),
420 __('Click the link below to retry the update process. Do not leave or refresh the page.', 'vikappointments')
421 );
422 }
423
424 // raise error message in case the update process is currently running
425 wp_die($message, __('Maintenance'), $args);
426 }
427
428 // ignore the maximum execution time to let the server safely completes the update
429 ignore_user_abort(true);
430 set_time_limit(0);
431
432 // enter in maintenance mode for the current version
433 JFile::write($maintenance_file_path, VIKAPPOINTMENTS_SOFTWARE_VERSION);
434 }
435 else
436 {
437 // turn off maintenance mode
438 JFile::delete($maintenance_file_path);
439 }
440 }
441
442 /**
443 * In case of an expired PRO version, prompts a message informing
444 * the user that it is going to lose the PRO features.
445 *
446 * @param array $data An array of plugin metadata.
447 * @param array $response An array of metadata about the available plugin update.
448 *
449 * @return void
450 *
451 * @since 1.1.11
452 */
453 public static function getUpdateMessage($data, $response)
454 {
455 // check whether the user is not running the PRO version
456 if (!VikAppointmentsLicense::isPro())
457 {
458 // not a PRO version, check whether a license
459 // key was registered
460 if (VikAppointmentsLicense::getKey())
461 {
462 // The plugin registered a key; the customer
463 // chose to let the license expires...
464 // We need to display an alert.
465 add_action('admin_footer', function() use ($data, $response)
466 {
467 // display layout
468 echo JLayoutHelper::render(
469 'html.license.update',
470 array($data, $response),
471 null,
472 array('component' => 'com_vikappointments')
473 );
474 });
475 }
476 }
477 }
478
479 /**
480 * Helper method used to obtain the list of breaking
481 * changes registered within the wordpress options.
482 *
483 * @return array
484 *
485 * @since 1.2.13
486 */
487 public static function getBreakingChanges()
488 {
489 // get existing breaking changes
490 $files = get_option('vikappointments_breaking_changes');
491 // decode from JSON
492 return (array) ($files ? json_decode($files, true) : null);
493 }
494
495 /**
496 * Helper method used to register a list of breaking
497 * changes within the wordpress options.
498 *
499 * @param array $files The files to register.
500 *
501 * @return void
502 *
503 * @since 1.2.13
504 */
505 public static function registerBreakingChanges(array $files)
506 {
507 if ($files)
508 {
509 // get existing files
510 $existing = static::getBreakingChanges();
511
512 foreach ($files as $client => $list)
513 {
514 if (!$list)
515 {
516 // ignore in case the list is empty
517 continue;
518 }
519
520 if (!isset($existing[$client]))
521 {
522 // no client set, register it right now
523 $existing[$client] = (array) $list;
524 }
525 else
526 {
527 // client already set, merge existing with new ones (get rid of duplicates)
528 $existing[$client] = array_values(array_unique(array_merge($existing[$client], (array) $list)));
529 }
530 }
531
532 // register within an option the breaking changes
533 update_option('vikappointments_breaking_changes', json_encode($existing));
534 }
535 }
536
537 /**
538 * Helper method used to unregister a list of breaking
539 * changes within the wordpress options.
540 *
541 * @param string|array|null $files The file(s) to unregister.
542 * Leave empty to clear all the files.
543 *
544 * @return void
545 *
546 * @since 1.2.13
547 */
548 public static function unregisterBreakingChanges($files = null)
549 {
550 if ($files)
551 {
552 $existing = static::getBreakingChanges();
553
554 // scan all the provided files
555 foreach ((array) $files as $file)
556 {
557 // search path under each client
558 foreach ($existing as $client => $list)
559 {
560 // get index of the specified file
561 $index = array_search($file, $list);
562
563 if ($index === false)
564 {
565 // path not found, ignore
566 continue;
567 }
568
569 // remove path at the specified index
570 array_splice($existing[$client], $index, 1);
571
572 if (!$existing[$client])
573 {
574 // remove client as there are no more pending overrides
575 unset($existing[$client]);
576 }
577 }
578 }
579
580 if ($existing)
581 {
582 // update breaking changes in case the list is not empty
583 update_option('vikappointments_breaking_changes', json_encode($existing));
584 }
585 else
586 {
587 // directly delete the breaking changes option otherwise
588 static::unregisterBreakingChanges();
589 }
590 }
591 else
592 {
593 // clear breaking changes
594 delete_option('vikappointments_breaking_changes');
595 }
596 }
597
598 /**
599 * Helper method used to show the breaking changes to
600 * the administrator. This method will have no effect
601 * in case the logged-in user is not an administrator.
602 *
603 * @return void
604 *
605 * @since 1.2.13
606 */
607 public static function showBreakingChanges()
608 {
609 // make sure the user is an administrator
610 if (JFactory::getUser()->authorise('core.admin', 'com_vikappointments'))
611 {
612 // retieve breaking changes list, if any
613 $bc = static::getBreakingChanges();
614
615 if ($bc)
616 {
617 // use layout to render the warning message
618 $warn = JLayoutHelper::render(
619 'html.overrides.bc',
620 ['files' => $bc],
621 null,
622 ['component' => 'com_vikappointments']
623 );
624
625 // enqueue warning to be displayed
626 JFactory::getApplication()->enqueueMessage($warn, 'warning');
627 }
628 }
629 }
630
631 /**
632 * Method used to check for any installation message to show.
633 *
634 * @return void
635 */
636 public static function handleMessage()
637 {
638 $app = JFactory::getApplication();
639
640 // if we are in the admin section and the plugin has been activated
641 if ($app->isAdmin() && get_option('vikappointments_onactivate') == 1)
642 {
643 // delete the activation flag to avoid displaying the message more than once
644 delete_option('vikappointments_onactivate');
645
646 ?>
647 <div class="notice is-dismissible notice-success">
648 <p>
649 <strong>Thanks for activating our plugin!</strong>
650 <a href="https://vikwp.com" target="_blank">https://vikwp.com</a>
651 </p>
652 </div>
653 <?php
654 }
655 }
656 }
657