Version Description
- Fixed the bug with JWT authentication
- Fixed the PHP bug in policy metabox when no errors with JSON is detected
- Fixed the bug with license expiration for Extended versions not properly displayed
- Fixed the bug with Admin Menu listed incorrectly when Default Access Settings defined
- Fixed the PHP bug in Post object when access settings are defined in a Policy
- Improved role creation feature
- Improved capability handling with Access & Security Policy
- Refactor the way extension is installed to eliminate cURL issues
- Deprecated and removed
aam_display_license
capability - Extended default policy document with dependencies
- Added support for
Features
in the Access & Security Policy - Added policy Validation functionality
- Reduced number of methods that use cURL to contact aamplugin.com API
Download this release
Release Info
Developer | vasyltech |
Plugin | Advanced Access Manager |
Version | 5.7.3 |
Comparing to | |
See all releases |
Code changes from version 5.7.2 to 5.7.3
- Application/Backend/Feature/Extension/Manager.php +3 -22
- Application/Backend/Feature/Main/Capability.php +1 -1
- Application/Backend/Feature/Main/Policy.php +40 -0
- Application/Backend/Feature/Subject/Role.php +2 -0
- Application/Backend/Filter.php +3 -1
- Application/Backend/Manager.php +21 -21
- Application/Backend/Subject.php +1 -0
- Application/Backend/View/Helper.php +34 -0
- Application/Backend/phtml/extensions.phtml +2 -2
- Application/Backend/phtml/metabox/policy-metabox.phtml +7 -22
- Application/Core/Compatibility.php +21 -0
- Application/Core/Config.php +1 -0
- Application/Core/Gateway.php +17 -0
- Application/Core/Object/Policy.php +160 -40
- Application/Core/Object/Post.php +1 -0
- Application/Core/Policy/Validator.php +181 -0
- Application/Core/Server.php +0 -54
- Application/Core/Subject/User.php +41 -12
- Application/Extension/List.php +2 -2
- Application/Extension/Repository.php +2 -2
- aam.php +11 -11
- media/js/{aam-5.7.2.js → aam-5.7.3.js} +142 -25
- readme.txt +16 -1
- vendor/autoload.php +27 -0
- vendor/composer/Comparator.php +111 -0
- vendor/composer/Constraint/AbstractConstraint.php +63 -0
- vendor/composer/Constraint/Constraint.php +219 -0
- vendor/composer/Constraint/ConstraintInterface.php +32 -0
- vendor/composer/Constraint/EmptyConstraint.php +59 -0
- vendor/composer/Constraint/MultiConstraint.php +120 -0
- vendor/composer/Semver.php +127 -0
- vendor/composer/VersionParser.php +548 -0
Application/Backend/Feature/Extension/Manager.php
CHANGED
@@ -62,16 +62,11 @@ class AAM_Backend_Feature_Extension_Manager extends AAM_Backend_Feature_Abstract
|
|
62 |
Â
public function install($storedLicense = null) {
|
63 |
Â
$repo = AAM_Extension_Repository::getInstance();
|
64 |
Â
$license = AAM_Core_Request::post('license', $storedLicense);
|
Â
|
|
65 |
Â
|
66 |
-
|
67 |
-
$package = AAM_Core_Server::download($license);
|
68 |
Â
|
69 |
-
if (
|
70 |
-
$response = array(
|
71 |
-
'status' => 'failure',
|
72 |
-
'error' => wp_strip_all_tags($package->get_error_message())
|
73 |
-
);
|
74 |
-
}elseif ($error = $repo->checkDirectory()) {
|
75 |
Â
$response = $this->installFailureResponse($error, $package);
|
76 |
Â
$repo->storeLicense($package, $license);
|
77 |
Â
} elseif (empty($package->content)) { //any unpredictable scenario
|
@@ -181,20 +176,6 @@ class AAM_Backend_Feature_Extension_Manager extends AAM_Backend_Feature_Abstract
|
|
181 |
Â
return $response;
|
182 |
Â
}
|
183 |
Â
|
184 |
-
/**
|
185 |
-
*
|
186 |
-
* @return type
|
187 |
-
*/
|
188 |
-
public function canShowLicense() {
|
189 |
-
$result = true;
|
190 |
-
|
191 |
-
if (AAM_Core_API::capabilityExists('aam_display_license')) {
|
192 |
-
$result = !empty(AAM::getUser()->allcaps['aam_display_license']);
|
193 |
-
}
|
194 |
-
|
195 |
-
return $result;
|
196 |
-
}
|
197 |
-
|
198 |
Â
/**
|
199 |
Â
* Install extension failure response
|
200 |
Â
*
|
62 |
Â
public function install($storedLicense = null) {
|
63 |
Â
$repo = AAM_Extension_Repository::getInstance();
|
64 |
Â
$license = AAM_Core_Request::post('license', $storedLicense);
|
65 |
+
$package = (object) AAM_Core_Request::post('package');
|
66 |
Â
|
67 |
+
$error = $repo->checkDirectory();
|
Â
|
|
68 |
Â
|
69 |
+
if ($error) {
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
70 |
Â
$response = $this->installFailureResponse($error, $package);
|
71 |
Â
$repo->storeLicense($package, $license);
|
72 |
Â
} elseif (empty($package->content)) { //any unpredictable scenario
|
176 |
Â
return $response;
|
177 |
Â
}
|
178 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
179 |
Â
/**
|
180 |
Â
* Install extension failure response
|
181 |
Â
*
|
Application/Backend/Feature/Main/Capability.php
CHANGED
@@ -266,7 +266,7 @@ class AAM_Backend_Feature_Main_Capability extends AAM_Backend_Feature_Abstract {
|
|
266 |
Â
}
|
267 |
Â
}
|
268 |
Â
|
269 |
-
$caps = array_merge(
|
270 |
Â
|
271 |
Â
foreach (array_keys($caps) as $cap) {
|
272 |
Â
if (AAM::api()->isAllowed("Capability:{$cap}:AAM:list") !== false) {
|
266 |
Â
}
|
267 |
Â
}
|
268 |
Â
|
269 |
+
$caps = array_merge(AAM_Core_API::getAllCapabilities(), $policyCaps);
|
270 |
Â
|
271 |
Â
foreach (array_keys($caps) as $cap) {
|
272 |
Â
if (AAM::api()->isAllowed("Capability:{$cap}:AAM:list") !== false) {
|
Application/Backend/Feature/Main/Policy.php
CHANGED
@@ -22,6 +22,46 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract {
|
|
22 |
Â
public function getTable() {
|
23 |
Â
return wp_json_encode($this->retrievePolicies());
|
24 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
25 |
Â
|
26 |
Â
/**
|
27 |
Â
* Save post properties
|
22 |
Â
public function getTable() {
|
23 |
Â
return wp_json_encode($this->retrievePolicies());
|
24 |
Â
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Install policy
|
28 |
+
*
|
29 |
+
* @return string
|
30 |
+
*
|
31 |
+
* @access public
|
32 |
+
* @since v5.7.3
|
33 |
+
*/
|
34 |
+
public function install() {
|
35 |
+
$package = (object) AAM_Core_Request::post('package');
|
36 |
+
|
37 |
+
if (!empty($package->content)) {
|
38 |
+
$json = base64_decode($package->content);
|
39 |
+
|
40 |
+
$result = wp_insert_post(array(
|
41 |
+
'post_author' => get_current_user_id(),
|
42 |
+
'post_content' => $json,
|
43 |
+
'post_title' => $package->title,
|
44 |
+
'post_excerpt' => $package->description,
|
45 |
+
'post_status' => 'publish',
|
46 |
+
'post_type' => 'aam_policy'
|
47 |
+
));
|
48 |
+
|
49 |
+
if (!is_wp_error($result)) {
|
50 |
+
$response = array('status' => 'success');
|
51 |
+
} else {
|
52 |
+
$response = array(
|
53 |
+
'status' => 'failure', 'reason' => $result->get_error_message()
|
54 |
+
);
|
55 |
+
}
|
56 |
+
} else {
|
57 |
+
$response = array(
|
58 |
+
'status' => 'failure',
|
59 |
+
'reason' => __('Failed to fetch policy. Please try again.', AAM_KEY)
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
return wp_json_encode($response);
|
64 |
+
}
|
65 |
Â
|
66 |
Â
/**
|
67 |
Â
* Save post properties
|
Application/Backend/Feature/Subject/Role.php
CHANGED
@@ -183,6 +183,8 @@ class AAM_Backend_Feature_Subject_Role {
|
|
183 |
Â
}
|
184 |
Â
|
185 |
Â
do_action('aam-post-add-role-action', $role, $parent);
|
Â
|
|
Â
|
|
186 |
Â
}
|
187 |
Â
}
|
188 |
Â
|
183 |
Â
}
|
184 |
Â
|
185 |
Â
do_action('aam-post-add-role-action', $role, $parent);
|
186 |
+
} else {
|
187 |
+
$response['reason'] = __("Role with slug [{$role_id}] already exists", AAM_KEY);
|
188 |
Â
}
|
189 |
Â
}
|
190 |
Â
|
Application/Backend/Filter.php
CHANGED
@@ -33,7 +33,9 @@ class AAM_Backend_Filter {
|
|
33 |
Â
*/
|
34 |
Â
protected function __construct() {
|
35 |
Â
//menu filter
|
36 |
-
|
Â
|
|
Â
|
|
37 |
Â
|
38 |
Â
//manager WordPress metaboxes
|
39 |
Â
add_action("in_admin_header", array($this, 'metaboxes'), 999);
|
33 |
Â
*/
|
34 |
Â
protected function __construct() {
|
35 |
Â
//menu filter
|
36 |
+
if (!AAM::isAAM() || !current_user_can('aam_manage_admin_menu')) {
|
37 |
+
add_filter('parent_file', array($this, 'filterMenu'), 999, 1);
|
38 |
+
}
|
39 |
Â
|
40 |
Â
//manager WordPress metaboxes
|
41 |
Â
add_action("in_admin_header", array($this, 'metaboxes'), 999);
|
Application/Backend/Manager.php
CHANGED
@@ -64,7 +64,7 @@ class AAM_Backend_Manager {
|
|
64 |
Â
//screen options & contextual help hooks
|
65 |
Â
add_filter('screen_options_show_screen', array($this, 'screenOptions'));
|
66 |
Â
add_filter('contextual_help', array($this, 'helpOptions'), 10, 3);
|
67 |
-
|
68 |
Â
//manager Admin Menu
|
69 |
Â
if (is_multisite() && is_network_admin()) {
|
70 |
Â
//register AAM in the network admin panel
|
@@ -144,23 +144,16 @@ class AAM_Backend_Manager {
|
|
144 |
Â
* @param type $data
|
145 |
Â
* @return type
|
146 |
Â
*/
|
147 |
-
public function filterPostData($data) {
|
148 |
Â
if (isset($data['post_type']) && ($data['post_type'] === 'aam_policy')) {
|
149 |
-
$
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
150 |
Â
|
151 |
Â
if (empty($data['post_content'])) {
|
152 |
-
$data['post_content'] =
|
153 |
-
{
|
154 |
-
"Version": "1.0.0",
|
155 |
-
"Statement": [
|
156 |
-
{
|
157 |
-
"Effect": "deny",
|
158 |
-
"Resource": [],
|
159 |
-
"Action": []
|
160 |
-
}
|
161 |
-
]
|
162 |
-
}
|
163 |
-
EOT;
|
164 |
Â
}
|
165 |
Â
|
166 |
Â
AAM_Core_API::clearCache();
|
@@ -744,7 +737,7 @@ EOT;
|
|
744 |
Â
public function printJavascript() {
|
745 |
Â
if (AAM::isAAM()) {
|
746 |
Â
wp_enqueue_script('aam-vendor', AAM_MEDIA . '/js/vendor.js');
|
747 |
-
wp_enqueue_script('aam-main', AAM_MEDIA . '/js/aam-5.7.
|
748 |
Â
|
749 |
Â
//add plugin localization
|
750 |
Â
$this->printLocalization('aam-main');
|
@@ -782,16 +775,18 @@ EOT;
|
|
782 |
Â
* @access protected
|
783 |
Â
*/
|
784 |
Â
protected function printLocalization($localKey) {
|
785 |
-
$subject
|
Â
|
|
786 |
Â
|
787 |
Â
$locals = array(
|
788 |
Â
'nonce' => wp_create_nonce('aam_ajax'),
|
789 |
-
'ajaxurl' => admin_url('admin-ajax.php'),
|
790 |
Â
'ui' => AAM_Core_Request::get('aamframe', 'main'),
|
791 |
Â
'url' => array(
|
792 |
-
'site'
|
793 |
-
'editUser'
|
794 |
-
'addUser'
|
Â
|
|
795 |
Â
),
|
796 |
Â
'level' => AAM_Core_API::maxLevel(wp_get_current_user()->allcaps),
|
797 |
Â
'subject' => array(
|
@@ -801,6 +796,11 @@ EOT;
|
|
801 |
Â
'level' => $subject->getMaxLevel(),
|
802 |
Â
'blog' => get_current_blog_id()
|
803 |
Â
),
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
804 |
Â
'translation' => AAM_Backend_View_Localization::get(),
|
805 |
Â
'caps' => array(
|
806 |
Â
'create_roles' => current_user_can('aam_create_roles'),
|
64 |
Â
//screen options & contextual help hooks
|
65 |
Â
add_filter('screen_options_show_screen', array($this, 'screenOptions'));
|
66 |
Â
add_filter('contextual_help', array($this, 'helpOptions'), 10, 3);
|
67 |
+
|
68 |
Â
//manager Admin Menu
|
69 |
Â
if (is_multisite() && is_network_admin()) {
|
70 |
Â
//register AAM in the network admin panel
|
144 |
Â
* @param type $data
|
145 |
Â
* @return type
|
146 |
Â
*/
|
147 |
+
public function filterPostData($data, $postarr) {
|
148 |
Â
if (isset($data['post_type']) && ($data['post_type'] === 'aam_policy')) {
|
149 |
+
$content = trim(filter_input(INPUT_POST, 'aam-policy'));
|
150 |
+
|
151 |
+
if (!empty($content)) { // Edit form was submitted
|
152 |
+
$data['post_content'] = $content;
|
153 |
+
}
|
154 |
Â
|
155 |
Â
if (empty($data['post_content'])) {
|
156 |
+
$data['post_content'] = AAM_Backend_View_Helper::getDefaultPolicy();
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
157 |
Â
}
|
158 |
Â
|
159 |
Â
AAM_Core_API::clearCache();
|
737 |
Â
public function printJavascript() {
|
738 |
Â
if (AAM::isAAM()) {
|
739 |
Â
wp_enqueue_script('aam-vendor', AAM_MEDIA . '/js/vendor.js');
|
740 |
+
wp_enqueue_script('aam-main', AAM_MEDIA . '/js/aam-5.7.3.js');
|
741 |
Â
|
742 |
Â
//add plugin localization
|
743 |
Â
$this->printLocalization('aam-main');
|
775 |
Â
* @access protected
|
776 |
Â
*/
|
777 |
Â
protected function printLocalization($localKey) {
|
778 |
+
$subject = AAM_Backend_Subject::getInstance();
|
779 |
+
$endpoint = getenv('AAM_ENDPOINT');
|
780 |
Â
|
781 |
Â
$locals = array(
|
782 |
Â
'nonce' => wp_create_nonce('aam_ajax'),
|
783 |
+
'ajaxurl' => esc_url(admin_url('admin-ajax.php')),
|
784 |
Â
'ui' => AAM_Core_Request::get('aamframe', 'main'),
|
785 |
Â
'url' => array(
|
786 |
+
'site' => esc_url(admin_url('index.php')),
|
787 |
+
'editUser' => esc_url(admin_url('user-edit.php')),
|
788 |
+
'addUser' => esc_url(admin_url('user-new.php')),
|
789 |
+
'addPolicy' => esc_url(admin_url('post-new.php?post_type=aam_policy'))
|
790 |
Â
),
|
791 |
Â
'level' => AAM_Core_API::maxLevel(wp_get_current_user()->allcaps),
|
792 |
Â
'subject' => array(
|
796 |
Â
'level' => $subject->getMaxLevel(),
|
797 |
Â
'blog' => get_current_blog_id()
|
798 |
Â
),
|
799 |
+
'system' => array(
|
800 |
+
'domain' => wp_parse_url(site_url(), PHP_URL_HOST),
|
801 |
+
'uid' => AAM_Core_API::getOption('aam-uid', null, 'site'),
|
802 |
+
'apiEndpoint' => ($endpoint ? $endpoint : AAM_Core_Server::SERVER_URL)
|
803 |
+
),
|
804 |
Â
'translation' => AAM_Backend_View_Localization::get(),
|
805 |
Â
'caps' => array(
|
806 |
Â
'create_roles' => current_user_can('aam_create_roles'),
|
Application/Backend/Subject.php
CHANGED
@@ -71,6 +71,7 @@ class AAM_Backend_Subject {
|
|
71 |
Â
|
72 |
Â
if (class_exists($classname)) {
|
73 |
Â
$subject = new $classname(stripslashes($id));
|
Â
|
|
74 |
Â
// Load access policy
|
75 |
Â
$subject->getObject('policy');
|
76 |
Â
|
71 |
Â
|
72 |
Â
if (class_exists($classname)) {
|
73 |
Â
$subject = new $classname(stripslashes($id));
|
74 |
+
|
75 |
Â
// Load access policy
|
76 |
Â
$subject->getObject('policy');
|
77 |
Â
|
Application/Backend/View/Helper.php
CHANGED
@@ -50,4 +50,38 @@ class AAM_Backend_View_Helper {
|
|
50 |
Â
$value = '/\\' . ($index % 2 ? ']' : '[') . '/';
|
51 |
Â
}
|
52 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
53 |
Â
}
|
50 |
Â
$value = '/\\' . ($index % 2 ? ']' : '[') . '/';
|
51 |
Â
}
|
52 |
Â
|
53 |
+
/**
|
54 |
+
* Get default Access Policy
|
55 |
+
*
|
56 |
+
* @global string $wp_version
|
57 |
+
*
|
58 |
+
* @return string
|
59 |
+
*
|
60 |
+
* @access public
|
61 |
+
* @static
|
62 |
+
* @since v5.7.3
|
63 |
+
*/
|
64 |
+
public static function getDefaultPolicy() {
|
65 |
+
global $wp_version;
|
66 |
+
|
67 |
+
$aamVersion = AAM_Core_API::version();
|
68 |
+
|
69 |
+
return <<<EOT
|
70 |
+
{
|
71 |
+
"Version": "1.0.0",
|
72 |
+
"Dependency": {
|
73 |
+
"wordpress": ">=$wp_version",
|
74 |
+
"advanced-access-manager": ">=$aamVersion"
|
75 |
+
},
|
76 |
+
"Statement": [
|
77 |
+
{
|
78 |
+
"Effect": "deny",
|
79 |
+
"Resource": [],
|
80 |
+
"Action": []
|
81 |
+
}
|
82 |
+
]
|
83 |
+
}
|
84 |
+
EOT;
|
85 |
+
}
|
86 |
+
|
87 |
Â
}
|
Application/Backend/phtml/extensions.phtml
CHANGED
@@ -39,7 +39,7 @@
|
|
39 |
Â
<tr>
|
40 |
Â
<td width="80%">
|
41 |
Â
<span class='aam-setting-title'><?php echo $product['title'], (!empty($product['tag']) ? '<sup><span class="badge sup">' . $product['tag'] . '</span></sup>' : ''), (!empty($product['version']) ? ' <small class="text-muted">v' . $product['version'] . '</small>' : ''); ?></span>
|
42 |
-
<?php if (!empty($product['license'])
|
43 |
Â
<p class="aam-extension-description">
|
44 |
Â
<?php echo $product['description']; ?>
|
45 |
Â
</p>
|
@@ -52,7 +52,7 @@
|
|
52 |
Â
<a href="#" class="btn btn-sm btn-danger btn-block aam-deactivate-extension" data-product="<?php echo $product['id']; ?>"><i class="icon-attention-circled"></i> <?php echo __('Deactivate', AAM_KEY); ?></a><small><?php echo __('extension is active', AAM_KEY); ?></small>
|
53 |
Â
<?php } ?>
|
54 |
Â
<?php } elseif ($product['status'] == AAM_Extension_Repository::STATUS_UPDATE) { ?>
|
55 |
-
<a href="#" class="btn btn-sm btn-warning btn-block aam-update-extension<?php echo (empty($product['license']) ? ' disabled' : ''); ?>" data-
|
56 |
Â
<?php } elseif ($product['status'] == AAM_Extension_Repository::STATUS_INACTIVE) { ?>
|
57 |
Â
<a href="#" class="btn btn-sm btn-success btn-block aam-activate-extension" data-product="<?php echo $product['id']; ?>"><i class="icon-check"></i> <?php echo __('Activate', AAM_KEY); ?></a><small><?php echo __('extension is inactive', AAM_KEY); ?></small>
|
58 |
Â
<?php } else { ?>
|
39 |
Â
<tr>
|
40 |
Â
<td width="80%">
|
41 |
Â
<span class='aam-setting-title'><?php echo $product['title'], (!empty($product['tag']) ? '<sup><span class="badge sup">' . $product['tag'] . '</span></sup>' : ''), (!empty($product['version']) ? ' <small class="text-muted">v' . $product['version'] . '</small>' : ''); ?></span>
|
42 |
+
<?php if (!empty($product['license'])) { ?><small class="aam-license-key"><b>License:</b> <?php echo $product['license'] . (!empty($product['expire']) ? " (updates expire on {$product['expire']})" : ''); ?></small><?php } ?>
|
43 |
Â
<p class="aam-extension-description">
|
44 |
Â
<?php echo $product['description']; ?>
|
45 |
Â
</p>
|
52 |
Â
<a href="#" class="btn btn-sm btn-danger btn-block aam-deactivate-extension" data-product="<?php echo $product['id']; ?>"><i class="icon-attention-circled"></i> <?php echo __('Deactivate', AAM_KEY); ?></a><small><?php echo __('extension is active', AAM_KEY); ?></small>
|
53 |
Â
<?php } ?>
|
54 |
Â
<?php } elseif ($product['status'] == AAM_Extension_Repository::STATUS_UPDATE) { ?>
|
55 |
+
<a href="#" class="btn btn-sm btn-warning btn-block aam-update-extension<?php echo (empty($product['license']) ? ' disabled' : ''); ?>" data-license="<?php echo (!empty($product['license']) ? $product['license'] : ''); ?>"><i class="icon-arrows-cw"></i> <?php echo __('Update', AAM_KEY); ?></a><?php echo (empty($product['license']) ? '<small>' . __('license is missing', AAM_KEY) . '</small>' : ''); ?>
|
56 |
Â
<?php } elseif ($product['status'] == AAM_Extension_Repository::STATUS_INACTIVE) { ?>
|
57 |
Â
<a href="#" class="btn btn-sm btn-success btn-block aam-activate-extension" data-product="<?php echo $product['id']; ?>"><i class="icon-check"></i> <?php echo __('Activate', AAM_KEY); ?></a><small><?php echo __('extension is inactive', AAM_KEY); ?></small>
|
58 |
Â
<?php } else { ?>
|
Application/Backend/phtml/metabox/policy-metabox.phtml
CHANGED
@@ -370,32 +370,17 @@
|
|
370 |
Â
|
371 |
Â
<?php
|
372 |
Â
if (!empty($post->post_content)) {
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
$error = AAM_Backend_View_Helper::preparePhrase(
|
377 |
-
esc_js('[' . json_last_error_msg() . ']: ' . __('Access & Security Policy is invalid and is ignored by AAM.', AAM_KEY)),
|
378 |
-
'b'
|
379 |
-
);
|
380 |
-
}
|
381 |
Â
} else {
|
382 |
-
$post->post_content =
|
383 |
-
|
384 |
-
"Version": "1.0.0",
|
385 |
-
"Statement": [
|
386 |
-
{
|
387 |
-
"Effect": "deny",
|
388 |
-
"Resource": [],
|
389 |
-
"Action": []
|
390 |
-
}
|
391 |
-
]
|
392 |
-
}
|
393 |
-
EOT;
|
394 |
Â
}
|
395 |
Â
?>
|
396 |
Â
|
397 |
-
<div class="aam-alert-danger<?php echo (empty($
|
398 |
-
<?php echo (
|
399 |
Â
</div>
|
400 |
Â
|
401 |
Â
<textarea id="aam-policy-editor" name="aam-policy" class="policy-editor" rows="10"><?php echo $post->post_content; ?></textarea>
|
370 |
Â
|
371 |
Â
<?php
|
372 |
Â
if (!empty($post->post_content)) {
|
373 |
+
// Validate the policy
|
374 |
+
$validator = new AAM_Core_Policy_Validator(htmlspecialchars_decode($post->post_content));
|
375 |
+
$errors = $validator->validate();
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
376 |
Â
} else {
|
377 |
+
$post->post_content = AAM_Backend_View_Helper::getDefaultPolicy();
|
378 |
+
$errors = array();
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
379 |
Â
}
|
380 |
Â
?>
|
381 |
Â
|
382 |
+
<div class="aam-alert-danger<?php echo (empty($errors) ? ' hidden' : ''); ?>" id="policy-parsing-error">
|
383 |
+
<?php echo implode('<br/>', $errors); ?>
|
384 |
Â
</div>
|
385 |
Â
|
386 |
Â
<textarea id="aam-policy-editor" name="aam-policy" class="policy-editor" rows="10"><?php echo $post->post_content; ?></textarea>
|
Application/Core/Compatibility.php
CHANGED
@@ -35,6 +35,27 @@ class AAM_Core_Compatibility {
|
|
35 |
Â
return $key;
|
36 |
Â
}
|
37 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
38 |
Â
/**
|
39 |
Â
* Converting metabox options from 2 dimensional to 1
|
40 |
Â
*
|
35 |
Â
return $key;
|
36 |
Â
}
|
37 |
Â
|
38 |
+
/**
|
39 |
+
*
|
40 |
+
* @param type $list
|
41 |
+
* @return type
|
42 |
+
* @since v5.7.3
|
43 |
+
*
|
44 |
+
* @todo Remove Jan 2020
|
45 |
+
*/
|
46 |
+
public static function preparePolicyList($list) {
|
47 |
+
if (!is_null($list)) {
|
48 |
+
if (empty($list['Statements'])) {
|
49 |
+
$list = array(
|
50 |
+
'Statements' => $list,
|
51 |
+
'Features' => array()
|
52 |
+
);
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
return $list;
|
57 |
+
}
|
58 |
+
|
59 |
Â
/**
|
60 |
Â
* Converting metabox options from 2 dimensional to 1
|
61 |
Â
*
|
Application/Core/Config.php
CHANGED
@@ -147,6 +147,7 @@ class AAM_Core_Config {
|
|
147 |
Â
*/
|
148 |
Â
protected static function readConfigPress($param, $default = null) {
|
149 |
Â
$config = AAM_Core_ConfigPress::get('aam.' . $param, $default);
|
Â
|
|
150 |
Â
if (is_array($config) && isset($config['userFunc'])) {
|
151 |
Â
if (is_callable($config['userFunc'])) {
|
152 |
Â
$response = call_user_func($config['userFunc']);
|
147 |
Â
*/
|
148 |
Â
protected static function readConfigPress($param, $default = null) {
|
149 |
Â
$config = AAM_Core_ConfigPress::get('aam.' . $param, $default);
|
150 |
+
|
151 |
Â
if (is_array($config) && isset($config['userFunc'])) {
|
152 |
Â
if (is_callable($config['userFunc'])) {
|
153 |
Â
$response = call_user_func($config['userFunc']);
|
Application/Core/Gateway.php
CHANGED
@@ -109,6 +109,23 @@ final class AAM_Core_Gateway {
|
|
109 |
Â
return $policy->isAllowed($resource, $action);
|
110 |
Â
}
|
111 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
112 |
Â
/**
|
113 |
Â
* Get policy manager
|
114 |
Â
*
|
109 |
Â
return $policy->isAllowed($resource, $action);
|
110 |
Â
}
|
111 |
Â
|
112 |
+
/**
|
113 |
+
* Check if feature is enabled
|
114 |
+
*
|
115 |
+
* @param string $feature
|
116 |
+
* @param string $plugin
|
117 |
+
*
|
118 |
+
* @return boolean|null
|
119 |
+
*
|
120 |
+
* @access public
|
121 |
+
* @since v5.7.3
|
122 |
+
*/
|
123 |
+
public function isEnabled($feature, $plugin = 'advanced-access-manager') {
|
124 |
+
$policy = AAM::api()->getUser()->getObject('policy');
|
125 |
+
|
126 |
+
return $policy->isEnabled($feature, $plugin);
|
127 |
+
}
|
128 |
+
|
129 |
Â
/**
|
130 |
Â
* Get policy manager
|
131 |
Â
*
|
Application/Core/Object/Policy.php
CHANGED
@@ -27,6 +27,18 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
27 |
Â
*/
|
28 |
Â
protected static $resources = array();
|
29 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
30 |
Â
/**
|
31 |
Â
* Constructor
|
32 |
Â
*
|
@@ -39,7 +51,7 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
39 |
Â
public function __construct(AAM_Core_Subject $subject) {
|
40 |
Â
parent::__construct($subject);
|
41 |
Â
|
42 |
-
$this->initialize();
|
43 |
Â
}
|
44 |
Â
|
45 |
Â
/**
|
@@ -77,27 +89,43 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
77 |
Â
*/
|
78 |
Â
public function load($subjectId, $policies) {
|
79 |
Â
$resources = array();
|
Â
|
|
Â
|
|
Â
|
|
80 |
Â
|
81 |
-
|
Â
|
|
82 |
Â
if (isset($statement['Resource']) && $this->applicable($statement)) {
|
83 |
Â
$this->evaluateStatement($statement, $resources);
|
84 |
Â
}
|
85 |
Â
}
|
86 |
-
|
87 |
Â
self::$resources[$subjectId] = $resources;
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
88 |
Â
}
|
89 |
Â
|
90 |
Â
/**
|
91 |
Â
*
|
92 |
Â
* @return type
|
93 |
Â
*/
|
94 |
-
protected function
|
95 |
-
$cache
|
96 |
-
$
|
Â
|
|
Â
|
|
97 |
Â
|
98 |
-
|
99 |
-
|
100 |
-
|
Â
|
|
Â
|
|
101 |
Â
|
102 |
Â
foreach($policies as $id => $effect) {
|
103 |
Â
if ($effect) {
|
@@ -106,19 +134,74 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
106 |
Â
if (is_a($policy, 'WP_Post')) {
|
107 |
Â
$obj = json_decode($policy->post_content, true);
|
108 |
Â
if (json_last_error() === JSON_ERROR_NONE) {
|
109 |
-
$
|
110 |
-
$
|
Â
|
|
Â
|
|
Â
|
|
111 |
Â
);
|
112 |
Â
}
|
113 |
Â
}
|
114 |
Â
}
|
115 |
Â
}
|
116 |
-
$cache->add('policy', $subjectId, $
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
117 |
Â
}
|
118 |
Â
|
119 |
Â
return $statements;
|
120 |
Â
}
|
121 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
122 |
Â
/**
|
123 |
Â
*
|
124 |
Â
* @param type $statement
|
@@ -146,6 +229,23 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
146 |
Â
}
|
147 |
Â
}
|
148 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
149 |
Â
/**
|
150 |
Â
*
|
151 |
Â
* @param type $resources
|
@@ -535,34 +635,6 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
535 |
Â
return date($prop);
|
536 |
Â
}
|
537 |
Â
|
538 |
-
/**
|
539 |
-
*
|
540 |
-
* @param type $policy
|
541 |
-
* @return type
|
542 |
-
*/
|
543 |
-
protected function extractStatements($policy) {
|
544 |
-
$statements = array();
|
545 |
-
|
546 |
-
if (isset($policy['Statement'])) {
|
547 |
-
if (is_array($policy['Statement'])) {
|
548 |
-
$statements = $policy['Statement'];
|
549 |
-
} else {
|
550 |
-
$statements = array($policy['Statement']);
|
551 |
-
}
|
552 |
-
}
|
553 |
-
|
554 |
-
// normalize each statement
|
555 |
-
foreach(array('Action', 'Condition') as $prop) {
|
556 |
-
foreach($statements as $i => $statement) {
|
557 |
-
if (isset($statement[$prop])) {
|
558 |
-
$statements[$i][$prop] = (array) $statement[$prop];
|
559 |
-
}
|
560 |
-
}
|
561 |
-
}
|
562 |
-
|
563 |
-
return $statements;
|
564 |
-
}
|
565 |
-
|
566 |
Â
/**
|
567 |
Â
*
|
568 |
Â
* @param type $left
|
@@ -627,6 +699,25 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
627 |
Â
return $allowed;
|
628 |
Â
}
|
629 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
630 |
Â
/**
|
631 |
Â
*
|
632 |
Â
* @param type $id
|
@@ -679,4 +770,33 @@ class AAM_Core_Object_Policy extends AAM_Core_Object {
|
|
679 |
Â
|
680 |
Â
return $response;
|
681 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
682 |
Â
}
|
27 |
Â
*/
|
28 |
Â
protected static $resources = array();
|
29 |
Â
|
30 |
+
/**
|
31 |
+
* Feature tree
|
32 |
+
*
|
33 |
+
* Shared features tree across all the policy instances
|
34 |
+
*
|
35 |
+
* @var array
|
36 |
+
*
|
37 |
+
* @access protected
|
38 |
+
* @static
|
39 |
+
*/
|
40 |
+
protected static $features = array();
|
41 |
+
|
42 |
Â
/**
|
43 |
Â
* Constructor
|
44 |
Â
*
|
51 |
Â
public function __construct(AAM_Core_Subject $subject) {
|
52 |
Â
parent::__construct($subject);
|
53 |
Â
|
54 |
+
$this->initialize();
|
55 |
Â
}
|
56 |
Â
|
57 |
Â
/**
|
89 |
Â
*/
|
90 |
Â
public function load($subjectId, $policies) {
|
91 |
Â
$resources = array();
|
92 |
+
$features = array();
|
93 |
+
|
94 |
+
$list = $this->parsePolicy($subjectId, $policies);
|
95 |
Â
|
96 |
+
// Evaluate all Statements first
|
97 |
+
foreach($list['Statements'] as $statement) {
|
98 |
Â
if (isset($statement['Resource']) && $this->applicable($statement)) {
|
99 |
Â
$this->evaluateStatement($statement, $resources);
|
100 |
Â
}
|
101 |
Â
}
|
Â
|
|
102 |
Â
self::$resources[$subjectId] = $resources;
|
103 |
+
|
104 |
+
// Evaluate all Features then
|
105 |
+
foreach($list['Features'] as $feature) {
|
106 |
+
if ($this->applicable($feature)) {
|
107 |
+
$this->evaluateFeature($feature, $features);
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
self::$features[$subjectId] = $features;
|
112 |
Â
}
|
113 |
Â
|
114 |
Â
/**
|
115 |
Â
*
|
116 |
Â
* @return type
|
117 |
Â
*/
|
118 |
+
protected function parsePolicy($subjectId, $policies) {
|
119 |
+
$cache = AAM::api()->getUser()->getObject('cache');
|
120 |
+
$list = AAM_Core_Compatibility::preparePolicyList(
|
121 |
+
$cache->get('policy', $subjectId, null)
|
122 |
+
);
|
123 |
Â
|
124 |
+
if (is_null($list)) {
|
125 |
+
$list = array(
|
126 |
+
'Statements' => array(),
|
127 |
+
'Features' => array()
|
128 |
+
);
|
129 |
Â
|
130 |
Â
foreach($policies as $id => $effect) {
|
131 |
Â
if ($effect) {
|
134 |
Â
if (is_a($policy, 'WP_Post')) {
|
135 |
Â
$obj = json_decode($policy->post_content, true);
|
136 |
Â
if (json_last_error() === JSON_ERROR_NONE) {
|
137 |
+
$list['Statements'] = array_merge(
|
138 |
+
$list['Statements'], $this->extractStatements($obj)
|
139 |
+
);
|
140 |
+
$list['Features'] = array_merge(
|
141 |
+
$list['Features'], $this->extractFeatures($obj)
|
142 |
Â
);
|
143 |
Â
}
|
144 |
Â
}
|
145 |
Â
}
|
146 |
Â
}
|
147 |
+
$cache->add('policy', $subjectId, $list);
|
148 |
+
}
|
149 |
+
|
150 |
+
return $list;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
*
|
155 |
+
* @param type $policy
|
156 |
+
* @return type
|
157 |
+
*/
|
158 |
+
protected function extractStatements($policy) {
|
159 |
+
$statements = array();
|
160 |
+
|
161 |
+
if (isset($policy['Statement'])) {
|
162 |
+
if (is_array($policy['Statement'])) {
|
163 |
+
$statements = $policy['Statement'];
|
164 |
+
} else {
|
165 |
+
$statements = array($policy['Statement']);
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
// normalize each statement
|
170 |
+
foreach(array('Action', 'Condition') as $prop) {
|
171 |
+
foreach($statements as $i => $statement) {
|
172 |
+
if (isset($statement[$prop])) {
|
173 |
+
$statements[$i][$prop] = (array) $statement[$prop];
|
174 |
+
}
|
175 |
+
}
|
176 |
Â
}
|
177 |
Â
|
178 |
Â
return $statements;
|
179 |
Â
}
|
180 |
Â
|
181 |
+
/**
|
182 |
+
* Extract list of policy features
|
183 |
+
*
|
184 |
+
* @param array $policy
|
185 |
+
*
|
186 |
+
* @return array
|
187 |
+
*
|
188 |
+
* @access protected
|
189 |
+
* @since v5.7.3
|
190 |
+
*/
|
191 |
+
protected function extractFeatures($policy) {
|
192 |
+
$features = array();
|
193 |
+
|
194 |
+
if (isset($policy['Feature'])) {
|
195 |
+
if (is_array($policy['Feature'])) {
|
196 |
+
$features = $policy['Feature'];
|
197 |
+
} else {
|
198 |
+
$features = array($policy['Feature']);
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
return $features;
|
203 |
+
}
|
204 |
+
|
205 |
Â
/**
|
206 |
Â
*
|
207 |
Â
* @param type $statement
|
229 |
Â
}
|
230 |
Â
}
|
231 |
Â
|
232 |
+
/**
|
233 |
+
*
|
234 |
+
* @param type $feature
|
235 |
+
* @param type $features
|
236 |
+
*/
|
237 |
+
protected function evaluateFeature($feature, &$features) {
|
238 |
+
$id = strtolower("{$feature['Plugin']}:{$feature['Feature']}");
|
239 |
+
|
240 |
+
// Add new statement
|
241 |
+
if (!isset($features[$id])) {
|
242 |
+
$features[$id] = $feature;
|
243 |
+
// Override feature unless the first one is marked as Enforced
|
244 |
+
} elseif (empty($features[$id]['Enforce'])) {
|
245 |
+
$features[$id] = $feature;
|
246 |
+
}
|
247 |
+
}
|
248 |
+
|
249 |
Â
/**
|
250 |
Â
*
|
251 |
Â
* @param type $resources
|
635 |
Â
return date($prop);
|
636 |
Â
}
|
637 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
638 |
Â
/**
|
639 |
Â
*
|
640 |
Â
* @param type $left
|
699 |
Â
return $allowed;
|
700 |
Â
}
|
701 |
Â
|
702 |
+
/**
|
703 |
+
*
|
704 |
+
* @param type $feature
|
705 |
+
* @param type $plugin
|
706 |
+
* @return type
|
707 |
+
*/
|
708 |
+
public function isEnabled($feature, $plugin) {
|
709 |
+
$enabled = null;
|
710 |
+
|
711 |
+
$id = strtolower("{$plugin}:{$feature}");
|
712 |
+
$res = $this->getFeatures();
|
713 |
+
|
714 |
+
if (isset($res[$id])) {
|
715 |
+
$enabled = in_array($res[$id]['Effect'], array('allow', 'enable'), true);
|
716 |
+
}
|
717 |
+
|
718 |
+
return $enabled;
|
719 |
+
}
|
720 |
+
|
721 |
Â
/**
|
722 |
Â
*
|
723 |
Â
* @param type $id
|
770 |
Â
|
771 |
Â
return $response;
|
772 |
Â
}
|
773 |
+
|
774 |
+
/**
|
775 |
+
*
|
776 |
+
* @return type
|
777 |
+
*/
|
778 |
+
public function getFeatures(AAM_Core_Subject $subject = null) {
|
779 |
+
$response = array();
|
780 |
+
|
781 |
+
if (is_null($subject)) {
|
782 |
+
if (!isset(self::$features['__combined'])) {
|
783 |
+
foreach(self::$features as $features) {
|
784 |
+
$response = array_merge($features, $response);
|
785 |
+
}
|
786 |
+
self::$features['__combined'] = $response;
|
787 |
+
} else {
|
788 |
+
$response = self::$features['__combined'];
|
789 |
+
}
|
790 |
+
} else {
|
791 |
+
$subjectId = $subject->getUID();
|
792 |
+
$subjectId .= ($subject->getId() ? ".{$subject->getId()}" : '');
|
793 |
+
|
794 |
+
if (isset(self::$features[$subjectId])) {
|
795 |
+
$response = self::$features[$subjectId];
|
796 |
+
}
|
797 |
+
}
|
798 |
+
|
799 |
+
return $response;
|
800 |
+
}
|
801 |
+
|
802 |
Â
}
|
Application/Core/Object/Post.php
CHANGED
@@ -99,6 +99,7 @@ class AAM_Core_Object_Post extends AAM_Core_Object {
|
|
99 |
Â
"/^post:{$post->post_type}:({$post->post_name}|{$post->ID}):/",
|
100 |
Â
$subject
|
101 |
Â
);
|
Â
|
|
102 |
Â
|
103 |
Â
foreach($stms as $key => $stm) {
|
104 |
Â
// TODO: Prepare better conversion from policy Action to AAM
|
99 |
Â
"/^post:{$post->post_type}:({$post->post_name}|{$post->ID}):/",
|
100 |
Â
$subject
|
101 |
Â
);
|
102 |
+
$option = array();
|
103 |
Â
|
104 |
Â
foreach($stms as $key => $stm) {
|
105 |
Â
// TODO: Prepare better conversion from policy Action to AAM
|
Application/Core/Policy/Validator.php
ADDED
@@ -0,0 +1,181 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* ======================================================================
|
5 |
+
* LICENSE: This file is subject to the terms and conditions defined in *
|
6 |
+
* file 'license.txt', which is part of this source code package. *
|
7 |
+
* ======================================================================
|
8 |
+
*/
|
9 |
+
|
10 |
+
use Composer\Semver\Semver;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* AAM core policy validator
|
14 |
+
*
|
15 |
+
* @package AAM
|
16 |
+
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
17 |
+
* @since AAM v5.7.3
|
18 |
+
*/
|
19 |
+
class AAM_Core_Policy_Validator {
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Raw policy text
|
23 |
+
*
|
24 |
+
* @var string
|
25 |
+
*
|
26 |
+
* @access protected
|
27 |
+
*/
|
28 |
+
protected $policy;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Parsed JSON document
|
32 |
+
*
|
33 |
+
* @var array
|
34 |
+
*
|
35 |
+
* @access protected
|
36 |
+
*/
|
37 |
+
protected $json;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Collection of errors
|
41 |
+
*
|
42 |
+
* @var array
|
43 |
+
*
|
44 |
+
* @access protected
|
45 |
+
*/
|
46 |
+
protected $errors = array();
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Constructor
|
50 |
+
*
|
51 |
+
* @param string $policy
|
52 |
+
*
|
53 |
+
* @access public
|
54 |
+
*/
|
55 |
+
public function __construct($policy) {
|
56 |
+
$this->policy = trim($policy);
|
57 |
+
$this->json = json_decode($policy, true);
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Validate the policy
|
62 |
+
*
|
63 |
+
* @return array
|
64 |
+
*
|
65 |
+
* @access public
|
66 |
+
*/
|
67 |
+
public function validate() {
|
68 |
+
$steps = array(
|
69 |
+
'isJSON', // #1. Check if policy is valid JSON
|
70 |
+
'isNotEmpty', // #2. Check if policy is not empty
|
71 |
+
'isValidDepenency', // #3. Check if all dependencies are defined properly
|
72 |
+
);
|
73 |
+
|
74 |
+
foreach($steps as $step) {
|
75 |
+
if (call_user_func(array($this, $step)) === false) {
|
76 |
+
break;
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
return $this->errors;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Check if policy is valid JSON
|
85 |
+
*
|
86 |
+
* @return boolean
|
87 |
+
*
|
88 |
+
* @access public
|
89 |
+
*/
|
90 |
+
public function isJSON() {
|
91 |
+
$result = is_array($this->json);
|
92 |
+
|
93 |
+
if ($result === false) {
|
94 |
+
$this->errors[] = __('The policy is not valid JSON object', AAM_KEY);
|
95 |
+
}
|
96 |
+
|
97 |
+
return $result;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Check if policy is empty
|
102 |
+
*
|
103 |
+
* @return boolean
|
104 |
+
*
|
105 |
+
* @access public
|
106 |
+
*/
|
107 |
+
public function isNotEmpty() {
|
108 |
+
$result = !empty($this->policy) && !empty($this->json);
|
109 |
+
|
110 |
+
if ($result === false) {
|
111 |
+
$this->errors[] = __('The policy document is empty', AAM_KEY);
|
112 |
+
}
|
113 |
+
|
114 |
+
return $result;
|
115 |
+
}
|
116 |
+
|
117 |
+
public function isValidDepenency() {
|
118 |
+
if (!empty($this->json['Dependency'])) {
|
119 |
+
foreach($this->json['Dependency'] as $app => $constraints) {
|
120 |
+
try {
|
121 |
+
$satifies = Semver::satisfies(
|
122 |
+
$this->getAppVersion(strtolower($app)), $constraints
|
123 |
+
);
|
124 |
+
if ($satifies === false) {
|
125 |
+
throw new Exception(
|
126 |
+
AAM_Backend_View_Helper::preparePhrase(
|
127 |
+
"The dependency [{$app}] does not satisfy version requirement by the policy",
|
128 |
+
'b'
|
129 |
+
)
|
130 |
+
);
|
131 |
+
}
|
132 |
+
} catch (Exception $e) {
|
133 |
+
$this->errors[] = $e->getMessage();
|
134 |
+
}
|
135 |
+
}
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
protected function getAppVersion($app) {
|
140 |
+
global $wp_version;
|
141 |
+
|
142 |
+
if ($app === 'wordpress') {
|
143 |
+
$version = $wp_version;
|
144 |
+
} else {
|
145 |
+
$version = $this->getPluginVersion($app);
|
146 |
+
}
|
147 |
+
|
148 |
+
return $version;
|
149 |
+
}
|
150 |
+
|
151 |
+
protected function getPluginVersion($slug) {
|
152 |
+
static $plugins = null;
|
153 |
+
|
154 |
+
if (is_null($plugins)) {
|
155 |
+
if (file_exists(ABSPATH . 'wp-admin/includes/plugin.php')) {
|
156 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
157 |
+
}
|
158 |
+
|
159 |
+
$plugins = get_plugins();
|
160 |
+
}
|
161 |
+
|
162 |
+
$version = null;
|
163 |
+
|
164 |
+
foreach($plugins as $plugin => $data) {
|
165 |
+
if (stripos($plugin, $slug . '/') === 0) {
|
166 |
+
$version = $data['Version'];
|
167 |
+
}
|
168 |
+
}
|
169 |
+
|
170 |
+
if (is_null($version)) {
|
171 |
+
throw new Exception(
|
172 |
+
AAM_Backend_View_Helper::preparePhrase(
|
173 |
+
"The plugin [{$slug}] is required by the policy",
|
174 |
+
'b'
|
175 |
+
)
|
176 |
+
);
|
177 |
+
}
|
178 |
+
|
179 |
+
return $version;
|
180 |
+
}
|
181 |
+
}
|
Application/Core/Server.php
CHANGED
@@ -22,26 +22,6 @@ final class AAM_Core_Server {
|
|
22 |
Â
*/
|
23 |
Â
const SERVER_URL = 'https://aamplugin.com/api/v1';
|
24 |
Â
|
25 |
-
/**
|
26 |
-
* Fetch the extension list
|
27 |
-
*
|
28 |
-
* Fetch the extension list with versions from the server
|
29 |
-
*
|
30 |
-
* @return array
|
31 |
-
*
|
32 |
-
* @access public
|
33 |
-
*/
|
34 |
-
public static function register() {
|
35 |
-
//prepare check params
|
36 |
-
$params = array(
|
37 |
-
'domain' => wp_parse_url(site_url(), PHP_URL_HOST),
|
38 |
-
'version' => AAM_Core_API::version(),
|
39 |
-
'uid' => AAM_Core_API::getOption('aam-uid', null, 'site')
|
40 |
-
);
|
41 |
-
|
42 |
-
self::send('/register', $params);
|
43 |
-
}
|
44 |
-
|
45 |
Â
/**
|
46 |
Â
* Fetch the extension list
|
47 |
Â
*
|
@@ -75,40 +55,6 @@ final class AAM_Core_Server {
|
|
75 |
Â
return $result;
|
76 |
Â
}
|
77 |
Â
|
78 |
-
/**
|
79 |
-
* Download the extension
|
80 |
-
*
|
81 |
-
* @param string $license
|
82 |
-
*
|
83 |
-
* @return base64|WP_Error
|
84 |
-
*
|
85 |
-
* @access public
|
86 |
-
*/
|
87 |
-
public static function download($license) {
|
88 |
-
$domain = wp_parse_url(site_url(), PHP_URL_HOST);
|
89 |
-
|
90 |
-
$response = self::send(
|
91 |
-
'/download',
|
92 |
-
array(
|
93 |
-
'license' => $license,
|
94 |
-
'domain' => $domain,
|
95 |
-
'uid' => AAM_Core_API::getOption('aam-uid', null, 'site')
|
96 |
-
)
|
97 |
-
);
|
98 |
-
|
99 |
-
if (!is_wp_error($response)) {
|
100 |
-
if ($response->error === true) {
|
101 |
-
$result = new WP_Error($response->code, $response->message);
|
102 |
-
} else {
|
103 |
-
$result = $response;
|
104 |
-
}
|
105 |
-
} else {
|
106 |
-
$result = $response;
|
107 |
-
}
|
108 |
-
|
109 |
-
return $result;
|
110 |
-
}
|
111 |
-
|
112 |
Â
/**
|
113 |
Â
* Send request
|
114 |
Â
*
|
22 |
Â
*/
|
23 |
Â
const SERVER_URL = 'https://aamplugin.com/api/v1';
|
24 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
25 |
Â
/**
|
26 |
Â
* Fetch the extension list
|
27 |
Â
*
|
55 |
Â
return $result;
|
56 |
Â
}
|
57 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
58 |
Â
/**
|
59 |
Â
* Send request
|
60 |
Â
*
|
Application/Core/Subject/User.php
CHANGED
@@ -29,12 +29,33 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
|
|
29 |
Â
*/
|
30 |
Â
const AAM_CAPKEY = 'aam_capability';
|
31 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
32 |
Â
/**
|
33 |
Â
*
|
34 |
Â
* @var type
|
35 |
Â
*/
|
36 |
Â
protected $parent = null;
|
37 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
38 |
Â
/**
|
39 |
Â
*
|
40 |
Â
*/
|
@@ -199,17 +220,8 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
|
|
199 |
Â
}
|
200 |
Â
|
201 |
Â
//reset the user capabilities
|
202 |
-
$subject->allcaps = array_merge($subject->allcaps, $policyCaps);
|
203 |
-
$subject->caps = array_merge($subject->caps, $policyCaps);
|
204 |
-
|
205 |
-
// Retrieve user capabilities set with AAM
|
206 |
-
$userCaps = get_user_option(self::AAM_CAPKEY, $this->getId());
|
207 |
-
|
208 |
-
if (is_array($userCaps)) {
|
209 |
-
//reset the user capabilities
|
210 |
-
$subject->allcaps = array_merge($subject->allcaps, $userCaps);
|
211 |
-
$subject->caps = array_merge($subject->caps, $userCaps);
|
212 |
-
}
|
213 |
Â
}
|
214 |
Â
|
215 |
Â
/**
|
@@ -233,7 +245,24 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
|
|
233 |
Â
* @access public
|
234 |
Â
*/
|
235 |
Â
public function hasCapability($capability) {
|
236 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
237 |
Â
}
|
238 |
Â
|
239 |
Â
/**
|
29 |
Â
*/
|
30 |
Â
const AAM_CAPKEY = 'aam_capability';
|
31 |
Â
|
32 |
+
/**
|
33 |
+
*
|
34 |
+
* @var type
|
35 |
+
*/
|
36 |
+
protected $aamCaps = array();
|
37 |
+
|
38 |
Â
/**
|
39 |
Â
*
|
40 |
Â
* @var type
|
41 |
Â
*/
|
42 |
Â
protected $parent = null;
|
43 |
Â
|
44 |
+
/**
|
45 |
+
*
|
46 |
+
* @param type $id
|
47 |
+
*/
|
48 |
+
public function __construct($id = '') {
|
49 |
+
parent::__construct($id);
|
50 |
+
|
51 |
+
// Retrieve user capabilities set with AAM
|
52 |
+
$aamCaps = get_user_option(self::AAM_CAPKEY, $id);
|
53 |
+
|
54 |
+
if (is_array($aamCaps)) {
|
55 |
+
$this->aamCaps = $aamCaps;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
Â
/**
|
60 |
Â
*
|
61 |
Â
*/
|
220 |
Â
}
|
221 |
Â
|
222 |
Â
//reset the user capabilities
|
223 |
+
$subject->allcaps = array_merge($subject->allcaps, $policyCaps, $this->aamCaps);
|
224 |
+
$subject->caps = array_merge($subject->caps, $policyCaps, $this->aamCaps);
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
225 |
Â
}
|
226 |
Â
|
227 |
Â
/**
|
245 |
Â
* @access public
|
246 |
Â
*/
|
247 |
Â
public function hasCapability($capability) {
|
248 |
+
// Priority #1: capability that has been explicitely set
|
249 |
+
if (isset($this->aamCaps[$capability])) {
|
250 |
+
$result = !empty($this->aamCaps[$capability]);
|
251 |
+
} else {
|
252 |
+
// Priority #2: capability that has been defined in policy
|
253 |
+
// Override by policy if is set
|
254 |
+
$stm = AAM::api()->getPolicyManager()->find(
|
255 |
+
"/^Capability:{$capability}$/i", $this
|
256 |
+
);
|
257 |
+
if (!empty($stm)) {
|
258 |
+
$val = end($stm);
|
259 |
+
$result = ($val['Effect'] === 'allow' ? 1 : 0);
|
260 |
+
} else {
|
261 |
+
$result = user_can($this->getSubject(), $capability);
|
262 |
+
}
|
263 |
+
}
|
264 |
+
|
265 |
+
return $result;
|
266 |
Â
}
|
267 |
Â
|
268 |
Â
/**
|
Application/Extension/List.php
CHANGED
@@ -22,7 +22,7 @@ class AAM_Extension_List {
|
|
22 |
Â
'description' => 'Get the complete list of all premium AAM extensions in one package and all future premium extensions already included for now additional cost.',
|
23 |
Â
'url' => 'https://aamplugin.com/complete-package',
|
24 |
Â
'version' => (defined('AAM_COMPLETE_PACKAGE') ? constant('AAM_COMPLETE_PACKAGE') : null),
|
25 |
-
'latest' => '3.8.
|
26 |
Â
),
|
27 |
Â
'AAM_PLUS_PACKAGE' => array(
|
28 |
Â
'title' => 'Plus Package',
|
@@ -31,7 +31,7 @@ class AAM_Extension_List {
|
|
31 |
Â
'description' => 'Manage access to your WordPress website posts, pages, media, custom post types, categories and hierarchical taxonomies for any role, individual user, visitors or even define default access for everybody; and do this separately for frontend, backend or API levels. As the bonus, define more granular access to how comments can be managed on the backend by other users.',
|
32 |
Â
'url' => 'https://aamplugin.com/extension/plus-package',
|
33 |
Â
'version' => (defined('AAM_PLUS_PACKAGE') ? constant('AAM_PLUS_PACKAGE') : null),
|
34 |
-
'latest' => '3.
|
35 |
Â
),
|
36 |
Â
'AAM_IP_CHECK' => array(
|
37 |
Â
'title' => 'IP Check',
|
22 |
Â
'description' => 'Get the complete list of all premium AAM extensions in one package and all future premium extensions already included for now additional cost.',
|
23 |
Â
'url' => 'https://aamplugin.com/complete-package',
|
24 |
Â
'version' => (defined('AAM_COMPLETE_PACKAGE') ? constant('AAM_COMPLETE_PACKAGE') : null),
|
25 |
+
'latest' => '3.8.12'
|
26 |
Â
),
|
27 |
Â
'AAM_PLUS_PACKAGE' => array(
|
28 |
Â
'title' => 'Plus Package',
|
31 |
Â
'description' => 'Manage access to your WordPress website posts, pages, media, custom post types, categories and hierarchical taxonomies for any role, individual user, visitors or even define default access for everybody; and do this separately for frontend, backend or API levels. As the bonus, define more granular access to how comments can be managed on the backend by other users.',
|
32 |
Â
'url' => 'https://aamplugin.com/extension/plus-package',
|
33 |
Â
'version' => (defined('AAM_PLUS_PACKAGE') ? constant('AAM_PLUS_PACKAGE') : null),
|
34 |
+
'latest' => '3.9'
|
35 |
Â
),
|
36 |
Â
'AAM_IP_CHECK' => array(
|
37 |
Â
'title' => 'IP Check',
|
Application/Extension/Repository.php
CHANGED
@@ -212,7 +212,7 @@ class AAM_Extension_Repository {
|
|
212 |
Â
$response[] = array(
|
213 |
Â
'license' => $data['license'],
|
214 |
Â
'extension' => $extensions[$key]['title'],
|
215 |
-
'expires' => (
|
216 |
Â
);
|
217 |
Â
} else {
|
218 |
Â
$response[] = $data['license'];
|
@@ -294,7 +294,7 @@ class AAM_Extension_Repository {
|
|
294 |
Â
if (empty($item['license'])) {
|
295 |
Â
if (!empty($index[$id]['license'])) {
|
296 |
Â
$item['license'] = $index[$id]['license'];
|
297 |
-
$item['expire'] = (
|
298 |
Â
} else {
|
299 |
Â
$item['license'] = '';
|
300 |
Â
}
|
212 |
Â
$response[] = array(
|
213 |
Â
'license' => $data['license'],
|
214 |
Â
'extension' => $extensions[$key]['title'],
|
215 |
+
'expires' => (!empty($data['expires']) ? $data['expires'] : null)
|
216 |
Â
);
|
217 |
Â
} else {
|
218 |
Â
$response[] = $data['license'];
|
294 |
Â
if (empty($item['license'])) {
|
295 |
Â
if (!empty($index[$id]['license'])) {
|
296 |
Â
$item['license'] = $index[$id]['license'];
|
297 |
+
$item['expire'] = (!empty($index[$id]['expire']) ? date('Y-m-d', strtotime($index[$id]['expire'])) : null);
|
298 |
Â
} else {
|
299 |
Â
$item['license'] = '';
|
300 |
Â
}
|
aam.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Â
/**
|
4 |
Â
Plugin Name: Advanced Access Manager
|
5 |
Â
Description: All you need to manage access to your WordPress website
|
6 |
-
Version: 5.7.
|
7 |
Â
Author: Vasyl Martyniuk <vasyl@vasyltech.com>
|
8 |
Â
Author URI: https://vasyltech.com
|
9 |
Â
|
@@ -115,6 +115,16 @@ class AAM {
|
|
115 |
Â
//load AAM core config
|
116 |
Â
AAM_Core_Config::bootstrap();
|
117 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
118 |
Â
// Load AAM
|
119 |
Â
AAM::getInstance();
|
120 |
Â
|
@@ -128,16 +138,6 @@ class AAM {
|
|
128 |
Â
|
129 |
Â
//load WP Core hooks
|
130 |
Â
AAM_Shared_Manager::bootstrap();
|
131 |
-
|
132 |
-
//login control
|
133 |
-
if (AAM_Core_Config::get('core.settings.secureLogin', true)) {
|
134 |
-
AAM_Core_Login::bootstrap();
|
135 |
-
}
|
136 |
-
|
137 |
-
//JWT Authentication
|
138 |
-
if (AAM_Core_Config::get('core.settings.jwtAuthentication', false)) {
|
139 |
-
AAM_Core_JwtAuth::bootstrap();
|
140 |
-
}
|
141 |
Â
}
|
142 |
Â
|
143 |
Â
/**
|
3 |
Â
/**
|
4 |
Â
Plugin Name: Advanced Access Manager
|
5 |
Â
Description: All you need to manage access to your WordPress website
|
6 |
+
Version: 5.7.3
|
7 |
Â
Author: Vasyl Martyniuk <vasyl@vasyltech.com>
|
8 |
Â
Author URI: https://vasyltech.com
|
9 |
Â
|
115 |
Â
//load AAM core config
|
116 |
Â
AAM_Core_Config::bootstrap();
|
117 |
Â
|
118 |
+
//login control
|
119 |
+
if (AAM_Core_Config::get('core.settings.secureLogin', true)) {
|
120 |
+
AAM_Core_Login::bootstrap();
|
121 |
+
}
|
122 |
+
|
123 |
+
//JWT Authentication
|
124 |
+
if (AAM_Core_Config::get('core.settings.jwtAuthentication', false)) {
|
125 |
+
AAM_Core_JwtAuth::bootstrap();
|
126 |
+
}
|
127 |
+
|
128 |
Â
// Load AAM
|
129 |
Â
AAM::getInstance();
|
130 |
Â
|
138 |
Â
|
139 |
Â
//load WP Core hooks
|
140 |
Â
AAM_Shared_Manager::bootstrap();
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
141 |
Â
}
|
142 |
Â
|
143 |
Â
/**
|
media/js/{aam-5.7.2.js → aam-5.7.3.js}
RENAMED
@@ -513,10 +513,9 @@
|
|
513 |
Â
response.role.level
|
514 |
Â
);
|
515 |
Â
getAAM().fetchContent('main');
|
516 |
-
$('#add-role-modal').modal('hide');
|
517 |
Â
} else {
|
518 |
Â
getAAM().notification(
|
519 |
-
|
520 |
Â
);
|
521 |
Â
}
|
522 |
Â
},
|
@@ -524,6 +523,7 @@
|
|
524 |
Â
getAAM().notification('danger', getAAM().__('Application error'));
|
525 |
Â
},
|
526 |
Â
complete: function () {
|
Â
|
|
527 |
Â
$(_this).text(getAAM().__('Add Role')).attr('disabled', false);
|
528 |
Â
}
|
529 |
Â
});
|
@@ -1330,6 +1330,56 @@
|
|
1330 |
Â
*/
|
1331 |
Â
(function ($) {
|
1332 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1333 |
Â
/**
|
1334 |
Â
*
|
1335 |
Â
* @param {type} subject
|
@@ -1357,6 +1407,23 @@
|
|
1357 |
Â
getAAM().reset('policy', $(this));
|
1358 |
Â
});
|
1359 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1360 |
Â
$('#policy-list').DataTable({
|
1361 |
Â
autoWidth: false,
|
1362 |
Â
ordering: false,
|
@@ -1386,6 +1453,26 @@
|
|
1386 |
Â
columnDefs: [
|
1387 |
Â
{visible: false, targets: [0,3]}
|
1388 |
Â
],
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1389 |
Â
createdRow: function (row, data) {
|
1390 |
Â
var actions = data[2].split(',');
|
1391 |
Â
|
@@ -3436,29 +3523,58 @@
|
|
3436 |
Â
* @returns {undefined}
|
3437 |
Â
*/
|
3438 |
Â
function downloadExtension(data, cb) {
|
3439 |
-
$.ajax(getLocal().
|
3440 |
-
type: '
|
3441 |
Â
dataType: 'json',
|
3442 |
-
data:
|
3443 |
-
|
3444 |
-
|
3445 |
-
|
3446 |
-
|
3447 |
-
|
Â
|
|
Â
|
|
3448 |
Â
} else {
|
3449 |
-
|
3450 |
-
|
3451 |
-
|
3452 |
-
|
3453 |
-
|
3454 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
3455 |
Â
}
|
3456 |
Â
},
|
3457 |
-
error: function () {
|
3458 |
-
getAAM().notification(
|
3459 |
-
|
3460 |
-
|
3461 |
-
cb();
|
3462 |
Â
}
|
3463 |
Â
});
|
3464 |
Â
}
|
@@ -3505,6 +3621,7 @@
|
|
3505 |
Â
function initialize() {
|
3506 |
Â
if ($('#extension-content').length) {
|
3507 |
Â
$('[data-toggle="toggle"]', '.extensions-metabox').bootstrapToggle();
|
Â
|
|
3508 |
Â
//check for updates
|
3509 |
Â
$('#aam-update-check').bind('click', function() {
|
3510 |
Â
$.ajax(getLocal().ajaxurl, {
|
@@ -3558,7 +3675,7 @@
|
|
3558 |
Â
action: 'aam',
|
3559 |
Â
sub_action: 'Extension_Manager.update',
|
3560 |
Â
_ajax_nonce: getLocal().nonce,
|
3561 |
-
|
3562 |
Â
}, function() {
|
3563 |
Â
$('i', _this).attr('class', 'icon-arrows-cw');
|
3564 |
Â
});
|
@@ -3648,9 +3765,9 @@
|
|
3648 |
Â
//bind the download handler
|
3649 |
Â
$('#download-extension').bind('click', function () {
|
3650 |
Â
download(
|
3651 |
-
|
3652 |
-
|
3653 |
-
|
3654 |
Â
);
|
3655 |
Â
$('#extension-notification-modal').modal('hide');
|
3656 |
Â
});
|
513 |
Â
response.role.level
|
514 |
Â
);
|
515 |
Â
getAAM().fetchContent('main');
|
Â
|
|
516 |
Â
} else {
|
517 |
Â
getAAM().notification(
|
518 |
+
'danger', response.reason
|
519 |
Â
);
|
520 |
Â
}
|
521 |
Â
},
|
523 |
Â
getAAM().notification('danger', getAAM().__('Application error'));
|
524 |
Â
},
|
525 |
Â
complete: function () {
|
526 |
+
$('#add-role-modal').modal('hide');
|
527 |
Â
$(_this).text(getAAM().__('Add Role')).attr('disabled', false);
|
528 |
Â
}
|
529 |
Â
});
|
1330 |
Â
*/
|
1331 |
Â
(function ($) {
|
1332 |
Â
|
1333 |
+
/**
|
1334 |
+
*
|
1335 |
+
* @param {type} data
|
1336 |
+
* @param {type} cb
|
1337 |
+
* @returns {undefined}
|
1338 |
+
*/
|
1339 |
+
function downloadLicense(data, cb) {
|
1340 |
+
$.ajax(getLocal().system.apiEndpoint + '/download', {
|
1341 |
+
type: 'GET',
|
1342 |
+
dataType: 'json',
|
1343 |
+
data: {
|
1344 |
+
license: data.license,
|
1345 |
+
domain: getLocal().system.domain,
|
1346 |
+
uid: getLocal().system.uid
|
1347 |
+
},
|
1348 |
+
success: function (package) {
|
1349 |
+
$.ajax(getLocal().ajaxurl, {
|
1350 |
+
type: 'POST',
|
1351 |
+
dataType: 'json',
|
1352 |
+
data: {
|
1353 |
+
action: 'aam',
|
1354 |
+
sub_action: 'Main_Policy.install',
|
1355 |
+
_ajax_nonce: getLocal().nonce,
|
1356 |
+
license: data.license,
|
1357 |
+
package: package
|
1358 |
+
},
|
1359 |
+
success: function (response) {
|
1360 |
+
if (response.status !== 'success') {
|
1361 |
+
getAAM().notification('danger', getAAM().__(response.error));
|
1362 |
+
}
|
1363 |
+
},
|
1364 |
+
error: function () {
|
1365 |
+
getAAM().notification(
|
1366 |
+
'danger',
|
1367 |
+
getAAM().__('Application error')
|
1368 |
+
);
|
1369 |
+
},
|
1370 |
+
complete: function() {
|
1371 |
+
cb();
|
1372 |
+
}
|
1373 |
+
});
|
1374 |
+
},
|
1375 |
+
error: function (response) {
|
1376 |
+
getAAM().notification(
|
1377 |
+
'danger', response.responseJSON.message
|
1378 |
+
);
|
1379 |
+
}
|
1380 |
+
});
|
1381 |
+
}
|
1382 |
+
|
1383 |
Â
/**
|
1384 |
Â
*
|
1385 |
Â
* @param {type} subject
|
1407 |
Â
getAAM().reset('policy', $(this));
|
1408 |
Â
});
|
1409 |
Â
|
1410 |
+
$('#download-policy').bind('click', function() {
|
1411 |
+
var license = $.trim($('#policy-license-key').val());
|
1412 |
+
|
1413 |
+
if (license) {
|
1414 |
+
$(this).text(getAAM().__('Downloading'));
|
1415 |
+
downloadLicense({
|
1416 |
+
license: license
|
1417 |
+
}, function() {
|
1418 |
+
$('#download-policy').text(getAAM().__('Download'));
|
1419 |
+
$('#policy-list').DataTable().ajax.reload();
|
1420 |
+
$('#download-policy-modal').modal('hide');
|
1421 |
+
});
|
1422 |
+
} else {
|
1423 |
+
$('#policy-license-key').focus();
|
1424 |
+
}
|
1425 |
+
});
|
1426 |
+
|
1427 |
Â
$('#policy-list').DataTable({
|
1428 |
Â
autoWidth: false,
|
1429 |
Â
ordering: false,
|
1453 |
Â
columnDefs: [
|
1454 |
Â
{visible: false, targets: [0,3]}
|
1455 |
Â
],
|
1456 |
+
initComplete: function () {
|
1457 |
+
var create = $('<a/>', {
|
1458 |
+
'href': '#',
|
1459 |
+
'class': 'btn btn-primary'
|
1460 |
+
}).html('<i class="icon-plus"></i> ' + getAAM().__('Create'))
|
1461 |
+
.bind('click', function () {
|
1462 |
+
window.open(getLocal().url.addPolicy, '_blank');
|
1463 |
+
});
|
1464 |
+
|
1465 |
+
/*var download = $('<a/>', {
|
1466 |
+
'href': '#',
|
1467 |
+
'class': 'btn btn-success'
|
1468 |
+
}).html('<i class="icon-download-cloud"></i> ' + getAAM().__('Download'))
|
1469 |
+
.bind('click', function () {
|
1470 |
+
$('#download-policy-modal').modal('show');
|
1471 |
+
});
|
1472 |
+
|
1473 |
+
$('.dataTables_filter', '#policy-list_wrapper').append(download);*/
|
1474 |
+
$('.dataTables_filter', '#policy-list_wrapper').append(create);
|
1475 |
+
},
|
1476 |
Â
createdRow: function (row, data) {
|
1477 |
Â
var actions = data[2].split(',');
|
1478 |
Â
|
3523 |
Â
* @returns {undefined}
|
3524 |
Â
*/
|
3525 |
Â
function downloadExtension(data, cb) {
|
3526 |
+
$.ajax(getLocal().system.apiEndpoint + '/download', {
|
3527 |
+
type: 'GET',
|
3528 |
Â
dataType: 'json',
|
3529 |
+
data: {
|
3530 |
+
license: data.license,
|
3531 |
+
domain: getLocal().system.domain,
|
3532 |
+
uid: getLocal().system.uid
|
3533 |
+
},
|
3534 |
+
success: function (package) {
|
3535 |
+
if (package.error === true) {
|
3536 |
+
getAAM().notification('danger', package.message);
|
3537 |
Â
} else {
|
3538 |
+
$.ajax(getLocal().ajaxurl, {
|
3539 |
+
type: 'POST',
|
3540 |
+
dataType: 'json',
|
3541 |
+
data: {
|
3542 |
+
action: 'aam',
|
3543 |
+
sub_action: 'Extension_Manager.install',
|
3544 |
+
_ajax_nonce: getLocal().nonce,
|
3545 |
+
license: data.license,
|
3546 |
+
package: package
|
3547 |
+
},
|
3548 |
+
success: function (response) {
|
3549 |
+
if (response.status === 'success') {
|
3550 |
+
setTimeout(function () {
|
3551 |
+
getAAM().fetchContent('extensions');
|
3552 |
+
}, 500);
|
3553 |
+
} else {
|
3554 |
+
getAAM().notification('danger', response.error);
|
3555 |
+
if (typeof package.content !== 'undefined') {
|
3556 |
+
dump = package;
|
3557 |
+
$('#installation-error').text(response.error);
|
3558 |
+
$('#extension-notification-modal').modal('show');
|
3559 |
+
}
|
3560 |
+
}
|
3561 |
+
},
|
3562 |
+
error: function () {
|
3563 |
+
getAAM().notification(
|
3564 |
+
'danger',
|
3565 |
+
getAAM().__('Application error')
|
3566 |
+
);
|
3567 |
+
},
|
3568 |
+
complete: function() {
|
3569 |
+
cb();
|
3570 |
+
}
|
3571 |
+
});
|
3572 |
Â
}
|
3573 |
Â
},
|
3574 |
+
error: function (response) {
|
3575 |
+
getAAM().notification(
|
3576 |
+
'danger', response.responseJSON.message
|
3577 |
+
);
|
Â
|
|
3578 |
Â
}
|
3579 |
Â
});
|
3580 |
Â
}
|
3621 |
Â
function initialize() {
|
3622 |
Â
if ($('#extension-content').length) {
|
3623 |
Â
$('[data-toggle="toggle"]', '.extensions-metabox').bootstrapToggle();
|
3624 |
+
|
3625 |
Â
//check for updates
|
3626 |
Â
$('#aam-update-check').bind('click', function() {
|
3627 |
Â
$.ajax(getLocal().ajaxurl, {
|
3675 |
Â
action: 'aam',
|
3676 |
Â
sub_action: 'Extension_Manager.update',
|
3677 |
Â
_ajax_nonce: getLocal().nonce,
|
3678 |
+
license: _this.data('license')
|
3679 |
Â
}, function() {
|
3680 |
Â
$('i', _this).attr('class', 'icon-arrows-cw');
|
3681 |
Â
});
|
3765 |
Â
//bind the download handler
|
3766 |
Â
$('#download-extension').bind('click', function () {
|
3767 |
Â
download(
|
3768 |
+
'data:application/zip;base64,' + dump.content,
|
3769 |
+
dump.title + '.zip',
|
3770 |
+
'application/zip'
|
3771 |
Â
);
|
3772 |
Â
$('#extension-notification-modal').modal('hide');
|
3773 |
Â
});
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: vasyltech
|
|
3 |
Â
Tags: access control, membership, backend menu, user role, restricted content, security, jwt
|
4 |
Â
Requires at least: 4.0
|
5 |
Â
Tested up to: 5.0.2
|
6 |
-
Stable tag: 5.7.
|
7 |
Â
|
8 |
Â
All you need to manage access to you WordPress websites on frontend, backend and API levels for any role, user or visitors.
|
9 |
Â
|
@@ -78,6 +78,21 @@ https://www.youtube.com/watch?v=mj5Xa_Wc16Y
|
|
78 |
Â
|
79 |
Â
== Changelog ==
|
80 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
81 |
Â
= 5.7.2 =
|
82 |
Â
* Fixed bug with Posts & Terms feature for WP version under 4.8
|
83 |
Â
* Fixed bug were Access Policy can't be attached to any principal on the Policy edit screen
|
3 |
Â
Tags: access control, membership, backend menu, user role, restricted content, security, jwt
|
4 |
Â
Requires at least: 4.0
|
5 |
Â
Tested up to: 5.0.2
|
6 |
+
Stable tag: 5.7.3
|
7 |
Â
|
8 |
Â
All you need to manage access to you WordPress websites on frontend, backend and API levels for any role, user or visitors.
|
9 |
Â
|
78 |
Â
|
79 |
Â
== Changelog ==
|
80 |
Â
|
81 |
+
= 5.7.3 =
|
82 |
+
* Fixed the bug with JWT authentication
|
83 |
+
* Fixed the PHP bug in policy metabox when no errors with JSON is detected
|
84 |
+
* Fixed the bug with license expiration for Extended versions not properly displayed
|
85 |
+
* Fixed the bug with Admin Menu listed incorrectly when Default Access Settings defined
|
86 |
+
* Fixed the PHP bug in Post object when access settings are defined in a Policy
|
87 |
+
* Improved role creation feature
|
88 |
+
* Improved capability handling with Access & Security Policy
|
89 |
+
* Refactor the way extension is installed to eliminate cURL issues
|
90 |
+
* Deprecated and removed `aam_display_license` capability
|
91 |
+
* Extended default policy document with dependencies
|
92 |
+
* Added support for `Features` in the Access & Security Policy
|
93 |
+
* Added policy Validation functionality
|
94 |
+
* Reduced number of methods that use cURL to contact aamplugin.com API
|
95 |
+
|
96 |
Â
= 5.7.2 =
|
97 |
Â
* Fixed bug with Posts & Terms feature for WP version under 4.8
|
98 |
Â
* Fixed bug were Access Policy can't be attached to any principal on the Policy edit screen
|
vendor/autoload.php
CHANGED
@@ -7,9 +7,36 @@
|
|
7 |
Â
* ======================================================================
|
8 |
Â
*/
|
9 |
Â
|
Â
|
|
10 |
Â
if (!class_exists('Firebase\JWT')) {
|
11 |
Â
require __DIR__ . '/firebase/BeforeValidException.php';
|
12 |
Â
require __DIR__ . '/firebase/ExpiredException.php';
|
13 |
Â
require __DIR__ . '/firebase/SignatureInvalidException.php';
|
14 |
Â
require __DIR__ . '/firebase/JWT.php';
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
15 |
Â
}
|
7 |
Â
* ======================================================================
|
8 |
Â
*/
|
9 |
Â
|
10 |
+
// Firebase for JWT token handling
|
11 |
Â
if (!class_exists('Firebase\JWT')) {
|
12 |
Â
require __DIR__ . '/firebase/BeforeValidException.php';
|
13 |
Â
require __DIR__ . '/firebase/ExpiredException.php';
|
14 |
Â
require __DIR__ . '/firebase/SignatureInvalidException.php';
|
15 |
Â
require __DIR__ . '/firebase/JWT.php';
|
16 |
+
}
|
17 |
+
|
18 |
+
//Composer Semver for Policy dependency versioning
|
19 |
+
if (!class_exists('Composer\Semver')) {
|
20 |
+
/**
|
21 |
+
* Load composer semantic version check
|
22 |
+
*
|
23 |
+
* @param string $classname
|
24 |
+
*
|
25 |
+
* @return void
|
26 |
+
*/
|
27 |
+
function loadComposerSemver($classname) {
|
28 |
+
if (strpos($classname, 'Composer\Semver') === 0) {
|
29 |
+
$normalized = str_replace(
|
30 |
+
array('Composer\Semver', '\\'),
|
31 |
+
array('Composer', '/'),
|
32 |
+
$classname
|
33 |
+
);
|
34 |
+
$filename = __DIR__ . '/' . $normalized . '.php';
|
35 |
+
}
|
36 |
+
|
37 |
+
if (!empty($filename) && file_exists($filename)) {
|
38 |
+
require $filename;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
spl_autoload_register('loadComposerSemver');
|
42 |
Â
}
|
vendor/composer/Comparator.php
ADDED
@@ -0,0 +1,111 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver;
|
13 |
+
|
14 |
+
use Composer\Semver\Constraint\Constraint;
|
15 |
+
|
16 |
+
class Comparator
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* Evaluates the expression: $version1 > $version2.
|
20 |
+
*
|
21 |
+
* @param string $version1
|
22 |
+
* @param string $version2
|
23 |
+
*
|
24 |
+
* @return bool
|
25 |
+
*/
|
26 |
+
public static function greaterThan($version1, $version2)
|
27 |
+
{
|
28 |
+
return self::compare($version1, '>', $version2);
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Evaluates the expression: $version1 >= $version2.
|
33 |
+
*
|
34 |
+
* @param string $version1
|
35 |
+
* @param string $version2
|
36 |
+
*
|
37 |
+
* @return bool
|
38 |
+
*/
|
39 |
+
public static function greaterThanOrEqualTo($version1, $version2)
|
40 |
+
{
|
41 |
+
return self::compare($version1, '>=', $version2);
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Evaluates the expression: $version1 < $version2.
|
46 |
+
*
|
47 |
+
* @param string $version1
|
48 |
+
* @param string $version2
|
49 |
+
*
|
50 |
+
* @return bool
|
51 |
+
*/
|
52 |
+
public static function lessThan($version1, $version2)
|
53 |
+
{
|
54 |
+
return self::compare($version1, '<', $version2);
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Evaluates the expression: $version1 <= $version2.
|
59 |
+
*
|
60 |
+
* @param string $version1
|
61 |
+
* @param string $version2
|
62 |
+
*
|
63 |
+
* @return bool
|
64 |
+
*/
|
65 |
+
public static function lessThanOrEqualTo($version1, $version2)
|
66 |
+
{
|
67 |
+
return self::compare($version1, '<=', $version2);
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Evaluates the expression: $version1 == $version2.
|
72 |
+
*
|
73 |
+
* @param string $version1
|
74 |
+
* @param string $version2
|
75 |
+
*
|
76 |
+
* @return bool
|
77 |
+
*/
|
78 |
+
public static function equalTo($version1, $version2)
|
79 |
+
{
|
80 |
+
return self::compare($version1, '==', $version2);
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Evaluates the expression: $version1 != $version2.
|
85 |
+
*
|
86 |
+
* @param string $version1
|
87 |
+
* @param string $version2
|
88 |
+
*
|
89 |
+
* @return bool
|
90 |
+
*/
|
91 |
+
public static function notEqualTo($version1, $version2)
|
92 |
+
{
|
93 |
+
return self::compare($version1, '!=', $version2);
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Evaluates the expression: $version1 $operator $version2.
|
98 |
+
*
|
99 |
+
* @param string $version1
|
100 |
+
* @param string $operator
|
101 |
+
* @param string $version2
|
102 |
+
*
|
103 |
+
* @return bool
|
104 |
+
*/
|
105 |
+
public static function compare($version1, $operator, $version2)
|
106 |
+
{
|
107 |
+
$constraint = new Constraint($operator, $version2);
|
108 |
+
|
109 |
+
return $constraint->matches(new Constraint('==', $version1));
|
110 |
+
}
|
111 |
+
}
|
vendor/composer/Constraint/AbstractConstraint.php
ADDED
@@ -0,0 +1,63 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver\Constraint;
|
13 |
+
|
14 |
+
trigger_error('The ' . __CLASS__ . ' abstract class is deprecated, there is no replacement for it, it will be removed in the next major version.', E_USER_DEPRECATED);
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Base constraint class.
|
18 |
+
*/
|
19 |
+
abstract class AbstractConstraint implements ConstraintInterface
|
20 |
+
{
|
21 |
+
/** @var string */
|
22 |
+
protected $prettyString;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @param ConstraintInterface $provider
|
26 |
+
*
|
27 |
+
* @return bool
|
28 |
+
*/
|
29 |
+
public function matches(ConstraintInterface $provider)
|
30 |
+
{
|
31 |
+
if ($provider instanceof $this) {
|
32 |
+
// see note at bottom of this class declaration
|
33 |
+
return $this->matchSpecific($provider);
|
34 |
+
}
|
35 |
+
|
36 |
+
// turn matching around to find a match
|
37 |
+
return $provider->matches($this);
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @param string $prettyString
|
42 |
+
*/
|
43 |
+
public function setPrettyString($prettyString)
|
44 |
+
{
|
45 |
+
$this->prettyString = $prettyString;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
public function getPrettyString()
|
52 |
+
{
|
53 |
+
if ($this->prettyString) {
|
54 |
+
return $this->prettyString;
|
55 |
+
}
|
56 |
+
|
57 |
+
return $this->__toString();
|
58 |
+
}
|
59 |
+
|
60 |
+
// implementations must implement a method of this format:
|
61 |
+
// not declared abstract here because type hinting violates parameter coherence (TODO right word?)
|
62 |
+
// public function matchSpecific(<SpecificConstraintType> $provider);
|
63 |
+
}
|
vendor/composer/Constraint/Constraint.php
ADDED
@@ -0,0 +1,219 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver\Constraint;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Defines a constraint.
|
16 |
+
*/
|
17 |
+
class Constraint implements ConstraintInterface
|
18 |
+
{
|
19 |
+
/* operator integer values */
|
20 |
+
const OP_EQ = 0;
|
21 |
+
const OP_LT = 1;
|
22 |
+
const OP_LE = 2;
|
23 |
+
const OP_GT = 3;
|
24 |
+
const OP_GE = 4;
|
25 |
+
const OP_NE = 5;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Operator to integer translation table.
|
29 |
+
*
|
30 |
+
* @var array
|
31 |
+
*/
|
32 |
+
private static $transOpStr = array(
|
33 |
+
'=' => self::OP_EQ,
|
34 |
+
'==' => self::OP_EQ,
|
35 |
+
'<' => self::OP_LT,
|
36 |
+
'<=' => self::OP_LE,
|
37 |
+
'>' => self::OP_GT,
|
38 |
+
'>=' => self::OP_GE,
|
39 |
+
'<>' => self::OP_NE,
|
40 |
+
'!=' => self::OP_NE,
|
41 |
+
);
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Integer to operator translation table.
|
45 |
+
*
|
46 |
+
* @var array
|
47 |
+
*/
|
48 |
+
private static $transOpInt = array(
|
49 |
+
self::OP_EQ => '==',
|
50 |
+
self::OP_LT => '<',
|
51 |
+
self::OP_LE => '<=',
|
52 |
+
self::OP_GT => '>',
|
53 |
+
self::OP_GE => '>=',
|
54 |
+
self::OP_NE => '!=',
|
55 |
+
);
|
56 |
+
|
57 |
+
/** @var string */
|
58 |
+
protected $operator;
|
59 |
+
|
60 |
+
/** @var string */
|
61 |
+
protected $version;
|
62 |
+
|
63 |
+
/** @var string */
|
64 |
+
protected $prettyString;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param ConstraintInterface $provider
|
68 |
+
*
|
69 |
+
* @return bool
|
70 |
+
*/
|
71 |
+
public function matches(ConstraintInterface $provider)
|
72 |
+
{
|
73 |
+
if ($provider instanceof $this) {
|
74 |
+
return $this->matchSpecific($provider);
|
75 |
+
}
|
76 |
+
|
77 |
+
// turn matching around to find a match
|
78 |
+
return $provider->matches($this);
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* @param string $prettyString
|
83 |
+
*/
|
84 |
+
public function setPrettyString($prettyString)
|
85 |
+
{
|
86 |
+
$this->prettyString = $prettyString;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @return string
|
91 |
+
*/
|
92 |
+
public function getPrettyString()
|
93 |
+
{
|
94 |
+
if ($this->prettyString) {
|
95 |
+
return $this->prettyString;
|
96 |
+
}
|
97 |
+
|
98 |
+
return $this->__toString();
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Get all supported comparison operators.
|
103 |
+
*
|
104 |
+
* @return array
|
105 |
+
*/
|
106 |
+
public static function getSupportedOperators()
|
107 |
+
{
|
108 |
+
return array_keys(self::$transOpStr);
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Sets operator and version to compare with.
|
113 |
+
*
|
114 |
+
* @param string $operator
|
115 |
+
* @param string $version
|
116 |
+
*
|
117 |
+
* @throws \InvalidArgumentException if invalid operator is given.
|
118 |
+
*/
|
119 |
+
public function __construct($operator, $version)
|
120 |
+
{
|
121 |
+
if (!isset(self::$transOpStr[$operator])) {
|
122 |
+
throw new \InvalidArgumentException(sprintf(
|
123 |
+
'Invalid operator "%s" given, expected one of: %s',
|
124 |
+
$operator,
|
125 |
+
implode(', ', self::getSupportedOperators())
|
126 |
+
));
|
127 |
+
}
|
128 |
+
|
129 |
+
$this->operator = self::$transOpStr[$operator];
|
130 |
+
$this->version = $version;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @param string $a
|
135 |
+
* @param string $b
|
136 |
+
* @param string $operator
|
137 |
+
* @param bool $compareBranches
|
138 |
+
*
|
139 |
+
* @throws \InvalidArgumentException if invalid operator is given.
|
140 |
+
*
|
141 |
+
* @return bool
|
142 |
+
*/
|
143 |
+
public function versionCompare($a, $b, $operator, $compareBranches = false)
|
144 |
+
{
|
145 |
+
if (!isset(self::$transOpStr[$operator])) {
|
146 |
+
throw new \InvalidArgumentException(sprintf(
|
147 |
+
'Invalid operator "%s" given, expected one of: %s',
|
148 |
+
$operator,
|
149 |
+
implode(', ', self::getSupportedOperators())
|
150 |
+
));
|
151 |
+
}
|
152 |
+
|
153 |
+
$aIsBranch = 'dev-' === substr($a, 0, 4);
|
154 |
+
$bIsBranch = 'dev-' === substr($b, 0, 4);
|
155 |
+
|
156 |
+
if ($aIsBranch && $bIsBranch) {
|
157 |
+
return $operator === '==' && $a === $b;
|
158 |
+
}
|
159 |
+
|
160 |
+
// when branches are not comparable, we make sure dev branches never match anything
|
161 |
+
if (!$compareBranches && ($aIsBranch || $bIsBranch)) {
|
162 |
+
return false;
|
163 |
+
}
|
164 |
+
|
165 |
+
return version_compare($a, $b, $operator);
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* @param Constraint $provider
|
170 |
+
* @param bool $compareBranches
|
171 |
+
*
|
172 |
+
* @return bool
|
173 |
+
*/
|
174 |
+
public function matchSpecific(Constraint $provider, $compareBranches = false)
|
175 |
+
{
|
176 |
+
$noEqualOp = str_replace('=', '', self::$transOpInt[$this->operator]);
|
177 |
+
$providerNoEqualOp = str_replace('=', '', self::$transOpInt[$provider->operator]);
|
178 |
+
|
179 |
+
$isEqualOp = self::OP_EQ === $this->operator;
|
180 |
+
$isNonEqualOp = self::OP_NE === $this->operator;
|
181 |
+
$isProviderEqualOp = self::OP_EQ === $provider->operator;
|
182 |
+
$isProviderNonEqualOp = self::OP_NE === $provider->operator;
|
183 |
+
|
184 |
+
// '!=' operator is match when other operator is not '==' operator or version is not match
|
185 |
+
// these kinds of comparisons always have a solution
|
186 |
+
if ($isNonEqualOp || $isProviderNonEqualOp) {
|
187 |
+
return !$isEqualOp && !$isProviderEqualOp
|
188 |
+
|| $this->versionCompare($provider->version, $this->version, '!=', $compareBranches);
|
189 |
+
}
|
190 |
+
|
191 |
+
// an example for the condition is <= 2.0 & < 1.0
|
192 |
+
// these kinds of comparisons always have a solution
|
193 |
+
if ($this->operator !== self::OP_EQ && $noEqualOp === $providerNoEqualOp) {
|
194 |
+
return true;
|
195 |
+
}
|
196 |
+
|
197 |
+
if ($this->versionCompare($provider->version, $this->version, self::$transOpInt[$this->operator], $compareBranches)) {
|
198 |
+
// special case, e.g. require >= 1.0 and provide < 1.0
|
199 |
+
// 1.0 >= 1.0 but 1.0 is outside of the provided interval
|
200 |
+
if ($provider->version === $this->version
|
201 |
+
&& self::$transOpInt[$provider->operator] === $providerNoEqualOp
|
202 |
+
&& self::$transOpInt[$this->operator] !== $noEqualOp) {
|
203 |
+
return false;
|
204 |
+
}
|
205 |
+
|
206 |
+
return true;
|
207 |
+
}
|
208 |
+
|
209 |
+
return false;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @return string
|
214 |
+
*/
|
215 |
+
public function __toString()
|
216 |
+
{
|
217 |
+
return self::$transOpInt[$this->operator] . ' ' . $this->version;
|
218 |
+
}
|
219 |
+
}
|
vendor/composer/Constraint/ConstraintInterface.php
ADDED
@@ -0,0 +1,32 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver\Constraint;
|
13 |
+
|
14 |
+
interface ConstraintInterface
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @param ConstraintInterface $provider
|
18 |
+
*
|
19 |
+
* @return bool
|
20 |
+
*/
|
21 |
+
public function matches(ConstraintInterface $provider);
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
public function getPrettyString();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @return string
|
30 |
+
*/
|
31 |
+
public function __toString();
|
32 |
+
}
|
vendor/composer/Constraint/EmptyConstraint.php
ADDED
@@ -0,0 +1,59 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver\Constraint;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Defines the absence of a constraint.
|
16 |
+
*/
|
17 |
+
class EmptyConstraint implements ConstraintInterface
|
18 |
+
{
|
19 |
+
/** @var string */
|
20 |
+
protected $prettyString;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param ConstraintInterface $provider
|
24 |
+
*
|
25 |
+
* @return bool
|
26 |
+
*/
|
27 |
+
public function matches(ConstraintInterface $provider)
|
28 |
+
{
|
29 |
+
return true;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param $prettyString
|
34 |
+
*/
|
35 |
+
public function setPrettyString($prettyString)
|
36 |
+
{
|
37 |
+
$this->prettyString = $prettyString;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @return string
|
42 |
+
*/
|
43 |
+
public function getPrettyString()
|
44 |
+
{
|
45 |
+
if ($this->prettyString) {
|
46 |
+
return $this->prettyString;
|
47 |
+
}
|
48 |
+
|
49 |
+
return $this->__toString();
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @return string
|
54 |
+
*/
|
55 |
+
public function __toString()
|
56 |
+
{
|
57 |
+
return '[]';
|
58 |
+
}
|
59 |
+
}
|
vendor/composer/Constraint/MultiConstraint.php
ADDED
@@ -0,0 +1,120 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver\Constraint;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Defines a conjunctive or disjunctive set of constraints.
|
16 |
+
*/
|
17 |
+
class MultiConstraint implements ConstraintInterface
|
18 |
+
{
|
19 |
+
/** @var ConstraintInterface[] */
|
20 |
+
protected $constraints;
|
21 |
+
|
22 |
+
/** @var string */
|
23 |
+
protected $prettyString;
|
24 |
+
|
25 |
+
/** @var bool */
|
26 |
+
protected $conjunctive;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @param ConstraintInterface[] $constraints A set of constraints
|
30 |
+
* @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive
|
31 |
+
*/
|
32 |
+
public function __construct(array $constraints, $conjunctive = true)
|
33 |
+
{
|
34 |
+
$this->constraints = $constraints;
|
35 |
+
$this->conjunctive = $conjunctive;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @return ConstraintInterface[]
|
40 |
+
*/
|
41 |
+
public function getConstraints()
|
42 |
+
{
|
43 |
+
return $this->constraints;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @return bool
|
48 |
+
*/
|
49 |
+
public function isConjunctive()
|
50 |
+
{
|
51 |
+
return $this->conjunctive;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* @return bool
|
56 |
+
*/
|
57 |
+
public function isDisjunctive()
|
58 |
+
{
|
59 |
+
return !$this->conjunctive;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @param ConstraintInterface $provider
|
64 |
+
*
|
65 |
+
* @return bool
|
66 |
+
*/
|
67 |
+
public function matches(ConstraintInterface $provider)
|
68 |
+
{
|
69 |
+
if (false === $this->conjunctive) {
|
70 |
+
foreach ($this->constraints as $constraint) {
|
71 |
+
if ($constraint->matches($provider)) {
|
72 |
+
return true;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
return false;
|
77 |
+
}
|
78 |
+
|
79 |
+
foreach ($this->constraints as $constraint) {
|
80 |
+
if (!$constraint->matches($provider)) {
|
81 |
+
return false;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
return true;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @param string $prettyString
|
90 |
+
*/
|
91 |
+
public function setPrettyString($prettyString)
|
92 |
+
{
|
93 |
+
$this->prettyString = $prettyString;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* @return string
|
98 |
+
*/
|
99 |
+
public function getPrettyString()
|
100 |
+
{
|
101 |
+
if ($this->prettyString) {
|
102 |
+
return $this->prettyString;
|
103 |
+
}
|
104 |
+
|
105 |
+
return $this->__toString();
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* @return string
|
110 |
+
*/
|
111 |
+
public function __toString()
|
112 |
+
{
|
113 |
+
$constraints = array();
|
114 |
+
foreach ($this->constraints as $constraint) {
|
115 |
+
$constraints[] = (string) $constraint;
|
116 |
+
}
|
117 |
+
|
118 |
+
return '[' . implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']';
|
119 |
+
}
|
120 |
+
}
|
vendor/composer/Semver.php
ADDED
@@ -0,0 +1,127 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver;
|
13 |
+
|
14 |
+
use Composer\Semver\Constraint\Constraint;
|
15 |
+
|
16 |
+
class Semver
|
17 |
+
{
|
18 |
+
const SORT_ASC = 1;
|
19 |
+
const SORT_DESC = -1;
|
20 |
+
|
21 |
+
/** @var VersionParser */
|
22 |
+
private static $versionParser;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Determine if given version satisfies given constraints.
|
26 |
+
*
|
27 |
+
* @param string $version
|
28 |
+
* @param string $constraints
|
29 |
+
*
|
30 |
+
* @return bool
|
31 |
+
*/
|
32 |
+
public static function satisfies($version, $constraints)
|
33 |
+
{
|
34 |
+
if (null === self::$versionParser) {
|
35 |
+
self::$versionParser = new VersionParser();
|
36 |
+
}
|
37 |
+
|
38 |
+
$versionParser = self::$versionParser;
|
39 |
+
$provider = new Constraint('==', $versionParser->normalize($version));
|
40 |
+
$constraints = $versionParser->parseConstraints($constraints);
|
41 |
+
|
42 |
+
return $constraints->matches($provider);
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Return all versions that satisfy given constraints.
|
47 |
+
*
|
48 |
+
* @param array $versions
|
49 |
+
* @param string $constraints
|
50 |
+
*
|
51 |
+
* @return array
|
52 |
+
*/
|
53 |
+
public static function satisfiedBy(array $versions, $constraints)
|
54 |
+
{
|
55 |
+
$versions = array_filter($versions, function ($version) use ($constraints) {
|
56 |
+
return Semver::satisfies($version, $constraints);
|
57 |
+
});
|
58 |
+
|
59 |
+
return array_values($versions);
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Sort given array of versions.
|
64 |
+
*
|
65 |
+
* @param array $versions
|
66 |
+
*
|
67 |
+
* @return array
|
68 |
+
*/
|
69 |
+
public static function sort(array $versions)
|
70 |
+
{
|
71 |
+
return self::usort($versions, self::SORT_ASC);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Sort given array of versions in reverse.
|
76 |
+
*
|
77 |
+
* @param array $versions
|
78 |
+
*
|
79 |
+
* @return array
|
80 |
+
*/
|
81 |
+
public static function rsort(array $versions)
|
82 |
+
{
|
83 |
+
return self::usort($versions, self::SORT_DESC);
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* @param array $versions
|
88 |
+
* @param int $direction
|
89 |
+
*
|
90 |
+
* @return array
|
91 |
+
*/
|
92 |
+
private static function usort(array $versions, $direction)
|
93 |
+
{
|
94 |
+
if (null === self::$versionParser) {
|
95 |
+
self::$versionParser = new VersionParser();
|
96 |
+
}
|
97 |
+
|
98 |
+
$versionParser = self::$versionParser;
|
99 |
+
$normalized = array();
|
100 |
+
|
101 |
+
// Normalize outside of usort() scope for minor performance increase.
|
102 |
+
// Creates an array of arrays: [[normalized, key], ...]
|
103 |
+
foreach ($versions as $key => $version) {
|
104 |
+
$normalized[] = array($versionParser->normalize($version), $key);
|
105 |
+
}
|
106 |
+
|
107 |
+
usort($normalized, function (array $left, array $right) use ($direction) {
|
108 |
+
if ($left[0] === $right[0]) {
|
109 |
+
return 0;
|
110 |
+
}
|
111 |
+
|
112 |
+
if (Comparator::lessThan($left[0], $right[0])) {
|
113 |
+
return -$direction;
|
114 |
+
}
|
115 |
+
|
116 |
+
return $direction;
|
117 |
+
});
|
118 |
+
|
119 |
+
// Recreate input array, using the original indexes which are now in sorted order.
|
120 |
+
$sorted = array();
|
121 |
+
foreach ($normalized as $item) {
|
122 |
+
$sorted[] = $versions[$item[1]];
|
123 |
+
}
|
124 |
+
|
125 |
+
return $sorted;
|
126 |
+
}
|
127 |
+
}
|
vendor/composer/VersionParser.php
ADDED
@@ -0,0 +1,548 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of composer/semver.
|
5 |
+
*
|
6 |
+
* (c) Composer <https://github.com/composer>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view
|
9 |
+
* the LICENSE file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Composer\Semver;
|
13 |
+
|
14 |
+
use Composer\Semver\Constraint\ConstraintInterface;
|
15 |
+
use Composer\Semver\Constraint\EmptyConstraint;
|
16 |
+
use Composer\Semver\Constraint\MultiConstraint;
|
17 |
+
use Composer\Semver\Constraint\Constraint;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Version parser.
|
21 |
+
*
|
22 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
23 |
+
*/
|
24 |
+
class VersionParser
|
25 |
+
{
|
26 |
+
/**
|
27 |
+
* Regex to match pre-release data (sort of).
|
28 |
+
*
|
29 |
+
* Due to backwards compatibility:
|
30 |
+
* - Instead of enforcing hyphen, an underscore, dot or nothing at all are also accepted.
|
31 |
+
* - Only stabilities as recognized by Composer are allowed to precede a numerical identifier.
|
32 |
+
* - Numerical-only pre-release identifiers are not supported, see tests.
|
33 |
+
*
|
34 |
+
* |--------------|
|
35 |
+
* [major].[minor].[patch] -[pre-release] +[build-metadata]
|
36 |
+
*
|
37 |
+
* @var string
|
38 |
+
*/
|
39 |
+
private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?';
|
40 |
+
|
41 |
+
/** @var array */
|
42 |
+
private static $stabilities = array('stable', 'RC', 'beta', 'alpha', 'dev');
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Returns the stability of a version.
|
46 |
+
*
|
47 |
+
* @param string $version
|
48 |
+
*
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
public static function parseStability($version)
|
52 |
+
{
|
53 |
+
$version = preg_replace('{#.+$}i', '', $version);
|
54 |
+
|
55 |
+
if ('dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4)) {
|
56 |
+
return 'dev';
|
57 |
+
}
|
58 |
+
|
59 |
+
preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match);
|
60 |
+
if (!empty($match[3])) {
|
61 |
+
return 'dev';
|
62 |
+
}
|
63 |
+
|
64 |
+
if (!empty($match[1])) {
|
65 |
+
if ('beta' === $match[1] || 'b' === $match[1]) {
|
66 |
+
return 'beta';
|
67 |
+
}
|
68 |
+
if ('alpha' === $match[1] || 'a' === $match[1]) {
|
69 |
+
return 'alpha';
|
70 |
+
}
|
71 |
+
if ('rc' === $match[1]) {
|
72 |
+
return 'RC';
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
return 'stable';
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @param string $stability
|
81 |
+
*
|
82 |
+
* @return string
|
83 |
+
*/
|
84 |
+
public static function normalizeStability($stability)
|
85 |
+
{
|
86 |
+
$stability = strtolower($stability);
|
87 |
+
|
88 |
+
return $stability === 'rc' ? 'RC' : $stability;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Normalizes a version string to be able to perform comparisons on it.
|
93 |
+
*
|
94 |
+
* @param string $version
|
95 |
+
* @param string $fullVersion optional complete version string to give more context
|
96 |
+
*
|
97 |
+
* @throws \UnexpectedValueException
|
98 |
+
*
|
99 |
+
* @return string
|
100 |
+
*/
|
101 |
+
public function normalize($version, $fullVersion = null)
|
102 |
+
{
|
103 |
+
$version = trim($version);
|
104 |
+
if (null === $fullVersion) {
|
105 |
+
$fullVersion = $version;
|
106 |
+
}
|
107 |
+
|
108 |
+
// strip off aliasing
|
109 |
+
if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) {
|
110 |
+
$version = $match[1];
|
111 |
+
}
|
112 |
+
|
113 |
+
// match master-like branches
|
114 |
+
if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) {
|
115 |
+
return '9999999-dev';
|
116 |
+
}
|
117 |
+
|
118 |
+
// if requirement is branch-like, use full name
|
119 |
+
if ('dev-' === strtolower(substr($version, 0, 4))) {
|
120 |
+
return 'dev-' . substr($version, 4);
|
121 |
+
}
|
122 |
+
|
123 |
+
// strip off build metadata
|
124 |
+
if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) {
|
125 |
+
$version = $match[1];
|
126 |
+
}
|
127 |
+
|
128 |
+
// match classical versioning
|
129 |
+
if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) {
|
130 |
+
$version = $matches[1]
|
131 |
+
. (!empty($matches[2]) ? $matches[2] : '.0')
|
132 |
+
. (!empty($matches[3]) ? $matches[3] : '.0')
|
133 |
+
. (!empty($matches[4]) ? $matches[4] : '.0');
|
134 |
+
$index = 5;
|
135 |
+
// match date(time) based versioning
|
136 |
+
} elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) {
|
137 |
+
$version = preg_replace('{\D}', '.', $matches[1]);
|
138 |
+
$index = 2;
|
139 |
+
}
|
140 |
+
|
141 |
+
// add version modifiers if a version was matched
|
142 |
+
if (isset($index)) {
|
143 |
+
if (!empty($matches[$index])) {
|
144 |
+
if ('stable' === $matches[$index]) {
|
145 |
+
return $version;
|
146 |
+
}
|
147 |
+
$version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index + 1]) ? ltrim($matches[$index + 1], '.-') : '');
|
148 |
+
}
|
149 |
+
|
150 |
+
if (!empty($matches[$index + 2])) {
|
151 |
+
$version .= '-dev';
|
152 |
+
}
|
153 |
+
|
154 |
+
return $version;
|
155 |
+
}
|
156 |
+
|
157 |
+
// match dev branches
|
158 |
+
if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) {
|
159 |
+
try {
|
160 |
+
return $this->normalizeBranch($match[1]);
|
161 |
+
} catch (\Exception $e) {
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
$extraMessage = '';
|
166 |
+
if (preg_match('{ +as +' . preg_quote($version) . '$}', $fullVersion)) {
|
167 |
+
$extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version';
|
168 |
+
} elseif (preg_match('{^' . preg_quote($version) . ' +as +}', $fullVersion)) {
|
169 |
+
$extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-';
|
170 |
+
}
|
171 |
+
|
172 |
+
throw new \UnexpectedValueException('Invalid version string "' . $version . '"' . $extraMessage);
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison.
|
177 |
+
*
|
178 |
+
* @param string $branch Branch name (e.g. 2.1.x-dev)
|
179 |
+
*
|
180 |
+
* @return string|false Numeric prefix if present (e.g. 2.1.) or false
|
181 |
+
*/
|
182 |
+
public function parseNumericAliasPrefix($branch)
|
183 |
+
{
|
184 |
+
if (preg_match('{^(?P<version>(\d++\\.)*\d++)(?:\.x)?-dev$}i', $branch, $matches)) {
|
185 |
+
return $matches['version'] . '.';
|
186 |
+
}
|
187 |
+
|
188 |
+
return false;
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Normalizes a branch name to be able to perform comparisons on it.
|
193 |
+
*
|
194 |
+
* @param string $name
|
195 |
+
*
|
196 |
+
* @return string
|
197 |
+
*/
|
198 |
+
public function normalizeBranch($name)
|
199 |
+
{
|
200 |
+
$name = trim($name);
|
201 |
+
|
202 |
+
if (in_array($name, array('master', 'trunk', 'default'))) {
|
203 |
+
return $this->normalize($name);
|
204 |
+
}
|
205 |
+
|
206 |
+
if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) {
|
207 |
+
$version = '';
|
208 |
+
for ($i = 1; $i < 5; ++$i) {
|
209 |
+
$version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
|
210 |
+
}
|
211 |
+
|
212 |
+
return str_replace('x', '9999999', $version) . '-dev';
|
213 |
+
}
|
214 |
+
|
215 |
+
return 'dev-' . $name;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Parses a constraint string into MultiConstraint and/or Constraint objects.
|
220 |
+
*
|
221 |
+
* @param string $constraints
|
222 |
+
*
|
223 |
+
* @return ConstraintInterface
|
224 |
+
*/
|
225 |
+
public function parseConstraints($constraints)
|
226 |
+
{
|
227 |
+
$prettyConstraint = $constraints;
|
228 |
+
|
229 |
+
if (preg_match('{^([^,\s]*?)@(' . implode('|', self::$stabilities) . ')$}i', $constraints, $match)) {
|
230 |
+
$constraints = empty($match[1]) ? '*' : $match[1];
|
231 |
+
}
|
232 |
+
|
233 |
+
if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraints, $match)) {
|
234 |
+
$constraints = $match[1];
|
235 |
+
}
|
236 |
+
|
237 |
+
$orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints));
|
238 |
+
$orGroups = array();
|
239 |
+
foreach ($orConstraints as $constraints) {
|
240 |
+
$andConstraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $constraints);
|
241 |
+
if (count($andConstraints) > 1) {
|
242 |
+
$constraintObjects = array();
|
243 |
+
foreach ($andConstraints as $constraint) {
|
244 |
+
foreach ($this->parseConstraint($constraint) as $parsedConstraint) {
|
245 |
+
$constraintObjects[] = $parsedConstraint;
|
246 |
+
}
|
247 |
+
}
|
248 |
+
} else {
|
249 |
+
$constraintObjects = $this->parseConstraint($andConstraints[0]);
|
250 |
+
}
|
251 |
+
|
252 |
+
if (1 === count($constraintObjects)) {
|
253 |
+
$constraint = $constraintObjects[0];
|
254 |
+
} else {
|
255 |
+
$constraint = new MultiConstraint($constraintObjects);
|
256 |
+
}
|
257 |
+
|
258 |
+
$orGroups[] = $constraint;
|
259 |
+
}
|
260 |
+
|
261 |
+
if (1 === count($orGroups)) {
|
262 |
+
$constraint = $orGroups[0];
|
263 |
+
} elseif (2 === count($orGroups)
|
264 |
+
// parse the two OR groups and if they are contiguous we collapse
|
265 |
+
// them into one constraint
|
266 |
+
&& $orGroups[0] instanceof MultiConstraint
|
267 |
+
&& $orGroups[1] instanceof MultiConstraint
|
268 |
+
&& 2 === count($orGroups[0]->getConstraints())
|
269 |
+
&& 2 === count($orGroups[1]->getConstraints())
|
270 |
+
&& ($a = (string) $orGroups[0])
|
271 |
+
&& substr($a, 0, 3) === '[>=' && (false !== ($posA = strpos($a, '<', 4)))
|
272 |
+
&& ($b = (string) $orGroups[1])
|
273 |
+
&& substr($b, 0, 3) === '[>=' && (false !== ($posB = strpos($b, '<', 4)))
|
274 |
+
&& substr($a, $posA + 2, -1) === substr($b, 4, $posB - 5)
|
275 |
+
) {
|
276 |
+
$constraint = new MultiConstraint(array(
|
277 |
+
new Constraint('>=', substr($a, 4, $posA - 5)),
|
278 |
+
new Constraint('<', substr($b, $posB + 2, -1)),
|
279 |
+
));
|
280 |
+
} else {
|
281 |
+
$constraint = new MultiConstraint($orGroups, false);
|
282 |
+
}
|
283 |
+
|
284 |
+
$constraint->setPrettyString($prettyConstraint);
|
285 |
+
|
286 |
+
return $constraint;
|
287 |
+
}
|
288 |
+
|
289 |
+
/**
|
290 |
+
* @param string $constraint
|
291 |
+
*
|
292 |
+
* @throws \UnexpectedValueException
|
293 |
+
*
|
294 |
+
* @return array
|
295 |
+
*/
|
296 |
+
private function parseConstraint($constraint)
|
297 |
+
{
|
298 |
+
if (preg_match('{^([^,\s]+?)@(' . implode('|', self::$stabilities) . ')$}i', $constraint, $match)) {
|
299 |
+
$constraint = $match[1];
|
300 |
+
if ($match[2] !== 'stable') {
|
301 |
+
$stabilityModifier = $match[2];
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
if (preg_match('{^v?[xX*](\.[xX*])*$}i', $constraint)) {
|
306 |
+
return array(new EmptyConstraint());
|
307 |
+
}
|
308 |
+
|
309 |
+
$versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?' . self::$modifierRegex . '(?:\+[^\s]+)?';
|
310 |
+
|
311 |
+
// Tilde Range
|
312 |
+
//
|
313 |
+
// Like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous
|
314 |
+
// version, to ensure that unstable instances of the current version are allowed. However, if a stability
|
315 |
+
// suffix is added to the constraint, then a >= match on the current version is used instead.
|
316 |
+
if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) {
|
317 |
+
if (substr($constraint, 0, 2) === '~>') {
|
318 |
+
throw new \UnexpectedValueException(
|
319 |
+
'Could not parse version constraint ' . $constraint . ': ' .
|
320 |
+
'Invalid operator "~>", you probably meant to use the "~" operator'
|
321 |
+
);
|
322 |
+
}
|
323 |
+
|
324 |
+
// Work out which position in the version we are operating at
|
325 |
+
if (isset($matches[4]) && '' !== $matches[4] && null !== $matches[4]) {
|
326 |
+
$position = 4;
|
327 |
+
} elseif (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
|
328 |
+
$position = 3;
|
329 |
+
} elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
|
330 |
+
$position = 2;
|
331 |
+
} else {
|
332 |
+
$position = 1;
|
333 |
+
}
|
334 |
+
|
335 |
+
// Calculate the stability suffix
|
336 |
+
$stabilitySuffix = '';
|
337 |
+
if (!empty($matches[5])) {
|
338 |
+
$stabilitySuffix .= '-' . $this->expandStability($matches[5]) . (!empty($matches[6]) ? $matches[6] : '');
|
339 |
+
}
|
340 |
+
|
341 |
+
if (!empty($matches[7])) {
|
342 |
+
$stabilitySuffix .= '-dev';
|
343 |
+
}
|
344 |
+
|
345 |
+
if (!$stabilitySuffix) {
|
346 |
+
$stabilitySuffix = '-dev';
|
347 |
+
}
|
348 |
+
|
349 |
+
$lowVersion = $this->manipulateVersionString($matches, $position, 0) . $stabilitySuffix;
|
350 |
+
$lowerBound = new Constraint('>=', $lowVersion);
|
351 |
+
|
352 |
+
// For upper bound, we increment the position of one more significance,
|
353 |
+
// but highPosition = 0 would be illegal
|
354 |
+
$highPosition = max(1, $position - 1);
|
355 |
+
$highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev';
|
356 |
+
$upperBound = new Constraint('<', $highVersion);
|
357 |
+
|
358 |
+
return array(
|
359 |
+
$lowerBound,
|
360 |
+
$upperBound,
|
361 |
+
);
|
362 |
+
}
|
363 |
+
|
364 |
+
// Caret Range
|
365 |
+
//
|
366 |
+
// Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple.
|
367 |
+
// In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for
|
368 |
+
// versions 0.X >=0.1.0, and no updates for versions 0.0.X
|
369 |
+
if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) {
|
370 |
+
// Work out which position in the version we are operating at
|
371 |
+
if ('0' !== $matches[1] || '' === $matches[2] || null === $matches[2]) {
|
372 |
+
$position = 1;
|
373 |
+
} elseif ('0' !== $matches[2] || '' === $matches[3] || null === $matches[3]) {
|
374 |
+
$position = 2;
|
375 |
+
} else {
|
376 |
+
$position = 3;
|
377 |
+
}
|
378 |
+
|
379 |
+
// Calculate the stability suffix
|
380 |
+
$stabilitySuffix = '';
|
381 |
+
if (empty($matches[5]) && empty($matches[7])) {
|
382 |
+
$stabilitySuffix .= '-dev';
|
383 |
+
}
|
384 |
+
|
385 |
+
$lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
|
386 |
+
$lowerBound = new Constraint('>=', $lowVersion);
|
387 |
+
|
388 |
+
// For upper bound, we increment the position of one more significance,
|
389 |
+
// but highPosition = 0 would be illegal
|
390 |
+
$highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
|
391 |
+
$upperBound = new Constraint('<', $highVersion);
|
392 |
+
|
393 |
+
return array(
|
394 |
+
$lowerBound,
|
395 |
+
$upperBound,
|
396 |
+
);
|
397 |
+
}
|
398 |
+
|
399 |
+
// X Range
|
400 |
+
//
|
401 |
+
// Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple.
|
402 |
+
// A partial version range is treated as an X-Range, so the special character is in fact optional.
|
403 |
+
if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) {
|
404 |
+
if (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
|
405 |
+
$position = 3;
|
406 |
+
} elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
|
407 |
+
$position = 2;
|
408 |
+
} else {
|
409 |
+
$position = 1;
|
410 |
+
}
|
411 |
+
|
412 |
+
$lowVersion = $this->manipulateVersionString($matches, $position) . '-dev';
|
413 |
+
$highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
|
414 |
+
|
415 |
+
if ($lowVersion === '0.0.0.0-dev') {
|
416 |
+
return array(new Constraint('<', $highVersion));
|
417 |
+
}
|
418 |
+
|
419 |
+
return array(
|
420 |
+
new Constraint('>=', $lowVersion),
|
421 |
+
new Constraint('<', $highVersion),
|
422 |
+
);
|
423 |
+
}
|
424 |
+
|
425 |
+
// Hyphen Range
|
426 |
+
//
|
427 |
+
// Specifies an inclusive set. If a partial version is provided as the first version in the inclusive range,
|
428 |
+
// then the missing pieces are replaced with zeroes. If a partial version is provided as the second version in
|
429 |
+
// the inclusive range, then all versions that start with the supplied parts of the tuple are accepted, but
|
430 |
+
// nothing that would be greater than the provided tuple parts.
|
431 |
+
if (preg_match('{^(?P<from>' . $versionRegex . ') +- +(?P<to>' . $versionRegex . ')($)}i', $constraint, $matches)) {
|
432 |
+
// Calculate the stability suffix
|
433 |
+
$lowStabilitySuffix = '';
|
434 |
+
if (empty($matches[6]) && empty($matches[8])) {
|
435 |
+
$lowStabilitySuffix = '-dev';
|
436 |
+
}
|
437 |
+
|
438 |
+
$lowVersion = $this->normalize($matches['from']);
|
439 |
+
$lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix);
|
440 |
+
|
441 |
+
$empty = function ($x) {
|
442 |
+
return ($x === 0 || $x === '0') ? false : empty($x);
|
443 |
+
};
|
444 |
+
|
445 |
+
if ((!$empty($matches[11]) && !$empty($matches[12])) || !empty($matches[14]) || !empty($matches[16])) {
|
446 |
+
$highVersion = $this->normalize($matches['to']);
|
447 |
+
$upperBound = new Constraint('<=', $highVersion);
|
448 |
+
} else {
|
449 |
+
$highMatch = array('', $matches[10], $matches[11], $matches[12], $matches[13]);
|
450 |
+
$highVersion = $this->manipulateVersionString($highMatch, $empty($matches[11]) ? 1 : 2, 1) . '-dev';
|
451 |
+
$upperBound = new Constraint('<', $highVersion);
|
452 |
+
}
|
453 |
+
|
454 |
+
return array(
|
455 |
+
$lowerBound,
|
456 |
+
$upperBound,
|
457 |
+
);
|
458 |
+
}
|
459 |
+
|
460 |
+
// Basic Comparators
|
461 |
+
if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
|
462 |
+
try {
|
463 |
+
$version = $this->normalize($matches[2]);
|
464 |
+
|
465 |
+
if (!empty($stabilityModifier) && $this->parseStability($version) === 'stable') {
|
466 |
+
$version .= '-' . $stabilityModifier;
|
467 |
+
} elseif ('<' === $matches[1] || '>=' === $matches[1]) {
|
468 |
+
if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
|
469 |
+
if (substr($matches[2], 0, 4) !== 'dev-') {
|
470 |
+
$version .= '-dev';
|
471 |
+
}
|
472 |
+
}
|
473 |
+
}
|
474 |
+
|
475 |
+
return array(new Constraint($matches[1] ?: '=', $version));
|
476 |
+
} catch (\Exception $e) {
|
477 |
+
}
|
478 |
+
}
|
479 |
+
|
480 |
+
$message = 'Could not parse version constraint ' . $constraint;
|
481 |
+
if (isset($e)) {
|
482 |
+
$message .= ': ' . $e->getMessage();
|
483 |
+
}
|
484 |
+
|
485 |
+
throw new \UnexpectedValueException($message);
|
486 |
+
}
|
487 |
+
|
488 |
+
/**
|
489 |
+
* Increment, decrement, or simply pad a version number.
|
490 |
+
*
|
491 |
+
* Support function for {@link parseConstraint()}
|
492 |
+
*
|
493 |
+
* @param array $matches Array with version parts in array indexes 1,2,3,4
|
494 |
+
* @param int $position 1,2,3,4 - which segment of the version to increment/decrement
|
495 |
+
* @param int $increment
|
496 |
+
* @param string $pad The string to pad version parts after $position
|
497 |
+
*
|
498 |
+
* @return string The new version
|
499 |
+
*/
|
500 |
+
private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0')
|
501 |
+
{
|
502 |
+
for ($i = 4; $i > 0; --$i) {
|
503 |
+
if ($i > $position) {
|
504 |
+
$matches[$i] = $pad;
|
505 |
+
} elseif ($i === $position && $increment) {
|
506 |
+
$matches[$i] += $increment;
|
507 |
+
// If $matches[$i] was 0, carry the decrement
|
508 |
+
if ($matches[$i] < 0) {
|
509 |
+
$matches[$i] = $pad;
|
510 |
+
--$position;
|
511 |
+
|
512 |
+
// Return null on a carry overflow
|
513 |
+
if ($i === 1) {
|
514 |
+
return;
|
515 |
+
}
|
516 |
+
}
|
517 |
+
}
|
518 |
+
}
|
519 |
+
|
520 |
+
return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
|
521 |
+
}
|
522 |
+
|
523 |
+
/**
|
524 |
+
* Expand shorthand stability string to long version.
|
525 |
+
*
|
526 |
+
* @param string $stability
|
527 |
+
*
|
528 |
+
* @return string
|
529 |
+
*/
|
530 |
+
private function expandStability($stability)
|
531 |
+
{
|
532 |
+
$stability = strtolower($stability);
|
533 |
+
|
534 |
+
switch ($stability) {
|
535 |
+
case 'a':
|
536 |
+
return 'alpha';
|
537 |
+
case 'b':
|
538 |
+
return 'beta';
|
539 |
+
case 'p':
|
540 |
+
case 'pl':
|
541 |
+
return 'patch';
|
542 |
+
case 'rc':
|
543 |
+
return 'RC';
|
544 |
+
default:
|
545 |
+
return $stability;
|
546 |
+
}
|
547 |
+
}
|
548 |
+
}
|