Version Description
2022-10-24 =
TWEAK: Add admin notice for DISABLE_WP_CRON constant and remove notices regarding WP_AUTO_UPDATE_CORE and AUTOMATIC_UPDATER_DISABLED constants as they now won't prevent automatic updates from being run
FIX: Core minor updates became major when two updates-related scheduled events got triggered in the same process
FIX: In some cases the serialisation of call stack could cause a PHP fatal error due to the output of a backtrace (debug_backtrace) containing 'Closure' (anonymous) functions
TWEAK: Update notice class
Download this release
Release Info
Developer | DavidAnderson |
Plugin | Easy Updates Manager |
Version | 9.0.14 |
Comparing to | |
See all releases |
Code changes from version 9.0.13 to 9.0.14
- includes/MPSUM_Admin.php +1 -1
- includes/MPSUM_Constant_Checks.php +7 -9
- includes/MPSUM_Disable_Updates.php +18 -8
- includes/MPSUM_Logs.php +43 -4
- {js → includes/blockui}/jquery.blockUI.js +16 -13
- includes/blockui/jquery.blockUI.min.js +1 -0
- includes/easy-updates-manager-notices.php +2 -2
- js/jquery.blockUI.min.js +0 -1
- languages/stops-core-theme-and-plugin-updates.pot +12 -12
- main.php +9 -9
- readme.txt +9 -2
- templates/notices/dashboard-constant-warning.php +12 -15
- vendor/autoload.php +7 -0
- vendor/composer/ClassLoader.php +481 -0
- vendor/composer/InstalledVersions.php +337 -0
- vendor/composer/LICENSE +21 -0
- vendor/composer/autoload_classmap.php +10 -0
- vendor/composer/autoload_namespaces.php +9 -0
- vendor/composer/autoload_psr4.php +9 -0
- vendor/composer/autoload_real.php +55 -0
- vendor/composer/autoload_static.php +20 -0
- vendor/composer/installed.json +42 -0
- vendor/composer/installed.php +32 -0
- vendor/team-updraft/common-libs/CI/php-compatibility.xml +17 -0
- vendor/team-updraft/common-libs/CI/php-syntax-check.xml +12 -0
- vendor/team-updraft/common-libs/README.md +10 -0
- vendor/team-updraft/common-libs/composer.json +21 -0
- {includes → vendor/team-updraft/common-libs/src/updraft-notices}/updraft-notices.php +57 -36
- vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc.php +1115 -0
- vendor/team-updraft/common-libs/src/updraft-semaphore/class-updraft-semaphore.php +213 -0
- vendor/team-updraft/common-libs/src/updraft-semaphore/test.php +56 -0
- vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-manager-commands.php +188 -0
- vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-manager.php +387 -0
- vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-meta.php +96 -0
- vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-options.php +127 -0
- vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php +693 -0
- vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php +207 -0
includes/MPSUM_Admin.php
CHANGED
@@ -271,7 +271,7 @@ class MPSUM_Admin {
|
|
271 |
wp_enqueue_style('thickbox');
|
272 |
}
|
273 |
|
274 |
-
wp_enqueue_script('jquery-blockui', MPSUM_Updates_Manager::get_plugin_url('/
|
275 |
wp_enqueue_script('jquery-serializejson', MPSUM_Updates_Manager::get_plugin_url('/js/jquery.serializejson' . $min_or_not . '.js'), array('jquery'), EASY_UPDATES_MANAGER_VERSION, true);
|
276 |
wp_enqueue_script('mpsum_dashboard_js', MPSUM_Updates_Manager::get_plugin_url('/js/eum-admin' . $min_or_not .'.js'), array( 'jquery', 'wp-ajax-response' ), EASY_UPDATES_MANAGER_VERSION, true);
|
277 |
wp_enqueue_script('mpsum_dashboard_react', MPSUM_Updates_Manager::get_plugin_url('/js/admin' . $min_or_not . '.js'), array( 'jquery', 'mpsum_dashboard_js' ), EASY_UPDATES_MANAGER_VERSION, true);
|
271 |
wp_enqueue_style('thickbox');
|
272 |
}
|
273 |
|
274 |
+
wp_enqueue_script('jquery-blockui', MPSUM_Updates_Manager::get_plugin_url('/includes/blockui/jquery.blockUI' . $min_or_not . '.js'), array('jquery'), EASY_UPDATES_MANAGER_VERSION, true);
|
275 |
wp_enqueue_script('jquery-serializejson', MPSUM_Updates_Manager::get_plugin_url('/js/jquery.serializejson' . $min_or_not . '.js'), array('jquery'), EASY_UPDATES_MANAGER_VERSION, true);
|
276 |
wp_enqueue_script('mpsum_dashboard_js', MPSUM_Updates_Manager::get_plugin_url('/js/eum-admin' . $min_or_not .'.js'), array( 'jquery', 'wp-ajax-response' ), EASY_UPDATES_MANAGER_VERSION, true);
|
277 |
wp_enqueue_script('mpsum_dashboard_react', MPSUM_Updates_Manager::get_plugin_url('/js/admin' . $min_or_not . '.js'), array( 'jquery', 'mpsum_dashboard_js' ), EASY_UPDATES_MANAGER_VERSION, true);
|
includes/MPSUM_Constant_Checks.php
CHANGED
@@ -30,17 +30,15 @@ class MPSUM_CONSTANT_CHECKS {
|
|
30 |
}
|
31 |
|
32 |
/**
|
33 |
-
*
|
34 |
*
|
35 |
-
* @return
|
36 |
*/
|
37 |
-
public function
|
38 |
-
|
39 |
-
|
|
|
40 |
}
|
41 |
-
|
42 |
-
return true;
|
43 |
-
}
|
44 |
-
return false;
|
45 |
}
|
46 |
}
|
30 |
}
|
31 |
|
32 |
/**
|
33 |
+
* Get a list of constants which are active but prohibited
|
34 |
*
|
35 |
+
* @return array a list of constants that may prevent automatic updates from being work properly
|
36 |
*/
|
37 |
+
public function get_prohibited_active_constants() {
|
38 |
+
$constants = array();
|
39 |
+
if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) {
|
40 |
+
$constants[] = 'DISABLE_WP_CRON';
|
41 |
}
|
42 |
+
return $constants;
|
|
|
|
|
|
|
43 |
}
|
44 |
}
|
includes/MPSUM_Disable_Updates.php
CHANGED
@@ -16,6 +16,8 @@ class MPSUM_Disable_Updates {
|
|
16 |
*/
|
17 |
private static $instance = null;
|
18 |
|
|
|
|
|
19 |
/**
|
20 |
* Set a class instance.
|
21 |
*
|
@@ -85,6 +87,10 @@ class MPSUM_Disable_Updates {
|
|
85 |
// Core Minor Updates
|
86 |
add_filter('allow_minor_auto_core_updates', array($this, 'core_should_update_to_new_version'), PHP_INT_MAX - 10);
|
87 |
|
|
|
|
|
|
|
|
|
88 |
// Manually update / Disables Core Automatic Updates
|
89 |
// When the __return_false function is hooked to the three filters above, that means core automatic updates is disabled or it's a manually update
|
90 |
}
|
@@ -369,24 +375,21 @@ class MPSUM_Disable_Updates {
|
|
369 |
/**
|
370 |
* Determine whether WordPress Core should update to a new version or not
|
371 |
*
|
372 |
-
* @param boolean $upgrade Whether to update or not
|
373 |
* @return boolean True if we should update to the new version, false otherwise
|
374 |
*/
|
375 |
-
public function core_should_update_to_new_version(
|
376 |
|
377 |
$core_options = MPSUM_Updates_Manager::get_options('core');
|
378 |
|
379 |
if (doing_filter('allow_dev_auto_core_updates')) {
|
380 |
-
$
|
381 |
} elseif (doing_filter('allow_major_auto_core_updates')) {
|
382 |
-
$
|
383 |
} elseif (doing_filter('allow_minor_auto_core_updates')) {
|
384 |
-
$
|
385 |
}
|
386 |
-
|
387 |
-
add_filter('auto_update_core', $upgrade ? '__return_true' : '__return_false', PHP_INT_MAX - 10);
|
388 |
|
389 |
-
return $
|
390 |
}
|
391 |
|
392 |
/**
|
@@ -400,4 +403,11 @@ class MPSUM_Disable_Updates {
|
|
400 |
if ('automatic_updater' === $context) return true;
|
401 |
return $file_mod_allowed;
|
402 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
403 |
}
|
16 |
*/
|
17 |
private static $instance = null;
|
18 |
|
19 |
+
private $is_core_updating_allowed = true;
|
20 |
+
|
21 |
/**
|
22 |
* Set a class instance.
|
23 |
*
|
87 |
// Core Minor Updates
|
88 |
add_filter('allow_minor_auto_core_updates', array($this, 'core_should_update_to_new_version'), PHP_INT_MAX - 10);
|
89 |
|
90 |
+
// Core Global Updates - a hook for making final decision as to whether core updating is allowed after examing other core-related hooks, also ensuring that no other filters will override our value
|
91 |
+
add_filter('auto_update_core', array($this, 'is_core_updating_allowed'), PHP_INT_MAX - 10);
|
92 |
+
if (!isset($core_options['core_updates']) || 'on' == $core_options['core_updates']) $this->is_core_updating_allowed = false; // on means manually update
|
93 |
+
|
94 |
// Manually update / Disables Core Automatic Updates
|
95 |
// When the __return_false function is hooked to the three filters above, that means core automatic updates is disabled or it's a manually update
|
96 |
}
|
375 |
/**
|
376 |
* Determine whether WordPress Core should update to a new version or not
|
377 |
*
|
|
|
378 |
* @return boolean True if we should update to the new version, false otherwise
|
379 |
*/
|
380 |
+
public function core_should_update_to_new_version() {
|
381 |
|
382 |
$core_options = MPSUM_Updates_Manager::get_options('core');
|
383 |
|
384 |
if (doing_filter('allow_dev_auto_core_updates')) {
|
385 |
+
$this->is_core_updating_allowed = isset($core_options['core_updates']) && in_array($core_options['core_updates'], array('automatic', 'automatic_minor')) && isset($core_options['automatic_development_updates']) && 'on' == $core_options['automatic_development_updates'] ? true : false;
|
386 |
} elseif (doing_filter('allow_major_auto_core_updates')) {
|
387 |
+
$this->is_core_updating_allowed = isset($core_options['core_updates']) && 'automatic' === $core_options['core_updates'] ? true : false;
|
388 |
} elseif (doing_filter('allow_minor_auto_core_updates')) {
|
389 |
+
$this->is_core_updating_allowed = isset($core_options['core_updates']) && in_array($core_options['core_updates'], array('automatic', 'automatic_minor')) ? true : false;
|
390 |
}
|
|
|
|
|
391 |
|
392 |
+
return $this->is_core_updating_allowed;
|
393 |
}
|
394 |
|
395 |
/**
|
403 |
if ('automatic_updater' === $context) return true;
|
404 |
return $file_mod_allowed;
|
405 |
}
|
406 |
+
|
407 |
+
/**
|
408 |
+
* Return the value that previously has been looked over the core-related filters as to whether core updating is allowed or not
|
409 |
+
*/
|
410 |
+
public function is_core_updating_allowed() {
|
411 |
+
return $this->is_core_updating_allowed;
|
412 |
+
}
|
413 |
}
|
includes/MPSUM_Logs.php
CHANGED
@@ -99,11 +99,18 @@ class MPSUM_Logs {
|
|
99 |
*/
|
100 |
public function perform_shutdown_task() {
|
101 |
if (!is_array($this->log_messages) || empty($this->log_messages)) return;
|
102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
foreach ($this->log_messages as $type => $entities) {
|
104 |
if (!is_array($entities) || empty($entities)) continue;
|
105 |
foreach ($entities as $data) {
|
106 |
-
$this->insert_log($data['name'], $type, $data['from'], $data['to'], $this->auto_update ? 'automatic' : 'manual', 0, get_current_user_id(), '', $stacktrace);
|
107 |
}
|
108 |
}
|
109 |
}
|
@@ -253,7 +260,14 @@ class MPSUM_Logs {
|
|
253 |
*/
|
254 |
public function log_updates($wp_upgrader, $hook_extra) {
|
255 |
if (isset($hook_extra['action']) && 'update' !== $hook_extra['action']) return;
|
256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
257 |
$notes = '';
|
258 |
if (is_a($wp_upgrader->skin, 'Automatic_Upgrader_Skin')) {
|
259 |
foreach ($wp_upgrader->skin->get_upgrade_messages() as $message) {
|
@@ -362,7 +376,14 @@ class MPSUM_Logs {
|
|
362 |
*/
|
363 |
public function log_automatic_updates($update_results) {
|
364 |
if (empty($update_results)) return;
|
365 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
foreach ($update_results as $type => $results) {
|
367 |
foreach ($results as $result) {
|
368 |
$notes = '';
|
@@ -629,4 +650,22 @@ class MPSUM_Logs {
|
|
629 |
$wpdb->query($sql);
|
630 |
delete_site_option('mpsum_log_table_version');
|
631 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
632 |
}
|
99 |
*/
|
100 |
public function perform_shutdown_task() {
|
101 |
if (!is_array($this->log_messages) || empty($this->log_messages)) return;
|
102 |
+
try {
|
103 |
+
$stacktrace = maybe_serialize(apply_filters('eum_normalized_call_stack_args', $this->normalise_call_stack_args(debug_backtrace(false)))); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
|
104 |
+
} catch (Exception $e) {
|
105 |
+
$stacktrace = serialize(array()); // if an exception still happens even after the call stack is already normalised then we won't provide stacktrace for a log entry
|
106 |
+
// @codingStandardsIgnoreLine
|
107 |
+
} catch (Error $e) {
|
108 |
+
$stacktrace = serialize(array());
|
109 |
+
}
|
110 |
foreach ($this->log_messages as $type => $entities) {
|
111 |
if (!is_array($entities) || empty($entities)) continue;
|
112 |
foreach ($entities as $data) {
|
113 |
+
$this->insert_log($data['name'], $type, $data['from'], $data['to'], $this->auto_update ? 'automatic' : 'manual', doing_action('upgrader_process_complete') || doing_action('automatic_updates_complete') ? 1 : 0, get_current_user_id(), '', $stacktrace);
|
114 |
}
|
115 |
}
|
116 |
}
|
260 |
*/
|
261 |
public function log_updates($wp_upgrader, $hook_extra) {
|
262 |
if (isset($hook_extra['action']) && 'update' !== $hook_extra['action']) return;
|
263 |
+
try {
|
264 |
+
$stacktrace = maybe_serialize(apply_filters('eum_normalized_call_stack_args', $this->normalise_call_stack_args(debug_backtrace(false)))); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
|
265 |
+
} catch (Exception $e) {
|
266 |
+
$stacktrace = serialize(array()); // if an exception still happens even after the call stack is already normalised then we won't provide stacktrace for a log entry
|
267 |
+
// @codingStandardsIgnoreLine
|
268 |
+
} catch (Error $e) {
|
269 |
+
$stacktrace = serialize(array());
|
270 |
+
}
|
271 |
$notes = '';
|
272 |
if (is_a($wp_upgrader->skin, 'Automatic_Upgrader_Skin')) {
|
273 |
foreach ($wp_upgrader->skin->get_upgrade_messages() as $message) {
|
376 |
*/
|
377 |
public function log_automatic_updates($update_results) {
|
378 |
if (empty($update_results)) return;
|
379 |
+
try {
|
380 |
+
$stacktrace = maybe_serialize(apply_filters('eum_normalized_call_stack_args', $this->normalise_call_stack_args(debug_backtrace(false)))); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
|
381 |
+
} catch (Exception $e) {
|
382 |
+
$stacktrace = serialize(array()); // if an exception still happens even after the call stack is already normalised then we won't provide stacktrace for a log entry
|
383 |
+
// @codingStandardsIgnoreLine
|
384 |
+
} catch (Error $e) {
|
385 |
+
$stacktrace = serialize(array());
|
386 |
+
}
|
387 |
foreach ($update_results as $type => $results) {
|
388 |
foreach ($results as $result) {
|
389 |
$notes = '';
|
650 |
$wpdb->query($sql);
|
651 |
delete_site_option('mpsum_log_table_version');
|
652 |
}
|
653 |
+
|
654 |
+
/**
|
655 |
+
* Normalise call stacks by clearing out unnecessary objects from their arguments list, leaving only the first arguments as a string. The call stacks should be one that is generated by debug_backtrace() function.
|
656 |
+
*
|
657 |
+
* @param array $backtrace The output of the debug_backtrace() function
|
658 |
+
* @return array An array of associative arrays after being normalised
|
659 |
+
*/
|
660 |
+
public function normalise_call_stack_args($backtrace) {
|
661 |
+
foreach ((array) $backtrace as $index => $element) {
|
662 |
+
if (!isset($element['args']) || !is_array($element['args']) || !isset($element['args'][0])) $backtrace[$index]['args'] = array('');
|
663 |
+
if (is_object($backtrace[$index]['args'][0])) {
|
664 |
+
$backtrace[$index]['args'] = array(get_class($backtrace[$index]['args'][0]));
|
665 |
+
} elseif (!is_string($backtrace[$index]['args'][0])) {
|
666 |
+
$backtrace[$index]['args'] = array('');
|
667 |
+
}
|
668 |
+
}
|
669 |
+
return $backtrace;
|
670 |
+
}
|
671 |
}
|
{js → includes/blockui}/jquery.blockUI.js
RENAMED
@@ -1,7 +1,7 @@
|
|
1 |
/*!
|
2 |
* jQuery blockUI plugin
|
3 |
-
* Version 2.
|
4 |
-
* Requires jQuery v1.
|
5 |
*
|
6 |
* Examples at: http://malsup.com/jquery/block/
|
7 |
* Copyright (c) 2007-2013 M. Alsup
|
@@ -17,6 +17,9 @@
|
|
17 |
"use strict";
|
18 |
|
19 |
function setup($) {
|
|
|
|
|
|
|
20 |
$.fn._fadeIn = $.fn.fadeIn;
|
21 |
|
22 |
var noOp = $.noop || function() {};
|
@@ -26,7 +29,7 @@
|
|
26 |
var msie = /MSIE/.test(navigator.userAgent);
|
27 |
var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent);
|
28 |
var mode = document.documentMode || 0;
|
29 |
-
var setExpr =
|
30 |
|
31 |
// global $ methods for blocking/unblocking the entire page
|
32 |
$.blockUI = function(opts) { install(window, opts); };
|
@@ -57,7 +60,7 @@
|
|
57 |
|
58 |
callBlock();
|
59 |
var nonmousedOpacity = $m.css('opacity');
|
60 |
-
$m.mouseover
|
61 |
callBlock({
|
62 |
fadeIn: 0,
|
63 |
timeout: 30000
|
@@ -66,7 +69,7 @@
|
|
66 |
var displayBlock = $('.blockMsg');
|
67 |
displayBlock.stop(); // cancel fadeout if it has started
|
68 |
displayBlock.fadeTo(300, 1); // make it easier to read the message by removing transparency
|
69 |
-
}).mouseout
|
70 |
$('.blockMsg').fadeOut(1000);
|
71 |
});
|
72 |
// End konapun additions
|
@@ -358,14 +361,14 @@
|
|
358 |
}
|
359 |
|
360 |
// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
|
361 |
-
var expr = setExpr && (
|
362 |
if (ie6 || expr) {
|
363 |
// give body 100% height
|
364 |
-
if (full && opts.allowBodyStretch &&
|
365 |
$('html,body').css('height','100%');
|
366 |
|
367 |
// fix ie6 issue when blocked element has a border width
|
368 |
-
if ((ie6 ||
|
369 |
var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
|
370 |
var fixT = t ? '(0 - '+t+')' : 0;
|
371 |
var fixL = l ? '(0 - '+l+')' : 0;
|
@@ -377,11 +380,11 @@
|
|
377 |
s.position = 'absolute';
|
378 |
if (i < 2) {
|
379 |
if (full)
|
380 |
-
s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (
|
381 |
else
|
382 |
s.setExpression('height','this.parentNode.offsetHeight + "px"');
|
383 |
if (full)
|
384 |
-
s.setExpression('width','
|
385 |
else
|
386 |
s.setExpression('width','this.parentNode.offsetWidth + "px"');
|
387 |
if (fixL) s.setExpression('left', fixL);
|
@@ -551,9 +554,9 @@
|
|
551 |
// bind anchors and inputs for mouse and key events
|
552 |
var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove';
|
553 |
if (b)
|
554 |
-
$(document).
|
555 |
else
|
556 |
-
$(document).
|
557 |
|
558 |
// former impl...
|
559 |
// var $e = $('a,:input');
|
@@ -606,7 +609,7 @@
|
|
606 |
function sz(el, p) {
|
607 |
return parseInt($.css(el,p),10)||0;
|
608 |
}
|
609 |
-
|
610 |
}
|
611 |
|
612 |
|
1 |
/*!
|
2 |
* jQuery blockUI plugin
|
3 |
+
* Version 2.71.0-2020.12.08
|
4 |
+
* Requires jQuery v1.12 or later
|
5 |
*
|
6 |
* Examples at: http://malsup.com/jquery/block/
|
7 |
* Copyright (c) 2007-2013 M. Alsup
|
17 |
"use strict";
|
18 |
|
19 |
function setup($) {
|
20 |
+
var migrateDeduplicateWarnings = jQuery.migrateDeduplicateWarnings || false;
|
21 |
+
jQuery.migrateDeduplicateWarnings = false;
|
22 |
+
|
23 |
$.fn._fadeIn = $.fn.fadeIn;
|
24 |
|
25 |
var noOp = $.noop || function() {};
|
29 |
var msie = /MSIE/.test(navigator.userAgent);
|
30 |
var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent);
|
31 |
var mode = document.documentMode || 0;
|
32 |
+
var setExpr = "function" === typeof document.createElement('div').style.setExpression;
|
33 |
|
34 |
// global $ methods for blocking/unblocking the entire page
|
35 |
$.blockUI = function(opts) { install(window, opts); };
|
60 |
|
61 |
callBlock();
|
62 |
var nonmousedOpacity = $m.css('opacity');
|
63 |
+
$m.on('mouseover', function() {
|
64 |
callBlock({
|
65 |
fadeIn: 0,
|
66 |
timeout: 30000
|
69 |
var displayBlock = $('.blockMsg');
|
70 |
displayBlock.stop(); // cancel fadeout if it has started
|
71 |
displayBlock.fadeTo(300, 1); // make it easier to read the message by removing transparency
|
72 |
+
}).on('mouseout', function() {
|
73 |
$('.blockMsg').fadeOut(1000);
|
74 |
});
|
75 |
// End konapun additions
|
361 |
}
|
362 |
|
363 |
// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
|
364 |
+
var expr = setExpr && ( "CSS1Compat" !== document.compatMode || $('object,embed', full ? null : el).length > 0);
|
365 |
if (ie6 || expr) {
|
366 |
// give body 100% height
|
367 |
+
if (full && opts.allowBodyStretch && "CSS1Compat" === document.compatMode)
|
368 |
$('html,body').css('height','100%');
|
369 |
|
370 |
// fix ie6 issue when blocked element has a border width
|
371 |
+
if ((ie6 || "CSS1Compat" !== document.compatMode) && !full) {
|
372 |
var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
|
373 |
var fixT = t ? '(0 - '+t+')' : 0;
|
374 |
var fixL = l ? '(0 - '+l+')' : 0;
|
380 |
s.position = 'absolute';
|
381 |
if (i < 2) {
|
382 |
if (full)
|
383 |
+
s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - ("CSS1Compat" === document.compatMode?0:'+opts.quirksmodeOffsetHack+') + "px"');
|
384 |
else
|
385 |
s.setExpression('height','this.parentNode.offsetHeight + "px"');
|
386 |
if (full)
|
387 |
+
s.setExpression('width','"CSS1Compat" === document.compatMode && document.documentElement.clientWidth || document.body.clientWidth + "px"');
|
388 |
else
|
389 |
s.setExpression('width','this.parentNode.offsetWidth + "px"');
|
390 |
if (fixL) s.setExpression('left', fixL);
|
554 |
// bind anchors and inputs for mouse and key events
|
555 |
var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove';
|
556 |
if (b)
|
557 |
+
$(document).on(events, opts, handler);
|
558 |
else
|
559 |
+
$(document).off(events, handler);
|
560 |
|
561 |
// former impl...
|
562 |
// var $e = $('a,:input');
|
609 |
function sz(el, p) {
|
610 |
return parseInt($.css(el,p),10)||0;
|
611 |
}
|
612 |
+
jQuery.migrateDeduplicateWarnings = migrateDeduplicateWarnings;
|
613 |
}
|
614 |
|
615 |
|
includes/blockui/jquery.blockUI.min.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
!function(){"use strict";function e(p){var e=jQuery.migrateDeduplicateWarnings||!1,b=(jQuery.migrateDeduplicateWarnings=!1,p.fn._fadeIn=p.fn.fadeIn,p.noop||function(){}),h=/MSIE/.test(navigator.userAgent),m=/MSIE 6.0/.test(navigator.userAgent)&&!/MSIE 8.0/.test(navigator.userAgent),I=(document.documentMode,"function"==typeof document.createElement("div").style.setExpression),k=(p.blockUI=function(e){o(window,e)},p.unblockUI=function(e){g(window,e)},p.growlUI=function(e,t,o,n){function i(e){p.blockUI({message:s,fadeIn:void 0!==(e=e||{}).fadeIn?e.fadeIn:700,fadeOut:void 0!==e.fadeOut?e.fadeOut:1e3,timeout:void 0!==e.timeout?e.timeout:o,centerY:!1,showOverlay:!1,onUnblock:n,css:p.blockUI.defaults.growlCSS})}var s=p('<div class="growlUI"></div>');e&&s.append("<h1>"+e+"</h1>"),t&&s.append("<h2>"+t+"</h2>"),void 0===o&&(o=3e3),i(),s.css("opacity");s.on("mouseover",function(){i({fadeIn:0,timeout:3e4});var e=p(".blockMsg");e.stop(),e.fadeTo(300,1)}).on("mouseout",function(){p(".blockMsg").fadeOut(1e3)})},p.fn.block=function(e){if(this[0]===window)return p.blockUI(e),this;var t=p.extend({},p.blockUI.defaults,e||{});return this.each(function(){var e=p(this);t.ignoreIfBlocked&&e.data("blockUI.isBlocked")||e.unblock({fadeOut:0})}),this.each(function(){"static"==p.css(this,"position")&&(this.style.position="relative",p(this).data("blockUI.static",!0)),this.style.zoom=1,o(this,e)})},p.fn.unblock=function(e){return this[0]===window?(p.unblockUI(e),this):this.each(function(){g(this,e)})},p.blockUI.version=2.7,p.blockUI.defaults={message:"<h1>Please wait...</h1>",title:null,draggable:!0,theme:!1,css:{padding:0,margin:0,width:"30%",top:"40%",left:"35%",textAlign:"center",color:"#000",border:"3px solid #aaa",backgroundColor:"#fff",cursor:"wait"},themedCSS:{width:"30%",top:"40%",left:"35%"},overlayCSS:{backgroundColor:"#000",opacity:.6,cursor:"wait"},cursorReset:"default",growlCSS:{width:"350px",top:"10px",left:"",right:"10px",border:"none",padding:"5px",opacity:.6,cursor:"default",color:"#fff",backgroundColor:"#000","-webkit-border-radius":"10px","-moz-border-radius":"10px","border-radius":"10px"},iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank",forceIframe:!1,baseZ:1e3,centerX:!0,centerY:!0,allowBodyStretch:!0,bindEvents:!0,constrainTabKey:!0,fadeIn:200,fadeOut:400,timeout:0,showOverlay:!0,focusInput:!0,focusableElements:":input:enabled:visible",onBlock:null,onUnblock:null,onOverlayClick:null,quirksmodeOffsetHack:4,blockMsgClass:"blockMsg",ignoreIfBlocked:!1},null),y=[];function o(e,o){var n=e==window,t=o&&void 0!==o.message?o.message:void 0;if(!(o=p.extend({},p.blockUI.defaults,o||{})).ignoreIfBlocked||!p(e).data("blockUI.isBlocked")){o.overlayCSS=p.extend({},p.blockUI.defaults.overlayCSS,o.overlayCSS||{}),c=p.extend({},p.blockUI.defaults.css,o.css||{}),o.onOverlayClick&&(o.overlayCSS.cursor="pointer"),d=p.extend({},p.blockUI.defaults.themedCSS,o.themedCSS||{}),t=void 0===t?o.message:t,n&&k&&g(window,{fadeOut:0}),t&&"string"!=typeof t&&(t.parentNode||t.jquery)&&(s=t.jquery?t[0]:t,i={},p(e).data("blockUI.history",i),i.el=s,i.parent=s.parentNode,i.display=s.style.display,i.position=s.style.position,i.parent&&i.parent.removeChild(s)),p(e).data("blockUI.onUnblock",o.onUnblock);var r,u,i=o.baseZ,s=h||o.forceIframe?p('<iframe class="blockUI" style="z-index:'+i+++';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+o.iframeSrc+'"></iframe>'):p('<div class="blockUI" style="display:none"></div>'),l=o.theme?p('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+i+++';display:none"></div>'):p('<div class="blockUI blockOverlay" style="z-index:'+i+++';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>'),a=(o.theme&&n?(a='<div class="blockUI '+o.blockMsgClass+' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(i+10)+';display:none;position:fixed">',o.title&&(a+='<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(o.title||" ")+"</div>"),a+='<div class="ui-widget-content ui-dialog-content"></div></div>'):o.theme?(a='<div class="blockUI '+o.blockMsgClass+' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(i+10)+';display:none;position:absolute">',o.title&&(a+='<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(o.title||" ")+"</div>"),a+='<div class="ui-widget-content ui-dialog-content"></div></div>'):a=n?'<div class="blockUI '+o.blockMsgClass+' blockPage" style="z-index:'+(i+10)+';display:none;position:fixed"></div>':'<div class="blockUI '+o.blockMsgClass+' blockElement" style="z-index:'+(i+10)+';display:none;position:absolute"></div>',i=p(a),t&&(o.theme?(i.css(d),i.addClass("ui-widget-content")):i.css(c)),o.theme||l.css(o.overlayCSS),l.css("position",n?"fixed":"absolute"),(h||o.forceIframe)&&s.css("opacity",0),[s,l,i]),f=p(n?"body":e),d=(p.each(a,function(){this.appendTo(f)}),o.theme&&o.draggable&&p.fn.draggable&&i.draggable({handle:".ui-dialog-titlebar",cancel:"li"}),I&&("CSS1Compat"!==document.compatMode||0<p("object,embed",n?null:e).length));if((m||d)&&(n&&o.allowBodyStretch&&"CSS1Compat"===document.compatMode&&p("html,body").css("height","100%"),!m&&"CSS1Compat"===document.compatMode||n||(c=v(e,"borderTopWidth"),d=v(e,"borderLeftWidth"),r=c?"(0 - "+c+")":0,u=d?"(0 - "+d+")":0),p.each(a,function(e,t){t=t[0].style;t.position="absolute",e<2?(n?t.setExpression("height",'Math.max(document.body.scrollHeight, document.body.offsetHeight) - ("CSS1Compat" === document.compatMode?0:'+o.quirksmodeOffsetHack+') + "px"'):t.setExpression("height",'this.parentNode.offsetHeight + "px"'),n?t.setExpression("width",'"CSS1Compat" === document.compatMode && document.documentElement.clientWidth || document.body.clientWidth + "px"'):t.setExpression("width",'this.parentNode.offsetWidth + "px"'),u&&t.setExpression("left",u),r&&t.setExpression("top",r)):o.centerY?(n&&t.setExpression("top",'(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'),t.marginTop=0):!o.centerY&&n&&(e=o.css&&o.css.top?parseInt(o.css.top,10):0,t.setExpression("top","((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "+e+') + "px"'))})),t&&((o.theme?i.find(".ui-widget-content"):i).append(t),(t.jquery||t.nodeType)&&p(t).show()),(h||o.forceIframe)&&o.showOverlay&&s.show(),o.fadeIn?(c=o.onBlock||b,d=o.showOverlay&&!t?c:b,a=t?c:b,o.showOverlay&&l._fadeIn(o.fadeIn,d),t&&i._fadeIn(o.fadeIn,a)):(o.showOverlay&&l.show(),t&&i.show(),o.onBlock&&o.onBlock.bind(i)()),w(1,e,o),n)k=i[0],y=p(o.focusableElements,k),o.focusInput&&setTimeout(U,20);else{var s=i[0],c=o.centerX,d=o.centerY,a=s.parentNode,l=s.style,t=(a.offsetWidth-s.offsetWidth)/2-v(a,"borderLeftWidth"),s=(a.offsetHeight-s.offsetHeight)/2-v(a,"borderTopWidth");c&&(l.left=0<t?t+"px":"0"),d&&(l.top=0<s?s+"px":"0")}o.timeout&&(i=setTimeout(function(){n?p.unblockUI(o):p(e).unblock(o)},o.timeout),p(e).data("blockUI.timeout",i))}}function g(e,t){var o,n,i=e==window,s=p(e),l=s.data("blockUI.history"),a=s.data("blockUI.timeout");a&&(clearTimeout(a),s.removeData("blockUI.timeout")),t=p.extend({},p.blockUI.defaults,t||{}),w(0,e,t),null===t.onUnblock&&(t.onUnblock=s.data("blockUI.onUnblock"),s.removeData("blockUI.onUnblock")),n=i?p("body").children().filter(".blockUI").add("body > .blockUI"):s.find(">.blockUI"),t.cursorReset&&(1<n.length&&(n[1].style.cursor=t.cursorReset),2<n.length&&(n[2].style.cursor=t.cursorReset)),i&&(k=y=null),t.fadeOut?(o=n.length,n.stop().fadeOut(t.fadeOut,function(){0==--o&&d(n,l,t,e)})):d(n,l,t,e)}function d(e,t,o,n){var i=p(n);i.data("blockUI.isBlocked")||(e.each(function(e,t){this.parentNode&&this.parentNode.removeChild(this)}),t&&t.el&&(t.el.style.display=t.display,t.el.style.position=t.position,t.el.style.cursor="default",t.parent&&t.parent.appendChild(t.el),i.removeData("blockUI.history")),i.data("blockUI.static")&&i.css("position","static"),"function"==typeof o.onUnblock&&o.onUnblock(n,o),t=(e=p(document.body)).width(),i=e[0].style.width,e.width(t-1).width(t),e[0].style.width=i)}function w(e,t,o){var n=t==window,t=p(t);!e&&(n&&!k||!n&&!t.data("blockUI.isBlocked"))||(t.data("blockUI.isBlocked",e),n&&o.bindEvents&&(!e||o.showOverlay)&&(t="mousedown mouseup keydown keypress keyup touchstart touchend touchmove",e?p(document).on(t,o,i):p(document).off(t,i)))}function i(e){if("keydown"===e.type&&e.keyCode&&9==e.keyCode&&k&&e.data.constrainTabKey){var t=y,o=!e.shiftKey&&e.target===t[t.length-1],n=e.shiftKey&&e.target===t[0];if(o||n)return setTimeout(function(){U(n)},10),!1}t=e.data,o=p(e.target);return o.hasClass("blockOverlay")&&t.onOverlayClick&&t.onOverlayClick(e),0<o.parents("div."+t.blockMsgClass).length||0===o.parents().children().filter("div.blockUI").length}function U(e){!y||(e=y[!0===e?y.length-1:0])&&e.focus()}function v(e,t){return parseInt(p.css(e,t),10)||0}jQuery.migrateDeduplicateWarnings=e}"function"==typeof define&&define.amd&&define.amd.jQuery?define(["jquery"],e):e(jQuery)}();
|
includes/easy-updates-manager-notices.php
CHANGED
@@ -2,12 +2,12 @@
|
|
2 |
|
3 |
if (!defined('EASY_UPDATES_MANAGER_MAIN_PATH')) die('No direct access allowed');
|
4 |
|
5 |
-
if (!class_exists('
|
6 |
|
7 |
/**
|
8 |
* Class Easy_Updates_Manager_Notices
|
9 |
*/
|
10 |
-
class Easy_Updates_Manager_Notices extends
|
11 |
|
12 |
protected static $_instance = null;
|
13 |
|
2 |
|
3 |
if (!defined('EASY_UPDATES_MANAGER_MAIN_PATH')) die('No direct access allowed');
|
4 |
|
5 |
+
if (!class_exists('Updraft_Notices_1_2')) require_once(EASY_UPDATES_MANAGER_MAIN_PATH.'/vendor/team-updraft/common-libs/src/updraft-notices/updraft-notices.php');
|
6 |
|
7 |
/**
|
8 |
* Class Easy_Updates_Manager_Notices
|
9 |
*/
|
10 |
+
class Easy_Updates_Manager_Notices extends Updraft_Notices_1_2 {
|
11 |
|
12 |
protected static $_instance = null;
|
13 |
|
js/jquery.blockUI.min.js
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
!function(){"use strict";function e(b){b.fn._fadeIn=b.fn.fadeIn;var p=b.noop||function(){},h=/MSIE/.test(navigator.userAgent),k=/MSIE 6.0/.test(navigator.userAgent)&&!/MSIE 8.0/.test(navigator.userAgent),y=(document.documentMode,b.isFunction(document.createElement("div").style.setExpression)),m=(b.blockUI=function(e){o(window,e)},b.unblockUI=function(e){v(window,e)},b.growlUI=function(e,t,o,n){function i(e){b.blockUI({message:s,fadeIn:void 0!==(e=e||{}).fadeIn?e.fadeIn:700,fadeOut:void 0!==e.fadeOut?e.fadeOut:1e3,timeout:void 0!==e.timeout?e.timeout:o,centerY:!1,showOverlay:!1,onUnblock:n,css:b.blockUI.defaults.growlCSS})}var s=b('<div class="growlUI"></div>');e&&s.append("<h1>"+e+"</h1>"),t&&s.append("<h2>"+t+"</h2>"),void 0===o&&(o=3e3),i(),s.css("opacity");s.mouseover(function(){i({fadeIn:0,timeout:3e4});var e=b(".blockMsg");e.stop(),e.fadeTo(300,1)}).mouseout(function(){b(".blockMsg").fadeOut(1e3)})},b.fn.block=function(e){if(this[0]===window)return b.blockUI(e),this;var t=b.extend({},b.blockUI.defaults,e||{});return this.each(function(){var e=b(this);t.ignoreIfBlocked&&e.data("blockUI.isBlocked")||e.unblock({fadeOut:0})}),this.each(function(){"static"==b.css(this,"position")&&(this.style.position="relative",b(this).data("blockUI.static",!0)),this.style.zoom=1,o(this,e)})},b.fn.unblock=function(e){return this[0]===window?(b.unblockUI(e),this):this.each(function(){v(this,e)})},b.blockUI.version=2.7,b.blockUI.defaults={message:"<h1>Please wait...</h1>",title:null,draggable:!0,theme:!1,css:{padding:0,margin:0,width:"30%",top:"40%",left:"35%",textAlign:"center",color:"#000",border:"3px solid #aaa",backgroundColor:"#fff",cursor:"wait"},themedCSS:{width:"30%",top:"40%",left:"35%"},overlayCSS:{backgroundColor:"#000",opacity:.6,cursor:"wait"},cursorReset:"default",growlCSS:{width:"350px",top:"10px",left:"",right:"10px",border:"none",padding:"5px",opacity:.6,cursor:"default",color:"#fff",backgroundColor:"#000","-webkit-border-radius":"10px","-moz-border-radius":"10px","border-radius":"10px"},iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank",forceIframe:!1,baseZ:1e3,centerX:!0,centerY:!0,allowBodyStretch:!0,bindEvents:!0,constrainTabKey:!0,fadeIn:200,fadeOut:400,timeout:0,showOverlay:!0,focusInput:!0,focusableElements:":input:enabled:visible",onBlock:null,onUnblock:null,onOverlayClick:null,quirksmodeOffsetHack:4,blockMsgClass:"blockMsg",ignoreIfBlocked:!1},null),g=[];function o(e,o){var n=e==window,t=o&&void 0!==o.message?o.message:void 0;if(!(o=b.extend({},b.blockUI.defaults,o||{})).ignoreIfBlocked||!b(e).data("blockUI.isBlocked")){o.overlayCSS=b.extend({},b.blockUI.defaults.overlayCSS,o.overlayCSS||{}),f=b.extend({},b.blockUI.defaults.css,o.css||{}),o.onOverlayClick&&(o.overlayCSS.cursor="pointer"),u=b.extend({},b.blockUI.defaults.themedCSS,o.themedCSS||{}),t=void 0===t?o.message:t,n&&m&&v(window,{fadeOut:0}),t&&"string"!=typeof t&&(t.parentNode||t.jquery)&&(d=t.jquery?t[0]:t,l={},b(e).data("blockUI.history",l),l.el=d,l.parent=d.parentNode,l.display=d.style.display,l.position=d.style.position,l.parent&&l.parent.removeChild(d)),b(e).data("blockUI.onUnblock",o.onUnblock);var i,s,l=o.baseZ,d=h||o.forceIframe?b('<iframe class="blockUI" style="z-index:'+l+++';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+o.iframeSrc+'"></iframe>'):b('<div class="blockUI" style="display:none"></div>'),a=o.theme?b('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+l+++';display:none"></div>'):b('<div class="blockUI blockOverlay" style="z-index:'+l+++';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>'),c=(o.theme&&n?(c='<div class="blockUI '+o.blockMsgClass+' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(l+10)+';display:none;position:fixed">',o.title&&(c+='<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(o.title||" ")+"</div>"),c+='<div class="ui-widget-content ui-dialog-content"></div></div>'):o.theme?(c='<div class="blockUI '+o.blockMsgClass+' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(l+10)+';display:none;position:absolute">',o.title&&(c+='<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(o.title||" ")+"</div>"),c+='<div class="ui-widget-content ui-dialog-content"></div></div>'):c=n?'<div class="blockUI '+o.blockMsgClass+' blockPage" style="z-index:'+(l+10)+';display:none;position:fixed"></div>':'<div class="blockUI '+o.blockMsgClass+' blockElement" style="z-index:'+(l+10)+';display:none;position:absolute"></div>',l=b(c),t&&(o.theme?(l.css(u),l.addClass("ui-widget-content")):l.css(f)),o.theme||a.css(o.overlayCSS),a.css("position",n?"fixed":"absolute"),(h||o.forceIframe)&&d.css("opacity",0),[d,a,l]),r=b(n?"body":e),u=(b.each(c,function(){this.appendTo(r)}),o.theme&&o.draggable&&b.fn.draggable&&l.draggable({handle:".ui-dialog-titlebar",cancel:"li"}),y&&(!b.support.boxModel||0<b("object,embed",n?null:e).length));if((k||u)&&(n&&o.allowBodyStretch&&b.support.boxModel&&b("html,body").css("height","100%"),!k&&b.support.boxModel||n||(f=U(e,"borderTopWidth"),u=U(e,"borderLeftWidth"),i=f?"(0 - "+f+")":0,s=u?"(0 - "+u+")":0),b.each(c,function(e,t){t=t[0].style;t.position="absolute",e<2?(n?t.setExpression("height","Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:"+o.quirksmodeOffsetHack+') + "px"'):t.setExpression("height",'this.parentNode.offsetHeight + "px"'),n?t.setExpression("width",'jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'):t.setExpression("width",'this.parentNode.offsetWidth + "px"'),s&&t.setExpression("left",s),i&&t.setExpression("top",i)):o.centerY?(n&&t.setExpression("top",'(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'),t.marginTop=0):!o.centerY&&n&&(e=o.css&&o.css.top?parseInt(o.css.top,10):0,t.setExpression("top","((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "+e+') + "px"'))})),t&&((o.theme?l.find(".ui-widget-content"):l).append(t),(t.jquery||t.nodeType)&&b(t).show()),(h||o.forceIframe)&&o.showOverlay&&d.show(),o.fadeIn?(f=o.onBlock||p,u=o.showOverlay&&!t?f:p,c=t?f:p,o.showOverlay&&a._fadeIn(o.fadeIn,u),t&&l._fadeIn(o.fadeIn,c)):(o.showOverlay&&a.show(),t&&l.show(),o.onBlock&&o.onBlock.bind(l)()),I(1,e,o),n)m=l[0],g=b(o.focusableElements,m),o.focusInput&&setTimeout(w,20);else{var d=l[0],f=o.centerX,u=o.centerY,c=d.parentNode,a=d.style,t=(c.offsetWidth-d.offsetWidth)/2-U(c,"borderLeftWidth"),d=(c.offsetHeight-d.offsetHeight)/2-U(c,"borderTopWidth");f&&(a.left=0<t?t+"px":"0"),u&&(a.top=0<d?d+"px":"0")}o.timeout&&(l=setTimeout(function(){n?b.unblockUI(o):b(e).unblock(o)},o.timeout),b(e).data("blockUI.timeout",l))}}function v(e,t){var o,n,i=e==window,s=b(e),l=s.data("blockUI.history"),d=s.data("blockUI.timeout");d&&(clearTimeout(d),s.removeData("blockUI.timeout")),t=b.extend({},b.blockUI.defaults,t||{}),I(0,e,t),null===t.onUnblock&&(t.onUnblock=s.data("blockUI.onUnblock"),s.removeData("blockUI.onUnblock")),n=i?b("body").children().filter(".blockUI").add("body > .blockUI"):s.find(">.blockUI"),t.cursorReset&&(1<n.length&&(n[1].style.cursor=t.cursorReset),2<n.length&&(n[2].style.cursor=t.cursorReset)),i&&(m=g=null),t.fadeOut?(o=n.length,n.stop().fadeOut(t.fadeOut,function(){0==--o&&a(n,l,t,e)})):a(n,l,t,e)}function a(e,t,o,n){var i=b(n);i.data("blockUI.isBlocked")||(e.each(function(e,t){this.parentNode&&this.parentNode.removeChild(this)}),t&&t.el&&(t.el.style.display=t.display,t.el.style.position=t.position,t.el.style.cursor="default",t.parent&&t.parent.appendChild(t.el),i.removeData("blockUI.history")),i.data("blockUI.static")&&i.css("position","static"),"function"==typeof o.onUnblock&&o.onUnblock(n,o),t=(e=b(document.body)).width(),i=e[0].style.width,e.width(t-1).width(t),e[0].style.width=i)}function I(e,t,o){var n=t==window,t=b(t);!e&&(n&&!m||!n&&!t.data("blockUI.isBlocked"))||(t.data("blockUI.isBlocked",e),n&&o.bindEvents&&(!e||o.showOverlay)&&(t="mousedown mouseup keydown keypress keyup touchstart touchend touchmove",e?b(document).bind(t,o,i):b(document).unbind(t,i)))}function i(e){if("keydown"===e.type&&e.keyCode&&9==e.keyCode&&m&&e.data.constrainTabKey){var t=g,o=!e.shiftKey&&e.target===t[t.length-1],n=e.shiftKey&&e.target===t[0];if(o||n)return setTimeout(function(){w(n)},10),!1}t=e.data,o=b(e.target);return o.hasClass("blockOverlay")&&t.onOverlayClick&&t.onOverlayClick(e),0<o.parents("div."+t.blockMsgClass).length||0===o.parents().children().filter("div.blockUI").length}function w(e){!g||(e=g[!0===e?g.length-1:0])&&e.focus()}function U(e,t){return parseInt(b.css(e,t),10)||0}}"function"==typeof define&&define.amd&&define.amd.jQuery?define(["jquery"],e):e(jQuery)}();
|
|
languages/stops-core-theme-and-plugin-updates.pot
CHANGED
@@ -1110,7 +1110,7 @@ msgstr ""
|
|
1110 |
msgid "Updates options"
|
1111 |
msgstr ""
|
1112 |
|
1113 |
-
#: src/includes/MPSUM_Admin.php:609, src/includes/MPSUM_Admin.php:747, src/includes/MPSUM_Send_Email_Notifications.php:135, src/main.php:792, src/templates/notices/dashboard-constant-warning.php:
|
1114 |
msgid "Easy Updates Manager"
|
1115 |
msgstr ""
|
1116 |
|
@@ -1412,7 +1412,7 @@ msgstr ""
|
|
1412 |
msgid "Show Trace"
|
1413 |
msgstr ""
|
1414 |
|
1415 |
-
#: src/includes/MPSUM_Logs.php:
|
1416 |
msgid "No items found."
|
1417 |
msgstr ""
|
1418 |
|
@@ -1884,28 +1884,28 @@ msgstr ""
|
|
1884 |
msgid "Force updates"
|
1885 |
msgstr ""
|
1886 |
|
1887 |
-
#: src/templates/notices/dashboard-constant-warning.php:
|
1888 |
-
msgid "
|
1889 |
msgstr ""
|
1890 |
|
1891 |
-
#: src/templates/notices/dashboard-constant-warning.php:
|
1892 |
-
msgid "
|
1893 |
msgstr ""
|
1894 |
|
1895 |
-
#: src/templates/notices/dashboard-constant-warning.php:
|
1896 |
-
msgid "
|
1897 |
msgstr ""
|
1898 |
|
1899 |
-
#: src/templates/notices/dashboard-constant-warning.php:16
|
1900 |
-
msgid "
|
1901 |
msgstr ""
|
1902 |
|
1903 |
#: src/templates/notices/dashboard-constant-warning.php:19
|
1904 |
-
msgid "
|
1905 |
msgstr ""
|
1906 |
|
1907 |
#: src/templates/notices/dashboard-constant-warning.php:22
|
1908 |
-
msgid "
|
1909 |
msgstr ""
|
1910 |
|
1911 |
#: src/templates/notices/horizontal-notice.php:6
|
1110 |
msgid "Updates options"
|
1111 |
msgstr ""
|
1112 |
|
1113 |
+
#: src/includes/MPSUM_Admin.php:609, src/includes/MPSUM_Admin.php:747, src/includes/MPSUM_Send_Email_Notifications.php:135, src/main.php:792, src/templates/notices/dashboard-constant-warning.php:18, src/templates/notices/thanks-for-using-main-dash.php:18
|
1114 |
msgid "Easy Updates Manager"
|
1115 |
msgstr ""
|
1116 |
|
1412 |
msgid "Show Trace"
|
1413 |
msgstr ""
|
1414 |
|
1415 |
+
#: src/includes/MPSUM_Logs.php:443
|
1416 |
msgid "No items found."
|
1417 |
msgstr ""
|
1418 |
|
1884 |
msgid "Force updates"
|
1885 |
msgstr ""
|
1886 |
|
1887 |
+
#: src/templates/notices/dashboard-constant-warning.php:7
|
1888 |
+
msgid "This constant prevents automatic updates scheduled tasks from being run within WordPress internal cron."
|
1889 |
msgstr ""
|
1890 |
|
1891 |
+
#: src/templates/notices/dashboard-constant-warning.php:7
|
1892 |
+
msgid "Typically, when enabled, automatic updates events are checked on every page load and any events due to run will be called during that page load."
|
1893 |
msgstr ""
|
1894 |
|
1895 |
+
#: src/templates/notices/dashboard-constant-warning.php:7
|
1896 |
+
msgid "However, if it's intentionally being set because you use external cron (server cron) then you can ignore this warning."
|
1897 |
msgstr ""
|
1898 |
|
1899 |
+
#: src/templates/notices/dashboard-constant-warning.php:14, src/templates/notices/horizontal-notice.php:16, src/templates/notices/horizontal-notice.php:18
|
1900 |
+
msgid "Dismiss"
|
1901 |
msgstr ""
|
1902 |
|
1903 |
#: src/templates/notices/dashboard-constant-warning.php:19
|
1904 |
+
msgid "The following constants are set and will prevent automatic updates in %s."
|
1905 |
msgstr ""
|
1906 |
|
1907 |
#: src/templates/notices/dashboard-constant-warning.php:22
|
1908 |
+
msgid "Please check your wp-config.php file or other files for these constants and remove them to allow Easy Updates Manager to have control."
|
1909 |
msgstr ""
|
1910 |
|
1911 |
#: src/templates/notices/horizontal-notice.php:6
|
main.php
CHANGED
@@ -5,7 +5,7 @@ Plugin Name: Easy Updates Manager
|
|
5 |
Plugin URI: https://easyupdatesmanager.com
|
6 |
Description: Manage and disable WordPress updates, including core, plugin, theme, and automatic updates - Works with Multisite and has built-in logging features.
|
7 |
Author: Easy Updates Manager Team
|
8 |
-
Version: 9.0.
|
9 |
Update URI: https://wordpress.org/plugins/stops-core-theme-and-plugin-updates/
|
10 |
Author URI: https://easyupdatesmanager.com
|
11 |
Contributors: kidsguide, ronalfy
|
@@ -18,7 +18,7 @@ Network: true
|
|
18 |
|
19 |
if (!defined('ABSPATH')) die('No direct access allowed');
|
20 |
|
21 |
-
if (!defined('EASY_UPDATES_MANAGER_VERSION')) define('EASY_UPDATES_MANAGER_VERSION', '9.0.
|
22 |
|
23 |
if (!defined('EASY_UPDATES_MANAGER_MAIN_PATH')) define('EASY_UPDATES_MANAGER_MAIN_PATH', plugin_dir_path(__FILE__));
|
24 |
if (!defined('EASY_UPDATES_MANAGER_URL')) define('EASY_UPDATES_MANAGER_URL', plugin_dir_url(__FILE__));
|
@@ -694,12 +694,12 @@ if (!class_exists('MPSUM_Updates_Manager')) {
|
|
694 |
$this->register_template_directories();
|
695 |
|
696 |
// Check for WP Constants that disable updates and display a notice.
|
697 |
-
$
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
}
|
704 |
|
705 |
// Add filters to overwrite auto update UI in WP 5.5
|
@@ -934,7 +934,7 @@ if (!class_exists('MPSUM_Updates_Manager')) {
|
|
934 |
} elseif ('dismiss_survey_notice_until' == $subaction) {
|
935 |
update_site_option('easy_updates_manager_dismiss_survey_notice_until', (time() + 366 * 86400));
|
936 |
} elseif ('dismiss_constant_notices' == $subaction) {
|
937 |
-
update_site_option('easy_updates_manager_dismiss_constant_notices',
|
938 |
}
|
939 |
|
940 |
wp_send_json($results);
|
5 |
Plugin URI: https://easyupdatesmanager.com
|
6 |
Description: Manage and disable WordPress updates, including core, plugin, theme, and automatic updates - Works with Multisite and has built-in logging features.
|
7 |
Author: Easy Updates Manager Team
|
8 |
+
Version: 9.0.14
|
9 |
Update URI: https://wordpress.org/plugins/stops-core-theme-and-plugin-updates/
|
10 |
Author URI: https://easyupdatesmanager.com
|
11 |
Contributors: kidsguide, ronalfy
|
18 |
|
19 |
if (!defined('ABSPATH')) die('No direct access allowed');
|
20 |
|
21 |
+
if (!defined('EASY_UPDATES_MANAGER_VERSION')) define('EASY_UPDATES_MANAGER_VERSION', '9.0.14');
|
22 |
|
23 |
if (!defined('EASY_UPDATES_MANAGER_MAIN_PATH')) define('EASY_UPDATES_MANAGER_MAIN_PATH', plugin_dir_path(__FILE__));
|
24 |
if (!defined('EASY_UPDATES_MANAGER_URL')) define('EASY_UPDATES_MANAGER_URL', plugin_dir_url(__FILE__));
|
694 |
$this->register_template_directories();
|
695 |
|
696 |
// Check for WP Constants that disable updates and display a notice.
|
697 |
+
$prohibited_active_constants = MPSUM_Constant_Checks::get_instance()->get_prohibited_active_constants();
|
698 |
+
$upgrade_constant_notice = get_site_option('easy_updates_manager_dismiss_constant_notices', array());
|
699 |
+
if (!is_array($upgrade_constant_notice)) $upgrade_constant_notice = array();
|
700 |
+
$prohibited_active_constants = array_diff($prohibited_active_constants, $upgrade_constant_notice);
|
701 |
+
if (!empty($prohibited_active_constants)) {
|
702 |
+
add_action('all_admin_notices', array($this, 'show_autoupdate_constant_warning'));
|
703 |
}
|
704 |
|
705 |
// Add filters to overwrite auto update UI in WP 5.5
|
934 |
} elseif ('dismiss_survey_notice_until' == $subaction) {
|
935 |
update_site_option('easy_updates_manager_dismiss_survey_notice_until', (time() + 366 * 86400));
|
936 |
} elseif ('dismiss_constant_notices' == $subaction) {
|
937 |
+
update_site_option('easy_updates_manager_dismiss_constant_notices', MPSUM_Constant_Checks::get_instance()->get_prohibited_active_constants());
|
938 |
}
|
939 |
|
940 |
wp_send_json($results);
|
readme.txt
CHANGED
@@ -5,7 +5,7 @@ Requires at least: 5.1
|
|
5 |
Requires PHP: 5.6
|
6 |
Donate link: https://easyupdatesmanager.com
|
7 |
Tested up to: 6.1
|
8 |
-
Stable tag: 9.0.
|
9 |
License: GPLv2 or later
|
10 |
|
11 |
Manage all your WordPress updates, including individual updates, automatic updates, logs, and loads more. This also works very well with WordPress Multisite.
|
@@ -126,6 +126,13 @@ For additional information and FAQs for Easy Updates Manager <a href="https://ea
|
|
126 |
|
127 |
== Changelog ==
|
128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
= 9.0.13 - 2022-06-07 =
|
130 |
|
131 |
* TWEAK: Update logs are sent separately causing a huge number of emails being sent to the user (Premium)
|
@@ -385,4 +392,4 @@ For past changelogs, <a href="https://easyupdatesmanager.com/blog/">please visit
|
|
385 |
|
386 |
== Upgrade Notice ==
|
387 |
|
388 |
-
* 9.0.
|
5 |
Requires PHP: 5.6
|
6 |
Donate link: https://easyupdatesmanager.com
|
7 |
Tested up to: 6.1
|
8 |
+
Stable tag: 9.0.14
|
9 |
License: GPLv2 or later
|
10 |
|
11 |
Manage all your WordPress updates, including individual updates, automatic updates, logs, and loads more. This also works very well with WordPress Multisite.
|
126 |
|
127 |
== Changelog ==
|
128 |
|
129 |
+
= 9.0.14 - 2022-10-24 =
|
130 |
+
|
131 |
+
* TWEAK: Add admin notice for DISABLE_WP_CRON constant and remove notices regarding WP_AUTO_UPDATE_CORE and AUTOMATIC_UPDATER_DISABLED constants as they now won't prevent automatic updates from being run
|
132 |
+
* FIX: Core minor updates became major when two updates-related scheduled events got triggered in the same process
|
133 |
+
* FIX: In some cases the serialisation of call stack could cause a PHP fatal error due to the output of a backtrace (debug_backtrace) containing 'Closure' (anonymous) functions
|
134 |
+
* TWEAK: Update notice class
|
135 |
+
|
136 |
= 9.0.13 - 2022-06-07 =
|
137 |
|
138 |
* TWEAK: Update logs are sent separately causing a huge number of emails being sent to the user (Premium)
|
392 |
|
393 |
== Upgrade Notice ==
|
394 |
|
395 |
+
* 9.0.14: Various tweaks and fixes. Core minor updates bug, and constant warnings that are no longer relevant. See changelog for full details. A recommended update for all.
|
templates/notices/dashboard-constant-warning.php
CHANGED
@@ -1,4 +1,14 @@
|
|
1 |
-
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
<div id="easy-updates-manager-constants-enabled" class="error">
|
4 |
<div style="float:right;"><a href="#" onclick="jQuery('#easy-updates-manager-constants-enabled').slideUp(); jQuery.post(ajaxurl, {action: 'easy_updates_manager_ajax', subaction: 'dismiss_constant_notices', nonce: '<?php echo wp_create_nonce('easy-updates-manager-ajax-nonce'); ?>' });"><?php printf(__('Dismiss', 'stops-core-theme-and-plugin-updates')); ?></a></div>
|
@@ -10,19 +20,6 @@
|
|
10 |
?></h3>
|
11 |
<div id="easy-updates-manager-constants-enabled-wrapper">
|
12 |
<p><?php sprintf(esc_html_e('Please check your wp-config.php file or other files for these constants and remove them to allow Easy Updates Manager to have control.', 'stops-core-theme-and-plugin-updates'), $eum_white_label); ?></p>
|
13 |
-
<?php
|
14 |
-
$html = '<ul>';
|
15 |
-
if (defined('AUTOMATIC_UPDATER_DISABLED') && true === AUTOMATIC_UPDATER_DISABLED) {
|
16 |
-
$html .= sprintf('<li><strong>%s</strong>: %s</li>', 'AUTOMATIC_UPDATER_DISABLED', esc_html__('This constant disables any automatic updates.', 'stops-core-theme-and-plugin-updates'));
|
17 |
-
}
|
18 |
-
if (defined('WP_AUTO_UPDATE_CORE') && 'minor' === WP_AUTO_UPDATE_CORE) {
|
19 |
-
$html .= sprintf('<li><strong>%s</strong>: %s</li>', 'WP_AUTO_UPDATE_CORE', esc_html__('This constant prevents automatic updates to new major releases of WordPress.', 'stops-core-theme-and-plugin-updates'));
|
20 |
-
}
|
21 |
-
if (defined('WP_AUTO_UPDATE_CORE') && false === WP_AUTO_UPDATE_CORE) {
|
22 |
-
$html .= sprintf('<li><strong>%s</strong>: %s</li>', 'WP_AUTO_UPDATE_CORE', esc_html__('This constant disables WordPress core from being automatically updated.', 'stops-core-theme-and-plugin-updates'));
|
23 |
-
}
|
24 |
-
$html .= '</ul>';
|
25 |
-
echo $html;
|
26 |
-
?>
|
27 |
</div>
|
28 |
</div>
|
1 |
+
<?php
|
2 |
+
if (!defined('EASY_UPDATES_MANAGER_MAIN_PATH')) die('No direct access allowed');
|
3 |
+
|
4 |
+
$prohibited_active_constants = MPSUM_Constant_Checks::get_instance()->get_prohibited_active_constants();
|
5 |
+
$html = '';
|
6 |
+
if (in_array('DISABLE_WP_CRON', $prohibited_active_constants)) {
|
7 |
+
$html .= sprintf('<li><strong>%s</strong>: %s</li>', 'DISABLE_WP_CRON', esc_html__('This constant prevents automatic updates scheduled tasks from being run within WordPress internal cron.', 'stops-core-theme-and-plugin-updates')." ".esc_html__('Typically, when enabled, automatic updates events are checked on every page load and any events due to run will be called during that page load.', 'stops-core-theme-and-plugin-updates')." ".esc_html__("However, if it's intentionally being set because you use external cron (server cron) then you can ignore this warning.", 'stops-core-theme-and-plugin-updates'));
|
8 |
+
}
|
9 |
+
$html = !empty($html) ? '<ul>'.$html.'</ul>' : $html;
|
10 |
+
if (empty($html)) return;
|
11 |
+
?>
|
12 |
|
13 |
<div id="easy-updates-manager-constants-enabled" class="error">
|
14 |
<div style="float:right;"><a href="#" onclick="jQuery('#easy-updates-manager-constants-enabled').slideUp(); jQuery.post(ajaxurl, {action: 'easy_updates_manager_ajax', subaction: 'dismiss_constant_notices', nonce: '<?php echo wp_create_nonce('easy-updates-manager-ajax-nonce'); ?>' });"><?php printf(__('Dismiss', 'stops-core-theme-and-plugin-updates')); ?></a></div>
|
20 |
?></h3>
|
21 |
<div id="easy-updates-manager-constants-enabled-wrapper">
|
22 |
<p><?php sprintf(esc_html_e('Please check your wp-config.php file or other files for these constants and remove them to allow Easy Updates Manager to have control.', 'stops-core-theme-and-plugin-updates'), $eum_white_label); ?></p>
|
23 |
+
<?php echo $html; ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
</div>
|
25 |
</div>
|
vendor/autoload.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload.php @generated by Composer
|
4 |
+
|
5 |
+
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
+
|
7 |
+
return ComposerAutoloaderInit5c27f3864a6e3814ff0decde3a95b7bb::getLoader();
|
vendor/composer/ClassLoader.php
ADDED
@@ -0,0 +1,481 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer\Autoload;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
+
*
|
18 |
+
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
+
*
|
20 |
+
* // register classes with namespaces
|
21 |
+
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
+
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
+
*
|
24 |
+
* // activate the autoloader
|
25 |
+
* $loader->register();
|
26 |
+
*
|
27 |
+
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
+
* $loader->setUseIncludePath(true);
|
29 |
+
*
|
30 |
+
* In this example, if you try to use a class in the Symfony\Component
|
31 |
+
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
+
* the autoloader will first look for the class under the component/
|
33 |
+
* directory, and it will then fallback to the framework/ directory if not
|
34 |
+
* found before giving up.
|
35 |
+
*
|
36 |
+
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
+
*
|
38 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see https://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see https://www.php-fig.org/psr/psr-4/
|
42 |
+
*/
|
43 |
+
class ClassLoader
|
44 |
+
{
|
45 |
+
private $vendorDir;
|
46 |
+
|
47 |
+
// PSR-4
|
48 |
+
private $prefixLengthsPsr4 = array();
|
49 |
+
private $prefixDirsPsr4 = array();
|
50 |
+
private $fallbackDirsPsr4 = array();
|
51 |
+
|
52 |
+
// PSR-0
|
53 |
+
private $prefixesPsr0 = array();
|
54 |
+
private $fallbackDirsPsr0 = array();
|
55 |
+
|
56 |
+
private $useIncludePath = false;
|
57 |
+
private $classMap = array();
|
58 |
+
private $classMapAuthoritative = false;
|
59 |
+
private $missingClasses = array();
|
60 |
+
private $apcuPrefix;
|
61 |
+
|
62 |
+
private static $registeredLoaders = array();
|
63 |
+
|
64 |
+
public function __construct($vendorDir = null)
|
65 |
+
{
|
66 |
+
$this->vendorDir = $vendorDir;
|
67 |
+
}
|
68 |
+
|
69 |
+
public function getPrefixes()
|
70 |
+
{
|
71 |
+
if (!empty($this->prefixesPsr0)) {
|
72 |
+
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
73 |
+
}
|
74 |
+
|
75 |
+
return array();
|
76 |
+
}
|
77 |
+
|
78 |
+
public function getPrefixesPsr4()
|
79 |
+
{
|
80 |
+
return $this->prefixDirsPsr4;
|
81 |
+
}
|
82 |
+
|
83 |
+
public function getFallbackDirs()
|
84 |
+
{
|
85 |
+
return $this->fallbackDirsPsr0;
|
86 |
+
}
|
87 |
+
|
88 |
+
public function getFallbackDirsPsr4()
|
89 |
+
{
|
90 |
+
return $this->fallbackDirsPsr4;
|
91 |
+
}
|
92 |
+
|
93 |
+
public function getClassMap()
|
94 |
+
{
|
95 |
+
return $this->classMap;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @param array $classMap Class to filename map
|
100 |
+
*/
|
101 |
+
public function addClassMap(array $classMap)
|
102 |
+
{
|
103 |
+
if ($this->classMap) {
|
104 |
+
$this->classMap = array_merge($this->classMap, $classMap);
|
105 |
+
} else {
|
106 |
+
$this->classMap = $classMap;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Registers a set of PSR-0 directories for a given prefix, either
|
112 |
+
* appending or prepending to the ones previously set for this prefix.
|
113 |
+
*
|
114 |
+
* @param string $prefix The prefix
|
115 |
+
* @param array|string $paths The PSR-0 root directories
|
116 |
+
* @param bool $prepend Whether to prepend the directories
|
117 |
+
*/
|
118 |
+
public function add($prefix, $paths, $prepend = false)
|
119 |
+
{
|
120 |
+
if (!$prefix) {
|
121 |
+
if ($prepend) {
|
122 |
+
$this->fallbackDirsPsr0 = array_merge(
|
123 |
+
(array) $paths,
|
124 |
+
$this->fallbackDirsPsr0
|
125 |
+
);
|
126 |
+
} else {
|
127 |
+
$this->fallbackDirsPsr0 = array_merge(
|
128 |
+
$this->fallbackDirsPsr0,
|
129 |
+
(array) $paths
|
130 |
+
);
|
131 |
+
}
|
132 |
+
|
133 |
+
return;
|
134 |
+
}
|
135 |
+
|
136 |
+
$first = $prefix[0];
|
137 |
+
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
138 |
+
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
139 |
+
|
140 |
+
return;
|
141 |
+
}
|
142 |
+
if ($prepend) {
|
143 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
144 |
+
(array) $paths,
|
145 |
+
$this->prefixesPsr0[$first][$prefix]
|
146 |
+
);
|
147 |
+
} else {
|
148 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
149 |
+
$this->prefixesPsr0[$first][$prefix],
|
150 |
+
(array) $paths
|
151 |
+
);
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Registers a set of PSR-4 directories for a given namespace, either
|
157 |
+
* appending or prepending to the ones previously set for this namespace.
|
158 |
+
*
|
159 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
160 |
+
* @param array|string $paths The PSR-4 base directories
|
161 |
+
* @param bool $prepend Whether to prepend the directories
|
162 |
+
*
|
163 |
+
* @throws \InvalidArgumentException
|
164 |
+
*/
|
165 |
+
public function addPsr4($prefix, $paths, $prepend = false)
|
166 |
+
{
|
167 |
+
if (!$prefix) {
|
168 |
+
// Register directories for the root namespace.
|
169 |
+
if ($prepend) {
|
170 |
+
$this->fallbackDirsPsr4 = array_merge(
|
171 |
+
(array) $paths,
|
172 |
+
$this->fallbackDirsPsr4
|
173 |
+
);
|
174 |
+
} else {
|
175 |
+
$this->fallbackDirsPsr4 = array_merge(
|
176 |
+
$this->fallbackDirsPsr4,
|
177 |
+
(array) $paths
|
178 |
+
);
|
179 |
+
}
|
180 |
+
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
181 |
+
// Register directories for a new namespace.
|
182 |
+
$length = strlen($prefix);
|
183 |
+
if ('\\' !== $prefix[$length - 1]) {
|
184 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
185 |
+
}
|
186 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
187 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
188 |
+
} elseif ($prepend) {
|
189 |
+
// Prepend directories for an already registered namespace.
|
190 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
191 |
+
(array) $paths,
|
192 |
+
$this->prefixDirsPsr4[$prefix]
|
193 |
+
);
|
194 |
+
} else {
|
195 |
+
// Append directories for an already registered namespace.
|
196 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
197 |
+
$this->prefixDirsPsr4[$prefix],
|
198 |
+
(array) $paths
|
199 |
+
);
|
200 |
+
}
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Registers a set of PSR-0 directories for a given prefix,
|
205 |
+
* replacing any others previously set for this prefix.
|
206 |
+
*
|
207 |
+
* @param string $prefix The prefix
|
208 |
+
* @param array|string $paths The PSR-0 base directories
|
209 |
+
*/
|
210 |
+
public function set($prefix, $paths)
|
211 |
+
{
|
212 |
+
if (!$prefix) {
|
213 |
+
$this->fallbackDirsPsr0 = (array) $paths;
|
214 |
+
} else {
|
215 |
+
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
216 |
+
}
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Registers a set of PSR-4 directories for a given namespace,
|
221 |
+
* replacing any others previously set for this namespace.
|
222 |
+
*
|
223 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
224 |
+
* @param array|string $paths The PSR-4 base directories
|
225 |
+
*
|
226 |
+
* @throws \InvalidArgumentException
|
227 |
+
*/
|
228 |
+
public function setPsr4($prefix, $paths)
|
229 |
+
{
|
230 |
+
if (!$prefix) {
|
231 |
+
$this->fallbackDirsPsr4 = (array) $paths;
|
232 |
+
} else {
|
233 |
+
$length = strlen($prefix);
|
234 |
+
if ('\\' !== $prefix[$length - 1]) {
|
235 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
236 |
+
}
|
237 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
238 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Turns on searching the include path for class files.
|
244 |
+
*
|
245 |
+
* @param bool $useIncludePath
|
246 |
+
*/
|
247 |
+
public function setUseIncludePath($useIncludePath)
|
248 |
+
{
|
249 |
+
$this->useIncludePath = $useIncludePath;
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Can be used to check if the autoloader uses the include path to check
|
254 |
+
* for classes.
|
255 |
+
*
|
256 |
+
* @return bool
|
257 |
+
*/
|
258 |
+
public function getUseIncludePath()
|
259 |
+
{
|
260 |
+
return $this->useIncludePath;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Turns off searching the prefix and fallback directories for classes
|
265 |
+
* that have not been registered with the class map.
|
266 |
+
*
|
267 |
+
* @param bool $classMapAuthoritative
|
268 |
+
*/
|
269 |
+
public function setClassMapAuthoritative($classMapAuthoritative)
|
270 |
+
{
|
271 |
+
$this->classMapAuthoritative = $classMapAuthoritative;
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Should class lookup fail if not found in the current class map?
|
276 |
+
*
|
277 |
+
* @return bool
|
278 |
+
*/
|
279 |
+
public function isClassMapAuthoritative()
|
280 |
+
{
|
281 |
+
return $this->classMapAuthoritative;
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
286 |
+
*
|
287 |
+
* @param string|null $apcuPrefix
|
288 |
+
*/
|
289 |
+
public function setApcuPrefix($apcuPrefix)
|
290 |
+
{
|
291 |
+
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* The APCu prefix in use, or null if APCu caching is not enabled.
|
296 |
+
*
|
297 |
+
* @return string|null
|
298 |
+
*/
|
299 |
+
public function getApcuPrefix()
|
300 |
+
{
|
301 |
+
return $this->apcuPrefix;
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* Registers this instance as an autoloader.
|
306 |
+
*
|
307 |
+
* @param bool $prepend Whether to prepend the autoloader or not
|
308 |
+
*/
|
309 |
+
public function register($prepend = false)
|
310 |
+
{
|
311 |
+
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
312 |
+
|
313 |
+
if (null === $this->vendorDir) {
|
314 |
+
return;
|
315 |
+
}
|
316 |
+
|
317 |
+
if ($prepend) {
|
318 |
+
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
319 |
+
} else {
|
320 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
321 |
+
self::$registeredLoaders[$this->vendorDir] = $this;
|
322 |
+
}
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Unregisters this instance as an autoloader.
|
327 |
+
*/
|
328 |
+
public function unregister()
|
329 |
+
{
|
330 |
+
spl_autoload_unregister(array($this, 'loadClass'));
|
331 |
+
|
332 |
+
if (null !== $this->vendorDir) {
|
333 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
334 |
+
}
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* Loads the given class or interface.
|
339 |
+
*
|
340 |
+
* @param string $class The name of the class
|
341 |
+
* @return true|null True if loaded, null otherwise
|
342 |
+
*/
|
343 |
+
public function loadClass($class)
|
344 |
+
{
|
345 |
+
if ($file = $this->findFile($class)) {
|
346 |
+
includeFile($file);
|
347 |
+
|
348 |
+
return true;
|
349 |
+
}
|
350 |
+
|
351 |
+
return null;
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Finds the path to the file where the class is defined.
|
356 |
+
*
|
357 |
+
* @param string $class The name of the class
|
358 |
+
*
|
359 |
+
* @return string|false The path if found, false otherwise
|
360 |
+
*/
|
361 |
+
public function findFile($class)
|
362 |
+
{
|
363 |
+
// class map lookup
|
364 |
+
if (isset($this->classMap[$class])) {
|
365 |
+
return $this->classMap[$class];
|
366 |
+
}
|
367 |
+
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
368 |
+
return false;
|
369 |
+
}
|
370 |
+
if (null !== $this->apcuPrefix) {
|
371 |
+
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
372 |
+
if ($hit) {
|
373 |
+
return $file;
|
374 |
+
}
|
375 |
+
}
|
376 |
+
|
377 |
+
$file = $this->findFileWithExtension($class, '.php');
|
378 |
+
|
379 |
+
// Search for Hack files if we are running on HHVM
|
380 |
+
if (false === $file && defined('HHVM_VERSION')) {
|
381 |
+
$file = $this->findFileWithExtension($class, '.hh');
|
382 |
+
}
|
383 |
+
|
384 |
+
if (null !== $this->apcuPrefix) {
|
385 |
+
apcu_add($this->apcuPrefix.$class, $file);
|
386 |
+
}
|
387 |
+
|
388 |
+
if (false === $file) {
|
389 |
+
// Remember that this class does not exist.
|
390 |
+
$this->missingClasses[$class] = true;
|
391 |
+
}
|
392 |
+
|
393 |
+
return $file;
|
394 |
+
}
|
395 |
+
|
396 |
+
/**
|
397 |
+
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
398 |
+
*
|
399 |
+
* @return self[]
|
400 |
+
*/
|
401 |
+
public static function getRegisteredLoaders()
|
402 |
+
{
|
403 |
+
return self::$registeredLoaders;
|
404 |
+
}
|
405 |
+
|
406 |
+
private function findFileWithExtension($class, $ext)
|
407 |
+
{
|
408 |
+
// PSR-4 lookup
|
409 |
+
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
410 |
+
|
411 |
+
$first = $class[0];
|
412 |
+
if (isset($this->prefixLengthsPsr4[$first])) {
|
413 |
+
$subPath = $class;
|
414 |
+
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
415 |
+
$subPath = substr($subPath, 0, $lastPos);
|
416 |
+
$search = $subPath . '\\';
|
417 |
+
if (isset($this->prefixDirsPsr4[$search])) {
|
418 |
+
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
419 |
+
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
420 |
+
if (file_exists($file = $dir . $pathEnd)) {
|
421 |
+
return $file;
|
422 |
+
}
|
423 |
+
}
|
424 |
+
}
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
// PSR-4 fallback dirs
|
429 |
+
foreach ($this->fallbackDirsPsr4 as $dir) {
|
430 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
431 |
+
return $file;
|
432 |
+
}
|
433 |
+
}
|
434 |
+
|
435 |
+
// PSR-0 lookup
|
436 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
437 |
+
// namespaced class name
|
438 |
+
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
439 |
+
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
440 |
+
} else {
|
441 |
+
// PEAR-like class name
|
442 |
+
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
443 |
+
}
|
444 |
+
|
445 |
+
if (isset($this->prefixesPsr0[$first])) {
|
446 |
+
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
447 |
+
if (0 === strpos($class, $prefix)) {
|
448 |
+
foreach ($dirs as $dir) {
|
449 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
450 |
+
return $file;
|
451 |
+
}
|
452 |
+
}
|
453 |
+
}
|
454 |
+
}
|
455 |
+
}
|
456 |
+
|
457 |
+
// PSR-0 fallback dirs
|
458 |
+
foreach ($this->fallbackDirsPsr0 as $dir) {
|
459 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
460 |
+
return $file;
|
461 |
+
}
|
462 |
+
}
|
463 |
+
|
464 |
+
// PSR-0 include paths.
|
465 |
+
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
466 |
+
return $file;
|
467 |
+
}
|
468 |
+
|
469 |
+
return false;
|
470 |
+
}
|
471 |
+
}
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Scope isolated include.
|
475 |
+
*
|
476 |
+
* Prevents access to $this/self from included files.
|
477 |
+
*/
|
478 |
+
function includeFile($file)
|
479 |
+
{
|
480 |
+
include $file;
|
481 |
+
}
|
vendor/composer/InstalledVersions.php
ADDED
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer;
|
14 |
+
|
15 |
+
use Composer\Autoload\ClassLoader;
|
16 |
+
use Composer\Semver\VersionParser;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* This class is copied in every Composer installed project and available to all
|
20 |
+
*
|
21 |
+
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
22 |
+
*
|
23 |
+
* To require it's presence, you can require `composer-runtime-api ^2.0`
|
24 |
+
*/
|
25 |
+
class InstalledVersions
|
26 |
+
{
|
27 |
+
private static $installed;
|
28 |
+
private static $canGetVendors;
|
29 |
+
private static $installedByVendor = array();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
33 |
+
*
|
34 |
+
* @return string[]
|
35 |
+
* @psalm-return list<string>
|
36 |
+
*/
|
37 |
+
public static function getInstalledPackages()
|
38 |
+
{
|
39 |
+
$packages = array();
|
40 |
+
foreach (self::getInstalled() as $installed) {
|
41 |
+
$packages[] = array_keys($installed['versions']);
|
42 |
+
}
|
43 |
+
|
44 |
+
if (1 === \count($packages)) {
|
45 |
+
return $packages[0];
|
46 |
+
}
|
47 |
+
|
48 |
+
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Returns a list of all package names with a specific type e.g. 'library'
|
53 |
+
*
|
54 |
+
* @param string $type
|
55 |
+
* @return string[]
|
56 |
+
* @psalm-return list<string>
|
57 |
+
*/
|
58 |
+
public static function getInstalledPackagesByType($type)
|
59 |
+
{
|
60 |
+
$packagesByType = array();
|
61 |
+
|
62 |
+
foreach (self::getInstalled() as $installed) {
|
63 |
+
foreach ($installed['versions'] as $name => $package) {
|
64 |
+
if (isset($package['type']) && $package['type'] === $type) {
|
65 |
+
$packagesByType[] = $name;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
return $packagesByType;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Checks whether the given package is installed
|
75 |
+
*
|
76 |
+
* This also returns true if the package name is provided or replaced by another package
|
77 |
+
*
|
78 |
+
* @param string $packageName
|
79 |
+
* @param bool $includeDevRequirements
|
80 |
+
* @return bool
|
81 |
+
*/
|
82 |
+
public static function isInstalled($packageName, $includeDevRequirements = true)
|
83 |
+
{
|
84 |
+
foreach (self::getInstalled() as $installed) {
|
85 |
+
if (isset($installed['versions'][$packageName])) {
|
86 |
+
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
return false;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Checks whether the given package satisfies a version constraint
|
95 |
+
*
|
96 |
+
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
97 |
+
*
|
98 |
+
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
99 |
+
*
|
100 |
+
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
101 |
+
* @param string $packageName
|
102 |
+
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
103 |
+
* @return bool
|
104 |
+
*/
|
105 |
+
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
106 |
+
{
|
107 |
+
$constraint = $parser->parseConstraints($constraint);
|
108 |
+
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
109 |
+
|
110 |
+
return $provided->matches($constraint);
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Returns a version constraint representing all the range(s) which are installed for a given package
|
115 |
+
*
|
116 |
+
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
117 |
+
* whether a given version of a package is installed, and not just whether it exists
|
118 |
+
*
|
119 |
+
* @param string $packageName
|
120 |
+
* @return string Version constraint usable with composer/semver
|
121 |
+
*/
|
122 |
+
public static function getVersionRanges($packageName)
|
123 |
+
{
|
124 |
+
foreach (self::getInstalled() as $installed) {
|
125 |
+
if (!isset($installed['versions'][$packageName])) {
|
126 |
+
continue;
|
127 |
+
}
|
128 |
+
|
129 |
+
$ranges = array();
|
130 |
+
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
131 |
+
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
132 |
+
}
|
133 |
+
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
134 |
+
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
135 |
+
}
|
136 |
+
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
137 |
+
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
138 |
+
}
|
139 |
+
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
140 |
+
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
141 |
+
}
|
142 |
+
|
143 |
+
return implode(' || ', $ranges);
|
144 |
+
}
|
145 |
+
|
146 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @param string $packageName
|
151 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
152 |
+
*/
|
153 |
+
public static function getVersion($packageName)
|
154 |
+
{
|
155 |
+
foreach (self::getInstalled() as $installed) {
|
156 |
+
if (!isset($installed['versions'][$packageName])) {
|
157 |
+
continue;
|
158 |
+
}
|
159 |
+
|
160 |
+
if (!isset($installed['versions'][$packageName]['version'])) {
|
161 |
+
return null;
|
162 |
+
}
|
163 |
+
|
164 |
+
return $installed['versions'][$packageName]['version'];
|
165 |
+
}
|
166 |
+
|
167 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @param string $packageName
|
172 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
173 |
+
*/
|
174 |
+
public static function getPrettyVersion($packageName)
|
175 |
+
{
|
176 |
+
foreach (self::getInstalled() as $installed) {
|
177 |
+
if (!isset($installed['versions'][$packageName])) {
|
178 |
+
continue;
|
179 |
+
}
|
180 |
+
|
181 |
+
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
182 |
+
return null;
|
183 |
+
}
|
184 |
+
|
185 |
+
return $installed['versions'][$packageName]['pretty_version'];
|
186 |
+
}
|
187 |
+
|
188 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* @param string $packageName
|
193 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
194 |
+
*/
|
195 |
+
public static function getReference($packageName)
|
196 |
+
{
|
197 |
+
foreach (self::getInstalled() as $installed) {
|
198 |
+
if (!isset($installed['versions'][$packageName])) {
|
199 |
+
continue;
|
200 |
+
}
|
201 |
+
|
202 |
+
if (!isset($installed['versions'][$packageName]['reference'])) {
|
203 |
+
return null;
|
204 |
+
}
|
205 |
+
|
206 |
+
return $installed['versions'][$packageName]['reference'];
|
207 |
+
}
|
208 |
+
|
209 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @param string $packageName
|
214 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
215 |
+
*/
|
216 |
+
public static function getInstallPath($packageName)
|
217 |
+
{
|
218 |
+
foreach (self::getInstalled() as $installed) {
|
219 |
+
if (!isset($installed['versions'][$packageName])) {
|
220 |
+
continue;
|
221 |
+
}
|
222 |
+
|
223 |
+
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
224 |
+
}
|
225 |
+
|
226 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* @return array
|
231 |
+
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
|
232 |
+
*/
|
233 |
+
public static function getRootPackage()
|
234 |
+
{
|
235 |
+
$installed = self::getInstalled();
|
236 |
+
|
237 |
+
return $installed[0]['root'];
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Returns the raw installed.php data for custom implementations
|
242 |
+
*
|
243 |
+
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
244 |
+
* @return array[]
|
245 |
+
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
|
246 |
+
*/
|
247 |
+
public static function getRawData()
|
248 |
+
{
|
249 |
+
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
250 |
+
|
251 |
+
if (null === self::$installed) {
|
252 |
+
// only require the installed.php file if this file is loaded from its dumped location,
|
253 |
+
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
254 |
+
if (substr(__DIR__, -8, 1) !== 'C') {
|
255 |
+
self::$installed = include __DIR__ . '/installed.php';
|
256 |
+
} else {
|
257 |
+
self::$installed = array();
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
return self::$installed;
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
266 |
+
*
|
267 |
+
* @return array[]
|
268 |
+
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
|
269 |
+
*/
|
270 |
+
public static function getAllRawData()
|
271 |
+
{
|
272 |
+
return self::getInstalled();
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Lets you reload the static array from another file
|
277 |
+
*
|
278 |
+
* This is only useful for complex integrations in which a project needs to use
|
279 |
+
* this class but then also needs to execute another project's autoloader in process,
|
280 |
+
* and wants to ensure both projects have access to their version of installed.php.
|
281 |
+
*
|
282 |
+
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
283 |
+
* the data it needs from this class, then call reload() with
|
284 |
+
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
285 |
+
* the project in which it runs can then also use this class safely, without
|
286 |
+
* interference between PHPUnit's dependencies and the project's dependencies.
|
287 |
+
*
|
288 |
+
* @param array[] $data A vendor/composer/installed.php data set
|
289 |
+
* @return void
|
290 |
+
*
|
291 |
+
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
|
292 |
+
*/
|
293 |
+
public static function reload($data)
|
294 |
+
{
|
295 |
+
self::$installed = $data;
|
296 |
+
self::$installedByVendor = array();
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* @return array[]
|
301 |
+
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
|
302 |
+
*/
|
303 |
+
private static function getInstalled()
|
304 |
+
{
|
305 |
+
if (null === self::$canGetVendors) {
|
306 |
+
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
307 |
+
}
|
308 |
+
|
309 |
+
$installed = array();
|
310 |
+
|
311 |
+
if (self::$canGetVendors) {
|
312 |
+
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
313 |
+
if (isset(self::$installedByVendor[$vendorDir])) {
|
314 |
+
$installed[] = self::$installedByVendor[$vendorDir];
|
315 |
+
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
316 |
+
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
317 |
+
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
318 |
+
self::$installed = $installed[count($installed) - 1];
|
319 |
+
}
|
320 |
+
}
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
if (null === self::$installed) {
|
325 |
+
// only require the installed.php file if this file is loaded from its dumped location,
|
326 |
+
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
327 |
+
if (substr(__DIR__, -8, 1) !== 'C') {
|
328 |
+
self::$installed = require __DIR__ . '/installed.php';
|
329 |
+
} else {
|
330 |
+
self::$installed = array();
|
331 |
+
}
|
332 |
+
}
|
333 |
+
$installed[] = self::$installed;
|
334 |
+
|
335 |
+
return $installed;
|
336 |
+
}
|
337 |
+
}
|
vendor/composer/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
Copyright (c) Nils Adermann, Jordi Boggiano
|
3 |
+
|
4 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 |
+
of this software and associated documentation files (the "Software"), to deal
|
6 |
+
in the Software without restriction, including without limitation the rights
|
7 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
9 |
+
to do so, subject to the following conditions:
|
10 |
+
|
11 |
+
The above copyright notice and this permission notice shall be included in all
|
12 |
+
copies or substantial portions of the Software.
|
13 |
+
|
14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20 |
+
THE SOFTWARE.
|
21 |
+
|
vendor/composer/autoload_classmap.php
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_classmap.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
10 |
+
);
|
vendor/composer/autoload_namespaces.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_namespaces.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
);
|
vendor/composer/autoload_psr4.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_psr4.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
);
|
vendor/composer/autoload_real.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_real.php @generated by Composer
|
4 |
+
|
5 |
+
class ComposerAutoloaderInit5c27f3864a6e3814ff0decde3a95b7bb
|
6 |
+
{
|
7 |
+
private static $loader;
|
8 |
+
|
9 |
+
public static function loadClassLoader($class)
|
10 |
+
{
|
11 |
+
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
+
require __DIR__ . '/ClassLoader.php';
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @return \Composer\Autoload\ClassLoader
|
18 |
+
*/
|
19 |
+
public static function getLoader()
|
20 |
+
{
|
21 |
+
if (null !== self::$loader) {
|
22 |
+
return self::$loader;
|
23 |
+
}
|
24 |
+
|
25 |
+
spl_autoload_register(array('ComposerAutoloaderInit5c27f3864a6e3814ff0decde3a95b7bb', 'loadClassLoader'), true, true);
|
26 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
27 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit5c27f3864a6e3814ff0decde3a95b7bb', 'loadClassLoader'));
|
28 |
+
|
29 |
+
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
30 |
+
if ($useStaticLoader) {
|
31 |
+
require __DIR__ . '/autoload_static.php';
|
32 |
+
|
33 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit5c27f3864a6e3814ff0decde3a95b7bb::getInitializer($loader));
|
34 |
+
} else {
|
35 |
+
$map = require __DIR__ . '/autoload_namespaces.php';
|
36 |
+
foreach ($map as $namespace => $path) {
|
37 |
+
$loader->set($namespace, $path);
|
38 |
+
}
|
39 |
+
|
40 |
+
$map = require __DIR__ . '/autoload_psr4.php';
|
41 |
+
foreach ($map as $namespace => $path) {
|
42 |
+
$loader->setPsr4($namespace, $path);
|
43 |
+
}
|
44 |
+
|
45 |
+
$classMap = require __DIR__ . '/autoload_classmap.php';
|
46 |
+
if ($classMap) {
|
47 |
+
$loader->addClassMap($classMap);
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
$loader->register(true);
|
52 |
+
|
53 |
+
return $loader;
|
54 |
+
}
|
55 |
+
}
|
vendor/composer/autoload_static.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_static.php @generated by Composer
|
4 |
+
|
5 |
+
namespace Composer\Autoload;
|
6 |
+
|
7 |
+
class ComposerStaticInit5c27f3864a6e3814ff0decde3a95b7bb
|
8 |
+
{
|
9 |
+
public static $classMap = array (
|
10 |
+
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
11 |
+
);
|
12 |
+
|
13 |
+
public static function getInitializer(ClassLoader $loader)
|
14 |
+
{
|
15 |
+
return \Closure::bind(function () use ($loader) {
|
16 |
+
$loader->classMap = ComposerStaticInit5c27f3864a6e3814ff0decde3a95b7bb::$classMap;
|
17 |
+
|
18 |
+
}, null, ClassLoader::class);
|
19 |
+
}
|
20 |
+
}
|
vendor/composer/installed.json
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"packages": [
|
3 |
+
{
|
4 |
+
"name": "team-updraft/common-libs",
|
5 |
+
"version": "3.0.3",
|
6 |
+
"version_normalized": "3.0.3.0",
|
7 |
+
"source": {
|
8 |
+
"type": "git",
|
9 |
+
"url": "https://source.updraftplus.com/team-updraft/common-libs.git",
|
10 |
+
"reference": "cd30bf3b65e2cadcea1cca01deb32f5cd21a0b63"
|
11 |
+
},
|
12 |
+
"dist": {
|
13 |
+
"type": "zip",
|
14 |
+
"url": "https://source.updraftplus.com/api/v4/projects/28/packages/composer/archives/team-updraft/common-libs.zip?sha=cd30bf3b65e2cadcea1cca01deb32f5cd21a0b63",
|
15 |
+
"reference": "cd30bf3b65e2cadcea1cca01deb32f5cd21a0b63",
|
16 |
+
"shasum": ""
|
17 |
+
},
|
18 |
+
"require-dev": {
|
19 |
+
"dealerdirect/phpcodesniffer-composer-installer": "0.7.*",
|
20 |
+
"phpcompatibility/php-compatibility": "9.3.*",
|
21 |
+
"sirbrillig/phpcs-variable-analysis": "2.11.*",
|
22 |
+
"squizlabs/php_codesniffer": "3.6.*",
|
23 |
+
"wp-coding-standards/wpcs": "2.3.*"
|
24 |
+
},
|
25 |
+
"type": "library",
|
26 |
+
"installation-source": "dist",
|
27 |
+
"license": [
|
28 |
+
"GPL-3.0-only"
|
29 |
+
],
|
30 |
+
"authors": [
|
31 |
+
{
|
32 |
+
"name": "Team Updraft",
|
33 |
+
"email": "team.updraft@gmail.com"
|
34 |
+
}
|
35 |
+
],
|
36 |
+
"description": "These are the common libs used across all of our projects",
|
37 |
+
"install-path": "../team-updraft/common-libs"
|
38 |
+
}
|
39 |
+
],
|
40 |
+
"dev": false,
|
41 |
+
"dev-package-names": []
|
42 |
+
}
|
vendor/composer/installed.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php return array(
|
2 |
+
'root' => array(
|
3 |
+
'pretty_version' => 'dev-master',
|
4 |
+
'version' => 'dev-master',
|
5 |
+
'type' => 'project',
|
6 |
+
'install_path' => __DIR__ . '/../../',
|
7 |
+
'aliases' => array(),
|
8 |
+
'reference' => '94db1fe10b91d20c60f0de21f1f79510497d6bf1',
|
9 |
+
'name' => 'updraftplus/stops-core-theme-and-plugin-updates',
|
10 |
+
'dev' => false,
|
11 |
+
),
|
12 |
+
'versions' => array(
|
13 |
+
'team-updraft/common-libs' => array(
|
14 |
+
'pretty_version' => '3.0.3',
|
15 |
+
'version' => '3.0.3.0',
|
16 |
+
'type' => 'library',
|
17 |
+
'install_path' => __DIR__ . '/../team-updraft/common-libs',
|
18 |
+
'aliases' => array(),
|
19 |
+
'reference' => 'cd30bf3b65e2cadcea1cca01deb32f5cd21a0b63',
|
20 |
+
'dev_requirement' => false,
|
21 |
+
),
|
22 |
+
'updraftplus/stops-core-theme-and-plugin-updates' => array(
|
23 |
+
'pretty_version' => 'dev-master',
|
24 |
+
'version' => 'dev-master',
|
25 |
+
'type' => 'project',
|
26 |
+
'install_path' => __DIR__ . '/../../',
|
27 |
+
'aliases' => array(),
|
28 |
+
'reference' => '94db1fe10b91d20c60f0de21f1f79510497d6bf1',
|
29 |
+
'dev_requirement' => false,
|
30 |
+
),
|
31 |
+
),
|
32 |
+
);
|
vendor/team-updraft/common-libs/CI/php-compatibility.xml
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<ruleset name="UpdraftPlus">
|
3 |
+
<!-- How to run on Commandline: vendor/bin/phpcs -p -s -d memory_limit=150M \-\-standard=CI/php-compatibility.xml src/ \-\-report-full \-\-extensions=php -->
|
4 |
+
<description>UpdraftPlus PHP Compatibility Check</description>
|
5 |
+
<!-- Set Memory Limit -->
|
6 |
+
<ini name="memory_limit" value="150M"/>
|
7 |
+
<!-- CI Cache -->
|
8 |
+
<arg name="cache" value="../CI/phpcs-cache-compatibility"/>
|
9 |
+
<!-- Check up to 4 files simultanously. -->
|
10 |
+
<arg name="parallel" value="4"/>
|
11 |
+
<!-- Only Test for PHP 5.2+ -->
|
12 |
+
<config name="testVersion" value="5.2-"/>
|
13 |
+
<!-- Ignoring Folders As they are part of Vendor packages -->
|
14 |
+
<!-- <exclude-pattern>src/tools/customer-tools/vendor</exclude-pattern> -->
|
15 |
+
<!-- PHPCompatibility -->
|
16 |
+
<rule ref="PHPCompatibility"/>
|
17 |
+
</ruleset>
|
vendor/team-updraft/common-libs/CI/php-syntax-check.xml
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<ruleset name="UpdraftPlus">
|
3 |
+
<description>UpdraftPlus PHP Syntax Check</description>
|
4 |
+
<!-- Set Memory Limit -->
|
5 |
+
<ini name="memory_limit" value="150M"/>
|
6 |
+
<!-- CI Cache -->
|
7 |
+
<arg name="cache" value="../CI/phpcs-cache-syntax-check"/>
|
8 |
+
<!-- Check up to 4 files simultanously. -->
|
9 |
+
<arg name="parallel" value="4"/>
|
10 |
+
<!-- Check for PHP syntax errors -->
|
11 |
+
<rule ref="Generic.PHP.Syntax"/>
|
12 |
+
</ruleset>
|
vendor/team-updraft/common-libs/README.md
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Common Libraries
|
2 |
+
|
3 |
+
This project contains many useful libraries that are currently used and can be reused across our projects. They are kept here for easy maintenance and also so that consumers get a uniform interface and things dont break across versions or on updates.
|
4 |
+
|
5 |
+
CHANGELOG
|
6 |
+
* TWEAK: Port from previous semaphore classes to Updraft_Semaphore_3_0 in updraft-tasks
|
7 |
+
* FIX: Wrong query value in `delete_task_meta` method
|
8 |
+
* TWEAK: Make the logging format uniform
|
9 |
+
* FIX: Wrong DB Schema reference
|
10 |
+
* TWEAK: Logging on the semaphore
|
vendor/team-updraft/common-libs/composer.json
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "team-updraft/common-libs",
|
3 |
+
"description": "These are the common libs used across all of our projects",
|
4 |
+
"type": "library",
|
5 |
+
"license": "GPL-3.0-only",
|
6 |
+
"authors": [{
|
7 |
+
"name": "Team Updraft",
|
8 |
+
"email": "team.updraft@gmail.com"
|
9 |
+
}],
|
10 |
+
"config": {
|
11 |
+
"platform-check": false
|
12 |
+
},
|
13 |
+
"require-dev": {
|
14 |
+
"squizlabs/php_codesniffer": "3.6.*",
|
15 |
+
"phpcompatibility/php-compatibility": "9.3.*",
|
16 |
+
"wp-coding-standards/wpcs": "2.3.*",
|
17 |
+
"sirbrillig/phpcs-variable-analysis": "2.11.*",
|
18 |
+
"dealerdirect/phpcodesniffer-composer-installer": "0.7.*"
|
19 |
+
},
|
20 |
+
"prefer-stable" : true
|
21 |
+
}
|
{includes → vendor/team-updraft/common-libs/src/updraft-notices}/updraft-notices.php
RENAMED
@@ -2,18 +2,11 @@
|
|
2 |
|
3 |
if (!defined('ABSPATH')) die('No direct access allowed');
|
4 |
|
5 |
-
|
6 |
-
* If we ever change the API of the Updraft_Notices class, then we'll need to rename and version it, e.g. Updraft_Notices_1_0, because otherwise a plugin may find that it's loaded an older instance than it wanted from another plugin.
|
7 |
-
*/
|
8 |
-
abstract class Updraft_Notices_1_0 {
|
9 |
|
10 |
protected $notices_content;
|
11 |
|
12 |
-
|
13 |
-
* These variables are just short-hands to be used in advert content.
|
14 |
-
*
|
15 |
-
* @var array
|
16 |
-
*/
|
17 |
protected $dashboard_top = array('top');
|
18 |
|
19 |
protected $dashboard_top_or_report = array('top', 'report', 'report-plain');
|
@@ -27,12 +20,11 @@ abstract class Updraft_Notices_1_0 {
|
|
27 |
protected $autobackup_bottom_or_report = array('autobackup', 'bottom', 'report', 'report-plain');
|
28 |
|
29 |
/**
|
30 |
-
*
|
31 |
*
|
32 |
* @return array
|
33 |
*/
|
34 |
protected function populate_notices_content() {
|
35 |
-
// Global adverts that appear in all products will be returned to the child to display.
|
36 |
return array();
|
37 |
}
|
38 |
|
@@ -40,14 +32,14 @@ abstract class Updraft_Notices_1_0 {
|
|
40 |
* Call this method to setup the notices.
|
41 |
*/
|
42 |
abstract protected function notices_init();
|
43 |
-
|
44 |
/**
|
45 |
-
* Checks
|
46 |
*
|
47 |
-
* @param null
|
48 |
-
* @param
|
49 |
*
|
50 |
-
* @return
|
51 |
*/
|
52 |
protected function is_plugin_installed($product = null, $also_require_active = false) {
|
53 |
if ($also_require_active) return class_exists($product);
|
@@ -63,12 +55,12 @@ abstract class Updraft_Notices_1_0 {
|
|
63 |
}
|
64 |
|
65 |
/**
|
66 |
-
* Checks
|
67 |
*
|
68 |
-
* @param string $plugin_base_dir Base directory of plugin
|
69 |
-
* @param string $product_name Plugin name
|
70 |
*
|
71 |
-
* @return
|
72 |
*/
|
73 |
protected function translation_needed($plugin_base_dir, $product_name) {
|
74 |
$wplang = get_locale();
|
@@ -78,26 +70,45 @@ abstract class Updraft_Notices_1_0 {
|
|
78 |
return true;
|
79 |
}
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
protected function url_start($html_allowed, $url, $https = false, $website_home = null) {
|
82 |
$proto = ($https) ? 'https' : 'http';
|
83 |
if (strpos($url, $website_home) !== false) {
|
84 |
-
return
|
85 |
} else {
|
86 |
-
return
|
87 |
}
|
88 |
}
|
89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
protected function url_end($html_allowed, $url, $https = false) {
|
91 |
-
$proto =
|
92 |
-
return
|
93 |
}
|
94 |
|
95 |
/**
|
96 |
* Renders notice
|
97 |
*
|
98 |
-
* @param
|
99 |
-
* @param string
|
100 |
-
* @param
|
101 |
*
|
102 |
* @return mixed Returns string or echos notice
|
103 |
*/
|
@@ -117,9 +128,10 @@ abstract class Updraft_Notices_1_0 {
|
|
117 |
/**
|
118 |
* This method will return a notice ready for display.
|
119 |
*
|
120 |
-
* @param boolean $notice
|
121 |
-
* @param string $position
|
122 |
-
*
|
|
|
123 |
*/
|
124 |
protected function get_notice_data($notice = false, $position = 'top') {
|
125 |
|
@@ -137,6 +149,9 @@ abstract class Updraft_Notices_1_0 {
|
|
137 |
|
138 |
if ($dismiss) return false;
|
139 |
|
|
|
|
|
|
|
140 |
return $this->notices_content[$notice];
|
141 |
}
|
142 |
|
@@ -165,6 +180,10 @@ abstract class Updraft_Notices_1_0 {
|
|
165 |
if (empty($available_notices)) return false;
|
166 |
|
167 |
// If a seasonal advert can't be returned then we will return a random advert.
|
|
|
|
|
|
|
|
|
168 |
// Using shuffle here as something like rand which produces a random number and uses that as the array index fails, this is because in future an advert may not be numbered and could have a string as its key which will then cause errors.
|
169 |
shuffle($available_notices);
|
170 |
return $available_notices[0];
|
@@ -173,25 +192,27 @@ abstract class Updraft_Notices_1_0 {
|
|
173 |
/**
|
174 |
* Skip seasonal notices
|
175 |
*
|
176 |
-
* @
|
|
|
|
|
177 |
*/
|
178 |
-
protected function skip_seasonal_notices($notice_data) {
|
179 |
return false;
|
180 |
}
|
181 |
|
182 |
/**
|
183 |
-
* Returns
|
184 |
*
|
185 |
-
* @return mixed Returns
|
186 |
*/
|
187 |
public function get_affiliate_id() {
|
188 |
return $this->self_affiliate_id;
|
189 |
}
|
190 |
|
191 |
/**
|
192 |
-
* Checks
|
193 |
*
|
194 |
-
* @param string $dismiss_time
|
195 |
*
|
196 |
* @return mixed
|
197 |
*/
|
2 |
|
3 |
if (!defined('ABSPATH')) die('No direct access allowed');
|
4 |
|
5 |
+
abstract class Updraft_Notices_1_2 {
|
|
|
|
|
|
|
6 |
|
7 |
protected $notices_content;
|
8 |
|
9 |
+
// These variables are just short-hands to be used in advert content.
|
|
|
|
|
|
|
|
|
10 |
protected $dashboard_top = array('top');
|
11 |
|
12 |
protected $dashboard_top_or_report = array('top', 'report', 'report-plain');
|
20 |
protected $autobackup_bottom_or_report = array('autobackup', 'bottom', 'report', 'report-plain');
|
21 |
|
22 |
/**
|
23 |
+
* Global adverts that appear in all products will be returned to the child to display
|
24 |
*
|
25 |
* @return array
|
26 |
*/
|
27 |
protected function populate_notices_content() {
|
|
|
28 |
return array();
|
29 |
}
|
30 |
|
32 |
* Call this method to setup the notices.
|
33 |
*/
|
34 |
abstract protected function notices_init();
|
35 |
+
|
36 |
/**
|
37 |
+
* Checks if the plugin is installed and checks status if needed.
|
38 |
*
|
39 |
+
* @param null $product - Plugin to check
|
40 |
+
* @param boolean $also_require_active - bool to indicate if active status is required or not
|
41 |
*
|
42 |
+
* @return boolean Returns true, if plugin is installed otherwise false
|
43 |
*/
|
44 |
protected function is_plugin_installed($product = null, $also_require_active = false) {
|
45 |
if ($also_require_active) return class_exists($product);
|
55 |
}
|
56 |
|
57 |
/**
|
58 |
+
* Checks if translation is needed or not
|
59 |
*
|
60 |
+
* @param string $plugin_base_dir - Base directory of plugin
|
61 |
+
* @param string $product_name - Plugin name
|
62 |
*
|
63 |
+
* @return boolean Returns true if translation is needed, otherwise false
|
64 |
*/
|
65 |
protected function translation_needed($plugin_base_dir, $product_name) {
|
66 |
$wplang = get_locale();
|
70 |
return true;
|
71 |
}
|
72 |
|
73 |
+
/**
|
74 |
+
* Generates the start of a HTML URL
|
75 |
+
*
|
76 |
+
* @param boolean $html_allowed - indicates if HTML is allowed or not
|
77 |
+
* @param string $url - the URL
|
78 |
+
* @param boolean $https - the protocol to use
|
79 |
+
* @param string $website_home - the product website name
|
80 |
+
*
|
81 |
+
* @return string returns a partial HTML URL
|
82 |
+
*/
|
83 |
protected function url_start($html_allowed, $url, $https = false, $website_home = null) {
|
84 |
$proto = ($https) ? 'https' : 'http';
|
85 |
if (strpos($url, $website_home) !== false) {
|
86 |
+
return $html_allowed ? "<a href=".apply_filters(str_replace('.', '_', $website_home).'_link', $proto.'://'.$url).'>' : '';
|
87 |
} else {
|
88 |
+
return $html_allowed ? '<a href="'.$proto.'://'.$url.'">' : '';
|
89 |
}
|
90 |
}
|
91 |
|
92 |
+
/**
|
93 |
+
* Generate the end of a HTML URL
|
94 |
+
*
|
95 |
+
* @param boolean $html_allowed - indicates if HTML is allowed or not
|
96 |
+
* @param string $url - the URL
|
97 |
+
* @param boolean $https - the protocol to use
|
98 |
+
*
|
99 |
+
* @return string returns a partial HTML URL
|
100 |
+
*/
|
101 |
protected function url_end($html_allowed, $url, $https = false) {
|
102 |
+
$proto = $https ? 'https' : 'http';
|
103 |
+
return $html_allowed ? '</a>' : ' ('.$proto.'://'.$url.')';
|
104 |
}
|
105 |
|
106 |
/**
|
107 |
* Renders notice
|
108 |
*
|
109 |
+
* @param mixed $notice - a specific notice to render or false
|
110 |
+
* @param string $position - position of the notice
|
111 |
+
* @param boolean $return_instead_of_echo - indicates if we should echo notice or return as string
|
112 |
*
|
113 |
* @return mixed Returns string or echos notice
|
114 |
*/
|
128 |
/**
|
129 |
* This method will return a notice ready for display.
|
130 |
*
|
131 |
+
* @param boolean $notice - a specific notice to render or false
|
132 |
+
* @param string $position - position of the notice
|
133 |
+
*
|
134 |
+
* @return array returns notice data
|
135 |
*/
|
136 |
protected function get_notice_data($notice = false, $position = 'top') {
|
137 |
|
149 |
|
150 |
if ($dismiss) return false;
|
151 |
|
152 |
+
// If the advert has a validity function, then require the advert to be valid
|
153 |
+
if (!empty($this->notices_content[$notice]['validity_function']) && !call_user_func(array($this, $this->notices_content[$notice]['validity_function']))) return false;
|
154 |
+
|
155 |
return $this->notices_content[$notice];
|
156 |
}
|
157 |
|
180 |
if (empty($available_notices)) return false;
|
181 |
|
182 |
// If a seasonal advert can't be returned then we will return a random advert.
|
183 |
+
|
184 |
+
// Here we give a 25% chance for the rate advert to be returned before selecting a random advert from the entire collection which also includes the rate advert
|
185 |
+
if (0 == rand(0, 3) && isset($available_notices['rate'])) return $available_notices['rate'];
|
186 |
+
|
187 |
// Using shuffle here as something like rand which produces a random number and uses that as the array index fails, this is because in future an advert may not be numbered and could have a string as its key which will then cause errors.
|
188 |
shuffle($available_notices);
|
189 |
return $available_notices[0];
|
192 |
/**
|
193 |
* Skip seasonal notices
|
194 |
*
|
195 |
+
* @param array $notice_data - an array of data for the chosen notice
|
196 |
+
*
|
197 |
+
* @return boolean
|
198 |
*/
|
199 |
+
protected function skip_seasonal_notices($notice_data) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
200 |
return false;
|
201 |
}
|
202 |
|
203 |
/**
|
204 |
+
* Returns affiliate ID
|
205 |
*
|
206 |
+
* @return mixed Returns affiliate ID
|
207 |
*/
|
208 |
public function get_affiliate_id() {
|
209 |
return $this->self_affiliate_id;
|
210 |
}
|
211 |
|
212 |
/**
|
213 |
+
* Checks if the notice has been dismissed
|
214 |
*
|
215 |
+
* @param string $dismiss_time - dismiss time
|
216 |
*
|
217 |
* @return mixed
|
218 |
*/
|
vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc.php
ADDED
@@ -0,0 +1,1115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// @codingStandardsIgnoreStart
|
3 |
+
/*
|
4 |
+
This class provides methods for encrypting, sending, receiving and decrypting messages of arbitrary length, using standard encryption methods and including protection against replay attacks.
|
5 |
+
|
6 |
+
Example:
|
7 |
+
|
8 |
+
// Set a key and encrypt with it
|
9 |
+
$ud_rpc = new UpdraftPlus_Remote_Communications($name_indicator); // $name_indicator is a key indicator - indicating which key is being used.
|
10 |
+
$ud_rpc->set_key_local($our_private_key);
|
11 |
+
$ud_rpc->set_key_remote($their_public_key);
|
12 |
+
$encrypted = $ud_rpc->encrypt_message('blah blah');
|
13 |
+
|
14 |
+
// Use the saved WP site option
|
15 |
+
$ud_rpc = new UpdraftPlus_Remote_Communications($name_indicator); // $name_indicator is a key indicator - indicating which key is being used.
|
16 |
+
$ud_rpc->set_option_name('udrpc_remotekey');
|
17 |
+
if (!$ud_rpc->get_key_remote()) throw new Exception('...');
|
18 |
+
$encrypted = $ud_rpc->encrypt_message('blah blah');
|
19 |
+
|
20 |
+
// Generate a new key
|
21 |
+
$ud_rpc = new UpdraftPlus_Remote_Communications('myindicator.example.com');
|
22 |
+
$ud_rpc->set_option_name('udrpc_localkey'); // Save as a WP site option
|
23 |
+
$new_pair = $ud_rpc->generate_new_keypair();
|
24 |
+
if ($new_pair) {
|
25 |
+
$local_private_key = $ud_rpc->get_key_local();
|
26 |
+
$remote_public_key = $ud_rpc->get_key_remote();
|
27 |
+
// ...
|
28 |
+
} else {
|
29 |
+
throw new Exception('...');
|
30 |
+
}
|
31 |
+
|
32 |
+
// Send a message
|
33 |
+
$ud_rpc->activate_replay_protection();
|
34 |
+
$ud_rpc->set_destination_url('https://example.com/path/to/wp');
|
35 |
+
$ud_rpc->send_message('ping');
|
36 |
+
$ud_rpc->send_message('somecommand', array('param1' => 'data', 'param2' => 'moredata'));
|
37 |
+
|
38 |
+
// N.B. The data sent needs to be something that will pass json_encode(). So, it may be desirable to base64-encode it first.
|
39 |
+
|
40 |
+
// Create a listener for incoming messages
|
41 |
+
|
42 |
+
add_filter('udrpc_command_somecommand', 'my_function', 10, 3);
|
43 |
+
// function my_function($response, $data, $name_indicator) { ... ; return array('response' => 'my_reply', 'data' => 'any mixed data'); }
|
44 |
+
// Or:
|
45 |
+
// add_filter('udrpc_action', 'some_function', 10, 4); // Function must return something other than false to indicate that it handled the specific command. Any returned value will be sent as the reply.
|
46 |
+
// function some_function($response, $command, $data, $name_indicator) { ...; return array('response' => 'my_reply', 'data' => 'any mixed data'); }
|
47 |
+
$ud_rpc->set_option_name('udrpc_local_private_key');
|
48 |
+
$ud_rpc->activate_replay_protection();
|
49 |
+
if ($ud_rpc->get_key_local()) {
|
50 |
+
// Make sure you call this before the wp_loaded action is fired (e.g. at init)
|
51 |
+
$ud_rpc->create_listener();
|
52 |
+
}
|
53 |
+
|
54 |
+
// Instead of using activate_replay_protection(), you can use activate_sequence_protection() (receiving side) and set_next_send_sequence_id(). They are very similar; but, the sequence number code isn't tested, and is problematic if you may have multiple clients that don't share storage (you can use the current time as a sequence number, but if two clients send at the same millisecond (or whatever granularity you use), you may have problems); whereas the replay protection code relies on database storage on the sending side (not just the receiving).
|
55 |
+
|
56 |
+
*/
|
57 |
+
// @codingStandardsIgnoreEnd
|
58 |
+
if (!class_exists('UpdraftPlus_Remote_Communications')) :
|
59 |
+
class UpdraftPlus_Remote_Communications {
|
60 |
+
|
61 |
+
// Version numbers relate to versions of this PHP library only (i.e. it's not a protocol support number, and version numbers of other compatible libraries (e.g. JavaScript) are not comparable)
|
62 |
+
public $version = '1.4.23';
|
63 |
+
|
64 |
+
private $key_name_indicator;
|
65 |
+
|
66 |
+
private $key_option_name = false;
|
67 |
+
|
68 |
+
private $key_remote = false;
|
69 |
+
|
70 |
+
private $key_local = false;
|
71 |
+
|
72 |
+
private $can_generate = false;
|
73 |
+
|
74 |
+
private $destination_url = false;
|
75 |
+
|
76 |
+
private $maximum_replay_time_difference = 300;
|
77 |
+
|
78 |
+
private $extra_replay_protection = false;
|
79 |
+
|
80 |
+
private $sequence_protection_tolerance;
|
81 |
+
|
82 |
+
private $sequence_protection_table;
|
83 |
+
|
84 |
+
private $sequence_protection_column;
|
85 |
+
|
86 |
+
private $sequence_protection_where_sql;
|
87 |
+
|
88 |
+
// Debug may log confidential data using $this->log() - so only use when you are in a secure environment
|
89 |
+
private $debug = false;
|
90 |
+
|
91 |
+
private $next_send_sequence_id;
|
92 |
+
|
93 |
+
private $allow_cors_from = array();
|
94 |
+
|
95 |
+
private $http_transport = null;
|
96 |
+
|
97 |
+
// Default protocol version - this can be over-ridden with set_message_format
|
98 |
+
// Protocol version 1 (which uses only one RSA key-pair, instead of two) is legacy/deprecated
|
99 |
+
private $format = 2;
|
100 |
+
|
101 |
+
private $http_credentials = array();
|
102 |
+
|
103 |
+
private $incoming_message = null;
|
104 |
+
|
105 |
+
private $message_random_number = null;
|
106 |
+
|
107 |
+
private $require_message_to_be_understood = false;
|
108 |
+
|
109 |
+
public function __construct($key_name_indicator = 'default') {
|
110 |
+
$this->set_key_name_indicator($key_name_indicator);
|
111 |
+
}
|
112 |
+
|
113 |
+
public function set_key_name_indicator($key_name_indicator) {
|
114 |
+
$this->key_name_indicator = $key_name_indicator;
|
115 |
+
}
|
116 |
+
|
117 |
+
public function set_can_generate($can_generate = true) {
|
118 |
+
$this->can_generate = $can_generate;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Which sites to allow CORS requests from
|
123 |
+
*
|
124 |
+
* @param string $allow_cors_from
|
125 |
+
*/
|
126 |
+
public function set_allow_cors_from($allow_cors_from) {
|
127 |
+
$this->allow_cors_from = $allow_cors_from;
|
128 |
+
}
|
129 |
+
|
130 |
+
public function set_maximum_replay_time_difference($replay_time_difference) {
|
131 |
+
$this->maximum_replay_time_difference = (int) $replay_time_difference;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* This will cause more things to be sent to $this->log()
|
136 |
+
*
|
137 |
+
* @param boolean $debug
|
138 |
+
*/
|
139 |
+
public function set_debug($debug = true) {
|
140 |
+
$this->debug = (bool) $debug;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Supported values: a Guzzle object, or, if not, then WP's HTTP API function siwll be used
|
145 |
+
*
|
146 |
+
* @param string $transport
|
147 |
+
*/
|
148 |
+
public function set_http_transport($transport) {
|
149 |
+
$this->http_transport = $transport;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Sequence protection and replay protection perform similar functions, and using both is often over-kill; the distinction is that sequence protection can be used without needing to do database writes on the sending side (e.g. use the value of time() as the sequence number).
|
154 |
+
* The only rule of sequences is that the receiving side will reject any sequence number that is less than the last previously seen one, within the bounds of the tolerance (but it may also reject those if they are repeats).
|
155 |
+
* The given table/column will record a comma-separated list of recently seen sequences numbers within the tolerance threshold.
|
156 |
+
*
|
157 |
+
* @param string $table
|
158 |
+
* @param string $column
|
159 |
+
* @param string $where_sql
|
160 |
+
* @param integer $tolerance
|
161 |
+
*/
|
162 |
+
public function activate_sequence_protection($table, $column, $where_sql, $tolerance = 5) {
|
163 |
+
$this->sequence_protection_tolerance = (int) $tolerance;
|
164 |
+
$this->sequence_protection_table = (string) $table;
|
165 |
+
$this->sequence_protection_column = (string) $column;
|
166 |
+
$this->sequence_protection_where_sql = (string) $where_sql;
|
167 |
+
}
|
168 |
+
|
169 |
+
private function ensure_crypto_loaded() {
|
170 |
+
if (!class_exists('Crypt_Rijndael') || !class_exists('Crypt_RSA') || !class_exists('Crypt_Hash')) {
|
171 |
+
global $updraftplus;
|
172 |
+
// phpseclib 1.x uses deprecated PHP4-style constructors
|
173 |
+
$this->no_deprecation_warnings_on_php7();
|
174 |
+
if (is_a($updraftplus, 'UpdraftPlus')) {
|
175 |
+
// Since May 2019, the second parameter is unused; but, since we don't know the version, we send it.
|
176 |
+
$ensure_phpseclib = $updraftplus->ensure_phpseclib(array('Crypt_Rijndael', 'Crypt_RSA', 'Crypt_Hash'), array('Crypt/Rijndael', 'Crypt/RSA', 'Crypt/Hash'));
|
177 |
+
if (is_wp_error($ensure_phpseclib)) return $ensure_phpseclib;
|
178 |
+
} elseif (defined('UPDRAFTPLUS_DIR') && file_exists(UPDRAFTPLUS_DIR.'/vendor/phpseclib/phpseclib/phpseclib')) {
|
179 |
+
$pdir = UPDRAFTPLUS_DIR.'/vendor/phpseclib/phpseclib/phpseclib';
|
180 |
+
if (false === strpos(get_include_path(), $pdir)) set_include_path($pdir.PATH_SEPARATOR.get_include_path());
|
181 |
+
if (!class_exists('Crypt_Rijndael')) include_once 'Crypt/Rijndael.php';
|
182 |
+
if (!class_exists('Crypt_RSA')) include_once 'Crypt/RSA.php';
|
183 |
+
if (!class_exists('Crypt_Hash')) include_once 'Crypt/Hash.php';
|
184 |
+
} elseif (file_exists(dirname(dirname(__FILE__)).'/vendor/phpseclib/phpseclib/phpseclib')) {
|
185 |
+
$pdir = dirname(dirname(__FILE__)).'/vendor/phpseclib/phpseclib/phpseclib';
|
186 |
+
if (false === strpos(get_include_path(), $pdir)) set_include_path($pdir.PATH_SEPARATOR.get_include_path());
|
187 |
+
if (!class_exists('Crypt_Rijndael')) include_once 'Crypt/Rijndael.php';
|
188 |
+
if (!class_exists('Crypt_RSA')) include_once 'Crypt/RSA.php';
|
189 |
+
if (!class_exists('Crypt_Hash')) include_once 'Crypt/Hash.php';
|
190 |
+
}
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Ugly, but necessary to prevent debug output breaking the conversation when the user has debug turned on
|
196 |
+
*/
|
197 |
+
private function no_deprecation_warnings_on_php7() {
|
198 |
+
// PHP_MAJOR_VERSION is defined in PHP 5.2.7+
|
199 |
+
// We don't test for PHP > 7 because the specific deprecated element will be removed in PHP 8 - and so no warning should come anyway (and we shouldn't suppress other stuff until we know we need to).
|
200 |
+
// @codingStandardsIgnoreLine
|
201 |
+
if (defined('PHP_MAJOR_VERSION') && PHP_MAJOR_VERSION == 7) {
|
202 |
+
$old_level = error_reporting();
|
203 |
+
// @codingStandardsIgnoreLine
|
204 |
+
$new_level = $old_level & ~E_DEPRECATED;
|
205 |
+
if ($old_level != $new_level) error_reporting($new_level);
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
public function set_destination_url($destination_url) {
|
210 |
+
$this->destination_url = $destination_url;
|
211 |
+
}
|
212 |
+
|
213 |
+
public function get_destination_url() {
|
214 |
+
return $this->destination_url;
|
215 |
+
}
|
216 |
+
|
217 |
+
public function set_option_name($key_option_name) {
|
218 |
+
$this->key_option_name = $key_option_name;
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Method to get the remote key
|
223 |
+
*
|
224 |
+
* @return string
|
225 |
+
*/
|
226 |
+
public function get_key_remote() {
|
227 |
+
if (empty($this->key_remote) && $this->can_generate) {
|
228 |
+
$this->generate_new_keypair();
|
229 |
+
}
|
230 |
+
|
231 |
+
return empty($this->key_remote) ? false : $this->key_remote;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Set the remote key
|
236 |
+
*
|
237 |
+
* @param string $key_remote
|
238 |
+
*/
|
239 |
+
public function set_key_remote($key_remote) {
|
240 |
+
$this->key_remote = $key_remote;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Used for sending - when receiving, the format is part of the message
|
245 |
+
*
|
246 |
+
* @param integer $format
|
247 |
+
*/
|
248 |
+
public function set_message_format($format = 2) {
|
249 |
+
$this->format = $format;
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Used for sending - when receiving, the format is part of the message
|
254 |
+
*
|
255 |
+
* @return integer
|
256 |
+
*/
|
257 |
+
public function get_message_format() {
|
258 |
+
return $this->format;
|
259 |
+
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Method to get the local key
|
263 |
+
*
|
264 |
+
* @return string
|
265 |
+
*/
|
266 |
+
public function get_key_local() {
|
267 |
+
if (empty($this->key_local)) {
|
268 |
+
if ($this->key_option_name) {
|
269 |
+
$key_local = get_site_option($this->key_option_name);
|
270 |
+
if ($key_local) {
|
271 |
+
$this->key_local = $key_local;
|
272 |
+
}
|
273 |
+
}
|
274 |
+
}
|
275 |
+
if (empty($this->key_local) && $this->can_generate) {
|
276 |
+
$this->generate_new_keypair();
|
277 |
+
}
|
278 |
+
|
279 |
+
return empty($this->key_local) ? false : $this->key_local;
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Tests whether a supplied string (after trimming) is a valid portable bundle
|
284 |
+
*
|
285 |
+
* @param string $bundle [description]
|
286 |
+
* @param string $format same as get_portable_bundle()
|
287 |
+
* @return array (which the consumer is free to use - e.g. convert into internationalised string), with keys 'code' and (perhaps) 'data'
|
288 |
+
*/
|
289 |
+
public function decode_portable_bundle($bundle, $format = 'raw') {
|
290 |
+
$bundle = trim($bundle);
|
291 |
+
if ('base64_with_count' == $format) {
|
292 |
+
if (strlen($bundle) < 5) return array('code' => 'invalid_wrong_length', 'data' => 'too_short');
|
293 |
+
$len = substr($bundle, 0, 4);
|
294 |
+
$bundle = substr($bundle, 4);
|
295 |
+
$len = hexdec($len);
|
296 |
+
if (strlen($bundle) != $len) return array('code' => 'invalid_wrong_length', 'data' => "1,$len,".strlen($bundle));
|
297 |
+
if (false === ($bundle = base64_decode($bundle))) return array('code' => 'invalid_corrupt', 'data' => 'not_base64');
|
298 |
+
if (null === ($bundle = json_decode($bundle, true))) return array('code' => 'invalid_corrupt', 'data' => 'not_json');
|
299 |
+
}
|
300 |
+
if (empty($bundle['key'])) return array('code' => 'invalid_corrupt', 'data' => 'no_key');
|
301 |
+
if (empty($bundle['url'])) return array('code' => 'invalid_corrupt', 'data' => 'no_url');
|
302 |
+
if (empty($bundle['name_indicator'])) return array('code' => 'invalid_corrupt', 'data' => 'no_name_indicator');
|
303 |
+
|
304 |
+
return $bundle;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Method to get a portable bundle sufficient to contact this site (i.e. remote site - so you need to have generated a key-pair, or stored the remote key somewhere and restored it)
|
309 |
+
*
|
310 |
+
* @param string $format Supported formats: base64_with_count and default)raw
|
311 |
+
* @param array $extra_info needs to be JSON-serialisable, so be careful about what you put into it.
|
312 |
+
* @param array $options [description]
|
313 |
+
* @return array
|
314 |
+
*/
|
315 |
+
public function get_portable_bundle($format = 'raw', $extra_info = array(), $options = array()) {
|
316 |
+
|
317 |
+
$bundle = array_merge($extra_info, array(
|
318 |
+
'key' => empty($options['key']) ? $this->get_key_remote() : $options['key'],
|
319 |
+
'name_indicator' => $this->key_name_indicator,
|
320 |
+
'url' => trailingslashit(network_site_url()),
|
321 |
+
'admin_url' => trailingslashit(admin_url()),
|
322 |
+
'network_admin_url' => trailingslashit(network_admin_url()),
|
323 |
+
'format_support' => 2,
|
324 |
+
));
|
325 |
+
|
326 |
+
if ('base64_with_count' == $format) {
|
327 |
+
$bundle = base64_encode(json_encode($bundle));
|
328 |
+
|
329 |
+
$len = strlen($bundle); // Get the length
|
330 |
+
$len = dechex($len); // The first bytes of the message are the bundle length
|
331 |
+
$len = str_pad($len, 4, '0', STR_PAD_LEFT); // Zero pad
|
332 |
+
|
333 |
+
return $len.$bundle;
|
334 |
+
|
335 |
+
} else {
|
336 |
+
return $bundle;
|
337 |
+
}
|
338 |
+
|
339 |
+
}
|
340 |
+
|
341 |
+
public function set_key_local($key_local) {
|
342 |
+
$this->key_local = $key_local;
|
343 |
+
if ($this->key_option_name) update_site_option($this->key_option_name, $this->key_local);
|
344 |
+
}
|
345 |
+
|
346 |
+
public function generate_new_keypair($key_size = 2048) {
|
347 |
+
|
348 |
+
$this->ensure_crypto_loaded();
|
349 |
+
|
350 |
+
$rsa = new Crypt_RSA();
|
351 |
+
$keys = $rsa->createKey($key_size);
|
352 |
+
|
353 |
+
if (empty($keys['privatekey'])) {
|
354 |
+
$this->set_key_local(false);
|
355 |
+
} else {
|
356 |
+
$this->set_key_local($keys['privatekey']);
|
357 |
+
}
|
358 |
+
|
359 |
+
if (empty($keys['publickey'])) {
|
360 |
+
$this->set_key_remote(false);
|
361 |
+
} else {
|
362 |
+
$this->set_key_remote($keys['publickey']);
|
363 |
+
}
|
364 |
+
|
365 |
+
return empty($keys['publickey']) ? false : true;
|
366 |
+
}
|
367 |
+
|
368 |
+
/**
|
369 |
+
* A base-64 encoded RSA hash (PKCS_1) of the message digest
|
370 |
+
*
|
371 |
+
* @param string $message
|
372 |
+
* @param boolean $use_key
|
373 |
+
* @return array
|
374 |
+
*/
|
375 |
+
public function signature_for_message($message, $use_key = false) {
|
376 |
+
|
377 |
+
$hash_algorithm = 'sha256';
|
378 |
+
|
379 |
+
// Sign with the private (local) key
|
380 |
+
if (!$use_key) {
|
381 |
+
if (!$this->key_local) throw new Exception('No signing key has been set');
|
382 |
+
$use_key = $this->key_local;
|
383 |
+
}
|
384 |
+
|
385 |
+
$this->ensure_crypto_loaded();
|
386 |
+
|
387 |
+
$rsa = new Crypt_RSA();
|
388 |
+
$rsa->loadKey($use_key);
|
389 |
+
// This is the older signature mode; phpseclib's default is the preferred CRYPT_RSA_SIGNATURE_PSS; however, Forge JS doesn't yet support this. More info: https://en.wikipedia.org/wiki/PKCS_1
|
390 |
+
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
|
391 |
+
|
392 |
+
// Don't do this: Crypt_RSA::sign() already calculates the digest of the hash
|
393 |
+
// $hash = new Crypt_Hash($hash_algorithm);
|
394 |
+
// $hashed = $hash->hash($message);
|
395 |
+
|
396 |
+
// if ($this->debug) $this->log("Message hash (hash=$hash_algorithm) (hex): ".bin2hex($hashed));
|
397 |
+
|
398 |
+
// phpseclib defaults to SHA1
|
399 |
+
$rsa->setHash($hash_algorithm);
|
400 |
+
$encrypted = $rsa->sign($message);
|
401 |
+
|
402 |
+
if ($this->debug) $this->log('Signed hash (mode='.CRYPT_RSA_SIGNATURE_PKCS1.') (hex): '.bin2hex($encrypted));
|
403 |
+
|
404 |
+
$signature = base64_encode($encrypted);
|
405 |
+
|
406 |
+
if ($this->debug) $this->log("Message signature (base64): $signature");
|
407 |
+
|
408 |
+
return $signature;
|
409 |
+
}
|
410 |
+
|
411 |
+
/**
|
412 |
+
* Log description
|
413 |
+
*
|
414 |
+
* @param string $message
|
415 |
+
* @param string $level $level is not yet used much
|
416 |
+
*/
|
417 |
+
private function log($message, $level = 'notice') {
|
418 |
+
// Allow other plugins to do something with the message
|
419 |
+
do_action('udrpc_log', $message, $level, $this->key_name_indicator, $this->debug, $this);
|
420 |
+
if ('info' != $level) error_log('UDRPC ('.$this->key_name_indicator.", $level): $message");
|
421 |
+
}
|
422 |
+
|
423 |
+
/**
|
424 |
+
* Encrypt the message, using the local key (which needs to exist)
|
425 |
+
*
|
426 |
+
* @param string $plaintext
|
427 |
+
* @param boolean $use_key
|
428 |
+
* @param integer $key_length
|
429 |
+
* @return array
|
430 |
+
*/
|
431 |
+
public function encrypt_message($plaintext, $use_key = false, $key_length = 32) {
|
432 |
+
|
433 |
+
if (!$use_key) {
|
434 |
+
if (1 == $this->format) {
|
435 |
+
if (!$this->key_local) throw new Exception('No encryption key has been set');
|
436 |
+
$use_key = $this->key_local;
|
437 |
+
} else {
|
438 |
+
if (!$this->key_remote) throw new Exception('No encryption key has been set');
|
439 |
+
$use_key = $this->key_remote;
|
440 |
+
}
|
441 |
+
}
|
442 |
+
|
443 |
+
$this->ensure_crypto_loaded();
|
444 |
+
|
445 |
+
$rsa = new Crypt_RSA();
|
446 |
+
|
447 |
+
if (defined('UDRPC_PHPSECLIB_ENCRYPTION_MODE')) $rsa->setEncryptionMode(UDRPC_PHPSECLIB_ENCRYPTION_MODE);
|
448 |
+
|
449 |
+
$rij = new Crypt_Rijndael();
|
450 |
+
|
451 |
+
// Generate Random Symmetric Key
|
452 |
+
$sym_key = crypt_random_string($key_length);
|
453 |
+
|
454 |
+
if ($this->debug) $this->log('Unencrypted symmetric key (hex): '.bin2hex($sym_key));
|
455 |
+
|
456 |
+
// Encrypt Message with new Symmetric Key
|
457 |
+
$rij->setKey($sym_key);
|
458 |
+
$ciphertext = $rij->encrypt($plaintext);
|
459 |
+
|
460 |
+
if ($this->debug) $this->log('Encrypted ciphertext (hex): '.bin2hex($ciphertext));
|
461 |
+
|
462 |
+
$ciphertext = base64_encode($ciphertext);
|
463 |
+
|
464 |
+
// Encrypt the Symmetric Key with the Asymmetric Key
|
465 |
+
$rsa->loadKey($use_key);
|
466 |
+
$sym_key = $rsa->encrypt($sym_key);
|
467 |
+
|
468 |
+
if ($this->debug) $this->log('Encrypted symmetric key (hex): '.bin2hex($sym_key));
|
469 |
+
|
470 |
+
// Base 64 encode the symmetric key for transport
|
471 |
+
$sym_key = base64_encode($sym_key);
|
472 |
+
|
473 |
+
if ($this->debug) $this->log('Encrypted symmetric key (b64): '.$sym_key);
|
474 |
+
|
475 |
+
$len = str_pad(dechex(strlen($sym_key)), 3, '0', STR_PAD_LEFT); // Zero pad to be sure.
|
476 |
+
|
477 |
+
// 16 characters of hex is enough for the payload to be to 16 exabytes (giga < tera < peta < exa) of data
|
478 |
+
$cipherlen = str_pad(dechex(strlen($ciphertext)), 16, '0', STR_PAD_LEFT);
|
479 |
+
|
480 |
+
// Concatenate the length, the encrypted symmetric key, and the message
|
481 |
+
return $len.$sym_key.$cipherlen.$ciphertext;
|
482 |
+
|
483 |
+
}
|
484 |
+
|
485 |
+
/**
|
486 |
+
* Decrypt the message, using the local key (which needs to exist)
|
487 |
+
*
|
488 |
+
* @param string $message
|
489 |
+
* @return array
|
490 |
+
*/
|
491 |
+
public function decrypt_message($message) {
|
492 |
+
|
493 |
+
if (!$this->key_local) throw new Exception('No decryption key has been set');
|
494 |
+
|
495 |
+
$this->ensure_crypto_loaded();
|
496 |
+
|
497 |
+
$rsa = new Crypt_RSA();
|
498 |
+
if (defined('UDRPC_PHPSECLIB_ENCRYPTION_MODE')) $rsa->setEncryptionMode(UDRPC_PHPSECLIB_ENCRYPTION_MODE);
|
499 |
+
// Defaults to CRYPT_AES_MODE_CBC
|
500 |
+
$rij = new Crypt_Rijndael();
|
501 |
+
|
502 |
+
// Extract the Symmetric Key
|
503 |
+
$len = substr($message, 0, 3);
|
504 |
+
$len = hexdec($len);
|
505 |
+
$sym_key = substr($message, 3, $len);
|
506 |
+
|
507 |
+
// Extract the encrypted message
|
508 |
+
$cipherlen = substr($message, ($len + 3), 16);
|
509 |
+
$cipherlen = hexdec($cipherlen);
|
510 |
+
|
511 |
+
$ciphertext = substr($message, ($len + 19), $cipherlen);
|
512 |
+
$ciphertext = base64_decode($ciphertext);
|
513 |
+
|
514 |
+
// Decrypt the encrypted symmetric key
|
515 |
+
$rsa->loadKey($this->key_local);
|
516 |
+
$sym_key = base64_decode($sym_key);
|
517 |
+
$sym_key = $rsa->decrypt($sym_key);
|
518 |
+
|
519 |
+
// Decrypt the message
|
520 |
+
$rij->setKey($sym_key);
|
521 |
+
|
522 |
+
return $rij->decrypt($ciphertext);
|
523 |
+
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* Creates a message
|
528 |
+
*
|
529 |
+
* @param string $command
|
530 |
+
* @param string $data
|
531 |
+
* @param boolean $is_response
|
532 |
+
* @param boolean $use_key_remote
|
533 |
+
* @param boolean $use_key_local
|
534 |
+
* @return array which the caller will then format as required (e.g. use as body in post, or JSON-encode, etc.) [description]
|
535 |
+
*/
|
536 |
+
public function create_message($command, $data = null, $is_response = false, $use_key_remote = false, $use_key_local = false) {
|
537 |
+
|
538 |
+
if ($is_response) {
|
539 |
+
$send_array = array('response' => $command);
|
540 |
+
} else {
|
541 |
+
$send_array = array('command' => $command);
|
542 |
+
}
|
543 |
+
|
544 |
+
$send_array['time'] = time();
|
545 |
+
// This goes in the encrypted portion as well to prevent replays with a different unencrypted name indicator
|
546 |
+
$send_array['key_name'] = $this->key_name_indicator;
|
547 |
+
|
548 |
+
// This random element means that if the site needs to send two identical commands or responses in the same second, then it can, and still use replay protection
|
549 |
+
// The value of PHP_INT_MAX on a 32-bit platform
|
550 |
+
$this->message_random_number = rand(1, 2147483647);
|
551 |
+
$send_array['rand'] = $this->message_random_number;
|
552 |
+
|
553 |
+
if ($this->next_send_sequence_id) {
|
554 |
+
$send_array['sequence_id'] = $this->next_send_sequence_id;
|
555 |
+
++$this->next_send_sequence_id;
|
556 |
+
}
|
557 |
+
|
558 |
+
if ($is_response && !empty($this->incoming_message) && isset($this->incoming_message['rand'])) {
|
559 |
+
$send_array['incoming_rand'] = $this->incoming_message['rand'];
|
560 |
+
}
|
561 |
+
|
562 |
+
if (null !== $data) $send_array['data'] = $data;
|
563 |
+
$send_data = $this->encrypt_message(json_encode($send_array), $use_key_remote);
|
564 |
+
|
565 |
+
$message = array(
|
566 |
+
'format' => $this->format,
|
567 |
+
'key_name' => $this->key_name_indicator,
|
568 |
+
'udrpc_message' => $send_data,
|
569 |
+
);
|
570 |
+
|
571 |
+
if ($this->format >= 2) {
|
572 |
+
$signature = $this->signature_for_message($send_data, $use_key_local);
|
573 |
+
$message['signature'] = $signature;
|
574 |
+
}
|
575 |
+
|
576 |
+
return $message;
|
577 |
+
|
578 |
+
}
|
579 |
+
|
580 |
+
/**
|
581 |
+
* N.B. There's already some time-based replay protection. This can be turned on to beef it up.
|
582 |
+
* This is only for listeners. Replays can only be detection if transients are working on the WP site (which by default only means that the option table is working).
|
583 |
+
*
|
584 |
+
* @param boolean $activate
|
585 |
+
*/
|
586 |
+
public function activate_replay_protection($activate = true) {
|
587 |
+
$this->extra_replay_protection = (bool) $activate;
|
588 |
+
}
|
589 |
+
|
590 |
+
public function set_next_send_sequence_id($id) {
|
591 |
+
$this->next_send_sequence_id = $id;
|
592 |
+
}
|
593 |
+
|
594 |
+
/**
|
595 |
+
* Set_http_credentials
|
596 |
+
*
|
597 |
+
* @param string $credentials should be an array with entries for 'username' and 'password'
|
598 |
+
*/
|
599 |
+
public function set_http_credentials($credentials) {
|
600 |
+
$this->http_credentials = $credentials;
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* This needs only to return an array with keys body and response - where response is also an array, with key 'code' (the HTTP status code)
|
605 |
+
* The $post_options array support these keys: timeout, body,
|
606 |
+
* Public, to allow short-circuiting of the library's own encoding/decoding (e.g. for acting as a proxy for a message already encrypted elsewhere)
|
607 |
+
*
|
608 |
+
* @param array $post_options
|
609 |
+
* @return array
|
610 |
+
*/
|
611 |
+
public function http_post($post_options) {
|
612 |
+
global $wp_version;
|
613 |
+
include ABSPATH.WPINC.'/version.php';
|
614 |
+
$http_credentials = $this->http_credentials;
|
615 |
+
|
616 |
+
if (is_a($this->http_transport, 'GuzzleHttp\Client')) {
|
617 |
+
|
618 |
+
// https://guzzle.readthedocs.org/en/5.3/clients.html
|
619 |
+
|
620 |
+
$client = $this->http_transport;
|
621 |
+
|
622 |
+
$guzzle_options = array(
|
623 |
+
'form_params' => $post_options['body'],
|
624 |
+
'headers' => array(
|
625 |
+
'User-Agent' => 'WordPress/'.$wp_version.'; class-udrpc.php-Guzzle/'.$this->version.'; '.get_bloginfo('url'),
|
626 |
+
),
|
627 |
+
'exceptions' => false,
|
628 |
+
'timeout' => $post_options['timeout'],
|
629 |
+
);
|
630 |
+
|
631 |
+
if (!class_exists('WP_HTTP_Proxy')) include_once ABSPATH.WPINC.'/class-http.php';
|
632 |
+
$proxy = new WP_HTTP_Proxy();
|
633 |
+
if ($proxy->is_enabled()) {
|
634 |
+
$user = $proxy->username();
|
635 |
+
$pass = $proxy->password();
|
636 |
+
$host = $proxy->host();
|
637 |
+
$port = (int) $proxy->port();
|
638 |
+
if (empty($port)) $port = 8080;
|
639 |
+
if (!empty($host) && $proxy->send_through_proxy($this->destination_url)) {
|
640 |
+
$proxy_auth = '';
|
641 |
+
if (!empty($user)) {
|
642 |
+
$proxy_auth = $user;
|
643 |
+
if (!empty($pass)) $proxy_auth .= ':'.$pass;
|
644 |
+
$proxy_auth .= '@';
|
645 |
+
}
|
646 |
+
$guzzle_options['proxy'] = array(
|
647 |
+
'http' => "http://${proxy_auth}$host:$port",
|
648 |
+
'https' => "http://${proxy_auth}$host:$port",
|
649 |
+
);
|
650 |
+
}
|
651 |
+
}
|
652 |
+
|
653 |
+
if (defined('UDRPC_GUZZLE_SSL_VERIFY')) {
|
654 |
+
$verify = UDRPC_GUZZLE_SSL_VERIFY;
|
655 |
+
} elseif (file_exists(ABSPATH.WPINC.'/certificates/ca-bundle.crt')) {
|
656 |
+
$verify = ABSPATH.WPINC.'/certificates/ca-bundle.crt';
|
657 |
+
} else {
|
658 |
+
$verify = true;
|
659 |
+
}
|
660 |
+
|
661 |
+
$guzzle_options['verify'] = apply_filters('udrpc_guzzle_verify', $verify);
|
662 |
+
|
663 |
+
if (!empty($http_credentials['username'])) {
|
664 |
+
|
665 |
+
$authentication_method = empty($http_credentials['authentication_method']) ? 'basic' : $http_credentials['authentication_method'];
|
666 |
+
|
667 |
+
$password = empty($http_credentials['password']) ? '' : $http_credentials['password'];
|
668 |
+
|
669 |
+
$guzzle_options['auth'] = array(
|
670 |
+
$http_credentials['username'],
|
671 |
+
$password,
|
672 |
+
$authentication_method,
|
673 |
+
);
|
674 |
+
|
675 |
+
}
|
676 |
+
|
677 |
+
$response = $client->post($this->destination_url, apply_filters('udrpc_guzzle_options', $guzzle_options, $this));
|
678 |
+
|
679 |
+
$formatted_response = array(
|
680 |
+
'response' => array(
|
681 |
+
'code' => $response->getStatusCode(),
|
682 |
+
),
|
683 |
+
'body' => $response->getBody(),
|
684 |
+
);
|
685 |
+
|
686 |
+
return $formatted_response;
|
687 |
+
|
688 |
+
} else {
|
689 |
+
|
690 |
+
$post_options['user-agent'] = 'WordPress/'.$wp_version.'; class-udrpc.php/'.$this->version.'; '.get_bloginfo('url');
|
691 |
+
|
692 |
+
if (!empty($http_credentials['username'])) {
|
693 |
+
|
694 |
+
$authentication_type = empty($http_credentials['authentication_type']) ? 'basic' : $http_credentials['authentication_type'];
|
695 |
+
|
696 |
+
if ('basic' != $authentication_type) {
|
697 |
+
return new WP_Error('unsupported_http_authentication_type', 'Only HTTP basic authentication is supported (for other types, use Guzzle)');
|
698 |
+
}
|
699 |
+
|
700 |
+
$password = empty($http_credentials['password']) ? '' : $http_credentials['password'];
|
701 |
+
$post_options['headers'] = array(
|
702 |
+
'Authorization' => 'Basic '.base64_encode($http_credentials['username'].':'.$password),
|
703 |
+
);
|
704 |
+
}
|
705 |
+
|
706 |
+
return wp_remote_post(
|
707 |
+
$this->destination_url,
|
708 |
+
$post_options
|
709 |
+
);
|
710 |
+
}
|
711 |
+
}
|
712 |
+
|
713 |
+
public function send_message($command, $data = null, $timeout = 20) {
|
714 |
+
|
715 |
+
if (empty($this->destination_url)) return new WP_Error('not_initialised', 'RPC error: URL not initialised');
|
716 |
+
|
717 |
+
$message = $this->create_message($command, $data);
|
718 |
+
|
719 |
+
$post_options = array(
|
720 |
+
'timeout' => $timeout,
|
721 |
+
'body' => $message,
|
722 |
+
);
|
723 |
+
|
724 |
+
$post_options = apply_filters('udrpc_post_options', $post_options, $command, $data, $timeout, $this);
|
725 |
+
|
726 |
+
// Make the memory available - may be useful if the message was large
|
727 |
+
unset($data);
|
728 |
+
|
729 |
+
try {
|
730 |
+
$post = $this->http_post($post_options);
|
731 |
+
} catch (Exception $e) {
|
732 |
+
// Curl can return an error code 0, which causes WP_Error to return early, without recording the message. So, we prefix the code.
|
733 |
+
return new WP_Error('http_post_'.$e->getCode(), $e->getMessage());
|
734 |
+
}
|
735 |
+
|
736 |
+
if (is_wp_error($post)) return $post;
|
737 |
+
|
738 |
+
$response_code = wp_remote_retrieve_response_code($post);
|
739 |
+
|
740 |
+
if (empty($response_code)) return new WP_Error('empty_http_code', 'Unexpected HTTP response code');
|
741 |
+
|
742 |
+
if ($response_code < 200 || $response_code >= 300) return new WP_Error('unexpected_http_code', 'Unexpected HTTP response code ('.$response_code.')', $post);
|
743 |
+
|
744 |
+
$response_body = wp_remote_retrieve_body($post);
|
745 |
+
|
746 |
+
if (empty($response_body)) return new WP_Error('empty_response', 'Empty response from remote site');
|
747 |
+
|
748 |
+
$decoded = json_decode($response_body, true);
|
749 |
+
|
750 |
+
if (empty($decoded)) {
|
751 |
+
|
752 |
+
if (false != ($found_at = strpos($response_body, '{"format":'))) {
|
753 |
+
$new_body = substr($response_body, $found_at);
|
754 |
+
$decoded = json_decode($new_body, true);
|
755 |
+
}
|
756 |
+
|
757 |
+
if (empty($decoded)) {
|
758 |
+
$this->log('response from remote site ('.$this->destination_url.') could not be understood: '.substr($response_body, 0, 100).' ... ', 'info');
|
759 |
+
return new WP_Error('response_not_understood', 'Response from remote site could not be understood', $response_body);
|
760 |
+
}
|
761 |
+
}
|
762 |
+
|
763 |
+
if (!is_array($decoded) || empty($decoded['udrpc_message'])) return new WP_Error('response_not_understood', 'Response from remote site was not in the expected format ('.$post['body'].')', $decoded);
|
764 |
+
|
765 |
+
if ($this->format >= 2) {
|
766 |
+
if (empty($decoded['signature'])) {
|
767 |
+
$this->log('No message signature found');
|
768 |
+
die;
|
769 |
+
}
|
770 |
+
if (!$this->key_remote) {
|
771 |
+
$this->log('No signature verification key has been set');
|
772 |
+
die;
|
773 |
+
}
|
774 |
+
if (!$this->verify_signature($decoded['udrpc_message'], $decoded['signature'], $this->key_remote)) {
|
775 |
+
$this->log('Signature verification failed; discarding');
|
776 |
+
die;
|
777 |
+
}
|
778 |
+
}
|
779 |
+
|
780 |
+
$decoded = $this->decrypt_message($decoded['udrpc_message']);
|
781 |
+
|
782 |
+
if (!is_string($decoded)) return new WP_Error('not_decrypted', 'Response from remote site was not successfully decrypted', $decoded['udrpc_message']);
|
783 |
+
|
784 |
+
$json_decoded = json_decode($decoded, true);
|
785 |
+
|
786 |
+
if (!is_array($json_decoded) || empty($json_decoded['response']) || empty($json_decoded['time']) || !is_numeric($json_decoded['time'])) return new WP_Error('response_corrupt', 'Response from remote site was not in the expected format', $decoded);
|
787 |
+
|
788 |
+
// Don't do the reply detection until now, because $post['body'] may not be a message that originated from the remote component at all (e.g. an HTTP error)
|
789 |
+
if ($this->extra_replay_protection) {
|
790 |
+
$message_hash = $this->calculate_message_hash((string) $post['body']);
|
791 |
+
if ($this->message_hash_seen($message_hash)) {
|
792 |
+
return new WP_Error('replay_detected', 'Message refused: replay detected', $message_hash);
|
793 |
+
}
|
794 |
+
}
|
795 |
+
|
796 |
+
$time_difference = absint((time() - $json_decoded['time']));
|
797 |
+
if ($time_difference > $this->maximum_replay_time_difference) return new WP_Error('window_error', 'Message refused: maxium replay time difference exceeded', $time_difference);
|
798 |
+
|
799 |
+
if (isset($json_decoded['incoming_rand']) && !empty($this->message_random_number) && $json_decoded['incoming_rand'] != $this->message_random_number) {
|
800 |
+
// @codingStandardsIgnoreLine
|
801 |
+
$this->log('UDRPC: Message mismatch (possibly MITM) (sent_rand=' + $this->message_random_number + ', returned_rand='.$json_decoded['incoming_rand'].'): dropping', 'error');
|
802 |
+
|
803 |
+
return new WP_Error('message_mismatch_error', 'Message refused: message mismatch (possible MITM)');
|
804 |
+
|
805 |
+
}
|
806 |
+
|
807 |
+
// Should be an array with keys including 'response' and (if relevant) 'data'
|
808 |
+
return $json_decoded;
|
809 |
+
|
810 |
+
}
|
811 |
+
|
812 |
+
/**
|
813 |
+
* Returns a boolean indicating whether a listener was created - which depends on whether one was needed (so, false does not necessarily indicate an error condition)
|
814 |
+
*
|
815 |
+
* @return boolean
|
816 |
+
*/
|
817 |
+
public function create_listener() {
|
818 |
+
|
819 |
+
$http_origin = function_exists('get_http_origin') ? get_http_origin() : (empty($_SERVER['HTTP_ORIGIN']) ? '' : $_SERVER['HTTP_ORIGIN']);
|
820 |
+
|
821 |
+
// Create the WP actions to handle incoming commands, handle built-in commands (e.g. ping, create_keys (authenticate with admin creds)), dispatch them to the right place, and die
|
822 |
+
if ((!empty($_POST) && !empty($_POST['udrpc_message']) && !empty($_POST['format'])) || (!empty($_SERVER['REQUEST_METHOD']) && 'OPTIONS' == $_SERVER['REQUEST_METHOD'] && $http_origin)) {
|
823 |
+
add_action('wp_loaded', array($this, 'wp_loaded'));
|
824 |
+
add_action('wp_loaded', array($this, 'wp_loaded_final'), 10000);
|
825 |
+
return true;
|
826 |
+
}
|
827 |
+
|
828 |
+
return false;
|
829 |
+
}
|
830 |
+
|
831 |
+
public function wp_loaded_final() {
|
832 |
+
if (empty($this->require_message_to_be_understood)) return;
|
833 |
+
$message_for = empty($_POST['key_name']) ? '' : (string) $_POST['key_name'];
|
834 |
+
$this->log("Message was received, but not understood by local site (for: $message_for)");
|
835 |
+
die;
|
836 |
+
}
|
837 |
+
|
838 |
+
public function wp_loaded() {
|
839 |
+
|
840 |
+
/*
|
841 |
+
// What if something else already set some response headers?
|
842 |
+
if (function_exists('apache_response_headers')) {
|
843 |
+
$apache_response_headers = apache_response_headers();
|
844 |
+
// Do something...
|
845 |
+
}
|
846 |
+
*/
|
847 |
+
|
848 |
+
// CORS: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
|
849 |
+
// get_http_origin() : since WP 3.4
|
850 |
+
$http_origin = function_exists('get_http_origin') ? get_http_origin() : (empty($_SERVER['HTTP_ORIGIN']) ? '' : $_SERVER['HTTP_ORIGIN']);
|
851 |
+
if (!empty($_SERVER['REQUEST_METHOD']) && 'OPTIONS' == $_SERVER['REQUEST_METHOD'] && $http_origin) {
|
852 |
+
if (in_array($http_origin, $this->allow_cors_from)) {
|
853 |
+
// @codingStandardsIgnoreLine
|
854 |
+
if (!defined('UDRPC_DO_NOT_SEND_CORS_HEADERS') || !UDRPC_DO_NOT_SEND_CORS_HEADERS) {
|
855 |
+
header("Access-Control-Allow-Origin: $http_origin");
|
856 |
+
header('Access-Control-Allow-Credentials: true');
|
857 |
+
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header('Access-Control-Allow-Methods: POST, OPTIONS');
|
858 |
+
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header('Access-Control-Allow-Headers: '.$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
859 |
+
}
|
860 |
+
die;
|
861 |
+
} elseif ($this->debug) {
|
862 |
+
$this->log('Non-allowed CORS from: '.$http_origin);
|
863 |
+
}
|
864 |
+
// Having detected that this is a CORS request, there's nothing more to do. We return, because a different listener might pick it up, even though we didn't.
|
865 |
+
return;
|
866 |
+
}
|
867 |
+
|
868 |
+
// Silently return, rather than dying, in case another instance is able to handle this
|
869 |
+
if (empty($_POST['format']) || (1 != $_POST['format'] && 2 != $_POST['format'])) return;
|
870 |
+
|
871 |
+
$this->require_message_to_be_understood = true;
|
872 |
+
|
873 |
+
$format = $_POST['format'];
|
874 |
+
|
875 |
+
/*
|
876 |
+
In format 1 (legacy/obsolete), the one encrypts (the shared AES key) using one half of the key-pair, and decrypts with the other; whereas the other side of the conversation does the reverse when replying (and uses a different shared AES key). Though this is possible in RSA, this is the wrong thing to do - see https://crypto.stackexchange.com/questions/2123/rsa-encryption-with-private-key-and-decryption-with-a-public-key
|
877 |
+
In format 2, both sides have their own private and public key. The sender encrypts using the other side's public key, and decrypts using its own private key. Messages are signed (the message digest is SHA-256).
|
878 |
+
*/
|
879 |
+
|
880 |
+
// Is this for us?
|
881 |
+
if (empty($_POST['key_name']) || $_POST['key_name'] != $this->key_name_indicator) {
|
882 |
+
return;
|
883 |
+
}
|
884 |
+
|
885 |
+
// wp_unslash() does not exist until after WP 3.5
|
886 |
+
// $udrpc_message = function_exists('wp_unslash') ? wp_unslash($_POST['udrpc_message']) : stripslashes_deep($_POST['udrpc_message']);
|
887 |
+
|
888 |
+
// Data should not have any slashes - it is base64-encoded
|
889 |
+
$udrpc_message = (string) $_POST['udrpc_message'];
|
890 |
+
|
891 |
+
// Check this now, rather than allow the decrypt method to thrown an Exception
|
892 |
+
|
893 |
+
if (empty($this->key_local)) {
|
894 |
+
$this->log('no local key (format 1): cannot decrypt', 'error');
|
895 |
+
die;
|
896 |
+
}
|
897 |
+
|
898 |
+
if ($format >= 2) {
|
899 |
+
if (empty($_POST['signature'])) {
|
900 |
+
$this->log('No message signature found', 'error');
|
901 |
+
die;
|
902 |
+
}
|
903 |
+
if (!$this->key_remote) {
|
904 |
+
$this->log('No signature verification key has been set', 'error');
|
905 |
+
die;
|
906 |
+
}
|
907 |
+
if (!$this->verify_signature($udrpc_message, $_POST['signature'], $this->key_remote)) {
|
908 |
+
$this->log('Signature verification failed; discarding', 'error');
|
909 |
+
die;
|
910 |
+
}
|
911 |
+
}
|
912 |
+
|
913 |
+
try {
|
914 |
+
$udrpc_message = $this->decrypt_message($udrpc_message);
|
915 |
+
} catch (Exception $e) {
|
916 |
+
$this->log('Exception ('.get_class($e).'): '.$e->getMessage(), 'error');
|
917 |
+
die;
|
918 |
+
}
|
919 |
+
|
920 |
+
$udrpc_message = json_decode($udrpc_message, true);
|
921 |
+
|
922 |
+
if (empty($udrpc_message) || !is_array($udrpc_message) || empty($udrpc_message['command']) || !is_string($udrpc_message['command'])) {
|
923 |
+
$this->log('Could not decode JSON on incoming message', 'error');
|
924 |
+
die;
|
925 |
+
}
|
926 |
+
|
927 |
+
if (empty($udrpc_message['time'])) {
|
928 |
+
$this->log('No time set in incoming message', 'error');
|
929 |
+
die;
|
930 |
+
}
|
931 |
+
|
932 |
+
// Mismatch indicating a replay of the message with a different key name in the unencrypted portion?
|
933 |
+
if (empty($udrpc_message['key_name']) || $_POST['key_name'] != $udrpc_message['key_name']) {
|
934 |
+
$this->log('key_name mismatch between encrypted and unencrypted portions', 'error');
|
935 |
+
die;
|
936 |
+
}
|
937 |
+
|
938 |
+
if ($this->extra_replay_protection) {
|
939 |
+
$message_hash = $this->calculate_message_hash((string) $_POST['udrpc_message']);
|
940 |
+
if ($this->message_hash_seen($message_hash)) {
|
941 |
+
$this->log("Message dropped: apparently a replay (hash: $message_hash)", 'error');
|
942 |
+
die;
|
943 |
+
}
|
944 |
+
}
|
945 |
+
|
946 |
+
// Do this after the extra replay protection, as that checks hashes within the maximum time window - so don't check the maximum time window until afterwards, to avoid a tiny window (race) in between.
|
947 |
+
$time_difference = absint($udrpc_message['time'] - time());
|
948 |
+
if ($time_difference > $this->maximum_replay_time_difference) {
|
949 |
+
$this->log("Time in incoming message is outside of allowed window ($time_difference > ".$this->maximum_replay_time_difference.')', 'error');
|
950 |
+
die;
|
951 |
+
}
|
952 |
+
|
953 |
+
// The sequence number should always be larger than any previously-sent sequence number
|
954 |
+
if ($this->sequence_protection_tolerance) {
|
955 |
+
|
956 |
+
if ($this->debug) $this->log('Sequence protection is active; tolerance: '.$this->sequence_protection_tolerance);
|
957 |
+
|
958 |
+
global $wpdb;
|
959 |
+
|
960 |
+
if (!isset($udrpc_message['sequence_id']) || !is_numeric($udrpc_message['sequence_id'])) {
|
961 |
+
$this->log('a numerical sequence number is required, but none was included in the message - dropping', 'error');
|
962 |
+
die;
|
963 |
+
}
|
964 |
+
|
965 |
+
$message_sequence_id = (int) $udrpc_message['sequence_id'];
|
966 |
+
$recently_seen_sequences_ids = $wpdb->get_var($wpdb->prepare('SELECT %s FROM %s LIMIT 1 WHERE '.$this->sequence_protection_where_sql, $this->sequence_protection_column, $this->sequence_protection_table));
|
967 |
+
|
968 |
+
if ('' === $recently_seen_sequences_ids) $recently_seen_sequences_ids = '0';
|
969 |
+
|
970 |
+
$recently_seen_sequences_ids_as_array = explode($recently_seen_sequences_ids, ',');
|
971 |
+
sort($recently_seen_sequences_ids_as_array);
|
972 |
+
|
973 |
+
// Seen before?
|
974 |
+
if (in_array($message_sequence_id, $recently_seen_sequences_ids_as_array)) {
|
975 |
+
$this->log("message with duplicate sequence number received - dropping (received=$message_sequence_id, seen=$recently_seen_sequences_ids)");
|
976 |
+
die;
|
977 |
+
}
|
978 |
+
|
979 |
+
// Within the tolerance threshold? That means: a) either bigger than the max, or b) no more than <tolerance> lower than the least
|
980 |
+
if ($message_sequence_id > max($recently_seen_sequences_ids)) {
|
981 |
+
if ($this->debug) $this->log("Sequence id ($message_sequence_id) is greater than any previous (".max($recently_seen_sequences_ids).') - message is thus OK');
|
982 |
+
// All is well
|
983 |
+
$recently_seen_sequences_ids_as_array[] = $message_sequence_id;
|
984 |
+
} elseif ((max($recently_seen_sequences_ids) - $message_sequence_id) <= $this->sequence_protection_tolerance) {
|
985 |
+
// All is well - was one of those 'missing' in the sequence
|
986 |
+
if ($this->debug) $this->log("Sequence id ($message_sequence_id) is within tolerance range of previous maximum (".max($recently_seen_sequences_ids).') - message is thus OK');
|
987 |
+
$recently_seen_sequences_ids_as_array[] = $message_sequence_id;
|
988 |
+
} else {
|
989 |
+
$this->log("message received outside of allowed sequence window - dropping (received=$message_sequence_id, seen=$recently_seen_sequences_ids, tolerance=".$this->sequence_protection_tolerance.')', 'error');
|
990 |
+
die;
|
991 |
+
}
|
992 |
+
|
993 |
+
// Remove out-of-bounds seen IDs
|
994 |
+
$max_sequence_id_seen = max($recently_seen_sequences_ids_as_array);
|
995 |
+
foreach ($recently_seen_sequences_ids_as_array as $k => $id) {
|
996 |
+
if ($max_sequence_id_seen - $id > $this->sequence_protection_tolerance) {
|
997 |
+
if ($this->debug) $this->log("Removing no-longer-relevant sequence from list of those recently seen: $id");
|
998 |
+
unset($recently_seen_sequences_ids_as_array[$k]);
|
999 |
+
}
|
1000 |
+
}
|
1001 |
+
|
1002 |
+
// Allow reset
|
1003 |
+
if ($message_sequence_id > PHP_INT_MAX - 10) {
|
1004 |
+
$recently_seen_sequences_ids_as_array = array(0);
|
1005 |
+
}
|
1006 |
+
|
1007 |
+
// Write them back to the database
|
1008 |
+
$sql = $wpdb->prepare('UPDATE %s SET %s=%s WHERE '.$this->sequence_protection_where_sql, $this->sequence_protection_table, $this->sequence_protection_column, implode(',', $recently_seen_sequences_ids_as_array));
|
1009 |
+
if ($this->debug) $this->log("SQL to send recent sequence IDs back to the database: $sql");
|
1010 |
+
$wpdb->query($sql);
|
1011 |
+
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
$this->incoming_message = $udrpc_message;
|
1015 |
+
|
1016 |
+
$command = (string) $udrpc_message['command'];
|
1017 |
+
$data = empty($udrpc_message['data']) ? null : $udrpc_message['data'];
|
1018 |
+
|
1019 |
+
// @codingStandardsIgnoreLine
|
1020 |
+
if ($http_origin && !empty($udrpc_message['cors_headers_wanted']) && (!defined('UDRPC_DO_NOT_SEND_CORS_HEADERS') || !UDRPC_DO_NOT_SEND_CORS_HEADERS)) {
|
1021 |
+
header("Access-Control-Allow-Origin: $http_origin");
|
1022 |
+
header('Access-Control-Allow-Credentials: true');
|
1023 |
+
}
|
1024 |
+
|
1025 |
+
$this->log('Command received: '.$command, 'info');
|
1026 |
+
|
1027 |
+
if ('ping' == $command) {
|
1028 |
+
$response = array('response' => 'pong', 'data' => null);
|
1029 |
+
} else {
|
1030 |
+
if (has_filter('udrpc_command_'.$command)) {
|
1031 |
+
$response = apply_filters('udrpc_command_'.$command, null, $data, $this->key_name_indicator);
|
1032 |
+
} else {
|
1033 |
+
$response = array('response' => 'rpcerror', 'data' => array('code' => 'unknown_rpc_command', 'data' => $command));
|
1034 |
+
}
|
1035 |
+
}
|
1036 |
+
|
1037 |
+
$response = apply_filters('udrpc_action', $response, $command, $data, $this->key_name_indicator, $this);
|
1038 |
+
|
1039 |
+
if (is_array($response)) {
|
1040 |
+
|
1041 |
+
if ($this->debug) {
|
1042 |
+
$this->log('UDRPC response (pre-encoding/encryption): '.serialize($response));
|
1043 |
+
}
|
1044 |
+
|
1045 |
+
$data = isset($response['data']) ? $response['data'] : null;
|
1046 |
+
|
1047 |
+
$final_response = json_encode($this->create_message($response['response'], $data, true));
|
1048 |
+
|
1049 |
+
do_action('udrpc_action_send_response', $final_response, $command);
|
1050 |
+
|
1051 |
+
echo $final_response;
|
1052 |
+
}
|
1053 |
+
|
1054 |
+
die;
|
1055 |
+
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
/**
|
1059 |
+
* The hash needs to be in a format that phpseclib likes. phpseclib uses lower case.
|
1060 |
+
* Pass in a base64-encoded signature (i.e. just as signature_for_message creates)
|
1061 |
+
*
|
1062 |
+
* @param string $message
|
1063 |
+
* @param string $signature
|
1064 |
+
* @param string $key
|
1065 |
+
* @param string $hash_algorithm
|
1066 |
+
* @return boolean
|
1067 |
+
*/
|
1068 |
+
public function verify_signature($message, $signature, $key, $hash_algorithm = 'sha256') {
|
1069 |
+
$this->ensure_crypto_loaded();
|
1070 |
+
$rsa = new Crypt_RSA();
|
1071 |
+
$rsa->setHash(strtolower($hash_algorithm));
|
1072 |
+
// This is not the default, but is what we use
|
1073 |
+
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
|
1074 |
+
$rsa->loadKey($key);
|
1075 |
+
|
1076 |
+
// Don't hash it - Crypt_RSA::verify() already does that
|
1077 |
+
// $hash = new Crypt_Hash($hash_algorithm);
|
1078 |
+
// $hashed = $hash->hash($message);
|
1079 |
+
|
1080 |
+
$verified = $rsa->verify($message, base64_decode($signature));
|
1081 |
+
|
1082 |
+
if ($this->debug) $this->log('Signature verification result: '.serialize($verified));
|
1083 |
+
|
1084 |
+
return $verified;
|
1085 |
+
}
|
1086 |
+
|
1087 |
+
private function calculate_message_hash($message) {
|
1088 |
+
return hash('sha256', $message);
|
1089 |
+
}
|
1090 |
+
|
1091 |
+
private function message_hash_seen($message_hash) {
|
1092 |
+
// 39 characters - less than the WP site transient name limit (40). Though, we use a normal transient, as these don't auto-load at all times.
|
1093 |
+
$transient_name = 'udrpch_'.md5($this->key_name_indicator);
|
1094 |
+
$seen_hashes = get_transient($transient_name);
|
1095 |
+
if (!is_array($seen_hashes)) $seen_hashes = array();
|
1096 |
+
$time_now = time();
|
1097 |
+
// $any_changes = false;
|
1098 |
+
// Prune the old hashes
|
1099 |
+
foreach ($seen_hashes as $hash => $last_seen) {
|
1100 |
+
if ($last_seen < ($time_now - $this->maximum_replay_time_difference)) {
|
1101 |
+
// $any_changes = true;
|
1102 |
+
unset($seen_hashes[$hash]);
|
1103 |
+
}
|
1104 |
+
}
|
1105 |
+
if (isset($seen_hashes[$message_hash])) {
|
1106 |
+
return true;
|
1107 |
+
}
|
1108 |
+
$seen_hashes[$message_hash] = $time_now;
|
1109 |
+
set_transient($transient_name, $seen_hashes, $this->maximum_replay_time_difference);
|
1110 |
+
|
1111 |
+
return false;
|
1112 |
+
}
|
1113 |
+
}
|
1114 |
+
|
1115 |
+
endif;
|
vendor/team-updraft/common-libs/src/updraft-semaphore/class-updraft-semaphore.php
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (!defined('ABSPATH')) die('No direct access.');
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Class Updraft_Semaphore_3_0
|
7 |
+
*
|
8 |
+
* This class is much simpler to use than the the previous series, as it has dropped support for complicated cases that were not being used. It also now only uses a single row in the options database, and takes care of creating it itself internally.
|
9 |
+
*
|
10 |
+
* Logging, though, may be noisier, unless your loggers are taking note of the log level and only registering what is required.
|
11 |
+
*
|
12 |
+
* Example of use (a lock that will expire if not released within 300 seconds)
|
13 |
+
*
|
14 |
+
* See test.php for a longer example (including logging).
|
15 |
+
*
|
16 |
+
* $my_lock = new Updraft_Semaphore_3_0('my_lock_name', 300);
|
17 |
+
* // If getting the lock does not succeed first time, try again up to twice
|
18 |
+
* if ($my_lock->lock(2)) {
|
19 |
+
* try {
|
20 |
+
* // do stuff ...
|
21 |
+
* } catch (Exception $e) {
|
22 |
+
* // We are making sure we release the lock in case of an error
|
23 |
+
* } catch (Error $e) {
|
24 |
+
* // We are making sure we release the lock in case of an error
|
25 |
+
* }
|
26 |
+
* $my_lock->release();
|
27 |
+
* } else {
|
28 |
+
* error_log("Sorry, could not get the lock");
|
29 |
+
* }
|
30 |
+
*/
|
31 |
+
class Updraft_Semaphore_3_0 {
|
32 |
+
|
33 |
+
// Time after which the lock will expire (in seconds)
|
34 |
+
protected $locked_for;
|
35 |
+
|
36 |
+
// Name for the lock in the WP options table
|
37 |
+
protected $option_name;
|
38 |
+
|
39 |
+
// Lock status - a boolean
|
40 |
+
protected $acquired = false;
|
41 |
+
|
42 |
+
// An array of loggers
|
43 |
+
protected $loggers = array();
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Constructor. Instantiating does not lock anything, but sets up the details for future operations.
|
47 |
+
*
|
48 |
+
* @param String $name - a unique (across the WP site) name for the lock. Should be no more than 51 characters in length (because of the use of the WP options table, with some further characters used internally)
|
49 |
+
* @param Integer $locked_for - time (in seconds) after which the lock will expire if not released. This needs to be positive if you don't want bad things to happen.
|
50 |
+
* @param Array $loggers - an array of loggers
|
51 |
+
*/
|
52 |
+
public function __construct($name, $locked_for = 300, $loggers = array()) {
|
53 |
+
$this->option_name = 'updraft_lock_'.$name;
|
54 |
+
$this->locked_for = $locked_for;
|
55 |
+
$this->loggers = $loggers;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Internal function to make sure that the lock is set up in the database
|
60 |
+
*
|
61 |
+
* @return Integer - 0 means 'failed' (which could include that someone else concurrently created it); 1 means 'already existed'; 2 means 'exists, because we created it). The intention is that non-zero results mean that the lock exists.
|
62 |
+
*/
|
63 |
+
private function ensure_database_initialised() {
|
64 |
+
|
65 |
+
global $wpdb;
|
66 |
+
|
67 |
+
$sql = $wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name = %s", $this->option_name);
|
68 |
+
|
69 |
+
if (1 === (int) $wpdb->get_var($sql)) {
|
70 |
+
$this->log('Lock option ('.$this->option_name.', '.$wpdb->options.') already existed in the database', 'debug');
|
71 |
+
return 1;
|
72 |
+
}
|
73 |
+
|
74 |
+
$sql = $wpdb->prepare("INSERT INTO {$wpdb->options} (option_name, option_value, autoload) VALUES(%s, '0', 'no');", $this->option_name);
|
75 |
+
|
76 |
+
$rows_affected = $wpdb->query($sql);
|
77 |
+
|
78 |
+
if ($rows_affected > 0) {
|
79 |
+
$this->log('Lock option ('.$this->option_name.', '.$wpdb->options.') was created in the database', 'debug');
|
80 |
+
} else {
|
81 |
+
$this->log('Lock option ('.$this->option_name.', '.$wpdb->options.') failed to be created in the database (could already exist)', 'notice');
|
82 |
+
}
|
83 |
+
|
84 |
+
return ($rows_affected > 0) ? 2 : 0;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Attempt to acquire the lock. If it was already acquired, then nothing extra will be done (the method will be a no-op).
|
89 |
+
*
|
90 |
+
* @param Integer $retries - how many times to retry (after a 1 second sleep each time)
|
91 |
+
*
|
92 |
+
* @return Boolean - whether the lock was successfully acquired or not
|
93 |
+
*/
|
94 |
+
public function lock($retries = 0) {
|
95 |
+
|
96 |
+
if ($this->acquired) return true;
|
97 |
+
|
98 |
+
global $wpdb;
|
99 |
+
|
100 |
+
$time_now = time();
|
101 |
+
$acquire_until = $time_now + $this->locked_for;
|
102 |
+
|
103 |
+
$sql = $wpdb->prepare("UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s AND option_value < %d", $acquire_until, $this->option_name, $time_now);
|
104 |
+
|
105 |
+
if (1 === $wpdb->query($sql)) {
|
106 |
+
$this->log('Lock ('.$this->option_name.', '.$wpdb->options.') acquired', 'info');
|
107 |
+
$this->acquired = true;
|
108 |
+
return true;
|
109 |
+
}
|
110 |
+
|
111 |
+
// See if the failure was caused by the row not existing (we check this only after failure, because it should only occur once on the site)
|
112 |
+
if (!$this->ensure_database_initialised()) return false;
|
113 |
+
|
114 |
+
do {
|
115 |
+
// Now that the row has been created, try again
|
116 |
+
if (1 === $wpdb->query($sql)) {
|
117 |
+
$this->log('Lock ('.$this->option_name.', '.$wpdb->options.') acquired after initialising the database', 'info');
|
118 |
+
$this->acquired = true;
|
119 |
+
return true;
|
120 |
+
}
|
121 |
+
$retries--;
|
122 |
+
if ($retries >=0) {
|
123 |
+
$this->log('Lock ('.$this->option_name.', '.$wpdb->options.') not yet acquired; sleeping', 'debug');
|
124 |
+
sleep(1);
|
125 |
+
// As a second has passed, update the time we are aiming for
|
126 |
+
$time_now = time();
|
127 |
+
$acquire_until = $time_now + $this->locked_for;
|
128 |
+
$sql = $wpdb->prepare("UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s AND option_value < %d", $acquire_until, $this->option_name, $time_now);
|
129 |
+
}
|
130 |
+
} while ($retries >= 0);
|
131 |
+
|
132 |
+
$this->log('Lock ('.$this->option_name.', '.$wpdb->options.') could not be acquired (it is locked)', 'info');
|
133 |
+
|
134 |
+
return false;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Release the lock
|
139 |
+
*
|
140 |
+
* N.B. We don't attempt to unlock it unless we locked it. i.e. Lost locks are left to expire rather than being forced. (If we want to force them, we'll need to introduce a new parameter).
|
141 |
+
*
|
142 |
+
* @return Boolean - if it returns false, then the lock was apparently not locked by us (and the caller will most likely therefore ignore the result, whatever it is).
|
143 |
+
*/
|
144 |
+
public function release() {
|
145 |
+
if (!$this->acquired) return false;
|
146 |
+
global $wpdb;
|
147 |
+
$sql = $wpdb->prepare("UPDATE {$wpdb->options} SET option_value = '0' WHERE option_name = %s", $this->option_name);
|
148 |
+
|
149 |
+
$this->log('Lock option ('.$this->option_name.', '.$wpdb->options.') released', 'info');
|
150 |
+
|
151 |
+
$result = (int) $wpdb->query($sql) === 1;
|
152 |
+
|
153 |
+
$this->acquired = false;
|
154 |
+
|
155 |
+
return $result;
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Cleans up the DB of any residual data. This should not be used as part of ordinary unlocking; only as part of deinstalling, or if you otherwise know that the lock will not be used again. If calling this, it's redundant to first unlock (and a no-op to attempt to do so afterwards).
|
160 |
+
*/
|
161 |
+
public function delete() {
|
162 |
+
$this->acquired = false;
|
163 |
+
|
164 |
+
global $wpdb;
|
165 |
+
$wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->options} WHERE option_name = %s", $this->option_name));
|
166 |
+
|
167 |
+
$this->log('Lock option ('.$this->option_name.', '.$wpdb->options.') was deleted from the database');
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Captures and logs any given messages
|
172 |
+
*
|
173 |
+
* @param String $message - the error message
|
174 |
+
* @param String $level - the message level (debug, notice, info, warning, error)
|
175 |
+
*/
|
176 |
+
public function log($message, $level = 'info') {
|
177 |
+
if (isset($this->loggers)) {
|
178 |
+
foreach ($this->loggers as $logger) {
|
179 |
+
$logger->log($message, $level);
|
180 |
+
}
|
181 |
+
}
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Sets the list of loggers for this instance (removing any others).
|
186 |
+
*
|
187 |
+
* @param Array $loggers - the loggers for this task
|
188 |
+
*/
|
189 |
+
public function set_loggers($loggers) {
|
190 |
+
$this->loggers = array();
|
191 |
+
foreach ($loggers as $logger) {
|
192 |
+
$this->add_logger($logger);
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Add a logger to loggers list
|
198 |
+
*
|
199 |
+
* @param Callable $logger - a logger (a method with a callable function 'log', taking string parameters $level $message)
|
200 |
+
*/
|
201 |
+
public function add_logger($logger) {
|
202 |
+
$this->loggers[] = $logger;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Return the current list of loggers
|
207 |
+
*
|
208 |
+
* @return Array
|
209 |
+
*/
|
210 |
+
public function get_loggers() {
|
211 |
+
return $this->loggers;
|
212 |
+
}
|
213 |
+
}
|
vendor/team-updraft/common-libs/src/updraft-semaphore/test.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
Example usage:
|
5 |
+
|
6 |
+
php -a
|
7 |
+
require 'wp-load.php';
|
8 |
+
define('I_AM_TESTING', true);
|
9 |
+
require 'test.php';
|
10 |
+
*/
|
11 |
+
|
12 |
+
if (!defined('ABSPATH')) die('No direct access.');
|
13 |
+
|
14 |
+
if (!defined('I_AM_TESTING') || !I_AM_TESTING) die('Please define I_AM_TESTING.');
|
15 |
+
|
16 |
+
require_once(dirname(__FILE__).'/class-updraft-semaphore.php');
|
17 |
+
|
18 |
+
class Test_Logger_1 {
|
19 |
+
function log($message, $level) {
|
20 |
+
echo "Test_Logger_1::log(level=$level, message=$message)\n";
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
24 |
+
class Test_Logger_2 {
|
25 |
+
function log($message, $level) {
|
26 |
+
echo "Test_Logger_2::log(level=$level, message=$message)\n";
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
$my_lock = new Updraft_Semaphore_3_0('my_test_lock_name', 4, array(new Test_Logger_1()));
|
31 |
+
|
32 |
+
if ($my_lock->lock()) {
|
33 |
+
try {
|
34 |
+
// do stuff ...
|
35 |
+
$my_lock_again = new Updraft_Semaphore_3_0('my_test_lock_name', 4, array(new Test_Logger_2()));
|
36 |
+
$time_now = microtime(true);
|
37 |
+
if ($my_lock_again->lock(6)) {
|
38 |
+
echo "Eventually got it after ".round(microtime(true) - $time_now, 3)." seconds\n";
|
39 |
+
$my_lock_again->release();
|
40 |
+
} else {
|
41 |
+
echo("Sorry, could not get the second lock\n");
|
42 |
+
}
|
43 |
+
|
44 |
+
} catch (Exception $e) {
|
45 |
+
var_dump($e);
|
46 |
+
// We are making sure we release the lock in case of an error
|
47 |
+
} catch (Error $e) { // phpcs:ignore PHPCompatibility.Classes.NewClasses.errorFound
|
48 |
+
var_dump($e);
|
49 |
+
// We are making sure we release the lock in case of an error
|
50 |
+
}
|
51 |
+
|
52 |
+
$my_lock->release();
|
53 |
+
|
54 |
+
} else {
|
55 |
+
echo("Sorry, could not get the first lock\n");
|
56 |
+
}
|
vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-manager-commands.php
ADDED
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The AJAX Commands manager class
|
4 |
+
*/
|
5 |
+
|
6 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
7 |
+
|
8 |
+
if (!defined('Updraft_Task_Manager_Commands_1_0')) :
|
9 |
+
|
10 |
+
class Updraft_Task_Manager_Commands_1_0 {
|
11 |
+
|
12 |
+
protected $task_manager;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Constructor
|
16 |
+
*
|
17 |
+
* @param Updraft_Task_Manager_1_3 $task_manager The task manager instance
|
18 |
+
*/
|
19 |
+
public function __construct($task_manager) {
|
20 |
+
$this->task_manager = $task_manager;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* A list of allowed commands via AJAX
|
25 |
+
*
|
26 |
+
* @return array - List of allowed commands
|
27 |
+
*/
|
28 |
+
public static function get_allowed_ajax_commands() {
|
29 |
+
|
30 |
+
$commands = array(
|
31 |
+
'process_task',
|
32 |
+
'get_task_status',
|
33 |
+
'end_task',
|
34 |
+
'process_queue',
|
35 |
+
'get_active_tasks',
|
36 |
+
'clean_up_old_tasks',
|
37 |
+
);
|
38 |
+
|
39 |
+
return apply_filters('updraft_task_manager_allowed_ajax_commands', $commands);
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Process a single task in the queue
|
44 |
+
*
|
45 |
+
* @param array $data data passed via AJAX
|
46 |
+
* @return void|WP_Error status of the operation
|
47 |
+
*/
|
48 |
+
public function process_task($data) {
|
49 |
+
|
50 |
+
if (!isset($data['task_id']))
|
51 |
+
return new WP_Error('id_missing', 'Task ID is missing or invalid');
|
52 |
+
|
53 |
+
$task_id = (int) $data['task_id'];
|
54 |
+
|
55 |
+
$response = apply_filters('updraft_task_manager_process_task_response', "Processing task: {$task_id}", $task_id);
|
56 |
+
$this->close_browser_connection($response);
|
57 |
+
$this->task_manager->process_task($task_id);
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Process a single task in the queue
|
62 |
+
*
|
63 |
+
* @param array $data data passed via AJAX
|
64 |
+
* @return String - status of task or false if none found
|
65 |
+
*/
|
66 |
+
public function get_task_status($data) {
|
67 |
+
|
68 |
+
if (!isset($data['task_id']))
|
69 |
+
return new WP_Error('id_missing', 'Task ID is missing or invalid');
|
70 |
+
|
71 |
+
$task_id = (int) $data['task_id'];
|
72 |
+
|
73 |
+
return $this->task_manager->get_task_status($task_id);
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Ends a given task
|
78 |
+
*
|
79 |
+
* @param array $data data passed via AJAX
|
80 |
+
* @return boolean - Status of the operation.
|
81 |
+
*/
|
82 |
+
public function end_task($data) {
|
83 |
+
|
84 |
+
if (!isset($data['task_id']))
|
85 |
+
return new WP_Error('id_missing', 'Task ID is missing or invalid');
|
86 |
+
|
87 |
+
$task_id = (int) $data['task_id'];
|
88 |
+
|
89 |
+
$status = $this->task_manager->end_task($task_id);
|
90 |
+
|
91 |
+
if (!$status) return new WP_Error('end_task_failed', 'Task is already ended');
|
92 |
+
|
93 |
+
$response = apply_filters('updraft_task_manager_end_task_response', "Successfully ended task with id : {$task_id}", $task_id);
|
94 |
+
$this->close_browser_connection($response);
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Fetches a list of all active tasks
|
99 |
+
*
|
100 |
+
* @param array $data data passed via AJAX
|
101 |
+
* @return Mixed - array of UpdraftPlus_Task ojects or NULL if none found
|
102 |
+
*/
|
103 |
+
public function get_active_tasks($data) {
|
104 |
+
|
105 |
+
if (!isset($data['type']))
|
106 |
+
return new WP_Error('type_missing', 'Task type is missing or invalid');
|
107 |
+
|
108 |
+
$type = $data['type'];
|
109 |
+
$tasks = $this->task_manager->get_active_tasks($type);
|
110 |
+
|
111 |
+
$ids = array();
|
112 |
+
|
113 |
+
if ($tasks) {
|
114 |
+
foreach ($tasks as $task) {
|
115 |
+
array_push($ids, $task->get_id());
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
$response = apply_filters('updraft_task_manager_get_active_tasks_response', $ids, $type);
|
120 |
+
return $response;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Cleans out all complete tasks from the DB.
|
125 |
+
*
|
126 |
+
* @param array $data data passed via AJAX
|
127 |
+
* @return void|WP_Error status of the operation
|
128 |
+
*/
|
129 |
+
public function clean_up_old_tasks($data) {
|
130 |
+
|
131 |
+
if (!isset($data['type']))
|
132 |
+
return new WP_Error('type_missing', 'Task type is missing or invalid');
|
133 |
+
|
134 |
+
$type = $data['type'];
|
135 |
+
$status = $this->task_manager->clean_up_old_tasks($type);
|
136 |
+
|
137 |
+
if (!$status) return new WP_Error('clean_up_failed', 'Queue is already empty or the task type invalid');
|
138 |
+
|
139 |
+
$response = apply_filters('updraft_task_manager_clean_up_old_tasks_response', "Cleaned up old tasks of type : $type", $type);
|
140 |
+
$this->close_browser_connection($response);
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Processes a queue of a specific type of task
|
145 |
+
*
|
146 |
+
* @param array $data data passed via AJAX
|
147 |
+
* @return void|WP_Error status of the operation
|
148 |
+
*/
|
149 |
+
public function process_queue($data) {
|
150 |
+
if (!isset($data['type']))
|
151 |
+
return new WP_Error('type_missing', 'Task type is missing or invalid');
|
152 |
+
|
153 |
+
$type = $data['type'];
|
154 |
+
|
155 |
+
$response = apply_filters('updraft_task_manager_process_queue_response', "Processing queue of type {$type}", $type);
|
156 |
+
|
157 |
+
$this->close_browser_connection(json_encode($response));
|
158 |
+
$status = $this->task_manager->process_queue($type);
|
159 |
+
|
160 |
+
if (!$status)
|
161 |
+
return new WP_Error('process_queue_operation_failed', 'Failed to process the queue');
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Close browser connection so that it can resume AJAX polling
|
166 |
+
*
|
167 |
+
* @param array $txt Response to browser
|
168 |
+
* @return void
|
169 |
+
*/
|
170 |
+
public function close_browser_connection($txt = '') {
|
171 |
+
header('Content-Length: '.((!empty($txt)) ? 5+strlen($txt) : '0'));
|
172 |
+
header('Connection: close');
|
173 |
+
header('Content-Encoding: none');
|
174 |
+
if (session_id()) session_write_close();
|
175 |
+
echo "\r\n\r\n";
|
176 |
+
echo $txt;
|
177 |
+
|
178 |
+
$levels = ob_get_level();
|
179 |
+
|
180 |
+
for ($i = 0; $i < $levels; $i++) {
|
181 |
+
ob_end_flush();
|
182 |
+
}
|
183 |
+
|
184 |
+
flush();
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
endif;
|
vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-manager.php
ADDED
@@ -0,0 +1,387 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* A task manager that locks and processes the task queue
|
4 |
+
*/
|
5 |
+
|
6 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
7 |
+
|
8 |
+
if (!class_exists('Updraft_Task_Manager_1_3')) :
|
9 |
+
|
10 |
+
abstract class Updraft_Task_Manager_1_3 {
|
11 |
+
|
12 |
+
protected $loggers;
|
13 |
+
|
14 |
+
public $commands;
|
15 |
+
|
16 |
+
private $queue_semaphore;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Set this to the number of seconds for the lock timeout, or 0 to not use a lock
|
20 |
+
*/
|
21 |
+
protected $use_per_task_lock = 0;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* The Task Manager constructor
|
25 |
+
*/
|
26 |
+
public function __construct() {
|
27 |
+
|
28 |
+
if (!class_exists('Updraft_Task_1_2')) require_once('class-updraft-task.php');
|
29 |
+
if (!class_exists('Updraft_Task_Manager_Commands_1_0')) require_once('class-updraft-task-manager-commands.php');
|
30 |
+
if (!class_exists('Updraft_Semaphore_3_0')) require_once(dirname(__FILE__).'/../updraft-semaphore/class-updraft-semaphore.php');
|
31 |
+
if (!class_exists('Updraft_Tasks_Activation')) require_once(dirname(__FILE__).'/class-updraft-tasks-activation.php');
|
32 |
+
|
33 |
+
$this->commands = new Updraft_Task_Manager_Commands_1_0($this);
|
34 |
+
|
35 |
+
add_action('wp_ajax_updraft_taskmanager_ajax', array($this, 'updraft_taskmanager_ajax'));
|
36 |
+
|
37 |
+
do_action('updraft_task_manager_loaded', $this);
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* The Task Manager AJAX handler
|
42 |
+
*/
|
43 |
+
public function updraft_taskmanager_ajax() {
|
44 |
+
|
45 |
+
$nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce'];
|
46 |
+
|
47 |
+
if (!wp_verify_nonce($nonce, 'updraft-task-manager-ajax-nonce') || empty($_REQUEST['subaction']))
|
48 |
+
die('Security check failed');
|
49 |
+
|
50 |
+
$subaction = $_REQUEST['subaction'];
|
51 |
+
|
52 |
+
$allowed_commands = Updraft_Task_Manager_Commands_1_0::get_allowed_ajax_commands();
|
53 |
+
|
54 |
+
if (in_array($subaction, $allowed_commands)) {
|
55 |
+
|
56 |
+
if (isset($_REQUEST['action_data']))
|
57 |
+
$data = $_REQUEST['action_data'];
|
58 |
+
|
59 |
+
$results = call_user_func(array($this->commands, $subaction), $data);
|
60 |
+
|
61 |
+
if (is_wp_error($results)) {
|
62 |
+
$results = array(
|
63 |
+
'result' => false,
|
64 |
+
'error_code' => $results->get_error_code(),
|
65 |
+
'error_message' => $results->get_error_message(),
|
66 |
+
'error_data' => $results->get_error_data(),
|
67 |
+
);
|
68 |
+
}
|
69 |
+
|
70 |
+
echo json_encode($results);
|
71 |
+
} else {
|
72 |
+
echo json_encode("{'error' : 'No such command found'}");
|
73 |
+
}
|
74 |
+
die;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Process a single task in the queue
|
79 |
+
*
|
80 |
+
* @param int|Updraft_Task - $task Task ID or Updraft_Task object.
|
81 |
+
* @return boolean|WP_Error - status of task or error if task not found
|
82 |
+
*/
|
83 |
+
public function process_task($task) {
|
84 |
+
|
85 |
+
if (!is_a($task, 'Updraft_Task_1_2')) {
|
86 |
+
$task_id = (int) $task;
|
87 |
+
$task = $this->get_task_instance($task_id);
|
88 |
+
}
|
89 |
+
|
90 |
+
if (!$task) return new WP_Error('id_invalid', 'Task not found or ID is invalid');
|
91 |
+
|
92 |
+
return $task->attempt(apply_filters('updraft_task_lock_for', $this->use_per_task_lock, $this));
|
93 |
+
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Gets a list of all tasks that matches the $status flag
|
98 |
+
*
|
99 |
+
* @param int|Updraft_Task - $task Task ID or Updraft_Task object.
|
100 |
+
* @return String|WP_Error - status of task or error if task not found.
|
101 |
+
*/
|
102 |
+
public function get_task_status($task) {
|
103 |
+
|
104 |
+
if (!($task instanceof Updraft_Task_1_2)) {
|
105 |
+
$task_id = (int) $task;
|
106 |
+
$task = $this->get_task_instance($task_id);
|
107 |
+
}
|
108 |
+
|
109 |
+
if (!$task) return new WP_Error('id_invalid', 'Task not found or ID is invalid');
|
110 |
+
|
111 |
+
return $task->get_status();
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Ends a given task
|
116 |
+
*
|
117 |
+
* @param int|Updraft_Task - $task Task ID or Updraft_Task object.
|
118 |
+
* @return boolean|WP_Error - Status of the operation or error if task not found.
|
119 |
+
*/
|
120 |
+
public function end_task($task) {
|
121 |
+
|
122 |
+
if (!($task instanceof Updraft_Task_1_2)) {
|
123 |
+
$task_id = (int) $task;
|
124 |
+
$task = $this->get_task_instance($task_id);
|
125 |
+
}
|
126 |
+
|
127 |
+
if (!$task) return new WP_Error('id_invalid', 'Task not found or ID is invalid');
|
128 |
+
|
129 |
+
return $task->complete();
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Process a the queue of a specifed task type
|
134 |
+
*
|
135 |
+
* @param string $type queue type to process
|
136 |
+
* @return bool true on success, false otherwise
|
137 |
+
*/
|
138 |
+
public function process_queue($type) {
|
139 |
+
|
140 |
+
$task_list = $this->get_active_tasks($type);
|
141 |
+
$total = is_array($task_list) ? count($task_list) : 0;
|
142 |
+
|
143 |
+
if (1 > $total) {
|
144 |
+
$this->log(sprintf('The queue for tasks of type "%s" is empty. Aborting!', $type));
|
145 |
+
return true;
|
146 |
+
} else {
|
147 |
+
$this->log(sprintf('A total of %d tasks of type %s found and will be processed in this iteration', $total, $type));
|
148 |
+
}
|
149 |
+
|
150 |
+
$this->queue_semaphore = new Updraft_Semaphore_3_0($type);
|
151 |
+
|
152 |
+
// Prevent PHP warning which trigger from semaphore class set_loggers method if $this->loggers is empty
|
153 |
+
if (!empty($this->loggers)) {
|
154 |
+
$this->queue_semaphore->set_loggers($this->loggers);
|
155 |
+
}
|
156 |
+
|
157 |
+
if (!$this->queue_semaphore->lock()) {
|
158 |
+
|
159 |
+
$this->log(sprintf('Failed to gain semaphore lock (%s) - another process is already processing the queue - aborting (if this is wrong - i.e. if the other process crashed without removing the lock, then another can be started after 1 minute', $type));
|
160 |
+
|
161 |
+
return false;
|
162 |
+
}
|
163 |
+
|
164 |
+
$done = 0;
|
165 |
+
foreach ($task_list as $task) {
|
166 |
+
$this->process_task($task);
|
167 |
+
$done++;
|
168 |
+
/**
|
169 |
+
* Filters if the queue should be interrupted. Used after processing each task.
|
170 |
+
*
|
171 |
+
* @param boolean $interrupt_queue - If the queue should be interrupted. Default to FALSE
|
172 |
+
* @param object $task - The current task object
|
173 |
+
* @param object $task_manager - The task manager instance
|
174 |
+
*/
|
175 |
+
if (apply_filters('updraft_interrupt_tasks_queue_'.$type, false, $task, $this)) {
|
176 |
+
break;
|
177 |
+
}
|
178 |
+
}
|
179 |
+
|
180 |
+
$this->queue_semaphore->release();
|
181 |
+
$this->log(sprintf('Successfully processed the queue (%s). %d tasks were processed out of %d.', $type, $done, $total));
|
182 |
+
$this->queue_semaphore->delete();
|
183 |
+
|
184 |
+
return $done == $total;
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Cleans out all complete tasks from the DB.
|
189 |
+
*
|
190 |
+
* @param String $type type of the task
|
191 |
+
*/
|
192 |
+
public function clean_up_old_tasks($type) {
|
193 |
+
$completed_tasks = $this->get_completed_tasks($type);
|
194 |
+
|
195 |
+
if (!$completed_tasks) return false;
|
196 |
+
|
197 |
+
$this->log(sprintf('Cleaning up tasks of type (%s). A total of %d tasks will be deleted.', $type, count($completed_tasks)));
|
198 |
+
|
199 |
+
foreach ($completed_tasks as $task) {
|
200 |
+
$task->delete_meta();
|
201 |
+
$task->delete();
|
202 |
+
}
|
203 |
+
|
204 |
+
return true;
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Delete all tasks from queue.
|
209 |
+
*
|
210 |
+
* @param string $type
|
211 |
+
*
|
212 |
+
* @return boolean|integer Number of rows deleted, or (boolean)false upon error
|
213 |
+
*/
|
214 |
+
public function delete_tasks($task_type) {
|
215 |
+
global $wpdb;
|
216 |
+
|
217 |
+
$sql = "DELETE t, tm FROM `{$wpdb->base_prefix}tm_tasks` t LEFT JOIN `{$wpdb->base_prefix}tm_taskmeta` tm ON t.id = tm.task_id WHERE t.type = '{$task_type}'";
|
218 |
+
|
219 |
+
return $wpdb->query($sql);
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Get count of completed and all tasks.
|
224 |
+
*
|
225 |
+
* @return array - [ ['complete_tasks' => , 'all_tasks' => ] ]
|
226 |
+
*/
|
227 |
+
public function get_status($task_type) {
|
228 |
+
global $wpdb;
|
229 |
+
|
230 |
+
$query = $wpdb->prepare(
|
231 |
+
"SELECT complete_tasks, all_tasks FROM (SELECT COUNT(*) AS complete_tasks FROM {$wpdb->base_prefix}tm_tasks WHERE `type` = %s AND `status` = %s) a, (SELECT COUNT(*) AS all_tasks FROM {$wpdb->base_prefix}tm_tasks WHERE `type` = %s) b",
|
232 |
+
array(
|
233 |
+
$task_type,
|
234 |
+
'complete',
|
235 |
+
$task_type,
|
236 |
+
)
|
237 |
+
);
|
238 |
+
|
239 |
+
$status = $wpdb->get_row($query, ARRAY_A);
|
240 |
+
|
241 |
+
if (empty($status)) {
|
242 |
+
$status = array(
|
243 |
+
'complete_tasks' => 0,
|
244 |
+
'all_tasks' => 0,
|
245 |
+
);
|
246 |
+
}
|
247 |
+
|
248 |
+
return $status;
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Fetches a list of all active tasks
|
253 |
+
*
|
254 |
+
* @param String $type type of the task
|
255 |
+
* @return Mixed - array of Task ojects or NULL if none found
|
256 |
+
*/
|
257 |
+
public function get_active_tasks($type) {
|
258 |
+
return $this->get_tasks('active', $type);
|
259 |
+
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Gets a list of all completed tasks
|
263 |
+
*
|
264 |
+
* @param String $type type of the task
|
265 |
+
* @return Mixed - array of Task ojects or NULL if none found
|
266 |
+
*/
|
267 |
+
public function get_completed_tasks($type) {
|
268 |
+
return $this->get_tasks('complete', $type);
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Gets a list of all tasks that matches the $status flag
|
273 |
+
*
|
274 |
+
* @param String $status - status of tasks to return, defaults to all tasks
|
275 |
+
* @param String $type - type of task
|
276 |
+
*
|
277 |
+
* @return Mixed - array of Task objects or NULL if none found
|
278 |
+
*/
|
279 |
+
public function get_tasks($status, $type) {
|
280 |
+
global $wpdb;
|
281 |
+
|
282 |
+
$tasks = array();
|
283 |
+
|
284 |
+
if (array_key_exists($status, Updraft_Task_1_2::get_allowed_statuses())) {
|
285 |
+
$sql = $wpdb->prepare("SELECT * FROM {$wpdb->base_prefix}tm_tasks WHERE status = %s AND type = %s", $status, $type);
|
286 |
+
} else {
|
287 |
+
$sql = $wpdb->prepare("SELECT * FROM {$wpdb->base_prefix}tm_tasks WHERE type = %s", $type);
|
288 |
+
}
|
289 |
+
|
290 |
+
$_tasks = $wpdb->get_results($sql);
|
291 |
+
|
292 |
+
if (!$_tasks) {
|
293 |
+
// if we got an error then check if task manager tables are in the database
|
294 |
+
// and recreate them if needed.
|
295 |
+
if ($wpdb->last_error) {
|
296 |
+
Updraft_Tasks_Activation::reinstall_if_needed();
|
297 |
+
}
|
298 |
+
return;
|
299 |
+
}
|
300 |
+
|
301 |
+
|
302 |
+
foreach ($_tasks as $_task) {
|
303 |
+
$task = $this->get_task_instance($_task->id);
|
304 |
+
if ($task) array_push($tasks, $task);
|
305 |
+
}
|
306 |
+
|
307 |
+
return $tasks;
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* Retrieve the task instance using its ID
|
312 |
+
*
|
313 |
+
* @access public
|
314 |
+
*
|
315 |
+
* @global wpdb $wpdb WordPress database abstraction object.
|
316 |
+
*
|
317 |
+
* @param int $task_id Task ID.
|
318 |
+
* @return Task|Boolean Task object, false otherwise.
|
319 |
+
*/
|
320 |
+
public function get_task_instance($task_id) {
|
321 |
+
global $wpdb;
|
322 |
+
|
323 |
+
$task_id = (int) $task_id;
|
324 |
+
if (!$task_id) return false;
|
325 |
+
|
326 |
+
$sql = $wpdb->prepare("SELECT * FROM {$wpdb->base_prefix}tm_tasks WHERE id = %d LIMIT 1", $task_id);
|
327 |
+
$_task = $wpdb->get_row($sql);
|
328 |
+
|
329 |
+
if (!$_task)
|
330 |
+
return false;
|
331 |
+
|
332 |
+
$class_identifier = $_task->class_identifier;
|
333 |
+
|
334 |
+
if (class_exists($class_identifier))
|
335 |
+
$task_instance = new $class_identifier($_task);
|
336 |
+
$task_instance->set_loggers($this->loggers);
|
337 |
+
return $task_instance;
|
338 |
+
|
339 |
+
return false;
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Sets the logger for this instance.
|
344 |
+
*
|
345 |
+
* @param array $loggers - the loggers for this task
|
346 |
+
*/
|
347 |
+
public function set_loggers($loggers) {
|
348 |
+
foreach ($loggers as $logger) {
|
349 |
+
$this->add_logger($logger);
|
350 |
+
}
|
351 |
+
}
|
352 |
+
|
353 |
+
/**
|
354 |
+
* Add a logger to loggers list
|
355 |
+
*
|
356 |
+
* @param Object $logger - a logger for the instance
|
357 |
+
*/
|
358 |
+
public function add_logger($logger) {
|
359 |
+
$this->loggers[] = $logger;
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
* Return list of loggers
|
364 |
+
*
|
365 |
+
* @return array
|
366 |
+
*/
|
367 |
+
public function get_loggers() {
|
368 |
+
return $this->loggers;
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Captures and logs any interesting messages
|
373 |
+
*
|
374 |
+
* @param String $message - the error message
|
375 |
+
* @param String $error_type - the error type
|
376 |
+
*/
|
377 |
+
public function log($message, $error_type = 'info') {
|
378 |
+
if (isset($this->loggers)) {
|
379 |
+
foreach ($this->loggers as $logger) {
|
380 |
+
$logger->log($message, $error_type);
|
381 |
+
}
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
}
|
386 |
+
|
387 |
+
endif;
|
vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-meta.php
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The DB handle class for the options framework
|
4 |
+
*/
|
5 |
+
|
6 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
7 |
+
|
8 |
+
if (!class_exists('Updraft_Task_Meta')) :
|
9 |
+
|
10 |
+
class Updraft_Task_Meta {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* This method gets data from the task meta table in the WordPress database
|
14 |
+
*
|
15 |
+
* @param int $id the instance id of the task
|
16 |
+
* @param String $key the key to get
|
17 |
+
*
|
18 |
+
* @return Mixed The option from the database
|
19 |
+
*/
|
20 |
+
public static function get_task_meta($id, $key) {
|
21 |
+
global $wpdb;
|
22 |
+
|
23 |
+
$id = (int) $id;
|
24 |
+
if (!$id) return false;
|
25 |
+
|
26 |
+
$sql = $wpdb->prepare("SELECT meta_value FROM {$wpdb->base_prefix}tm_taskmeta WHERE task_id = %d AND meta_key = %s LIMIT 1", $id, $key);
|
27 |
+
|
28 |
+
$meta = $wpdb->get_var($sql);
|
29 |
+
|
30 |
+
if ($meta)
|
31 |
+
return maybe_unserialize($meta);
|
32 |
+
else return false;
|
33 |
+
}
|
34 |
+
|
35 |
+
|
36 |
+
/**
|
37 |
+
* This method is used to update data stored in the WordPress database
|
38 |
+
*
|
39 |
+
* @param int $id the instance id of the task
|
40 |
+
* @param String $key the key of the data to update
|
41 |
+
* @param Mixed $value the value to save to the option
|
42 |
+
*
|
43 |
+
* @return Mixed the status of the update operation
|
44 |
+
*/
|
45 |
+
public static function update_task_meta($id, $key, $value) {
|
46 |
+
global $wpdb;
|
47 |
+
|
48 |
+
$id = (int) $id;
|
49 |
+
if (!$id) return false;
|
50 |
+
|
51 |
+
$value = maybe_serialize($value);
|
52 |
+
|
53 |
+
if (false !== self::get_task_meta($id, $key)) {
|
54 |
+
$sql = $wpdb->prepare("UPDATE {$wpdb->base_prefix}tm_taskmeta SET meta_value = %s WHERE meta_key = %s AND task_id = %d", $value, $key, $id);
|
55 |
+
} else {
|
56 |
+
$sql = $wpdb->prepare("INSERT INTO {$wpdb->base_prefix}tm_taskmeta (task_id, meta_key, meta_value) VALUES (%d, %s, %s)", $id, $key, $value);
|
57 |
+
}
|
58 |
+
|
59 |
+
return $wpdb->query($sql);
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* This method is used to delete task data stored in the WordPress database
|
64 |
+
*
|
65 |
+
* @param int $id the instance id of the task
|
66 |
+
* @param String $key the key to delete
|
67 |
+
*
|
68 |
+
* @return Mixed the status of the delete operation
|
69 |
+
*/
|
70 |
+
public static function delete_task_meta($id, $key) {
|
71 |
+
global $wpdb;
|
72 |
+
|
73 |
+
$id = (int) $id;
|
74 |
+
if (!$id) return false;
|
75 |
+
|
76 |
+
$sql = $wpdb->prepare("DELETE FROM {$wpdb->base_prefix}tm_taskmeta WHERE task_id = %d AND meta_key = %s LIMIT 1", $id, $key);
|
77 |
+
return $wpdb->query($sql);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Bulk delete task
|
82 |
+
*
|
83 |
+
* @param int $id the instance id of the task
|
84 |
+
*/
|
85 |
+
public static function bulk_delete_task_meta($id) {
|
86 |
+
global $wpdb;
|
87 |
+
|
88 |
+
$id = (int) $id;
|
89 |
+
if (!$id) return false;
|
90 |
+
|
91 |
+
$sql = $wpdb->prepare("DELETE FROM {$wpdb->base_prefix}tm_taskmeta WHERE task_id = %d", $id);
|
92 |
+
return $wpdb->query($sql);
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
endif;
|
vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-options.php
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The options framework for tasks
|
4 |
+
*/
|
5 |
+
|
6 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
7 |
+
|
8 |
+
if (!class_exists('Updraft_Task_Options')) :
|
9 |
+
|
10 |
+
class Updraft_Task_Options {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* This method gets an option from the task meta table in the WordPress database
|
14 |
+
*
|
15 |
+
* @param int $instance_id the instance id of the task
|
16 |
+
* @param String $option the name of the option to get
|
17 |
+
* @param Mixed $default a value to return if the option is not currently set
|
18 |
+
*
|
19 |
+
* @return Mixed The option from the database
|
20 |
+
*/
|
21 |
+
public static function get_task_option($instance_id, $option, $default = null) {
|
22 |
+
|
23 |
+
$tmp = Updraft_Task_Meta::get_task_meta($instance_id, 'task_options');
|
24 |
+
|
25 |
+
if (isset($tmp[$option])) {
|
26 |
+
$value = $tmp[$option];
|
27 |
+
} else {
|
28 |
+
$value = $default;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Filters the value of an existing option.
|
33 |
+
*
|
34 |
+
* The dynamic portion of the hook name, `$option`, refers to the option name.
|
35 |
+
*/
|
36 |
+
return apply_filters("ud_task_option_{$option}", maybe_unserialize($value), $option, $default, $instance_id);
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* This method is used to update a task option stored in the WordPress database
|
41 |
+
*
|
42 |
+
* @param int $instance_id the instance id of the task
|
43 |
+
* @param String $option the name of the option to update
|
44 |
+
* @param Mixed $value the value to save to the option
|
45 |
+
*
|
46 |
+
* @return Mixed the status of the update operation
|
47 |
+
*/
|
48 |
+
public static function update_task_option($instance_id, $option, $value) {
|
49 |
+
|
50 |
+
$option = trim($option);
|
51 |
+
|
52 |
+
if (empty($option)) return false;
|
53 |
+
|
54 |
+
$old_value = self::get_task_option($instance_id, $option);
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Filters a specific option before its value is (maybe) serialized and updated.
|
58 |
+
*/
|
59 |
+
$value = apply_filters("ud_pre_update_task_option_{$option}", $value, $old_value, $option, $instance_id);
|
60 |
+
|
61 |
+
$tmp = Updraft_Task_Meta::get_task_meta($instance_id, 'task_options');
|
62 |
+
|
63 |
+
if (!is_array($tmp)) $tmp = array();
|
64 |
+
$tmp[$option] = maybe_serialize($value);
|
65 |
+
|
66 |
+
$result = Updraft_Task_Meta::update_task_meta($instance_id, 'task_options', $tmp);
|
67 |
+
|
68 |
+
if ($result) {
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Fires after the value of a specific option has been successfully updated.
|
72 |
+
*/
|
73 |
+
do_action("ud_update_task_option_{$option}", $value, $old_value, $option, $instance_id);
|
74 |
+
}
|
75 |
+
|
76 |
+
return $result;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* This method is used to delete a task option stored in the WordPress database
|
81 |
+
*
|
82 |
+
* @param int $instance_id the instance id of the task
|
83 |
+
* @param String $option the option to delete
|
84 |
+
*/
|
85 |
+
public static function delete_task_option($instance_id, $option) {
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Fires immediately before an option is deleted.
|
89 |
+
*/
|
90 |
+
do_action("ud_before_delete_task_option", $option, $instance_id);
|
91 |
+
|
92 |
+
$tmp = Updraft_Task_Meta::get_task_meta($instance_id, 'task_options');
|
93 |
+
|
94 |
+
if (is_array($tmp)) {
|
95 |
+
if (isset($tmp[$option])) unset($tmp[$option]);
|
96 |
+
} else {
|
97 |
+
$tmp = array();
|
98 |
+
}
|
99 |
+
|
100 |
+
$result = Updraft_Task_Meta::update_task_meta($instance_id, 'task_options', $tmp);
|
101 |
+
|
102 |
+
if ($result) {
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Fires after a specific option has been successfully deleted.
|
106 |
+
*/
|
107 |
+
do_action("ud_delete_task_option_{$option}", $option);
|
108 |
+
}
|
109 |
+
|
110 |
+
return $result;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* This method gets all options assoicated with a task
|
115 |
+
*
|
116 |
+
* @param int $instance_id the instance id of the task
|
117 |
+
*
|
118 |
+
* @return Mixed The options from the database
|
119 |
+
*/
|
120 |
+
public static function get_all_task_options($instance_id) {
|
121 |
+
|
122 |
+
$value = Updraft_Task_Meta::get_task_meta($instance_id, 'task_options');
|
123 |
+
return apply_filters("ud_all_task_options", maybe_unserialize($value), $instance_id);
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
endif;
|
vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php
ADDED
@@ -0,0 +1,693 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The base class which must be extended to use the tasks library
|
4 |
+
*/
|
5 |
+
|
6 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
7 |
+
|
8 |
+
if (!class_exists('Updraft_Task_1_2')) :
|
9 |
+
|
10 |
+
if (!class_exists('Updraft_Task_Options')) require_once('class-updraft-task-options.php');
|
11 |
+
if (!class_exists('Updraft_Task_Meta')) require_once('class-updraft-task-meta.php');
|
12 |
+
|
13 |
+
abstract class Updraft_Task_1_2 {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* A unique ID for the specific task
|
17 |
+
*
|
18 |
+
* @var int
|
19 |
+
*/
|
20 |
+
private $id;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* The user id of the creator of this task
|
24 |
+
*
|
25 |
+
* @var string
|
26 |
+
*/
|
27 |
+
private $user_id;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* A text description for the task
|
31 |
+
*
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
private $description;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* A type for the task
|
38 |
+
*
|
39 |
+
* @var string
|
40 |
+
*/
|
41 |
+
private $type;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* A timestamp indicating the time the task was created
|
45 |
+
*
|
46 |
+
* @var string
|
47 |
+
*/
|
48 |
+
private $time_created;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* The number of times this task was attempted
|
52 |
+
*
|
53 |
+
* @var int
|
54 |
+
*/
|
55 |
+
private $attempts;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* A text description describing the status of the task
|
59 |
+
*
|
60 |
+
* @var string
|
61 |
+
*/
|
62 |
+
private $status;
|
63 |
+
|
64 |
+
/**
|
65 |
+
* An identifier indicating which child class created this instance
|
66 |
+
*
|
67 |
+
* @var string
|
68 |
+
*/
|
69 |
+
private $class_identifier;
|
70 |
+
|
71 |
+
/**
|
72 |
+
* A logger object that can be used to capture interesting events / messages
|
73 |
+
*
|
74 |
+
* @var Object
|
75 |
+
*/
|
76 |
+
protected $_loggers;
|
77 |
+
|
78 |
+
/**
|
79 |
+
* The Task constructor
|
80 |
+
*
|
81 |
+
* @param UpdraftPlus_Task|object $task UpdraftPlus_Task object.
|
82 |
+
*/
|
83 |
+
public function __construct($task) {
|
84 |
+
foreach (get_object_vars($task) as $key => $value)
|
85 |
+
$this->$key = $value;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Sets the instance ID.
|
90 |
+
*
|
91 |
+
* @param String $instance_id - the instance ID
|
92 |
+
*/
|
93 |
+
public function set_id($instance_id) {
|
94 |
+
$this->id = $instance_id;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Gets the instance ID.
|
99 |
+
*
|
100 |
+
* @return String the instance ID
|
101 |
+
*/
|
102 |
+
public function get_id() {
|
103 |
+
return $this->id;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Sets the description.
|
108 |
+
*
|
109 |
+
* @param String $description - the description of the task
|
110 |
+
*/
|
111 |
+
public function set_description($description) {
|
112 |
+
$this->description = $description;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Gets the task description
|
117 |
+
*
|
118 |
+
* @return String $description - the description of the task
|
119 |
+
*/
|
120 |
+
public function get_description() {
|
121 |
+
return $this->description;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Sets the type.
|
126 |
+
*
|
127 |
+
* @param String $type - the type of the task
|
128 |
+
*/
|
129 |
+
public function set_type($type) {
|
130 |
+
$this->type = $type;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Gets the number of times this task was attempted
|
135 |
+
*
|
136 |
+
* @return int $attempts - the count
|
137 |
+
*/
|
138 |
+
public function get_attempts() {
|
139 |
+
return $this->attempts;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Sets the number of times this task was attempted
|
144 |
+
*
|
145 |
+
* @param String $attempts - the count
|
146 |
+
*/
|
147 |
+
public function set_attempts($attempts) {
|
148 |
+
if (is_numeric($attempts))
|
149 |
+
$this->attempts = $attempts;
|
150 |
+
else return false;
|
151 |
+
|
152 |
+
return $this->update_attempts($this->id, $this->attempts);
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Gets the task type
|
157 |
+
*
|
158 |
+
* @return String $type - the type of the task
|
159 |
+
*/
|
160 |
+
public function get_type() {
|
161 |
+
return $this->type;
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Sets the task status.
|
166 |
+
*
|
167 |
+
* @param String $status - the status of the task
|
168 |
+
*
|
169 |
+
* @return Boolean - the result of the status update
|
170 |
+
*/
|
171 |
+
public function set_status($status) {
|
172 |
+
|
173 |
+
if (array_key_exists($status, self::get_allowed_statuses()))
|
174 |
+
$this->status = $status;
|
175 |
+
else return false;
|
176 |
+
|
177 |
+
return $this->update_status($this->id, $this->status);
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Gets the task status
|
182 |
+
*
|
183 |
+
* @return String $status - the status of the task
|
184 |
+
*/
|
185 |
+
public function get_status() {
|
186 |
+
return $this->status;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Sets the logger for this task.
|
191 |
+
*
|
192 |
+
* @param array $loggers - the loggers for this task
|
193 |
+
*/
|
194 |
+
public function set_loggers($loggers) {
|
195 |
+
if (is_array($loggers)) {
|
196 |
+
foreach ($loggers as $logger) {
|
197 |
+
$this->add_logger($logger);
|
198 |
+
}
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Add a logger to loggers list
|
204 |
+
*
|
205 |
+
* @param Object $logger - a logger for the task
|
206 |
+
*/
|
207 |
+
public function add_logger($logger) {
|
208 |
+
$this->_loggers[] = $logger;
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Return list of loggers
|
213 |
+
*
|
214 |
+
* @return array
|
215 |
+
*/
|
216 |
+
public function get_loggers() {
|
217 |
+
return $this->_loggers;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* The initialisation function that accepts and processes any parameters needed before the task starts
|
222 |
+
*
|
223 |
+
* @param Array $options - array of options
|
224 |
+
*
|
225 |
+
* @uses update_option
|
226 |
+
*/
|
227 |
+
public function initialise($options = array()) {
|
228 |
+
|
229 |
+
do_action('ud_task_before_initialise', $this, $options);
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Parse incoming $options into an array and merge it with defaults
|
233 |
+
*/
|
234 |
+
$defaults = $this->get_default_options();
|
235 |
+
$options = wp_parse_args($options, $defaults);
|
236 |
+
|
237 |
+
foreach ($options as $option => $value) {
|
238 |
+
$this->update_option($option, $value);
|
239 |
+
}
|
240 |
+
|
241 |
+
do_action('ud_task_initialise_complete', $this, $options);
|
242 |
+
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Attempts to perform the task
|
247 |
+
*
|
248 |
+
* @param integer $lock_for - if greater than zero, then lock the task, and don't break until this number of seconds has passed
|
249 |
+
*
|
250 |
+
* @return boolean Status of the attempt
|
251 |
+
*/
|
252 |
+
public function attempt($lock_for = 0) {
|
253 |
+
|
254 |
+
$_task = $this->get_task_from_db($this->get_id());
|
255 |
+
|
256 |
+
if (!$_task) {
|
257 |
+
$this->log("The task with id : {$this->get_id()}, and type '{$this->get_type()}' seems to have been deleted from the database.");
|
258 |
+
return false;
|
259 |
+
}
|
260 |
+
|
261 |
+
if ('complete' == $this->get_status()) {
|
262 |
+
$this->log("Attempting already complete task with ID : {$this->get_id()}, and type '{$this->get_type()}'. Aborting !");
|
263 |
+
return true;
|
264 |
+
}
|
265 |
+
|
266 |
+
if ($lock_for) {
|
267 |
+
$try = 1;
|
268 |
+
$locked = false;
|
269 |
+
while ($try < 4) {
|
270 |
+
if ($locked = $this->lock($this->get_id(), true, $lock_for)) break;
|
271 |
+
$try ++;
|
272 |
+
sleep(1);
|
273 |
+
}
|
274 |
+
if (!$locked) {
|
275 |
+
$this->fail('could_not_lock', 'The task could not be locked');
|
276 |
+
return false;
|
277 |
+
}
|
278 |
+
}
|
279 |
+
|
280 |
+
$attempts = $this->get_attempts();
|
281 |
+
|
282 |
+
if ($attempts >= $this->get_max_attempts()) {
|
283 |
+
$this->fail("max_attempts_exceeded", "Maximum attempts ($attempts) exceeded for task");
|
284 |
+
return false;
|
285 |
+
}
|
286 |
+
|
287 |
+
$this->log("Processing task with ID : {$this->get_id()}, and type '{$this->get_type()}'");
|
288 |
+
$this->set_attempts(++$attempts);
|
289 |
+
$status = $this->run();
|
290 |
+
|
291 |
+
if ($status) {
|
292 |
+
$this->complete();
|
293 |
+
$this->log("Completed processing task with ID : {$this->get_id()}, and type '{$this->get_type()}'");
|
294 |
+
}
|
295 |
+
|
296 |
+
if ($lock_for) $this->lock($this->get_id(), false);
|
297 |
+
|
298 |
+
return $status;
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Lock or unlock a task
|
303 |
+
*
|
304 |
+
* @param Integer - $task_id - task identifier
|
305 |
+
* @param Boolean - $lock - whether to lock or unlock
|
306 |
+
* @param Integer - $lock_for - if already locked, how long after which to break the lock
|
307 |
+
*
|
308 |
+
* @return Boolean - whether the operation was successful
|
309 |
+
*/
|
310 |
+
public function lock($task_id, $lock = true, $lock_for = 60) {
|
311 |
+
|
312 |
+
global $wpdb;
|
313 |
+
|
314 |
+
if (!$lock) {
|
315 |
+
return $wpdb->update($wpdb->base_prefix.'tm_tasks', array('last_locked_at' => 0), array('id' => $task_id)) ? true : false;
|
316 |
+
}
|
317 |
+
|
318 |
+
// Mode: lock. Attempt to set the lock
|
319 |
+
$affected = $wpdb->update($wpdb->base_prefix.'tm_tasks', array('last_locked_at' => time()), array('id' => $task_id, 'last_locked_at' => 0));
|
320 |
+
|
321 |
+
// Success.
|
322 |
+
if (1 == $affected) return true;
|
323 |
+
|
324 |
+
// Failed - something else already had it locked. Grab the lock if it had expired.
|
325 |
+
$affected = $wpdb->update($wpdb->base_prefix.'tm_tasks', array('last_locked_at' => time()), array('id' => $task_id, 'last_locked_at' => 0));
|
326 |
+
|
327 |
+
$expires_at = time() - $lock_for;
|
328 |
+
|
329 |
+
$affected = $wpdb->query($wpdb->prepare("
|
330 |
+
UPDATE {$wpdb->base_prefix}tm_tasks
|
331 |
+
SET last_locked_at = %d
|
332 |
+
WHERE id = %d
|
333 |
+
AND last_locked_at <= %s
|
334 |
+
", time(), $task_id, $expires_at));
|
335 |
+
|
336 |
+
return $affected ? true : false;
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* This function is called to allow for the task to perform a small chunk of work.
|
341 |
+
* It should be written in a way that anticipates it being killed off at any time.
|
342 |
+
*/
|
343 |
+
abstract public function run();
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Any clean up code goes here.
|
347 |
+
*/
|
348 |
+
public function complete() {
|
349 |
+
|
350 |
+
do_action('ud_task_before_complete', $this);
|
351 |
+
|
352 |
+
$this->set_status('complete');
|
353 |
+
|
354 |
+
do_action('ud_task_completed', $this);
|
355 |
+
|
356 |
+
return true;
|
357 |
+
}
|
358 |
+
|
359 |
+
/**
|
360 |
+
* Fires if the task fails, any clean up code and logging should go here
|
361 |
+
*
|
362 |
+
* @param String $error_code - A code for the failure
|
363 |
+
* @param String $error_message - A description for the failure
|
364 |
+
*/
|
365 |
+
public function fail($error_code = "Unknown", $error_message = "Unknown") {
|
366 |
+
|
367 |
+
do_action('ud_task_before_failed', $this);
|
368 |
+
|
369 |
+
$this->set_status('failed');
|
370 |
+
$this->log(sprintf("Task with ID %d and type (%s) failed with error code %s - %s", $this->id, $this->type, $error_code, $error_message));
|
371 |
+
|
372 |
+
$this->update_option("error_code", $error_code);
|
373 |
+
$this->update_option("error_message", $error_message);
|
374 |
+
|
375 |
+
do_action('ud_task_failed', $this);
|
376 |
+
|
377 |
+
return true;
|
378 |
+
}
|
379 |
+
|
380 |
+
/**
|
381 |
+
* Prints any information about the task that the UI can use on the front end for debugging
|
382 |
+
* @param String $title the header to use in the report
|
383 |
+
*
|
384 |
+
* @return String The task report HTML
|
385 |
+
*/
|
386 |
+
public function print_task_report_widget($title = 'Task Summary') {
|
387 |
+
|
388 |
+
$ret = "";
|
389 |
+
|
390 |
+
$status = $this->get_status();
|
391 |
+
$stage = $this->get_option('stage') ? $this->get_option('stage') : 'Unknown';
|
392 |
+
$description = $this->get_status_description($status);
|
393 |
+
|
394 |
+
|
395 |
+
$ret .= "<div class='task task-report task-{$this->type}' id='task-id-{$this->id}'>";
|
396 |
+
$ret .= "<h4>Task Summary</h4>";
|
397 |
+
$ret .= "<ul class='properties-list task-{$this->type}'>";
|
398 |
+
|
399 |
+
foreach ($this as $key => $value) {
|
400 |
+
$ret .= sprintf("<li> %s : %s </li>", $key, $value);
|
401 |
+
}
|
402 |
+
$ret .='</ul>';
|
403 |
+
|
404 |
+
$ret .= "<h4> $title </h4>";
|
405 |
+
$ret .= "<ul class='data-list task-{$this->type}'>";
|
406 |
+
|
407 |
+
foreach ($this->get_all_options() as $key => $value) {
|
408 |
+
if (is_array(maybe_unserialize($value))) {
|
409 |
+
$ret .= sprintf("<li> %s</li>", $key);
|
410 |
+
$ret .= "<ul class='sub-list'>";
|
411 |
+
foreach (maybe_unserialize($value) as $k => $v) {
|
412 |
+
$ret .= sprintf("<li> %s => %s </li>", $k, $v);
|
413 |
+
}
|
414 |
+
$ret .= "</ul>";
|
415 |
+
} else {
|
416 |
+
$ret .= sprintf("<li> %s : %s </li>", $key, $value);
|
417 |
+
}
|
418 |
+
}
|
419 |
+
$ret .='</ul>';
|
420 |
+
$ret .='</div>';
|
421 |
+
|
422 |
+
return apply_filters('ud_print_task_report_widget', $ret, $this);
|
423 |
+
|
424 |
+
}
|
425 |
+
|
426 |
+
/**
|
427 |
+
* This method gets an option from the task options in the WordPress database if available,
|
428 |
+
* otherwise returns the default for this task type
|
429 |
+
*
|
430 |
+
* @param String $option the name of the option to get
|
431 |
+
* @param Mixed $default a value to return if the option is not currently set
|
432 |
+
*
|
433 |
+
* @return Mixed The option from the database
|
434 |
+
*/
|
435 |
+
public function get_option($option = null, $default = null) {
|
436 |
+
return Updraft_Task_Options::get_task_option($this->id, $option, $default);
|
437 |
+
}
|
438 |
+
|
439 |
+
/**
|
440 |
+
* This method is used to add a task option stored in the WordPress database
|
441 |
+
*
|
442 |
+
* @param String $option the name of the option to update
|
443 |
+
* @param Mixed $value the value to save to the option
|
444 |
+
*
|
445 |
+
* @return Mixed the status of the add operation
|
446 |
+
*/
|
447 |
+
public function add_option($option, $value) {
|
448 |
+
return Updraft_Task_Options::update_task_option($this->id, $option, $value);
|
449 |
+
}
|
450 |
+
|
451 |
+
/**
|
452 |
+
* This method is used to update a task option stored in the WordPress database
|
453 |
+
*
|
454 |
+
* @param String $option the name of the option to update
|
455 |
+
* @param Mixed $value the value to save to the option
|
456 |
+
*
|
457 |
+
* @return Mixed the status of the update operation
|
458 |
+
*/
|
459 |
+
public function update_option($option, $value) {
|
460 |
+
return Updraft_Task_Options::update_task_option($this->id, $option, $value);
|
461 |
+
}
|
462 |
+
|
463 |
+
/**
|
464 |
+
* This method is used to delete a task option stored in the WordPress database
|
465 |
+
*
|
466 |
+
* @param String $option the option to delete
|
467 |
+
*
|
468 |
+
* @return Boolean the result of the delete operation
|
469 |
+
*/
|
470 |
+
public function delete_option($option) {
|
471 |
+
return Updraft_Task_Options::delete_task_option($this->id, $option);
|
472 |
+
}
|
473 |
+
|
474 |
+
/**
|
475 |
+
* This method gets all options assoicated with a task
|
476 |
+
*/
|
477 |
+
public function get_all_options() {
|
478 |
+
return Updraft_Task_Options::get_all_task_options($this->id);
|
479 |
+
}
|
480 |
+
|
481 |
+
/**
|
482 |
+
* Retrieve default options for this task.
|
483 |
+
* This method should normally be over-ridden by the child.
|
484 |
+
*
|
485 |
+
* @return Array - an array of options
|
486 |
+
*/
|
487 |
+
public function get_default_options() {
|
488 |
+
|
489 |
+
$this->log(sprintf('The get_default_options() method was not over-ridden for the class : %s', $this->get_description()));
|
490 |
+
|
491 |
+
return array();
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* Returns a unique label for this instance that can be used as an identifier
|
496 |
+
*
|
497 |
+
* @return String - a unique label for this instance
|
498 |
+
*/
|
499 |
+
protected function get_unique_label() {
|
500 |
+
return apply_filters('ud_task_unique_label', $this->id."-".$this->type, $this);
|
501 |
+
}
|
502 |
+
|
503 |
+
/**
|
504 |
+
* Updates the status of the given task in the DB
|
505 |
+
*
|
506 |
+
* @param String $id - the id of the task
|
507 |
+
* @param String $status - the status of the task
|
508 |
+
*
|
509 |
+
* @return Boolean - the stauts of the update operation
|
510 |
+
*/
|
511 |
+
public function update_status($id, $status) {
|
512 |
+
|
513 |
+
if (!array_key_exists($status, self::get_allowed_statuses()))
|
514 |
+
return false;
|
515 |
+
|
516 |
+
global $wpdb;
|
517 |
+
$sql = $wpdb->prepare("UPDATE {$wpdb->base_prefix}tm_tasks SET status = %s WHERE id = %d", $status, $id);
|
518 |
+
|
519 |
+
return $wpdb->query($sql);
|
520 |
+
}
|
521 |
+
|
522 |
+
/**
|
523 |
+
* Updates the number of attempts made for the given task in the DB
|
524 |
+
*
|
525 |
+
* @param String $id - the id of the task
|
526 |
+
* @param int $attempts - the status of the task
|
527 |
+
*
|
528 |
+
* @return Boolean - the stauts of the update operation
|
529 |
+
*/
|
530 |
+
public function update_attempts($id, $attempts) {
|
531 |
+
|
532 |
+
if (!is_numeric($attempts))
|
533 |
+
return false;
|
534 |
+
|
535 |
+
global $wpdb;
|
536 |
+
$sql = $wpdb->prepare("UPDATE {$wpdb->base_prefix}tm_tasks SET attempts = %s WHERE id = %d", $attempts, $id);
|
537 |
+
|
538 |
+
return $wpdb->query($sql);
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* Cleans out the given task from the DB
|
543 |
+
*
|
544 |
+
* @return Boolean - the status of the delete operation
|
545 |
+
*/
|
546 |
+
public function delete() {
|
547 |
+
global $wpdb;
|
548 |
+
|
549 |
+
$sql = $wpdb->prepare("DELETE FROM {$wpdb->base_prefix}tm_tasks WHERE id = %d", $this->id);
|
550 |
+
return $wpdb->query($sql);
|
551 |
+
}
|
552 |
+
|
553 |
+
/**
|
554 |
+
* Cleans out the given task meta from the DB
|
555 |
+
*
|
556 |
+
* @return Boolean - the status of the delete operation
|
557 |
+
*/
|
558 |
+
public function delete_meta() {
|
559 |
+
return Updraft_Task_Meta::bulk_delete_task_meta($this->id);
|
560 |
+
}
|
561 |
+
|
562 |
+
/**
|
563 |
+
* Helper function to convert object to array.
|
564 |
+
*
|
565 |
+
* @return array Object as array.
|
566 |
+
*/
|
567 |
+
public function to_array() {
|
568 |
+
$task = get_object_vars($this);
|
569 |
+
|
570 |
+
foreach (array( 'task_options', 'task_data', 'task_logs', 'task_extras' ) as $key) {
|
571 |
+
if ($this->__isset($key))
|
572 |
+
$task[$key] = $this->__get($key);
|
573 |
+
}
|
574 |
+
|
575 |
+
return $task;
|
576 |
+
}
|
577 |
+
|
578 |
+
/**
|
579 |
+
* Captures and logs any interesting messages
|
580 |
+
*
|
581 |
+
* @param String $message - the error message
|
582 |
+
* @param String $error_type - the error type
|
583 |
+
*/
|
584 |
+
public function log($message, $error_type = 'info') {
|
585 |
+
|
586 |
+
if (isset($this->_loggers)) {
|
587 |
+
foreach ($this->_loggers as $logger) {
|
588 |
+
$logger->log($message, $error_type);
|
589 |
+
}
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
/**
|
594 |
+
* Retrieve all the supported task statuses.
|
595 |
+
*
|
596 |
+
* Tasks should have a limited set of valid status values, this method provides a
|
597 |
+
* list of values and descriptions.
|
598 |
+
*
|
599 |
+
* @return array List of task statuses.
|
600 |
+
*/
|
601 |
+
public static function get_allowed_statuses() {
|
602 |
+
$status = array(
|
603 |
+
'initialised' => __('Initialised'),
|
604 |
+
'active' => __('Active'),
|
605 |
+
'paused' => __('Paused'),
|
606 |
+
'complete' => __('Completed'),
|
607 |
+
'failed' => __('Failed')
|
608 |
+
);
|
609 |
+
|
610 |
+
return apply_filters('ud_allowed_task_statuses', $status);
|
611 |
+
}
|
612 |
+
|
613 |
+
|
614 |
+
/**
|
615 |
+
* Retrieve the max attempts permitted for task type
|
616 |
+
*
|
617 |
+
* @return int Max attempts permitted for task type
|
618 |
+
*/
|
619 |
+
private function get_max_attempts() {
|
620 |
+
return apply_filters('ud_max_attempts', 5, $this);
|
621 |
+
}
|
622 |
+
|
623 |
+
/**
|
624 |
+
* Retrieve the text description of the task status.
|
625 |
+
*
|
626 |
+
* @param String $status - The task status
|
627 |
+
*
|
628 |
+
* @return String Description of the task status.
|
629 |
+
*/
|
630 |
+
public static function get_status_description($status) {
|
631 |
+
$list = self::get_allowed_statuses();
|
632 |
+
|
633 |
+
if (!array_key_exists($status, self::get_allowed_statuses()))
|
634 |
+
return __('Unknown');
|
635 |
+
|
636 |
+
return apply_filters("ud_task_status_description_{$status}", $list[$status], $status, $list);
|
637 |
+
}
|
638 |
+
|
639 |
+
|
640 |
+
/**
|
641 |
+
* Creates a new task instance and returns it
|
642 |
+
*
|
643 |
+
* @access public
|
644 |
+
*
|
645 |
+
* @global wpdb $wpdb WordPress database abstraction object.
|
646 |
+
*
|
647 |
+
* @param String $type A identifier for the task
|
648 |
+
* @param String $description A description of the task
|
649 |
+
* @param Mixed $options A list of options to initialise the task
|
650 |
+
* @param String $task_class Class name of task; only needed/used on PHP 5.2 (due to lack of late static binding)
|
651 |
+
*
|
652 |
+
* @return Updraft_Task|false Task object, false otherwise.
|
653 |
+
*/
|
654 |
+
public static function create_task($type, $description, $options = array(), $task_class = '') {
|
655 |
+
global $wpdb;
|
656 |
+
|
657 |
+
$user_id = get_current_user_id();
|
658 |
+
$class_identifier = function_exists('get_called_class') ? get_called_class() : $task_class;// phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.get_called_classFound
|
659 |
+
|
660 |
+
$is_anonymous_user_allowed = isset($options['anonymous_user_allowed']) && $options['anonymous_user_allowed'];
|
661 |
+
if (!$user_id && !$is_anonymous_user_allowed) return false;
|
662 |
+
|
663 |
+
$sql = $wpdb->prepare("INSERT INTO {$wpdb->base_prefix}tm_tasks (type, user_id, description, class_identifier, status) VALUES (%s, %d, %s, %s, %s)", $type, $user_id, $description, $class_identifier, 'active');
|
664 |
+
|
665 |
+
$wpdb->query($sql);
|
666 |
+
|
667 |
+
$task_id = $wpdb->insert_id;
|
668 |
+
|
669 |
+
if (!$task_id) return false;
|
670 |
+
|
671 |
+
$_task = $wpdb->get_row("SELECT * FROM {$wpdb->base_prefix}tm_tasks WHERE id = {$task_id} LIMIT 1");
|
672 |
+
|
673 |
+
$task = new $class_identifier($_task);
|
674 |
+
|
675 |
+
if (!$task) return false;
|
676 |
+
|
677 |
+
$task->initialise($options);
|
678 |
+
|
679 |
+
return $task;
|
680 |
+
}
|
681 |
+
|
682 |
+
/**
|
683 |
+
* Select the current task from the database
|
684 |
+
*
|
685 |
+
* @param int $task_id - The task ID
|
686 |
+
* @return object|false
|
687 |
+
*/
|
688 |
+
private function get_task_from_db($task_id) {
|
689 |
+
global $wpdb;
|
690 |
+
return $wpdb->get_row("SELECT * FROM {$wpdb->base_prefix}tm_tasks WHERE id = {$task_id} LIMIT 1");
|
691 |
+
}
|
692 |
+
}
|
693 |
+
endif;
|
vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php
ADDED
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Initialise the tasks module and create the needed DB tables
|
4 |
+
*/
|
5 |
+
|
6 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
7 |
+
|
8 |
+
if (!class_exists('Updraft_Tasks_Activation')) :
|
9 |
+
|
10 |
+
class Updraft_Tasks_Activation {
|
11 |
+
|
12 |
+
private static $table_prefix;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Format: key=<version>, value=array of method names to call
|
16 |
+
* Example Usage:
|
17 |
+
* private static $db_updates = array(
|
18 |
+
* '1.0.1' => array(
|
19 |
+
* 'update_101_add_new_column',
|
20 |
+
* ),
|
21 |
+
* );
|
22 |
+
*
|
23 |
+
* @var Mixed
|
24 |
+
*/
|
25 |
+
private static $db_updates = array(
|
26 |
+
'0.0.1' => array('create_tables'),
|
27 |
+
'1.0.1' => array('add_attempts_and_class_identifier'),
|
28 |
+
'1.1' => array('add_lock_column'),
|
29 |
+
);
|
30 |
+
|
31 |
+
|
32 |
+
const UPDRAFT_TASKS_DB_VERSION = '1.1';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Initialise this class
|
36 |
+
*/
|
37 |
+
public static function init_db() {
|
38 |
+
self::$table_prefix = defined('UPDRAFT_TASKS_TABLE_PREFIX') ? UPDRAFT_TASKS_TABLE_PREFIX : 'tm_';
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* This is the class entry point
|
43 |
+
*/
|
44 |
+
public static function install() {
|
45 |
+
self::init_db();
|
46 |
+
self::create_tables();
|
47 |
+
// we need walk through all updates when install at first.
|
48 |
+
self::check_updates();
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Check needed tables in data base and if one of them doesn't exist force reinstall.
|
53 |
+
*/
|
54 |
+
public static function reinstall_if_needed() {
|
55 |
+
static $done = false;
|
56 |
+
|
57 |
+
if ($done) return;
|
58 |
+
|
59 |
+
if (!self::check_if_tables_exist()) self::reinstall();
|
60 |
+
|
61 |
+
$done = true;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Drop database version variable from option from database and run install again.
|
66 |
+
*/
|
67 |
+
public static function reinstall() {
|
68 |
+
if (is_multisite()) {
|
69 |
+
delete_site_option('updraft_task_manager_dbversion');
|
70 |
+
} else {
|
71 |
+
delete_option('updraft_task_manager_dbversion');
|
72 |
+
}
|
73 |
+
self::install();
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Check if needed task manager tables exist.
|
78 |
+
*
|
79 |
+
* @return bool
|
80 |
+
*/
|
81 |
+
public static function check_if_tables_exist() {
|
82 |
+
global $wpdb;
|
83 |
+
self::init_db();
|
84 |
+
$our_prefix = $wpdb->base_prefix.self::$table_prefix;
|
85 |
+
$tables = array($our_prefix.'tasks', $our_prefix.'taskmeta');
|
86 |
+
|
87 |
+
foreach ($tables as $table) {
|
88 |
+
$query = "SHOW TABLES LIKE '{$table}'";
|
89 |
+
$tables = $wpdb->get_results($query, ARRAY_A);
|
90 |
+
if (!is_array($tables) || 0 == count($tables)) return false;
|
91 |
+
}
|
92 |
+
|
93 |
+
return true;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* See if any database schema updates are needed, and perform them if so.
|
98 |
+
* Example Usage:
|
99 |
+
* public static function update_101_add_new_column() {
|
100 |
+
* $wpdb = $GLOBALS['wpdb'];
|
101 |
+
* $wpdb->query('ALTER TABLE tm_tasks ADD task_expiry varchar(300) AFTER id');
|
102 |
+
* }
|
103 |
+
*/
|
104 |
+
public static function check_updates() {
|
105 |
+
self::init_db();
|
106 |
+
$our_version = self::get_version();
|
107 |
+
if (is_multisite()) {
|
108 |
+
$db_version = get_site_option('updraft_task_manager_dbversion');
|
109 |
+
} else {
|
110 |
+
$db_version = get_option('updraft_task_manager_dbversion');
|
111 |
+
}
|
112 |
+
if (!$db_version || version_compare($our_version, $db_version, '>')) {
|
113 |
+
foreach (self::$db_updates as $version => $updates) {
|
114 |
+
if (version_compare($version, $db_version, '>')) {
|
115 |
+
foreach ($updates as $update) {
|
116 |
+
call_user_func(array(__CLASS__, $update));
|
117 |
+
}
|
118 |
+
}
|
119 |
+
}
|
120 |
+
if (is_multisite()) {
|
121 |
+
update_site_option('updraft_task_manager_dbversion', self::get_version());
|
122 |
+
} else {
|
123 |
+
update_option('updraft_task_manager_dbversion', self::get_version());
|
124 |
+
}
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Returns the current version of the plugin
|
130 |
+
*/
|
131 |
+
public static function get_version() {
|
132 |
+
return self::UPDRAFT_TASKS_DB_VERSION;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Create the database tables
|
137 |
+
*/
|
138 |
+
public static function create_tables() {
|
139 |
+
|
140 |
+
$wpdb = $GLOBALS['wpdb'];
|
141 |
+
|
142 |
+
$our_prefix = $wpdb->base_prefix.self::$table_prefix;
|
143 |
+
$collate = '';
|
144 |
+
|
145 |
+
if ($wpdb->has_cap('collation')) {
|
146 |
+
if (!empty($wpdb->charset)) {
|
147 |
+
$collate .= "DEFAULT CHARACTER SET $wpdb->charset";
|
148 |
+
}
|
149 |
+
if (!empty($wpdb->collate)) {
|
150 |
+
$collate .= " COLLATE $wpdb->collate";
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
include_once ABSPATH.'wp-admin/includes/upgrade.php';
|
155 |
+
|
156 |
+
// Important: obey the magical/arbitrary rules for formatting this stuff: https://codex.wordpress.org/Creating_Tables_with_Plugins
|
157 |
+
// Otherwise, you get SQL errors and unwanted header output warnings when activating
|
158 |
+
|
159 |
+
$create_tables = 'CREATE TABLE '.$our_prefix."tasks (
|
160 |
+
task_id bigint(20) NOT NULL auto_increment,
|
161 |
+
user_id bigint(20) NOT NULL,
|
162 |
+
type varchar(300) NOT NULL,
|
163 |
+
description varchar(300),
|
164 |
+
PRIMARY KEY (task_id),
|
165 |
+
KEY user_id (user_id),
|
166 |
+
time_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
167 |
+
status varchar(300)
|
168 |
+
) $collate;
|
169 |
+
";
|
170 |
+
// KEY attribute_name (attribute_name)
|
171 |
+
dbDelta($create_tables);
|
172 |
+
|
173 |
+
$max_index_length = 191;
|
174 |
+
|
175 |
+
$create_tables = 'CREATE TABLE '.$our_prefix."taskmeta (
|
176 |
+
meta_id bigint(20) NOT NULL auto_increment,
|
177 |
+
task_id bigint(20) NOT NULL default '0',
|
178 |
+
meta_key varchar(255) DEFAULT NULL,
|
179 |
+
meta_value longtext,
|
180 |
+
PRIMARY KEY (meta_id),
|
181 |
+
KEY meta_key (meta_key($max_index_length)),
|
182 |
+
KEY task_id (task_id)
|
183 |
+
) $collate;
|
184 |
+
";
|
185 |
+
|
186 |
+
dbDelta($create_tables);
|
187 |
+
}
|
188 |
+
|
189 |
+
public static function add_attempts_and_class_identifier() {
|
190 |
+
$wpdb = $GLOBALS['wpdb'];
|
191 |
+
$our_prefix = $wpdb->base_prefix.self::$table_prefix;
|
192 |
+
|
193 |
+
$wpdb->query("ALTER TABLE ".$our_prefix."tasks CHANGE COLUMN `task_id` `id` INT NOT NULL");
|
194 |
+
$wpdb->query("ALTER TABLE ".$our_prefix."tasks MODIFY COLUMN `id` INT auto_increment");
|
195 |
+
$wpdb->query("ALTER TABLE ".$our_prefix."tasks ADD attempts INT DEFAULT 0 AFTER type");
|
196 |
+
$wpdb->query("ALTER TABLE ".$our_prefix."tasks ADD class_identifier varchar(300) DEFAULT 0 AFTER type");
|
197 |
+
}
|
198 |
+
|
199 |
+
public static function add_lock_column() {
|
200 |
+
$wpdb = $GLOBALS['wpdb'];
|
201 |
+
$our_prefix = $wpdb->base_prefix.self::$table_prefix;
|
202 |
+
$wpdb->query('ALTER TABLE '.$our_prefix.'tasks ADD last_locked_at BIGINT DEFAULT 0 AFTER time_created');
|
203 |
+
}
|
204 |
+
|
205 |
+
}
|
206 |
+
|
207 |
+
endif;
|