Advanced Access Manager - Version 6.0.0-alpha.2

Version Description

Download this release

Release Info

Developer vasyltech
Plugin Icon 128x128 Advanced Access Manager
Version 6.0.0-alpha.2
Comparing to
See all releases

Code changes from version 6.0.0-alpha.1 to 6.0.0-alpha.2

Files changed (200) hide show
  1. aam.php +2 -5
  2. application/Backend/Feature.php +77 -28
  3. application/Backend/Feature/Abstract.php +1 -1
  4. application/Backend/Feature/Main/404Redirect.php +1 -1
  5. application/Backend/Feature/Main/Capability.php +21 -6
  6. application/Backend/Feature/Main/Jwt.php +1 -1
  7. application/Backend/Feature/Main/LoginRedirect.php +1 -1
  8. application/Backend/Feature/Main/LogoutRedirect.php +1 -1
  9. application/Backend/Feature/Main/Menu.php +1 -1
  10. application/Backend/Feature/Main/Metabox.php +1 -1
  11. application/Backend/Feature/Main/Policy.php +195 -111
  12. application/Backend/Feature/Main/Post.php +32 -22
  13. application/Backend/Feature/Main/Redirect.php +1 -1
  14. application/Backend/Feature/Main/Route.php +1 -1
  15. application/Backend/Feature/Main/Toolbar.php +1 -1
  16. application/Backend/Feature/Main/Uri.php +14 -20
  17. application/Backend/Feature/Main/Welcome.php +1 -1
  18. application/Backend/Feature/Settings/ConfigPress.php +1 -1
  19. application/Backend/Feature/Settings/Content.php +1 -1
  20. application/Backend/Feature/Settings/Core.php +1 -1
  21. application/Backend/Feature/Settings/Manager.php +19 -2
  22. application/Backend/Feature/Settings/Security.php +1 -1
  23. application/Backend/Feature/Settings/Service.php +1 -1
  24. application/Backend/Feature/Subject/Role.php +23 -9
  25. application/Backend/Feature/Subject/User.php +30 -8
  26. application/Backend/Manager.php +1 -1
  27. application/Backend/Subject.php +2 -2
  28. application/Backend/View.php +104 -41
  29. application/Backend/View/Localization.php +2 -0
  30. application/Backend/Widget/Login.php +2 -2
  31. application/Backend/phtml/metabox/policy-metabox.phtml +0 -427
  32. application/Backend/phtml/metabox/policy-principal-metabox.phtml +0 -3
  33. application/Backend/{phtml/index.phtml → tmpl/index.php} +4 -6
  34. application/Backend/{phtml/metabox/iframe-footer.phtml → tmpl/metabox/iframe-footer.php} +0 -0
  35. application/Backend/{phtml/metabox/iframe-header.phtml → tmpl/metabox/iframe-header.php} +1 -1
  36. application/Backend/tmpl/metabox/policy-metabox.php +59 -0
  37. application/Backend/tmpl/metabox/policy-principal-metabox.php +3 -0
  38. application/Backend/{phtml/metabox/post-iframe.phtml → tmpl/metabox/post-iframe.php} +3 -3
  39. application/Backend/{phtml/metabox/post-metabox.phtml → tmpl/metabox/post-metabox.php} +0 -0
  40. application/Backend/tmpl/metabox/principal-iframe.php +12 -0
  41. application/Backend/{phtml/metabox/term-metabox.phtml → tmpl/metabox/term-metabox.php} +0 -0
  42. application/Backend/tmpl/metabox/user-iframe.php +21 -0
  43. application/Backend/tmpl/metabox/user-metabox.php +5 -0
  44. application/Backend/{phtml/page/addon-panel.phtml → tmpl/page/addon-panel.php} +0 -0
  45. application/Backend/{phtml/page/current-subject.phtml → tmpl/page/current-subject.php} +0 -0
  46. application/Backend/{phtml/page/main-panel.phtml → tmpl/page/main-panel.php} +0 -0
  47. application/Backend/{phtml/page/subject-panel-advanced.phtml → tmpl/page/subject-panel-advanced.php} +0 -0
  48. application/Backend/{phtml/page/subject-panel.phtml → tmpl/page/subject-panel.php} +7 -13
  49. application/Backend/tmpl/partial/default-principal-subject-tab.php +9 -0
  50. application/Backend/tmpl/partial/default-subject-tab.php +8 -0
  51. application/Backend/{phtml/partial/jwt-login-url.phtml → tmpl/partial/jwt-login-url.php} +0 -0
  52. application/Backend/tmpl/partial/loading-content.php +7 -0
  53. application/Backend/{phtml/partial/post-access-form.phtml → tmpl/partial/post-access-form.php} +10 -1
  54. application/Backend/{phtml/partial/posts-terms-help-tips.phtml → tmpl/partial/posts-terms-help-tips.php} +0 -0
  55. application/Backend/{phtml/partial/role-inheritance.phtml → tmpl/partial/role-inheritance.php} +0 -0
  56. application/Backend/{phtml/partial/taxonomy-access-form.phtml → tmpl/partial/taxonomy-access-form.php} +0 -0
  57. application/Backend/{phtml/partial/term-access-form.phtml → tmpl/partial/term-access-form.php} +0 -0
  58. application/Backend/{phtml/partial/type-access-form.phtml → tmpl/partial/type-access-form.php} +0 -0
  59. application/Backend/tmpl/partial/visitor-principal-subject-tab.php +17 -0
  60. application/Backend/tmpl/partial/visitor-subject-tab.php +8 -0
  61. application/Backend/tmpl/policy/default-policy.php +27 -0
  62. application/Backend/{phtml/service/404redirect.phtml → tmpl/service/404redirect.php} +0 -0
  63. application/Backend/{phtml/service/capability.phtml → tmpl/service/capability.php} +0 -0
  64. application/Backend/{phtml/service/jwt.phtml → tmpl/service/jwt.php} +0 -0
  65. application/Backend/{phtml/service/login-redirect.phtml → tmpl/service/login-redirect.php} +0 -0
  66. application/Backend/{phtml/service/logout-redirect.phtml → tmpl/service/logout-redirect.php} +0 -0
  67. application/Backend/{phtml/service/menu.phtml → tmpl/service/menu.php} +5 -1
  68. application/Backend/{phtml/service/metabox.phtml → tmpl/service/metabox.php} +7 -3
  69. application/Backend/{phtml/service/policy.phtml → tmpl/service/policy.php} +1 -1
  70. application/Backend/{phtml/service/post.phtml → tmpl/service/post.php} +0 -0
  71. application/Backend/{phtml/service/redirect.phtml → tmpl/service/redirect.php} +0 -0
  72. application/Backend/{phtml/service/route.phtml → tmpl/service/route.php} +0 -0
  73. application/Backend/{phtml/service/toolbar.phtml → tmpl/service/toolbar.php} +5 -5
  74. application/Backend/{phtml/service/uri.phtml → tmpl/service/uri.php} +0 -1
  75. application/Backend/{phtml/service/welcome.phtml → tmpl/service/welcome.php} +0 -0
  76. application/Backend/{phtml/settings/configpress.phtml → tmpl/settings/configpress.php} +0 -0
  77. application/Backend/{phtml/settings/content.phtml → tmpl/settings/content.php} +0 -0
  78. application/Backend/{phtml/settings/core.phtml → tmpl/settings/core.php} +0 -0
  79. application/Backend/{phtml/settings/security.phtml → tmpl/settings/security.php} +0 -0
  80. application/Backend/{phtml/settings/service.phtml → tmpl/settings/service.php} +1 -1
  81. application/Backend/{phtml/user/multiple-roles.phtml → tmpl/user/multiple-roles.php} +0 -0
  82. application/Backend/{phtml/widget/login-backend.phtml → tmpl/widget/login-backend.php} +0 -0
  83. application/Backend/{phtml/widget/login-frontend.phtml → tmpl/widget/login-frontend.php} +0 -0
  84. application/Core/API.php +11 -72
  85. application/Core/ConfigPress/Reader.php +4 -4
  86. application/Core/Contract/RequestTrait.php +62 -5
  87. application/Core/Gateway.php +5 -5
  88. application/Core/Jwt/Issuer.php +2 -2
  89. application/Core/Migration/2019_06_30-migrate-settings-to-6.0.0.php +4 -1
  90. application/Core/Object.php +65 -4
  91. application/Core/Object/LoginRedirect.php +1 -3
  92. application/Core/Object/LogoutRedirect.php +1 -3
  93. application/Core/Object/Menu.php +2 -6
  94. application/Core/Object/Metabox.php +4 -17
  95. application/Core/Object/Policy.php +16 -32
  96. application/Core/Object/Post.php +11 -46
  97. application/Core/Object/Redirect.php +1 -4
  98. application/Core/Object/Route.php +1 -3
  99. application/Core/Object/Toolbar.php +3 -5
  100. application/Core/Object/Uri.php +19 -23
  101. application/Core/Object/Visibility.php +47 -6
  102. application/Core/Policy/Condition.php +91 -87
  103. application/Core/Policy/Factory.php +39 -21
  104. application/Core/Policy/Manager.php +319 -284
  105. application/Core/Policy/Resource.php +82 -0
  106. application/Core/Policy/Token.php +50 -41
  107. application/Core/Policy/Validator.php +116 -62
  108. application/Core/Subject.php +2 -2
  109. application/Core/Subject/Role.php +19 -10
  110. application/Core/Subject/User.php +16 -8
  111. application/Service/AccessPolicy.php +434 -489
  112. application/Service/Content.php +38 -11
  113. application/Service/Core.php +28 -0
  114. application/Service/ExtendedCapabilities.php +3 -2
  115. application/Service/Jwt.php +46 -9
  116. application/Service/Metabox.php +2 -2
  117. application/Service/Route.php +1 -1
  118. application/Service/SecureLogin.php +4 -5
  119. application/Service/Settings.php +1 -0
  120. application/Service/Toolbar.php +2 -2
  121. application/Service/UserLevelFilter.php +42 -18
  122. application/Shortcode/Factory.php +2 -2
  123. lang/advanced-access-manager-en_US.po +1068 -989
  124. media/css/aam.css +16 -0
  125. media/css/vendor.min.css +6 -6
  126. media/js/aam.js +79 -49
  127. tests/Addon/IpCheck/IpCheckTest.php +343 -0
  128. tests/Addon/PlusPackage/ContentAccessTest.php +451 -0
  129. tests/Addon/PlusPackage/ContentVisibilityTest.php +204 -0
  130. tests/Addon/PlusPackage/DefaultCategoryTest.php +226 -0
  131. tests/Addon/PlusPackage/TermRESTfulAccessTest.php +221 -0
  132. tests/Addon/PlusPackage/UriAccessTest.php +101 -0
  133. tests/Addon/RoleHierarchy/RoleHierarchyTest.php +58 -0
  134. tests/Core/GatewayTest.php +90 -0
  135. tests/Core/SubjectLoadTest.php +32 -0
  136. tests/Libs/AuthManagerUserTrait.php +36 -0
  137. tests/Libs/AuthMultiRoleUserTrait.php +57 -0
  138. tests/Libs/AuthUserTrait.php +37 -0
  139. tests/Libs/MultiRoleOptionInterface.php +17 -0
  140. tests/Libs/ResetTrait.php +92 -0
  141. tests/Service/AccessPolicy/PolicyConditionTest.php +543 -0
  142. tests/Service/AccessPolicy/PolicyManagerTest.php +170 -0
  143. tests/Service/AccessPolicy/PolicyServiceIntegrationTest.php +413 -0
  144. tests/Service/AccessPolicy/PolicyTokenTest.php +236 -0
  145. tests/Service/AccessPolicy/PolicyUserRoleIntegrationTest.php +143 -0
  146. tests/Service/AccessPolicy/PolicyValidationTest.php +125 -0
  147. tests/Service/AccessPolicy/policies/admin-menu.json +11 -0
  148. tests/Service/AccessPolicy/policies/capability-changes.json +17 -0
  149. tests/Service/AccessPolicy/policies/dynamic-param.json +9 -0
  150. tests/Service/AccessPolicy/policies/dynamic-resource.json +12 -0
  151. tests/Service/AccessPolicy/policies/metabox.json +12 -0
  152. tests/Service/AccessPolicy/policies/option-override-policy.json +9 -0
  153. tests/Service/AccessPolicy/policies/plugins.json +8 -0
  154. tests/Service/AccessPolicy/policies/post-complex-actions.json +18 -0
  155. tests/Service/AccessPolicy/policies/post-hidden.json +10 -0
  156. tests/Service/AccessPolicy/policies/post-redirect-callback.json +16 -0
  157. tests/Service/AccessPolicy/policies/post-redirect-page-id.json +17 -0
  158. tests/Service/AccessPolicy/policies/post-redirect-page-slug.json +17 -0
  159. tests/Service/AccessPolicy/policies/post-redirect-url.json +14 -0
  160. tests/Service/AccessPolicy/policies/post-restricted.json +10 -0
  161. tests/Service/AccessPolicy/policies/post-simple-actions.json +10 -0
  162. tests/Service/AccessPolicy/policies/role-add.json +11 -0
  163. tests/Service/AccessPolicy/policies/role-remove.json +11 -0
  164. tests/Service/AccessPolicy/policies/simple-policy-with-action.json +12 -0
  165. tests/Service/AccessPolicy/policies/simple-policy.json +9 -0
  166. tests/Service/AccessPolicy/policies/single-plugin.json +10 -0
  167. tests/Service/AccessPolicy/policies/toolbar.json +11 -0
  168. tests/Service/AccessPolicy/policies/uri.json +57 -0
  169. tests/Service/AdminMenu/MultipleRoleInheritanceTest.php +182 -0
  170. tests/Service/AdminMenu/SingleRoleInheritanceTest.php +226 -0
  171. tests/Service/Capabilities/CapabilityManagerTest.php +345 -0
  172. tests/Service/Content/Callback.php +13 -0
  173. tests/Service/Content/MultipleRoleInheritanceTest.php +190 -0
  174. tests/Service/Content/RESTfulSingleRoleAccessControlTest.php +579 -0
  175. tests/Service/Content/SingleRoleAccessControlTest.php +602 -0
  176. tests/Service/Content/SingleRoleInheritanceTest.php +221 -0
  177. tests/Service/Content/VisitorAccessControlTest.php +432 -0
  178. tests/Service/Core/CoreServiceTest.php +43 -0
  179. tests/Service/DeniedRedirect/Callback.php +14 -0
  180. tests/Service/DeniedRedirect/DeniedRedirectTest.php +189 -0
  181. tests/Service/Jwt/JwtTest.php +360 -0
  182. tests/Service/LoginRedirect/Callback.php +14 -0
  183. tests/Service/LoginRedirect/LoginRedirectTest.php +215 -0
  184. tests/Service/LogoutRedirect/Callback.php +14 -0
  185. tests/Service/LogoutRedirect/LogoutRedirectTest.php +115 -0
  186. tests/Service/Metabox/MultipleRoleInheritanceTest.php +192 -0
  187. tests/Service/Metabox/SingleRoleInheritanceTest.php +231 -0
  188. tests/Service/Metabox/VisitorInheritanceTest.php +187 -0
  189. tests/Service/NotFoundRedirect/Callback.php +14 -0
  190. tests/Service/NotFoundRedirect/NotFoundRedirectTest.php +141 -0
  191. tests/Service/Route/RouteTest.php +99 -0
  192. tests/Service/SecureLogin/SecureLoginTest.php +151 -0
  193. tests/Service/Toolbar/MultipleRoleInheritanceTest.php +179 -0
  194. tests/Service/Toolbar/SingleRoleInheritanceTest.php +225 -0
  195. tests/Service/Uri/Callback.php +14 -0
  196. tests/Service/Uri/UriTest.php +177 -0
  197. tests/Service/UserLevelFilter/UserLevelFilterTest.php +151 -0
  198. tests/bootstrap.php +27 -0
  199. vendor/composer/VersionParser.php +10 -0
  200. vendor/firebase/JWT.php +2 -0
aam.php CHANGED
@@ -3,7 +3,7 @@
3
  /**
4
  * Plugin Name: Advanced Access Manager
5
  * Description: Collection of features to manage your WordPress website authentication, authorization and monitoring
6
- * Version: 6.0.0-alpha.1
7
  * Author: Vasyl Martyniuk <vasyl@vasyltech.com>
8
  * Author URI: https://vasyltech.com
9
  * Text Domain: advanced-access-manager
@@ -118,7 +118,7 @@ class AAM
118
 
119
  // Change current user
120
  if ($id) {
121
- $this->setUser(new AAM_Core_Subject_User($id));
122
  } else {
123
  $this->setUser(new AAM_Core_Subject_Visitor());
124
  }
@@ -246,9 +246,6 @@ class AAM
246
 
247
  //clear all AAM settings
248
  AAM_Core_API::clearSettings();
249
-
250
- //clear schedules
251
- wp_clear_scheduled_hook('aam-cron');
252
  }
253
 
254
  }
3
  /**
4
  * Plugin Name: Advanced Access Manager
5
  * Description: Collection of features to manage your WordPress website authentication, authorization and monitoring
6
+ * Version: 6.0.0-alpha.2
7
  * Author: Vasyl Martyniuk <vasyl@vasyltech.com>
8
  * Author URI: https://vasyltech.com
9
  * Text Domain: advanced-access-manager
118
 
119
  // Change current user
120
  if ($id) {
121
+ $this->setUser(self::api()->getUser($id));
122
  } else {
123
  $this->setUser(new AAM_Core_Subject_Visitor());
124
  }
246
 
247
  //clear all AAM settings
248
  AAM_Core_API::clearSettings();
 
 
 
249
  }
250
 
251
  }
application/Backend/Feature.php CHANGED
@@ -44,6 +44,7 @@ class AAM_Backend_Feature
44
  public static function registerFeature($feature)
45
  {
46
  $response = false;
 
47
 
48
  // Determine correct AAM UI capability
49
  if (empty($feature->capability)) {
@@ -60,13 +61,25 @@ class AAM_Backend_Feature
60
  }
61
 
62
  // Determine that current user has enough user level to manage
63
- // requested subject
64
- $allowed = AAM_Core_API::isUserLevelAllowed(
65
- AAM_Backend_Subject::getInstance()->getSubject()->getMaxLevel()
66
- );
 
 
 
 
 
67
 
68
  if ($show && $allowed && current_user_can($cap)) {
69
- self::$_features[] = $feature;
 
 
 
 
 
 
 
70
  $response = true;
71
  }
72
 
@@ -74,46 +87,39 @@ class AAM_Backend_Feature
74
  }
75
 
76
  /**
77
- * Check if feature is visible
78
  *
79
- * There is a way to show/hide feature based on the option. For example some
80
- * features should be visible only when Backend Access options is enabled.
81
- *
82
- * @param string $options
83
  *
84
- * @return boolean
85
  *
86
- * @access protected
87
  * @version 6.0.0
88
  */
89
- protected static function isVisible($options)
90
  {
91
- $count = 0;
92
-
93
- foreach (explode(',', $options) as $option) {
94
- $count += AAM_Core_Config::get($option, true);
95
  }
96
 
97
- return ($count > 0);
98
  }
99
 
100
  /**
101
- * Initiate the view controller
102
  *
103
- * @param object $feature
104
  *
105
- * @return AAM_Backend_Feature_Abstract
106
  *
107
  * @access public
108
  * @version 6.0.0
109
  */
110
- public static function initView($feature)
111
  {
112
- if (is_string($feature->view)) {
113
- $feature->view = new $feature->view(AAM_Backend_Subject::getInstance());
114
- }
115
-
116
- return $feature;
117
  }
118
 
119
  /**
@@ -131,8 +137,8 @@ class AAM_Backend_Feature
131
  public static function retrieveList($type)
132
  {
133
  $response = array();
 
134
 
135
- $subject = AAM_Backend_Subject::getInstance()->getSubjectType();
136
  foreach (self::$_features as $feature) {
137
  if (
138
  $feature->type === $type
@@ -158,4 +164,47 @@ class AAM_Backend_Feature
158
  return $response;
159
  }
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  }
44
  public static function registerFeature($feature)
45
  {
46
  $response = false;
47
+ $subject = AAM_Backend_Subject::getInstance();
48
 
49
  // Determine correct AAM UI capability
50
  if (empty($feature->capability)) {
61
  }
62
 
63
  // Determine that current user has enough user level to manage
64
+ // requested subject but only if it is manages settings for individual
65
+ // subjects
66
+ if (!empty($feature->subjects)) {
67
+ $allowed = apply_filters(
68
+ 'aam_user_can_manage_level_filter', true, $subject->getSubject()->getMaxLevel()
69
+ );
70
+ } else { // Other allow because access to the feature is managed with cap
71
+ $allowed = true;
72
+ }
73
 
74
  if ($show && $allowed && current_user_can($cap)) {
75
+ if (is_object($feature->view)) {
76
+ self::$_features[get_class($feature->view)] = $feature;
77
+ } else {
78
+ self::$_features[$feature->view] = $feature;
79
+ // Initialize view manage so it can register any necessary hooks
80
+ $feature->view = new $feature->view($subject);
81
+ }
82
+
83
  $response = true;
84
  }
85
 
87
  }
88
 
89
  /**
90
+ * Get feature view manager
91
  *
92
+ * @param string $id
 
 
 
93
  *
94
+ * @return object
95
  *
96
+ * @access public
97
  * @version 6.0.0
98
  */
99
+ public static function getFeatureView($id)
100
  {
101
+ if (self::isFeatureRegistered($id)) {
102
+ $view = self::$_features[$id]->view;
103
+ } else {
104
+ $view = null;
105
  }
106
 
107
+ return $view;
108
  }
109
 
110
  /**
111
+ * Check if feature is registered
112
  *
113
+ * @param string $id
114
  *
115
+ * @return boolean
116
  *
117
  * @access public
118
  * @version 6.0.0
119
  */
120
+ public static function isFeatureRegistered($id)
121
  {
122
+ return array_key_exists($id, self::$_features);
 
 
 
 
123
  }
124
 
125
  /**
137
  public static function retrieveList($type)
138
  {
139
  $response = array();
140
+ $subject = AAM_Backend_Subject::getInstance()->getSubjectType();
141
 
 
142
  foreach (self::$_features as $feature) {
143
  if (
144
  $feature->type === $type
164
  return $response;
165
  }
166
 
167
+ /**
168
+ * Check if feature is visible
169
+ *
170
+ * There is a way to show/hide feature based on the option. For example some
171
+ * features should be visible only when Backend Access options is enabled.
172
+ *
173
+ * @param string $options
174
+ *
175
+ * @return boolean
176
+ *
177
+ * @access protected
178
+ * @version 6.0.0
179
+ */
180
+ protected static function isVisible($options)
181
+ {
182
+ $count = 0;
183
+
184
+ foreach (explode(',', $options) as $option) {
185
+ $count += AAM_Core_Config::get($option, true);
186
+ }
187
+
188
+ return ($count > 0);
189
+ }
190
+
191
+ /**
192
+ * Initiate the view controller
193
+ *
194
+ * @param object $feature
195
+ *
196
+ * @return array
197
+ *
198
+ * @access protected
199
+ * @version 6.0.0
200
+ */
201
+ protected static function initView($feature)
202
+ {
203
+ if (is_string($feature->view)) {
204
+ $feature->view = new $feature->view(AAM_Backend_Subject::getInstance());
205
+ }
206
+
207
+ return $feature;
208
+ }
209
+
210
  }
application/Backend/Feature/Abstract.php CHANGED
@@ -104,7 +104,7 @@ abstract class AAM_Backend_Feature_Abstract
104
  public function getContent()
105
  {
106
  ob_start();
107
- require_once(dirname(__DIR__) . '/phtml/' . static::TEMPLATE);
108
  $content = ob_get_contents();
109
  ob_end_clean();
110
 
104
  public function getContent()
105
  {
106
  ob_start();
107
+ require_once(dirname(__DIR__) . '/tmpl/' . static::TEMPLATE);
108
  $content = ob_get_contents();
109
  ob_end_clean();
110
 
application/Backend/Feature/Main/404Redirect.php CHANGED
@@ -33,7 +33,7 @@ class AAM_Backend_Feature_Main_404Redirect
33
  *
34
  * @version 6.0.0
35
  */
36
- const TEMPLATE = 'service/404redirect.phtml';
37
 
38
  /**
39
  * Save 404 redirect options
33
  *
34
  * @version 6.0.0
35
  */
36
+ const TEMPLATE = 'service/404redirect.php';
37
 
38
  /**
39
  * Save 404 redirect options
application/Backend/Feature/Main/Capability.php CHANGED
@@ -31,7 +31,7 @@ class AAM_Backend_Feature_Main_Capability
31
  *
32
  * @version 6.0.0
33
  */
34
- const TEMPLATE = 'service/capability.phtml';
35
 
36
  /**
37
  * Capability groups
@@ -83,7 +83,7 @@ class AAM_Backend_Feature_Main_Capability
83
  $effect = $this->getFromPost('effect', FILTER_VALIDATE_BOOLEAN);
84
  $assign = $this->getFromPost('assignToMe', FILTER_VALIDATE_BOOLEAN);
85
 
86
- if ($cap) {
87
  // Add capability to current user if checkbox checked
88
  if ($assign === true) {
89
  AAM::getUser()->addCapability($cap);
@@ -186,7 +186,7 @@ class AAM_Backend_Feature_Main_Capability
186
  $caps = array_merge($caps, $this->getSubject()->getCapabilities());
187
 
188
  foreach (array_keys($caps) as $cap) {
189
- if (apply_filters('aam_allowed_cap_filter', true, $cap, 'list') !== false) {
190
  $data[] = array(
191
  $cap,
192
  $this->getGroup($cap),
@@ -219,7 +219,7 @@ class AAM_Backend_Feature_Main_Capability
219
 
220
  $toggle = ($subject->hasCapability($cap) ? 'checked' : 'unchecked');
221
 
222
- if (apply_filters('aam_allowed_cap_filter', true, $cap, 'toggle') === false) {
223
  $toggle = 'no-' . $toggle;
224
  }
225
 
@@ -242,6 +242,21 @@ class AAM_Backend_Feature_Main_Capability
242
  return implode(',', $actions);
243
  }
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  /**
246
  * Check if current user can edit capability
247
  *
@@ -261,7 +276,7 @@ class AAM_Backend_Feature_Main_Capability
261
  }
262
 
263
  // Access & Security policy has higher priority
264
- if (apply_filters('aam_allowed_cap_filter', true, $cap, 'update') === false) {
265
  $allowed = false;
266
  }
267
 
@@ -293,7 +308,7 @@ class AAM_Backend_Feature_Main_Capability
293
  }
294
 
295
  // Access & Security policy has higher priority
296
- if (apply_filters('aam_allowed_cap_filter', true, $cap, 'delete') === false) {
297
  $allowed = false;
298
  }
299
 
31
  *
32
  * @version 6.0.0
33
  */
34
+ const TEMPLATE = 'service/capability.php';
35
 
36
  /**
37
  * Capability groups
83
  $effect = $this->getFromPost('effect', FILTER_VALIDATE_BOOLEAN);
84
  $assign = $this->getFromPost('assignToMe', FILTER_VALIDATE_BOOLEAN);
85
 
86
+ if ($cap && $this->isAllowedToToggle($cap)) {
87
  // Add capability to current user if checkbox checked
88
  if ($assign === true) {
89
  AAM::getUser()->addCapability($cap);
186
  $caps = array_merge($caps, $this->getSubject()->getCapabilities());
187
 
188
  foreach (array_keys($caps) as $cap) {
189
+ if (apply_filters('aam_cap_can_filter', true, $cap, 'list') !== false) {
190
  $data[] = array(
191
  $cap,
192
  $this->getGroup($cap),
219
 
220
  $toggle = ($subject->hasCapability($cap) ? 'checked' : 'unchecked');
221
 
222
+ if ($this->isAllowedToToggle($cap) === false) {
223
  $toggle = 'no-' . $toggle;
224
  }
225
 
242
  return implode(',', $actions);
243
  }
244
 
245
+ /**
246
+ * Check if current user is allowed to toggle capability
247
+ *
248
+ * @param string $cap
249
+ *
250
+ * @return boolean
251
+ *
252
+ * @access protected
253
+ * @version 6.0.0
254
+ */
255
+ protected function isAllowedToToggle($cap)
256
+ {
257
+ return apply_filters('aam_cap_can_filter', true, $cap, 'toggle');
258
+ }
259
+
260
  /**
261
  * Check if current user can edit capability
262
  *
276
  }
277
 
278
  // Access & Security policy has higher priority
279
+ if (apply_filters('aam_cap_can_filter', true, $cap, 'update') === false) {
280
  $allowed = false;
281
  }
282
 
308
  }
309
 
310
  // Access & Security policy has higher priority
311
+ if (apply_filters('aam_cap_can_filter', true, $cap, 'delete') === false) {
312
  $allowed = false;
313
  }
314
 
application/Backend/Feature/Main/Jwt.php CHANGED
@@ -33,7 +33,7 @@ class AAM_Backend_Feature_Main_Jwt
33
  *
34
  * @version 6.0.0
35
  */
36
- const TEMPLATE = 'service/jwt.phtml';
37
 
38
  /**
39
  * Get list of tokens
33
  *
34
  * @version 6.0.0
35
  */
36
+ const TEMPLATE = 'service/jwt.php';
37
 
38
  /**
39
  * Get list of tokens
application/Backend/Feature/Main/LoginRedirect.php CHANGED
@@ -38,7 +38,7 @@ class AAM_Backend_Feature_Main_LoginRedirect
38
  *
39
  * @version 6.0.0
40
  */
41
- const TEMPLATE = 'service/login-redirect.phtml';
42
 
43
  /**
44
  * Get option value
38
  *
39
  * @version 6.0.0
40
  */
41
+ const TEMPLATE = 'service/login-redirect.php';
42
 
43
  /**
44
  * Get option value
application/Backend/Feature/Main/LogoutRedirect.php CHANGED
@@ -38,7 +38,7 @@ class AAM_Backend_Feature_Main_LogoutRedirect
38
  *
39
  * @version 6.0.0
40
  */
41
- const TEMPLATE = 'service/logout-redirect.phtml';
42
 
43
  /**
44
  * Get option value
38
  *
39
  * @version 6.0.0
40
  */
41
+ const TEMPLATE = 'service/logout-redirect.php';
42
 
43
  /**
44
  * Get option value
application/Backend/Feature/Main/Menu.php CHANGED
@@ -38,7 +38,7 @@ class AAM_Backend_Feature_Main_Menu
38
  *
39
  * @version 6.0.0
40
  */
41
- const TEMPLATE = 'service/menu.phtml';
42
 
43
  /**
44
  * Save menu settings
38
  *
39
  * @version 6.0.0
40
  */
41
+ const TEMPLATE = 'service/menu.php';
42
 
43
  /**
44
  * Save menu settings
application/Backend/Feature/Main/Metabox.php CHANGED
@@ -45,7 +45,7 @@ class AAM_Backend_Feature_Main_Metabox
45
  *
46
  * @version 6.0.0
47
  */
48
- const TEMPLATE = 'service/metabox.phtml';
49
 
50
  /**
51
  * Save metabox access settings
45
  *
46
  * @version 6.0.0
47
  */
48
+ const TEMPLATE = 'service/metabox.php';
49
 
50
  /**
51
  * Save metabox access settings
application/Backend/Feature/Main/Policy.php CHANGED
@@ -5,143 +5,170 @@
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
  /**
11
- * WordPress API manager
12
- *
13
  * @package AAM
14
- * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
  */
16
- class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract implements AAM_Backend_Feature_ISubjectAware
 
17
  {
18
 
 
 
19
  /**
20
  * Default access capability to the feature
 
 
21
  */
22
  const ACCESS_CAPABILITY = 'aam_manage_policy';
23
 
24
  /**
25
- *
26
- * @return type
 
27
  */
28
- public function getTable()
29
- {
30
- return wp_json_encode($this->retrievePolicies());
31
- }
32
 
33
  /**
34
- * Install policy
35
- *
36
- * @return string
37
- *
 
 
 
 
 
 
 
38
  * @access public
39
- * @since v5.7.3
40
  */
41
- public function install()
42
  {
43
- $package = (object)AAM_Core_Request::post('package');
44
-
45
- if (!empty($package->content)) {
46
- $json = base64_decode($package->content);
47
-
48
- $result = wp_insert_post(array(
49
- 'post_author' => get_current_user_id(),
50
- 'post_content' => $json,
51
- 'post_title' => $package->title,
52
- 'post_excerpt' => $package->description,
53
- 'post_status' => 'publish',
54
- 'post_type' => 'aam_policy'
55
- ));
56
-
57
- if (!is_wp_error($result)) {
58
- $response = array('status' => 'success');
59
- } else {
60
- $response = array(
61
- 'status' => 'failure', 'reason' => $result->get_error_message()
62
  );
63
  }
64
- } else {
65
- $response = array(
66
- 'status' => 'failure',
67
- 'reason' => __('Failed to fetch policy. Please try again.', AAM_KEY)
68
- );
69
- }
70
 
71
- return wp_json_encode($response);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
 
74
  /**
75
- * Save post properties
76
- *
 
 
 
 
77
  * @return string
78
- *
79
  * @access public
 
80
  */
81
- public function save()
82
  {
83
- $subject = AAM_Backend_Subject::getInstance();
84
- $id = AAM_Core_Request::post('id');
85
- $effect = AAM_Core_Request::post('effect');
86
-
87
- $action = (!empty($effect) ? 'attach' : 'detach');
88
-
89
- // Verify that current user can perform following action
90
- if (AAM_Core_Policy_Factory::get()->canTogglePolicy($id, $action)) {
91
- $object = $subject->getObject('policy', null, true);
92
- $result = $object->updateOptionItem($id, $effect)->save();
93
- } else {
94
- $result = false;
95
  }
96
 
97
- return wp_json_encode(array(
98
- 'status' => ($result ? 'success' : 'failure')
99
- ));
100
  }
101
 
102
  /**
103
- *
104
- * @return type
 
 
 
 
 
 
 
105
  */
106
- public function reset()
107
  {
108
- $object = AAM_Backend_Subject::getInstance()->getObject('policy');
109
-
110
- return $object->reset();
111
- }
 
 
 
112
 
113
- /**
114
- * @inheritdoc
115
- */
116
- public static function getTemplate()
117
- {
118
- return 'service/policy.phtml';
119
  }
120
 
121
  /**
122
- * Check inheritance status
123
- *
124
- * Check if menu settings are overwritten
125
- *
126
- * @return boolean
127
- *
128
- * @access protected
 
 
129
  */
130
- protected function isOverwritten()
131
  {
132
- $object = AAM_Backend_Subject::getInstance()->getObject('policy');
 
 
 
 
133
 
134
- return $object->isOverwritten();
135
  }
136
 
137
  /**
138
- *
139
- * @return type
 
 
 
 
140
  */
141
- protected function retrievePolicies()
142
  {
143
  $list = get_posts(array(
144
- 'post_type' => 'aam_policy',
145
  'numberposts' => -1,
146
  'post_status' => 'publish'
147
  ));
@@ -149,7 +176,7 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract imple
149
  $response = array(
150
  'recordsTotal' => count($list),
151
  'recordsFiltered' => count($list),
152
- 'draw' => AAM_Core_Request::request('draw'),
153
  'data' => array(),
154
  );
155
 
@@ -159,24 +186,34 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract imple
159
  if ($policy) {
160
  $response['data'][] = array(
161
  $record->ID,
162
- $this->buildTitle($record),
163
- $this->buildActionList($record),
164
  get_edit_post_link($record->ID, 'link')
165
  );
166
  }
167
  }
168
 
169
- return $response;
170
  }
171
 
172
  /**
173
- *
174
- * @param type $record
 
 
175
  * @return string
 
 
 
176
  */
177
- protected function buildTitle($record)
178
  {
179
- $title = (!empty($record->post_title) ? $record->post_title : __('(no title)'));
 
 
 
 
 
180
  $title .= '<br/>';
181
 
182
  if (isset($record->post_excerpt)) {
@@ -187,22 +224,24 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract imple
187
  }
188
 
189
  /**
190
- *
191
- * @param type $record
192
- * @return type
 
 
 
 
 
193
  */
194
- protected function buildActionList($record)
195
  {
196
- //'assign,edit,clone,delete'
197
  $subject = AAM_Backend_Subject::getInstance();
198
- $policy = $subject->getObject('policy');
199
- $post = $subject->getObject('post', $record->ID);
200
 
201
- $action = $policy->has($record->ID) ? 'detach' : 'attach';
202
- $prefix = AAM_Core_Policy_Factory::get()->canTogglePolicy($record->ID, $action) ? '' : 'no-';
203
 
204
  $actions = array(
205
- $policy->has($record->ID) ? "{$prefix}detach" : "{$prefix}attach",
206
  $post->isAllowedTo('edit') ? 'edit' : 'no-edit'
207
  );
208
 
@@ -210,15 +249,59 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract imple
210
  }
211
 
212
  /**
213
- * Register Menu feature
214
- *
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  * @return void
216
- *
217
  * @access public
 
218
  */
219
  public static function register()
220
  {
221
- AAM_Backend_Feature::registerFeature((object)array(
222
  'uid' => 'policy',
223
  'position' => 2,
224
  'title' => __('Access Policies', AAM_KEY),
@@ -233,4 +316,5 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract imple
233
  'view' => __CLASS__
234
  ));
235
  }
236
- }
 
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
+ * @version 6.0.0
10
  */
11
 
12
  /**
13
+ * Access Policy UI manager
14
+ *
15
  * @package AAM
16
+ * @version 6.0.0
17
  */
18
+ class AAM_Backend_Feature_Main_Policy
19
+ extends AAM_Backend_Feature_Abstract implements AAM_Backend_Feature_ISubjectAware
20
  {
21
 
22
+ use AAM_Core_Contract_RequestTrait;
23
+
24
  /**
25
  * Default access capability to the feature
26
+ *
27
+ * @version 6.0.0
28
  */
29
  const ACCESS_CAPABILITY = 'aam_manage_policy';
30
 
31
  /**
32
+ * Type of AAM core object
33
+ *
34
+ * @version 6.0.0
35
  */
36
+ const OBJECT_TYPE = AAM_Core_Object_Policy::OBJECT_TYPE;
 
 
 
37
 
38
  /**
39
+ * HTML template to render
40
+ *
41
+ * @version 6.0.0
42
+ */
43
+ const TEMPLATE = 'service/policy.php';
44
+
45
+ /**
46
+ * Constructor
47
+ *
48
+ * @return void
49
+ *
50
  * @access public
51
+ * @version 6.0.0
52
  */
53
+ public function __construct()
54
  {
55
+ add_filter('aam_iframe_content_filter', array($this, 'renderPrincipalIframe'), 1, 3);
56
+ add_filter('aam_role_row_actions_filter', array($this, 'renderRoleActions'), 1, 2);
57
+ add_filter('aam_user_row_actions_filter', array($this, 'renderUserActions'), 1, 2);
58
+
59
+ add_filter('aam_visitor_subject_tab_filter', function ($content, $params) {
60
+ global $post;
61
+
62
+ if (is_a($post, 'WP_Post')
63
+ && ($post->post_type === AAM_Service_AccessPolicy::POLICY_CPT)) {
64
+ $content = AAM_Backend_View::getInstance()->loadPartial(
65
+ 'visitor-principal-subject-tab',
66
+ $params
 
 
 
 
 
 
 
67
  );
68
  }
 
 
 
 
 
 
69
 
70
+ return $content;
71
+ }, 10, 2);
72
+
73
+ add_filter('aam_default_subject_tab_filter', function ($content, $params) {
74
+ global $post;
75
+
76
+ if (is_a($post, 'WP_Post')
77
+ && ($post->post_type === AAM_Service_AccessPolicy::POLICY_CPT)) {
78
+ $content = AAM_Backend_View::getInstance()->loadPartial(
79
+ 'default-principal-subject-tab',
80
+ $params
81
+ );
82
+ }
83
+
84
+ return $content;
85
+ }, 10, 2);
86
  }
87
 
88
  /**
89
+ * Render access policy principal metabox
90
+ *
91
+ * @param null|string $content
92
+ * @param string $type
93
+ * @param AAM_Backend_View $view
94
+ *
95
  * @return string
96
+ *
97
  * @access public
98
+ * @version 6.0.0
99
  */
100
+ public function renderPrincipalIframe($content, $type, $view)
101
  {
102
+ if ($type === 'principal') {
103
+ $content = $view->loadTemplate(
104
+ dirname(__DIR__) . '/../tmpl/metabox/principal-iframe.php',
105
+ (object) array(
106
+ 'policyId' => $this->getFromQuery('id', FILTER_VALIDATE_INT)
107
+ )
108
+ );
 
 
 
 
 
109
  }
110
 
111
+ return $content;
 
 
112
  }
113
 
114
  /**
115
+ * Render role actions
116
+ *
117
+ * @param array $actions
118
+ * @param string $id
119
+ *
120
+ * @return array
121
+ *
122
+ * @access public
123
+ * @version 6.0.0
124
  */
125
+ public function renderRoleActions($actions, $id)
126
  {
127
+ if ($this->getFromPost('ui') === 'principal') {
128
+ $object = AAM::api()->getRole($id)->getObject(
129
+ AAM_Core_Object_Policy::OBJECT_TYPE
130
+ );
131
+ $policyId = $this->getFromPost('policyId', FILTER_VALIDATE_INT);
132
+ $actions = array($object->has($policyId) ? 'detach' : 'attach');
133
+ }
134
 
135
+ return $actions;
 
 
 
 
 
136
  }
137
 
138
  /**
139
+ * Render user actions
140
+ *
141
+ * @param array $actions
142
+ * @param AAM_Core_Subject_User $user
143
+ *
144
+ * @return array
145
+ *
146
+ * @access public
147
+ * @version 6.0.0
148
  */
149
+ public function renderUserActions($actions, $user)
150
  {
151
+ if ($this->getFromPost('ui') === 'principal') {
152
+ $object = $user->getObject(AAM_Core_Object_Policy::OBJECT_TYPE);
153
+ $policyId = $this->getFromPost('policyId', FILTER_VALIDATE_INT);
154
+ $actions = array($object->has($policyId) ? 'detach' : 'attach');
155
+ }
156
 
157
+ return $actions;
158
  }
159
 
160
  /**
161
+ * Get list of access policies
162
+ *
163
+ * @return string
164
+ *
165
+ * @access public
166
+ * @version 6.0.0
167
  */
168
+ public function getTable()
169
  {
170
  $list = get_posts(array(
171
+ 'post_type' => AAM_Service_AccessPolicy::POLICY_CPT,
172
  'numberposts' => -1,
173
  'post_status' => 'publish'
174
  ));
176
  $response = array(
177
  'recordsTotal' => count($list),
178
  'recordsFiltered' => count($list),
179
+ 'draw' => $this->getFromRequest('draw'),
180
  'data' => array(),
181
  );
182
 
186
  if ($policy) {
187
  $response['data'][] = array(
188
  $record->ID,
189
+ $this->preparePolicyTitle($record),
190
+ $this->preparePolicyActionList($record),
191
  get_edit_post_link($record->ID, 'link')
192
  );
193
  }
194
  }
195
 
196
+ return wp_json_encode($response);
197
  }
198
 
199
  /**
200
+ * Prepare policy title
201
+ *
202
+ * @param WP_Post $record
203
+ *
204
  * @return string
205
+ *
206
+ * @access protected
207
+ * @version 6.0.0
208
  */
209
+ protected function preparePolicyTitle($record)
210
  {
211
+ if (!empty($record->post_title)) {
212
+ $title = $record->post_title;
213
+ } else {
214
+ $title = __('(no title)', AAM_KEY);
215
+ }
216
+
217
  $title .= '<br/>';
218
 
219
  if (isset($record->post_excerpt)) {
224
  }
225
 
226
  /**
227
+ * Prepare the list of policy actions
228
+ *
229
+ * @param WP_Post $record
230
+ *
231
+ * @return string
232
+ *
233
+ * @access protected
234
+ * @version 6.0.0
235
  */
236
+ protected function preparePolicyActionList($record)
237
  {
 
238
  $subject = AAM_Backend_Subject::getInstance();
 
 
239
 
240
+ $policy = $subject->getObject(AAM_Core_Object_Policy::OBJECT_TYPE);
241
+ $post = $subject->getObject(AAM_Core_Object_Post::OBJECT_TYPE, $record->ID);
242
 
243
  $actions = array(
244
+ $policy->has($record->ID) ? "detach" : "attach",
245
  $post->isAllowedTo('edit') ? 'edit' : 'no-edit'
246
  );
247
 
249
  }
250
 
251
  /**
252
+ * Save access policy effect
253
+ *
254
+ * @return string
255
+ *
256
+ * @access public
257
+ * @version 6.0.0
258
+ */
259
+ public function save()
260
+ {
261
+ $subject = AAM_Backend_Subject::getInstance();
262
+
263
+ $id = $this->getFromPost('id');
264
+ $effect = $this->getFromPost('effect', FILTER_VALIDATE_BOOLEAN);
265
+
266
+ // Verify that current user can perform following action
267
+ if (current_user_can('read_post', $id)) {
268
+ $object = $subject->getObject(self::OBJECT_TYPE, null, true);
269
+ $result = $object->updateOptionItem($id, $effect)->save();
270
+ } else {
271
+ $result = false;
272
+ }
273
+
274
+ return wp_json_encode(array(
275
+ 'status' => ($result ? 'success' : 'failure')
276
+ ));
277
+ }
278
+
279
+ /**
280
+ * Get default Access Policy
281
+ *
282
+ * @global string $wp_version
283
+ *
284
+ * @return string
285
+ *
286
+ * @access public
287
+ * @version 6.0.0
288
+ */
289
+ public static function getDefaultPolicy()
290
+ {
291
+ return include dirname(__DIR__) . '/../tmpl/policy/default-policy.php';
292
+ }
293
+
294
+ /**
295
+ * Register Access Policy UI feature
296
+ *
297
  * @return void
298
+ *
299
  * @access public
300
+ * @version 6.0.0
301
  */
302
  public static function register()
303
  {
304
+ AAM_Backend_Feature::registerFeature((object) array(
305
  'uid' => 'policy',
306
  'position' => 2,
307
  'title' => __('Access Policies', AAM_KEY),
316
  'view' => __CLASS__
317
  ));
318
  }
319
+
320
+ }
application/Backend/Feature/Main/Post.php CHANGED
@@ -41,7 +41,7 @@ class AAM_Backend_Feature_Main_Post
41
  *
42
  * @version 6.0.0
43
  */
44
- const TEMPLATE = 'service/post.phtml';
45
 
46
  /**
47
  * Get posts & terms list
@@ -71,7 +71,7 @@ class AAM_Backend_Feature_Main_Post
71
 
72
  // Extend the response with some required props and return JSON
73
  // response.
74
- $response['draw'] = AAM_Core_Request::request('draw');
75
 
76
  return wp_json_encode($response);
77
  }
@@ -244,7 +244,7 @@ class AAM_Backend_Feature_Main_Post
244
  break;
245
 
246
  case 'protected':
247
- $preview = $this->prepareProtectedPreview($value);
248
  break;
249
 
250
  case 'ceased':
@@ -355,21 +355,6 @@ class AAM_Backend_Feature_Main_Post
355
  return $preview;
356
  }
357
 
358
- /**
359
- * Prepare password protected option preview
360
- *
361
- * @param array $protected
362
- *
363
- * @return string
364
- *
365
- * @access protected
366
- * @version 6.0.0
367
- */
368
- protected function prepareProtectedPreview($protected)
369
- {
370
- return AAM_Core_API::crypt($protected['password'], 'decrypt');
371
- }
372
-
373
  /**
374
  * Prepare ceased option preview
375
  *
@@ -408,6 +393,35 @@ class AAM_Backend_Feature_Main_Post
408
  ));
409
  }
410
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  /**
412
  * Reset the object access settings
413
  *
@@ -440,10 +454,6 @@ class AAM_Backend_Feature_Main_Post
440
  */
441
  protected function sanitizeOption($option, $value)
442
  {
443
- if ($option === 'protected') {
444
- $value['password'] = AAM_Core_API::crypt($value['password']);
445
- }
446
-
447
  if (is_array($value)) {
448
  $value['enabled'] = filter_var($value['enabled'], FILTER_VALIDATE_BOOLEAN);
449
  } else { // Any scalar value has to be boolean
41
  *
42
  * @version 6.0.0
43
  */
44
+ const TEMPLATE = 'service/post.php';
45
 
46
  /**
47
  * Get posts & terms list
71
 
72
  // Extend the response with some required props and return JSON
73
  // response.
74
+ $response['draw'] = $this->getFromRequest('draw');
75
 
76
  return wp_json_encode($response);
77
  }
244
  break;
245
 
246
  case 'protected':
247
+ $preview = $value['password'];
248
  break;
249
 
250
  case 'ceased':
355
  return $preview;
356
  }
357
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  /**
359
  * Prepare ceased option preview
360
  *
393
  ));
394
  }
395
 
396
+ /**
397
+ * Reset view counter
398
+ *
399
+ * @return string
400
+ *
401
+ * @access public
402
+ * @version 6.0.0
403
+ */
404
+ public function resetCounter()
405
+ {
406
+ $type = $this->getFromPost('object');
407
+ $id = $this->getFromPost('objectId');
408
+
409
+ if ($type === 'post') {
410
+ $result = delete_user_meta(
411
+ $this->getSubject()->getId(),
412
+ sprintf(AAM_Service_Content::POST_COUNTER_DB_OPTION, $id)
413
+ );
414
+ } else {
415
+ $result = apply_filters(
416
+ 'aam_ajax_filter', false, $this->getSubject(), 'Main_Post.resetCounter'
417
+ );
418
+ }
419
+
420
+ return wp_json_encode(array(
421
+ 'status' => ($result ? 'success' : 'failure')
422
+ ));
423
+ }
424
+
425
  /**
426
  * Reset the object access settings
427
  *
454
  */
455
  protected function sanitizeOption($option, $value)
456
  {
 
 
 
 
457
  if (is_array($value)) {
458
  $value['enabled'] = filter_var($value['enabled'], FILTER_VALIDATE_BOOLEAN);
459
  } else { // Any scalar value has to be boolean
application/Backend/Feature/Main/Redirect.php CHANGED
@@ -38,7 +38,7 @@ class AAM_Backend_Feature_Main_Redirect
38
  *
39
  * @version 6.0.0
40
  */
41
- const TEMPLATE = 'service/redirect.phtml';
42
 
43
  /**
44
  * Get access denied redirect option
38
  *
39
  * @version 6.0.0
40
  */
41
+ const TEMPLATE = 'service/redirect.php';
42
 
43
  /**
44
  * Get access denied redirect option
application/Backend/Feature/Main/Route.php CHANGED
@@ -40,7 +40,7 @@ class AAM_Backend_Feature_Main_Route
40
  *
41
  * @version 6.0.0
42
  */
43
- const TEMPLATE = 'service/route.phtml';
44
 
45
  /**
46
  * Get list of API routes
40
  *
41
  * @version 6.0.0
42
  */
43
+ const TEMPLATE = 'service/route.php';
44
 
45
  /**
46
  * Get list of API routes
application/Backend/Feature/Main/Toolbar.php CHANGED
@@ -40,7 +40,7 @@ class AAM_Backend_Feature_Main_Toolbar
40
  *
41
  * @version 6.0.0
42
  */
43
- const TEMPLATE = 'service/toolbar.phtml';
44
 
45
  /**
46
  * Save toolbar settings
40
  *
41
  * @version 6.0.0
42
  */
43
+ const TEMPLATE = 'service/toolbar.php';
44
 
45
  /**
46
  * Save toolbar settings
application/Backend/Feature/Main/Uri.php CHANGED
@@ -19,6 +19,8 @@ class AAM_Backend_Feature_Main_Uri
19
  extends AAM_Backend_Feature_Abstract implements AAM_Backend_Feature_ISubjectAware
20
  {
21
 
 
 
22
  /**
23
  * Default access capability to the feature
24
  *
@@ -38,7 +40,7 @@ class AAM_Backend_Feature_Main_Uri
38
  *
39
  * @version 6.0.0
40
  */
41
- const TEMPLATE = 'service/uri.phtml';
42
 
43
  /**
44
  * Get list of all rules
@@ -56,14 +58,13 @@ class AAM_Backend_Feature_Main_Uri
56
  $response = array(
57
  'recordsTotal' => count($rules),
58
  'recordsFiltered' => count($rules),
59
- 'draw' => AAM_Core_Request::request('draw'),
60
  'data' => array(),
61
  );
62
 
63
- foreach ($rules as $id => $rule) {
64
  $response['data'][] = array(
65
- $id,
66
- $rule['uri'],
67
  $rule['type'],
68
  $rule['action'],
69
  isset($rule['code']) ? $rule['code'] : 307,
@@ -84,27 +85,20 @@ class AAM_Backend_Feature_Main_Uri
84
  */
85
  public function save()
86
  {
87
- $uri = filter_input(INPUT_POST, 'uri');
88
- $id = filter_input(INPUT_POST, 'id');
89
- $type = filter_input(INPUT_POST, 'type');
90
- $value = filter_input(INPUT_POST, 'value');
91
- $code = filter_input(INPUT_POST, 'code');
92
 
93
  $object = AAM_Backend_Subject::getInstance()->getObject(self::OBJECT_TYPE);
94
 
95
- // If ID is not specified, then we are creating a new rule
96
- if (empty($id)) {
97
- $id = uniqid();
98
- }
99
-
100
- $object->updateOptionItem($id, array(
101
- 'uri' => str_replace(site_url(), '', $uri),
102
  'type' => $type,
103
  'action' => $value,
104
  'code' => $code
105
  ))->save();
106
 
107
- return wp_json_encode(array('status' => 'success'));
108
  }
109
 
110
  /**
@@ -117,11 +111,11 @@ class AAM_Backend_Feature_Main_Uri
117
  */
118
  public function delete()
119
  {
120
- $id = filter_input(INPUT_POST, 'id');
121
  $object = AAM_Backend_Subject::getInstance()->getObject(self::OBJECT_TYPE);
122
 
123
  return wp_json_encode(
124
- array('status' => ($object->delete($id) ? 'success' : 'failure'))
125
  );
126
  }
127
 
19
  extends AAM_Backend_Feature_Abstract implements AAM_Backend_Feature_ISubjectAware
20
  {
21
 
22
+ use AAM_Core_Contract_RequestTrait;
23
+
24
  /**
25
  * Default access capability to the feature
26
  *
40
  *
41
  * @version 6.0.0
42
  */
43
+ const TEMPLATE = 'service/uri.php';
44
 
45
  /**
46
  * Get list of all rules
58
  $response = array(
59
  'recordsTotal' => count($rules),
60
  'recordsFiltered' => count($rules),
61
+ 'draw' => $this->getFromRequest('draw'),
62
  'data' => array(),
63
  );
64
 
65
+ foreach ($rules as $uri => $rule) {
66
  $response['data'][] = array(
67
+ $uri,
 
68
  $rule['type'],
69
  $rule['action'],
70
  isset($rule['code']) ? $rule['code'] : 307,
85
  */
86
  public function save()
87
  {
88
+ $uri = str_replace(site_url(), '', $this->getFromPost('uri'));
89
+ $type = $this->getFromPost('type');
90
+ $value = $this->getFromPost('value');
91
+ $code = $this->getFromPost('code');
 
92
 
93
  $object = AAM_Backend_Subject::getInstance()->getObject(self::OBJECT_TYPE);
94
 
95
+ $result = $object->updateOptionItem($uri, array(
 
 
 
 
 
 
96
  'type' => $type,
97
  'action' => $value,
98
  'code' => $code
99
  ))->save();
100
 
101
+ return wp_json_encode(array('status' => ($result ? 'success' : 'failure')));
102
  }
103
 
104
  /**
111
  */
112
  public function delete()
113
  {
114
+ $uri = filter_input(INPUT_POST, 'uri');
115
  $object = AAM_Backend_Subject::getInstance()->getObject(self::OBJECT_TYPE);
116
 
117
  return wp_json_encode(
118
+ array('status' => ($object->delete($uri) ? 'success' : 'failure'))
119
  );
120
  }
121
 
application/Backend/Feature/Main/Welcome.php CHANGED
@@ -23,7 +23,7 @@ class AAM_Backend_Feature_Main_Welcome extends AAM_Backend_Feature_Abstract
23
  *
24
  * @version 6.0.0
25
  */
26
- const TEMPLATE = 'service/welcome.phtml';
27
 
28
  /**
29
  * Register welcome service
23
  *
24
  * @version 6.0.0
25
  */
26
+ const TEMPLATE = 'service/welcome.php';
27
 
28
  /**
29
  * Register welcome service
application/Backend/Feature/Settings/ConfigPress.php CHANGED
@@ -32,7 +32,7 @@ class AAM_Backend_Feature_Settings_ConfigPress extends AAM_Backend_Feature_Abstr
32
  *
33
  * @version 6.0.0
34
  */
35
- const TEMPLATE = 'settings/configpress.phtml';
36
 
37
  /**
38
  * Save config
32
  *
33
  * @version 6.0.0
34
  */
35
+ const TEMPLATE = 'settings/configpress.php';
36
 
37
  /**
38
  * Save config
application/Backend/Feature/Settings/Content.php CHANGED
@@ -30,7 +30,7 @@ class AAM_Backend_Feature_Settings_Content extends AAM_Backend_Feature_Abstract
30
  *
31
  * @version 6.0.0
32
  */
33
- const TEMPLATE = 'settings/content.phtml';
34
 
35
  /**
36
  * Get list of content options
30
  *
31
  * @version 6.0.0
32
  */
33
+ const TEMPLATE = 'settings/content.php';
34
 
35
  /**
36
  * Get list of content options
application/Backend/Feature/Settings/Core.php CHANGED
@@ -30,7 +30,7 @@ class AAM_Backend_Feature_Settings_Core extends AAM_Backend_Feature_Abstract
30
  *
31
  * @version 6.0.0
32
  */
33
- const TEMPLATE = 'settings/core.phtml';
34
 
35
  /**
36
  * Get list of core options
30
  *
31
  * @version 6.0.0
32
  */
33
+ const TEMPLATE = 'settings/core.php';
34
 
35
  /**
36
  * Get list of core options
application/Backend/Feature/Settings/Manager.php CHANGED
@@ -10,12 +10,12 @@
10
  */
11
 
12
  /**
13
- * Backend Settings area manager
14
  *
15
  * @package AAM
16
  * @version 6.0.0
17
  */
18
- class AAM_Backend_Feature_Settings_Manager extends AAM_Backend_Feature_Abstract
19
  {
20
 
21
  use AAM_Core_Contract_RequestTrait;
@@ -60,4 +60,21 @@ class AAM_Backend_Feature_Settings_Manager extends AAM_Backend_Feature_Abstract
60
  return wp_json_encode(array('status' => 'success'));
61
  }
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
10
  */
11
 
12
  /**
13
+ * Backend Settings area abstract manager
14
  *
15
  * @package AAM
16
  * @version 6.0.0
17
  */
18
+ class AAM_Backend_Feature_Settings_Manager extends AAM_Backend_Feature_Abstract
19
  {
20
 
21
  use AAM_Core_Contract_RequestTrait;
60
  return wp_json_encode(array('status' => 'success'));
61
  }
62
 
63
+ /**
64
+ * Register settings UI manager
65
+ *
66
+ * @return void
67
+ *
68
+ * @access public
69
+ * @version 6.0.0
70
+ */
71
+ public static function register()
72
+ {
73
+ AAM_Backend_Feature::registerFeature((object) array(
74
+ 'capability' => self::ACCESS_CAPABILITY,
75
+ 'type' => 'core',
76
+ 'view' => __CLASS__
77
+ ));
78
+ }
79
+
80
  }
application/Backend/Feature/Settings/Security.php CHANGED
@@ -30,7 +30,7 @@ class AAM_Backend_Feature_Settings_Security extends AAM_Backend_Feature_Abstract
30
  *
31
  * @version 6.0.0
32
  */
33
- const TEMPLATE = 'settings/security.phtml';
34
 
35
  /**
36
  * Get list of security options
30
  *
31
  * @version 6.0.0
32
  */
33
+ const TEMPLATE = 'settings/security.php';
34
 
35
  /**
36
  * Get list of security options
application/Backend/Feature/Settings/Service.php CHANGED
@@ -30,7 +30,7 @@ class AAM_Backend_Feature_Settings_Service extends AAM_Backend_Feature_Abstract
30
  *
31
  * @version 6.0.0
32
  */
33
- const TEMPLATE = 'settings/service.phtml';
34
 
35
  /**
36
  * Get list of services
30
  *
31
  * @version 6.0.0
32
  */
33
+ const TEMPLATE = 'settings/service.php';
34
 
35
  /**
36
  * Get list of services
application/Backend/Feature/Subject/Role.php CHANGED
@@ -57,11 +57,7 @@ class AAM_Backend_Feature_Subject_Role
57
  $id,
58
  $user_count,
59
  translate_user_role($data['name']),
60
- apply_filters(
61
- 'aam_role_row_actions_filter',
62
- implode(',', $this->prepareRowActions($user_count)),
63
- $data
64
- ),
65
  AAM_Core_API::maxLevel($data['capabilities'])
66
  );
67
  }
@@ -72,14 +68,15 @@ class AAM_Backend_Feature_Subject_Role
72
  /**
73
  * Prepare the list of role actions
74
  *
75
- * @param int $user_count
 
76
  *
77
  * @return array
78
  *
79
  * @access protected
80
  * @version 6.0.0
81
  */
82
- protected function prepareRowActions($user_count)
83
  {
84
  $actions = array('manage');
85
 
@@ -99,7 +96,7 @@ class AAM_Backend_Feature_Subject_Role
99
  $actions[] = 'no-delete';
100
  }
101
 
102
- return $actions;
103
  }
104
 
105
  /**
@@ -127,7 +124,7 @@ class AAM_Backend_Feature_Subject_Role
127
  _doing_it_wrong(
128
  __CLASS__ . '::' . $method,
129
  'User Manager does not have this method defined',
130
- '6.0.0'
131
  );
132
  }
133
 
@@ -303,4 +300,21 @@ class AAM_Backend_Feature_Subject_Role
303
  return $response;
304
  }
305
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  }
57
  $id,
58
  $user_count,
59
  translate_user_role($data['name']),
60
+ implode(',', $this->prepareRowActions($user_count, $id)),
 
 
 
 
61
  AAM_Core_API::maxLevel($data['capabilities'])
62
  );
63
  }
68
  /**
69
  * Prepare the list of role actions
70
  *
71
+ * @param int $user_count
72
+ * @param string $roleId
73
  *
74
  * @return array
75
  *
76
  * @access protected
77
  * @version 6.0.0
78
  */
79
+ protected function prepareRowActions($user_count, $roleId)
80
  {
81
  $actions = array('manage');
82
 
96
  $actions[] = 'no-delete';
97
  }
98
 
99
+ return apply_filters('aam_role_row_actions_filter', $actions, $roleId);
100
  }
101
 
102
  /**
124
  _doing_it_wrong(
125
  __CLASS__ . '::' . $method,
126
  'User Manager does not have this method defined',
127
+ AAM_VERSION
128
  );
129
  }
130
 
300
  return $response;
301
  }
302
 
303
+ /**
304
+ * Register Role UI feature
305
+ *
306
+ * @return void
307
+ *
308
+ * @access public
309
+ * @version 6.0.0
310
+ */
311
+ public static function register()
312
+ {
313
+ AAM_Backend_Feature::registerFeature((object) array(
314
+ 'capability' => self::ACCESS_CAPABILITY,
315
+ 'type' => 'subject',
316
+ 'view' => __CLASS__
317
+ ));
318
+ }
319
+
320
  }
application/Backend/Feature/Subject/User.php CHANGED
@@ -53,7 +53,7 @@ class AAM_Backend_Feature_Subject_User
53
 
54
  foreach ($result->get_results() as $row) {
55
  $response['data'][] = $this->prepareRow(
56
- new AAM_Core_Subject_User($row->ID)
57
  );
58
  }
59
 
@@ -96,7 +96,7 @@ class AAM_Backend_Feature_Subject_User
96
  _doing_it_wrong(
97
  __CLASS__ . '::' . $method,
98
  'User Manager does not have this method defined',
99
- '6.0.0'
100
  );
101
  }
102
 
@@ -168,14 +168,17 @@ class AAM_Backend_Feature_Subject_User
168
  $actions = array();
169
 
170
  if ($allowed) {
171
- $actions = array(
172
- 'manage',
173
- current_user_can('edit_users') ? 'edit' : 'no-edit',
174
- current_user_can('aam_switch_users') ? 'switch' : 'no-switch'
 
 
 
175
  );
176
  }
177
 
178
- return apply_filters('aam_user_row_actions_filter', $actions, $user, $allowed);
179
  }
180
 
181
  /**
@@ -307,7 +310,26 @@ class AAM_Backend_Feature_Subject_User
307
  $user = AAM::api()->getUser($user);
308
  }
309
 
310
- return AAM_Core_API::isUserLevelAllowed($user->getMaxLevel());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  }
312
 
313
  }
53
 
54
  foreach ($result->get_results() as $row) {
55
  $response['data'][] = $this->prepareRow(
56
+ AAM::api()->getUser($row->ID)
57
  );
58
  }
59
 
96
  _doing_it_wrong(
97
  __CLASS__ . '::' . $method,
98
  'User Manager does not have this method defined',
99
+ AAM_VERSION
100
  );
101
  }
102
 
168
  $actions = array();
169
 
170
  if ($allowed) {
171
+ $actions = apply_filters(
172
+ 'aam_user_row_actions_filter',
173
+ array(
174
+ 'manage',
175
+ current_user_can('edit_users') ? 'edit' : 'no-edit'
176
+ ),
177
+ $user
178
  );
179
  }
180
 
181
+ return $actions;
182
  }
183
 
184
  /**
310
  $user = AAM::api()->getUser($user);
311
  }
312
 
313
+ return apply_filters(
314
+ 'aam_user_can_manage_level_filter', true, $user->getMaxLevel()
315
+ );
316
+ }
317
+
318
+ /**
319
+ * Register User UI feature
320
+ *
321
+ * @return void
322
+ *
323
+ * @access public
324
+ * @version 6.0.0
325
+ */
326
+ public static function register()
327
+ {
328
+ AAM_Backend_Feature::registerFeature((object) array(
329
+ 'capability' => self::ACCESS_CAPABILITY,
330
+ 'type' => 'subject',
331
+ 'view' => __CLASS__
332
+ ));
333
  }
334
 
335
  }
application/Backend/Manager.php CHANGED
@@ -148,7 +148,7 @@ class AAM_Backend_Manager
148
  */
149
  public function addMultiRoleSupport($param)
150
  {
151
- require_once dirname(__FILE__) . '/phtml/user/multiple-roles.phtml';
152
  }
153
 
154
  /**
148
  */
149
  public function addMultiRoleSupport($param)
150
  {
151
+ require_once dirname(__FILE__) . '/tmpl/user/multiple-roles.php';
152
  }
153
 
154
  /**
application/Backend/Subject.php CHANGED
@@ -134,7 +134,7 @@ class AAM_Backend_Subject
134
  protected function initRequestedSubject($type, $id)
135
  {
136
  if ($type === AAM_Core_Subject_User::UID) {
137
- $subject = new AAM_Core_Subject_User(intval($id), true);
138
  } elseif ($type === AAM_Core_Subject_Default::UID) {
139
  $subject = AAM_Core_Subject_Default::getInstance();
140
  } else {
@@ -225,7 +225,7 @@ class AAM_Backend_Subject
225
  _doing_it_wrong(
226
  static::class . '::' . $name,
227
  'Backend Subject does not have method defined',
228
- '6.0.0'
229
  );
230
  }
231
 
134
  protected function initRequestedSubject($type, $id)
135
  {
136
  if ($type === AAM_Core_Subject_User::UID) {
137
+ $subject = AAM::api()->getUser(intval($id));
138
  } elseif ($type === AAM_Core_Subject_Default::UID) {
139
  $subject = AAM_Core_Subject_Default::getInstance();
140
  } else {
225
  _doing_it_wrong(
226
  static::class . '::' . $name,
227
  'Backend Subject does not have method defined',
228
+ AAM_VERSION
229
  );
230
  }
231
 
application/Backend/View.php CHANGED
@@ -46,23 +46,22 @@ class AAM_Backend_View
46
  /**
47
  * Load partial template
48
  *
49
- * The specified template has to be located inside the ./phtml/partial folder
50
  *
51
  * @param string $tmpl
52
- * @param array $args
53
  *
54
  * @return string|null
55
  *
56
  * @access public
57
  * @version 6.0.0
58
  */
59
- public function loadPartial($tmpl, $args = array())
60
  {
61
  if (preg_match('/^[a-z-]+$/i', $tmpl)) {
62
- $params = (object) (is_array($args) ? $args : array());
63
- $html = $this->loadTemplate(
64
- __DIR__ . "/phtml/partial/{$tmpl}.phtml",
65
- $params
66
  );
67
  } else {
68
  $html = null;
@@ -79,10 +78,10 @@ class AAM_Backend_View
79
  *
80
  * @return string
81
  *
82
- * @access protected
83
  * @version 6.0.0
84
  */
85
- protected function loadTemplate($file_path, $params = null)
86
  {
87
  ob_start();
88
 
@@ -146,34 +145,18 @@ class AAM_Backend_View
146
  $parts = explode('.', $action);
147
  $subject = AAM_Backend_Subject::getInstance();
148
 
149
- // Check if subject is allowed to be managed based on user level
150
- $allowed = AAM_Core_API::isUserLevelAllowed(
151
- $subject->getSubject()->getMaxLevel()
152
- );
153
-
154
  if (count($parts) === 2) {
155
- $class_name = 'AAM_Backend_Feature_' . $parts[0];
156
 
157
- if (class_exists($class_name)) {
158
- // If requested feature is subject aware, make sure that current
159
- // user is allowed to manage it
160
- $isSubjectAware = class_implements(
161
- $class_name, 'AAM_Backend_Feature_ISubjectAware'
162
  );
163
-
164
- // Check if service is allowed based on capability
165
- $isServiceAllowed = current_user_can($class_name::ACCESS_CAPABILITY);
166
-
167
- if (($isSubjectAware && !$allowed) || !$isServiceAllowed) {
168
- wp_die(__('Access denied', AAM_KEY), 'aam_access_denied');
169
- }
170
-
171
- $response = call_user_func(array(new $class_name, $parts[1]));
172
  }
173
  }
174
 
175
  return apply_filters(
176
- 'aam_ajax_filter', $response, $subject->getSubject(), $action, $allowed
177
  );
178
  }
179
 
@@ -189,7 +172,7 @@ class AAM_Backend_View
189
  */
190
  public function renderPage()
191
  {
192
- return $this->loadTemplate(dirname(__FILE__) . '/phtml/index.phtml');
193
  }
194
 
195
  /**
@@ -202,18 +185,28 @@ class AAM_Backend_View
202
  */
203
  public function renderIFrame($type)
204
  {
205
- $basedir = dirname(__FILE__) . '/phtml/metabox/';
206
 
207
- if (current_user_can('aam_manager') && ($type === 'post')) {
208
- if (current_user_can('aam_manage_posts')) {
209
  echo $this->loadTemplate(
210
- $basedir . 'post-iframe.phtml',
211
  (object) array(
212
  'objectId' => filter_input(INPUT_GET, 'id'),
213
  'objectType' => filter_input(INPUT_GET, 'type'),
214
  'postManager' => new AAM_Backend_Feature_Main_Post()
215
  )
216
  );
 
 
 
 
 
 
 
 
 
 
217
  }
218
  }
219
 
@@ -233,7 +226,7 @@ class AAM_Backend_View
233
  public function renderPostMetabox($post)
234
  {
235
  return $this->loadTemplate(
236
- dirname(__FILE__) . '/phtml/metabox/post-metabox.phtml',
237
  (object) array('post' => $post)
238
  );
239
  }
@@ -251,7 +244,7 @@ class AAM_Backend_View
251
  public function renderTermMetabox($term)
252
  {
253
  return $this->loadTemplate(
254
- dirname(__FILE__) . '/phtml/metabox/term-metabox.phtml',
255
  (object) array(
256
  'term' => $term,
257
  'postType' => filter_input(INPUT_GET, 'post_type')
@@ -259,6 +252,76 @@ class AAM_Backend_View
259
  );
260
  }
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  /**
263
  * Render the AAM HTML content
264
  *
@@ -274,14 +337,14 @@ class AAM_Backend_View
274
  */
275
  public function renderContent($type = 'main')
276
  {
277
- $basedir = __DIR__ . '/phtml/page/';
278
 
279
  switch ($type) {
280
  case 'main':
281
  // No need to do the authorization as this is already done in the
282
  // AAM_Backend_Manager class
283
  $content = $this->loadTemplate(
284
- $basedir . 'main-panel.phtml',
285
  (object) array('type' => 'main')
286
  );
287
  break;
@@ -289,7 +352,7 @@ class AAM_Backend_View
289
  case 'settings':
290
  if (current_user_can('aam_manage_settings')) {
291
  $content = $this->loadTemplate(
292
- $basedir . 'main-panel.phtml',
293
  (object) array('type' => 'settings')
294
  );
295
  }
@@ -297,7 +360,7 @@ class AAM_Backend_View
297
 
298
  case 'extensions':
299
  if (current_user_can('aam_manage_extensions')) {
300
- $content = $this->loadTemplate($basedir . 'addon-panel.phtml');
301
  }
302
  break;
303
 
46
  /**
47
  * Load partial template
48
  *
49
+ * The specified template has to be located inside the ./tmpl/partial folder
50
  *
51
  * @param string $tmpl
52
+ * @param array $params
53
  *
54
  * @return string|null
55
  *
56
  * @access public
57
  * @version 6.0.0
58
  */
59
+ public function loadPartial($tmpl, $params = array())
60
  {
61
  if (preg_match('/^[a-z-]+$/i', $tmpl)) {
62
+ $html = $this->loadTemplate(
63
+ __DIR__ . "/tmpl/partial/{$tmpl}.php",
64
+ (is_object($params) ? $params : (object) $params)
 
65
  );
66
  } else {
67
  $html = null;
78
  *
79
  * @return string
80
  *
81
+ * @access public
82
  * @version 6.0.0
83
  */
84
+ public function loadTemplate($file_path, $params = null)
85
  {
86
  ob_start();
87
 
145
  $parts = explode('.', $action);
146
  $subject = AAM_Backend_Subject::getInstance();
147
 
 
 
 
 
 
148
  if (count($parts) === 2) {
149
+ $id = 'AAM_Backend_Feature_' . $parts[0];
150
 
151
+ if (AAM_Backend_Feature::isFeatureRegistered($id)) {
152
+ $response = call_user_func(
153
+ array(AAM_Backend_Feature::getFeatureView($id), $parts[1])
 
 
154
  );
 
 
 
 
 
 
 
 
 
155
  }
156
  }
157
 
158
  return apply_filters(
159
+ 'aam_ajax_filter', $response, $subject->getSubject(), $action
160
  );
161
  }
162
 
172
  */
173
  public function renderPage()
174
  {
175
+ return $this->loadTemplate(dirname(__FILE__) . '/tmpl/index.php');
176
  }
177
 
178
  /**
185
  */
186
  public function renderIFrame($type)
187
  {
188
+ $basedir = dirname(__FILE__) . '/tmpl/metabox/';
189
 
190
+ if (current_user_can('aam_manager')) {
191
+ if (($type === 'post') && current_user_can('aam_manage_posts')) {
192
  echo $this->loadTemplate(
193
+ $basedir . 'post-iframe.php',
194
  (object) array(
195
  'objectId' => filter_input(INPUT_GET, 'id'),
196
  'objectType' => filter_input(INPUT_GET, 'type'),
197
  'postManager' => new AAM_Backend_Feature_Main_Post()
198
  )
199
  );
200
+ } elseif ($type === 'user' && current_user_can('aam_manage_users')) {
201
+ echo $this->loadTemplate(
202
+ $basedir . 'user-iframe.php',
203
+ (object) array(
204
+ 'user' => new WP_User(filter_input(INPUT_GET, 'id')),
205
+ 'type' => 'main'
206
+ )
207
+ );
208
+ } else {
209
+ echo apply_filters('aam_iframe_content_filter', null, $type, $this);
210
  }
211
  }
212
 
226
  public function renderPostMetabox($post)
227
  {
228
  return $this->loadTemplate(
229
+ dirname(__FILE__) . '/tmpl/metabox/post-metabox.php',
230
  (object) array('post' => $post)
231
  );
232
  }
244
  public function renderTermMetabox($term)
245
  {
246
  return $this->loadTemplate(
247
+ dirname(__FILE__) . '/tmpl/metabox/term-metabox.php',
248
  (object) array(
249
  'term' => $term,
250
  'postType' => filter_input(INPUT_GET, 'post_type')
252
  );
253
  }
254
 
255
+ /**
256
+ * Render Access Manager metabox iFrame element for user
257
+ *
258
+ * @param WP_User $term
259
+ *
260
+ * @return string
261
+ *
262
+ * @access public
263
+ * @version 6.0.0
264
+ */
265
+ public function renderUserMetabox($user)
266
+ {
267
+ return $this->loadTemplate(
268
+ dirname(__FILE__) . '/tmpl/metabox/user-metabox.php',
269
+ (object) array(
270
+ 'user' => $user
271
+ )
272
+ );
273
+ }
274
+
275
+ /**
276
+ * Render Access Policy editor
277
+ *
278
+ * @return string
279
+ *
280
+ * @access public
281
+ * @global WP_Post $post
282
+ * @version 6.0.0
283
+ */
284
+ public function renderPolicyMetabox()
285
+ {
286
+ global $post;
287
+
288
+ if (is_a($post, 'WP_Post')) {
289
+ $content = $this->loadTemplate(
290
+ dirname(__FILE__) . '/tmpl/metabox/policy-metabox.php',
291
+ (object) array('post' => $post)
292
+ );
293
+ } else {
294
+ $content = null;
295
+ }
296
+
297
+ return $content;
298
+ }
299
+
300
+ /**
301
+ * Render policy principal metabox
302
+ *
303
+ * @return string
304
+ *
305
+ * @access public
306
+ * @global WP_Post $post
307
+ * @version 6.0.0
308
+ */
309
+ public function renderPolicyPrincipalMetabox()
310
+ {
311
+ global $post;
312
+
313
+ if (is_a($post, 'WP_Post')) {
314
+ $content = $this->loadTemplate(
315
+ dirname(__FILE__) . '/tmpl/metabox/policy-principal-metabox.php',
316
+ (object) array('post' => $post)
317
+ );
318
+ } else {
319
+ $content = null;
320
+ }
321
+
322
+ return $content;
323
+ }
324
+
325
  /**
326
  * Render the AAM HTML content
327
  *
337
  */
338
  public function renderContent($type = 'main')
339
  {
340
+ $basedir = __DIR__ . '/tmpl/page/';
341
 
342
  switch ($type) {
343
  case 'main':
344
  // No need to do the authorization as this is already done in the
345
  // AAM_Backend_Manager class
346
  $content = $this->loadTemplate(
347
+ $basedir . 'main-panel.php',
348
  (object) array('type' => 'main')
349
  );
350
  break;
352
  case 'settings':
353
  if (current_user_can('aam_manage_settings')) {
354
  $content = $this->loadTemplate(
355
+ $basedir . 'main-panel.php',
356
  (object) array('type' => 'settings')
357
  );
358
  }
360
 
361
  case 'extensions':
362
  if (current_user_can('aam_manage_extensions')) {
363
+ $content = $this->loadTemplate($basedir . 'addon-panel.php');
364
  }
365
  break;
366
 
application/Backend/View/Localization.php CHANGED
@@ -60,6 +60,7 @@ class AAM_Backend_View_Localization
60
  'Delete Role' => __('Delete Role', AAM_KEY),
61
  'Failed to lock user' => __('Failed to lock user', AAM_KEY),
62
  'Search user' => __('Search user', AAM_KEY),
 
63
  '_TOTAL_ user(s)' => __('_TOTAL_ user(s)', AAM_KEY),
64
  'Create New User' => __('Create New User', AAM_KEY),
65
  'Role' => __('Role', AAM_KEY),
@@ -72,6 +73,7 @@ class AAM_Backend_View_Localization
72
  'Processing...' => __('Processing...', AAM_KEY),
73
  'Loading roles...' => __('Loading roles...', AAM_KEY),
74
  'Failed to generate JWT token' => __('Failed to generate JWT token', AAM_KEY),
 
75
  'Current user' => __('Current user', AAM_KEY),
76
  'Current role' => __('Current role', AAM_KEY),
77
  'Manage Access' => __('Manage Access', AAM_KEY),
60
  'Delete Role' => __('Delete Role', AAM_KEY),
61
  'Failed to lock user' => __('Failed to lock user', AAM_KEY),
62
  'Search user' => __('Search user', AAM_KEY),
63
+ 'Counter was reset successfully' => __('Counter was reset successfully', AAM_KEY),
64
  '_TOTAL_ user(s)' => __('_TOTAL_ user(s)', AAM_KEY),
65
  'Create New User' => __('Create New User', AAM_KEY),
66
  'Role' => __('Role', AAM_KEY),
73
  'Processing...' => __('Processing...', AAM_KEY),
74
  'Loading roles...' => __('Loading roles...', AAM_KEY),
75
  'Failed to generate JWT token' => __('Failed to generate JWT token', AAM_KEY),
76
+ 'Failed to process request' => __('Failed to process request', AAM_KEY),
77
  'Current user' => __('Current user', AAM_KEY),
78
  'Current role' => __('Current role', AAM_KEY),
79
  'Manage Access' => __('Manage Access', AAM_KEY),
application/Backend/Widget/Login.php CHANGED
@@ -62,7 +62,7 @@ class AAM_Backend_Widget_Login extends WP_Widget
62
 
63
  require AAM_Core_Config::get(
64
  'service.secureLogin.settings.widget.template',
65
- realpath(dirname(__DIR__) . '/phtml/widget/login-frontend.phtml')
66
  );
67
  }
68
 
@@ -80,7 +80,7 @@ class AAM_Backend_Widget_Login extends WP_Widget
80
  {
81
  $instance = $this->normalize($instance);
82
 
83
- require dirname(__DIR__) . '/phtml/widget/login-backend.phtml';
84
  }
85
 
86
  /**
62
 
63
  require AAM_Core_Config::get(
64
  'service.secureLogin.settings.widget.template',
65
+ realpath(dirname(__DIR__) . '/tmpl/widget/login-frontend.php')
66
  );
67
  }
68
 
80
  {
81
  $instance = $this->normalize($instance);
82
 
83
+ require dirname(__DIR__) . '/tmpl/widget/login-backend.php';
84
  }
85
 
86
  /**
application/Backend/phtml/metabox/policy-metabox.phtml DELETED
@@ -1,427 +0,0 @@
1
- <?php if (defined('AAM_KEY')) { ?>
2
- <div>
3
- <style type="text/css">
4
- /* CODEMIRROR CSS RULES */
5
- /* BASICS */
6
-
7
- .CodeMirror {
8
- /* Set height, width, borders, and global font properties here */
9
- font-family: monospace;
10
- height: 300px;
11
- color: black;
12
- direction: ltr;
13
- border: 1px solid #EEEEEE;
14
- padding: 5px;
15
- }
16
-
17
- /* PADDING */
18
-
19
- .CodeMirror-lines {
20
- padding: 4px 0; /* Vertical padding around content */
21
- }
22
- .CodeMirror pre {
23
- padding: 0 4px; /* Horizontal padding of content */
24
- }
25
-
26
- .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
27
- background-color: white; /* The little square between H and V scrollbars */
28
- }
29
-
30
- /* GUTTER */
31
-
32
- .CodeMirror-gutters {
33
- white-space: nowrap;
34
- }
35
- .CodeMirror-linenumbers {}
36
- .CodeMirror-linenumber {
37
- padding: 0 3px 0 0px;
38
- min-width: 15px;
39
- text-align: right;
40
- color: #999;
41
- white-space: nowrap;
42
- }
43
-
44
- .CodeMirror-guttermarker { color: black; }
45
- .CodeMirror-guttermarker-subtle { color: #999; }
46
-
47
- /* CURSOR */
48
-
49
- .CodeMirror-cursor {
50
- border-left: 1px solid black;
51
- border-right: none;
52
- width: 0;
53
- }
54
- /* Shown when moving in bi-directional text */
55
- .CodeMirror div.CodeMirror-secondarycursor {
56
- border-left: 1px solid silver;
57
- }
58
- .cm-fat-cursor .CodeMirror-cursor {
59
- width: auto;
60
- border: 0 !important;
61
- background: #7e7;
62
- }
63
- .cm-fat-cursor div.CodeMirror-cursors {
64
- z-index: 1;
65
- }
66
- .cm-fat-cursor-mark {
67
- background-color: rgba(20, 255, 20, 0.5);
68
- -webkit-animation: blink 1.06s steps(1) infinite;
69
- -moz-animation: blink 1.06s steps(1) infinite;
70
- animation: blink 1.06s steps(1) infinite;
71
- }
72
- .cm-animate-fat-cursor {
73
- width: auto;
74
- border: 0;
75
- -webkit-animation: blink 1.06s steps(1) infinite;
76
- -moz-animation: blink 1.06s steps(1) infinite;
77
- animation: blink 1.06s steps(1) infinite;
78
- background-color: #7e7;
79
- }
80
- @-moz-keyframes blink {
81
- 0% {}
82
- 50% { background-color: transparent; }
83
- 100% {}
84
- }
85
- @-webkit-keyframes blink {
86
- 0% {}
87
- 50% { background-color: transparent; }
88
- 100% {}
89
- }
90
- @keyframes blink {
91
- 0% {}
92
- 50% { background-color: transparent; }
93
- 100% {}
94
- }
95
-
96
- /* Can style cursor different in overwrite (non-insert) mode */
97
- .CodeMirror-overwrite .CodeMirror-cursor {}
98
-
99
- .cm-tab { display: inline-block; text-decoration: inherit; }
100
-
101
- .CodeMirror-rulers {
102
- position: absolute;
103
- left: 0; right: 0; top: -50px; bottom: -20px;
104
- overflow: hidden;
105
- }
106
- .CodeMirror-ruler {
107
- border-left: 1px solid #ccc;
108
- top: 0; bottom: 0;
109
- position: absolute;
110
- }
111
-
112
- /* DEFAULT THEME */
113
-
114
- .cm-s-default .cm-header {color: blue;}
115
- .cm-s-default .cm-quote {color: #090;}
116
- .cm-negative {color: #d44;}
117
- .cm-positive {color: #292;}
118
- .cm-header, .cm-strong {font-weight: bold;}
119
- .cm-em {font-style: italic;}
120
- .cm-link {text-decoration: underline;}
121
- .cm-strikethrough {text-decoration: line-through;}
122
-
123
- .cm-s-default .cm-keyword {color: #708;}
124
- .cm-s-default .cm-atom {color: #219;}
125
- .cm-s-default .cm-number {color: #164;}
126
- .cm-s-default .cm-def {color: #00f;}
127
- .cm-s-default .cm-variable,
128
- .cm-s-default .cm-punctuation,
129
- .cm-s-default .cm-property,
130
- .cm-s-default .cm-operator {}
131
- .cm-s-default .cm-variable-2 {color: #05a;}
132
- .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
133
- .cm-s-default .cm-comment {color: #a50;}
134
- .cm-s-default .cm-string {color: #a11;}
135
- .cm-s-default .cm-string-2 {color: #f50;}
136
- .cm-s-default .cm-meta {color: #555;}
137
- .cm-s-default .cm-qualifier {color: #555;}
138
- .cm-s-default .cm-builtin {color: #30a;}
139
- .cm-s-default .cm-bracket {color: #997;}
140
- .cm-s-default .cm-tag {color: #170;}
141
- .cm-s-default .cm-attribute {color: #00c;}
142
- .cm-s-default .cm-hr {color: #999;}
143
- .cm-s-default .cm-link {color: #00c;}
144
-
145
- .cm-s-default .cm-error {color: #f00;}
146
- .cm-invalidchar {color: #f00;}
147
-
148
- .CodeMirror-composing { border-bottom: 2px solid; }
149
-
150
- /* Default styles for common addons */
151
-
152
- div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
153
- div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
154
- .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
155
- .CodeMirror-activeline-background {background: #e8f2ff;}
156
-
157
- /* STOP */
158
-
159
- /* The rest of this file contains styles related to the mechanics of
160
- the editor. You probably shouldn't touch them. */
161
-
162
- .CodeMirror {
163
- position: relative;
164
- overflow: hidden;
165
- background: white;
166
- }
167
-
168
- .CodeMirror-scroll {
169
- overflow: scroll !important; /* Things will break if this is overridden */
170
- /* 30px is the magic margin used to hide the element's real scrollbars */
171
- /* See overflow: hidden in .CodeMirror */
172
- margin-bottom: -30px; margin-right: -30px;
173
- padding-bottom: 30px;
174
- height: 100%;
175
- outline: none; /* Prevent dragging from highlighting the element */
176
- position: relative;
177
- }
178
- .CodeMirror-sizer {
179
- position: relative;
180
- border-right: 30px solid transparent;
181
- }
182
-
183
- /* The fake, visible scrollbars. Used to force redraw during scrolling
184
- before actual scrolling happens, thus preventing shaking and
185
- flickering artifacts. */
186
- .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
187
- position: absolute;
188
- z-index: 6;
189
- display: none;
190
- }
191
- .CodeMirror-vscrollbar {
192
- right: 0; top: 0;
193
- overflow-x: hidden;
194
- overflow-y: scroll;
195
- }
196
- .CodeMirror-hscrollbar {
197
- bottom: 0; left: 0;
198
- overflow-y: hidden;
199
- overflow-x: scroll;
200
- }
201
- .CodeMirror-scrollbar-filler {
202
- right: 0; bottom: 0;
203
- }
204
- .CodeMirror-gutter-filler {
205
- left: 0; bottom: 0;
206
- }
207
-
208
- .CodeMirror-gutters {
209
- position: absolute; left: 0; top: 0;
210
- min-height: 100%;
211
- z-index: 3;
212
- }
213
- .CodeMirror-gutter {
214
- white-space: normal;
215
- height: 100%;
216
- display: inline-block;
217
- vertical-align: top;
218
- margin-bottom: -30px;
219
- }
220
- .CodeMirror-gutter-wrapper {
221
- position: absolute;
222
- z-index: 4;
223
- background: none !important;
224
- border: none !important;
225
- }
226
- .CodeMirror-gutter-background {
227
- position: absolute;
228
- top: 0; bottom: 0;
229
- z-index: 4;
230
- }
231
- .CodeMirror-gutter-elt {
232
- position: absolute;
233
- cursor: default;
234
- z-index: 4;
235
- }
236
- .CodeMirror-gutter-wrapper ::selection { background-color: transparent }
237
- .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
238
-
239
- .CodeMirror-lines {
240
- cursor: text;
241
- min-height: 1px; /* prevents collapsing before first draw */
242
- }
243
- .CodeMirror pre {
244
- /* Reset some styles that the rest of the page might have set */
245
- -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
246
- border-width: 0;
247
- background: transparent;
248
- font-family: inherit;
249
- font-size: inherit;
250
- margin: 0;
251
- white-space: pre;
252
- word-wrap: normal;
253
- line-height: inherit;
254
- color: inherit;
255
- z-index: 2;
256
- position: relative;
257
- overflow: visible;
258
- -webkit-tap-highlight-color: transparent;
259
- -webkit-font-variant-ligatures: contextual;
260
- font-variant-ligatures: contextual;
261
- }
262
- #policy-model .CodeMirror pre {
263
- padding-left: 20px;
264
- }
265
- .CodeMirror-wrap pre {
266
- word-wrap: break-word;
267
- white-space: pre-wrap;
268
- word-break: normal;
269
- }
270
-
271
- .CodeMirror-linebackground {
272
- position: absolute;
273
- left: 0; right: 0; top: 0; bottom: 0;
274
- z-index: 0;
275
- }
276
-
277
- .CodeMirror-linewidget {
278
- position: relative;
279
- z-index: 2;
280
- padding: 0.1px; /* Force widget margins to stay inside of the container */
281
- }
282
-
283
- .CodeMirror-widget {}
284
-
285
- .CodeMirror-rtl pre { direction: rtl; }
286
-
287
- .CodeMirror-code {
288
- outline: none;
289
- }
290
-
291
- /* Force content-box sizing for the elements where we expect it */
292
- .CodeMirror-scroll,
293
- .CodeMirror-sizer,
294
- .CodeMirror-gutter,
295
- .CodeMirror-gutters,
296
- .CodeMirror-linenumber {
297
- -moz-box-sizing: content-box;
298
- box-sizing: content-box;
299
- }
300
-
301
- .CodeMirror-measure {
302
- position: absolute;
303
- width: 100%;
304
- height: 0;
305
- overflow: hidden;
306
- visibility: hidden;
307
- }
308
-
309
- .CodeMirror-cursor {
310
- position: absolute;
311
- pointer-events: none;
312
- }
313
- .CodeMirror-measure pre { position: static; }
314
-
315
- div.CodeMirror-cursors {
316
- visibility: hidden;
317
- position: relative;
318
- z-index: 3;
319
- }
320
- div.CodeMirror-dragcursors {
321
- visibility: visible;
322
- }
323
-
324
- .CodeMirror-focused div.CodeMirror-cursors {
325
- visibility: visible;
326
- }
327
-
328
- .CodeMirror-selected { background: #d9d9d9; }
329
- .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
330
- .CodeMirror-crosshair { cursor: crosshair; }
331
- .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
332
- .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
333
-
334
- .cm-searching {
335
- background-color: #ffa;
336
- background-color: rgba(255, 255, 0, .4);
337
- }
338
-
339
- /* Used to force a border model for a node */
340
- .cm-force-border { padding-right: .1px; }
341
-
342
- @media print {
343
- /* Hide the cursor when printing */
344
- .CodeMirror div.CodeMirror-cursors {
345
- visibility: hidden;
346
- }
347
- }
348
-
349
- /* See issue #2901 */
350
- .cm-tab-wrap-hack:after { content: ''; }
351
-
352
- /* Help users use markselection to safely style text background */
353
- span.CodeMirror-selectedtext { background: none; }
354
-
355
- .aam-alert-danger{
356
- border-radius: 0;
357
- margin: 10px 0;
358
- color: #a94442;
359
- background-color: #f2dede;
360
- border-color: #ebccd1;
361
- padding: 15px;
362
- border: 1px solid transparent;
363
- }
364
- .aam-infobox {
365
- border-left: 5px solid #257fad;
366
- padding: 20px;
367
- background-color: #d9edf7;
368
- margin-bottom: 0;
369
- }
370
- </style>
371
-
372
- <?php
373
- if (!empty($args->post->post_content)) {
374
- // Validate the policy
375
- $validator = new AAM_Core_Policy_Validator(htmlspecialchars_decode($args->post->post_content));
376
- $errors = $validator->validate();
377
- } else {
378
- $args->post->post_content = AAM_Backend_View_Helper::getDefaultPolicy();
379
- $errors = array();
380
- }
381
- ?>
382
-
383
- <div class="aam-alert-danger<?php echo empty($errors) ? ' hidden' : ''; ?>" id="policy-parsing-error">
384
- <?php echo implode('<br/>', $errors); ?>
385
- </div>
386
-
387
- <textarea id="aam-policy-editor" name="aam-policy" class="policy-editor" rows="10"><?php echo stripslashes($args->post->post_content); ?></textarea>
388
-
389
- <p class="aam-infobox">
390
- <?php echo sprintf(AAM_Backend_View_Helper::preparePhrase('To learn more about Access &amp; Security policy document, please check [%sAccess &amp; Security Policy%s] page.', 'b'), '<a href="https://aamplugin.com/reference/policy" target="_blank">', '</a>'); ?>
391
- </p>
392
-
393
- <script type='text/javascript' src="<?php echo AAM_MEDIA . '/js/vendor.js'; ?>"></script>
394
-
395
- <script type='text/javascript'>
396
- (function($){
397
- var editor = CodeMirror.fromTextArea(
398
- document.getElementById("aam-policy-editor"),
399
- {
400
- mode: "application/json",
401
- lineNumbers: true
402
- }
403
- );
404
-
405
- $(document).ready(function () {
406
- $('form[name="post"]').bind('submit', function(event) {
407
- var json = editor.getValue();
408
-
409
- $('#policy-parsing-error').addClass('hidden');
410
-
411
- try {
412
- JSON.parse(json);
413
-
414
- $('#aam-policy-editor').val(json);
415
- } catch (e) {
416
- event.preventDefault();
417
-
418
- $('#policy-parsing-error').removeClass('hidden').html(
419
- '<b><?php echo __('Syntax Error', AAM_KEY); ?></b>: ' + e.message.replace('JSON.parse:', '')
420
- );
421
- }
422
- });
423
- });
424
- }(jQuery));
425
- </script>
426
- </div>
427
- <?php }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
application/Backend/phtml/metabox/policy-principal-metabox.phtml DELETED
@@ -1,3 +0,0 @@
1
- <?php if (defined('AAM_KEY')) { ?>
2
- <iframe src="<?php echo admin_url('admin.php?page=aam&aamframe=principal&oid=' . $args->post->ID . '&otype=post'); ?>" width="100%" height="450" style="border: 0; margin-top:0;" id="policy-principal"></iframe>
3
- <?php }
 
 
 
application/Backend/{phtml/index.phtml → tmpl/index.php} RENAMED
@@ -1,6 +1,6 @@
1
  <?php if (defined('AAM_KEY')) { ?>
2
  <div class="wrap" id="aam-container">
3
- <?php echo $this->loadTemplate(__DIR__ . '/page/current-subject.phtml'); ?>
4
 
5
  <div class="row">
6
  <div class="col-xs-12 col-md-8">
@@ -8,9 +8,7 @@
8
  <div class="postbox">
9
  <div class="inside" id="access-manager-inside">
10
  <div class="aam-postbox-inside" id="aam-content">
11
- <p class="alert alert-info text-larger text-center" id="aam-initial-load">
12
- <?php echo AAM_Backend_View_Helper::preparePhrase('[Loading AAM UI]. Please wait. If content will not load within next 30 seconds, clear your browser cache and reload the page. If still nothing, it is most likely some sort of JavaScript or CSS conflict with one your active plugins or theme. Try to deactivate all plugins and switch to any default WordPress theme to find out what causes the issue.', 'strong'); ?>
13
- </p>
14
  </div>
15
  </div>
16
  </div>
@@ -111,8 +109,8 @@
111
  </div>
112
  </div>
113
 
114
- <?php echo $this->loadTemplate(__DIR__ . '/page/subject-panel.phtml'); ?>
115
- <?php echo $this->loadTemplate(__DIR__ . '/page/subject-panel-advanced.phtml'); ?>
116
  </div>
117
  </div>
118
  </div>
1
  <?php if (defined('AAM_KEY')) { ?>
2
  <div class="wrap" id="aam-container">
3
+ <?php echo $this->loadTemplate(__DIR__ . '/page/current-subject.php'); ?>
4
 
5
  <div class="row">
6
  <div class="col-xs-12 col-md-8">
8
  <div class="postbox">
9
  <div class="inside" id="access-manager-inside">
10
  <div class="aam-postbox-inside" id="aam-content">
11
+ <?php echo $this->loadPartial('loading-content'); ?>
 
 
12
  </div>
13
  </div>
14
  </div>
109
  </div>
110
  </div>
111
 
112
+ <?php echo $this->loadTemplate(__DIR__ . '/page/subject-panel.php'); ?>
113
+ <?php echo $this->loadTemplate(__DIR__ . '/page/subject-panel-advanced.php'); ?>
114
  </div>
115
  </div>
116
  </div>
application/Backend/{phtml/metabox/iframe-footer.phtml → tmpl/metabox/iframe-footer.php} RENAMED
File without changes
application/Backend/{phtml/metabox/iframe-header.phtml → tmpl/metabox/iframe-header.php} RENAMED
@@ -18,5 +18,5 @@
18
  <?php do_action('aam_iframe_header_action'); ?>
19
  </head>
20
 
21
- <body id="aam-container">
22
  <?php }
18
  <?php do_action('aam_iframe_header_action'); ?>
19
  </head>
20
 
21
+ <body id="aam-container" class="aam-iframe">
22
  <?php }
application/Backend/tmpl/metabox/policy-metabox.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if (defined('AAM_KEY')) { ?>
2
+ <div>
3
+ <style type="text/css">.CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr;border:1px solid #eee;padding:5px}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 0;min-width:15px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor-mark{background-color:rgba(20,255,20,.5);-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:-20px;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:red}.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}#policy-model .CodeMirror pre{padding-left:20px}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.aam-alert-danger{border-radius:0;margin:10px 0;color:#a94442;background-color:#f2dede;border-color:#ebccd1;padding:15px;border:1px solid transparent}.aam-infobox{border-left:5px solid #257fad;padding:20px;background-color:#d9edf7;margin-bottom:0}</style>
4
+
5
+ <?php
6
+ if (!empty($params->post->post_content)) {
7
+ // Validate the policy
8
+ $validator = new AAM_Core_Policy_Validator(htmlspecialchars_decode($params->post->post_content));
9
+ $errors = $validator->validate();
10
+ } else {
11
+ $params->post->post_content = AAM_Backend_Feature_Main_Policy::getDefaultPolicy();
12
+ $errors = array();
13
+ }
14
+ ?>
15
+
16
+ <div class="aam-alert-danger<?php echo (empty($errors) ? ' hidden' : ''); ?>" id="policy-parsing-error">
17
+ <?php echo implode('<br/>', $errors); ?>
18
+ </div>
19
+
20
+ <textarea id="aam-policy-editor" name="aam-policy" class="policy-editor" rows="10"><?php echo $params->post->post_content; ?></textarea>
21
+
22
+ <p class="aam-infobox">
23
+ <?php echo sprintf(AAM_Backend_View_Helper::preparePhrase('To learn more about Access &amp; Security policy document, please check [%sAccess &amp; Security Policy%s] page.', 'b'), '<a href="https://aamplugin.com/reference/policy" target="_blank">', '</a>'); ?>
24
+ </p>
25
+
26
+ <script type='text/javascript' src="<?php echo AAM_MEDIA . '/js/vendor.js'; ?>"></script>
27
+
28
+ <script type='text/javascript'>
29
+ (function($) {
30
+ var editor = CodeMirror.fromTextArea(
31
+ document.getElementById("aam-policy-editor"), {
32
+ mode: "application/json",
33
+ lineNumbers: true
34
+ }
35
+ );
36
+
37
+ $(document).ready(function() {
38
+ $('form[name="post"]').bind('submit', function(event) {
39
+ var json = editor.getValue();
40
+
41
+ $('#policy-parsing-error').addClass('hidden');
42
+
43
+ try {
44
+ JSON.parse(json);
45
+
46
+ $('#aam-policy-editor').val(json);
47
+ } catch (e) {
48
+ event.preventDefault();
49
+
50
+ $('#policy-parsing-error').removeClass('hidden').html(
51
+ '<b><?php echo __('Syntax Error', AAM_KEY); ?></b>: ' + e.message.replace('JSON.parse:', '')
52
+ );
53
+ }
54
+ });
55
+ });
56
+ }(jQuery));
57
+ </script>
58
+ </div>
59
+ <?php }
application/Backend/tmpl/metabox/policy-principal-metabox.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php if (defined('AAM_KEY')) { ?>
2
+ <iframe src="<?php echo admin_url('admin.php?page=aam&aamframe=principal&id=' . $params->post->ID); ?>" width="100%" height="450" style="border: 0; margin-top:0;" id="policy-principal"></iframe>
3
+ <?php }
application/Backend/{phtml/metabox/post-iframe.phtml → tmpl/metabox/post-iframe.php} RENAMED
@@ -1,11 +1,11 @@
1
  <?php /** @version 6.0.0 */ ?>
2
 
3
  <?php if (defined('AAM_KEY')) { ?>
4
- <?php echo $this->loadTemplate(__DIR__ . '/iframe-header.phtml'); ?>
5
 
6
  <div class="row" style="margin: 10px 0 0 0;">
7
  <div class="col-sm-4" style="padding: 0;">
8
- <?php echo $this->loadTemplate(dirname(__DIR__) . '/page/subject-panel.phtml'); ?>
9
  </div>
10
 
11
  <div class="col-sm-8">
@@ -15,5 +15,5 @@
15
  </div>
16
  </div>
17
 
18
- <?php echo $this->loadTemplate(__DIR__ . '/iframe-footer.phtml'); ?>
19
  <?php }
1
  <?php /** @version 6.0.0 */ ?>
2
 
3
  <?php if (defined('AAM_KEY')) { ?>
4
+ <?php echo $this->loadTemplate(__DIR__ . '/iframe-header.php'); ?>
5
 
6
  <div class="row" style="margin: 10px 0 0 0;">
7
  <div class="col-sm-4" style="padding: 0;">
8
+ <?php echo $this->loadTemplate(dirname(__DIR__) . '/page/subject-panel.php'); ?>
9
  </div>
10
 
11
  <div class="col-sm-8">
15
  </div>
16
  </div>
17
 
18
+ <?php echo $this->loadTemplate(__DIR__ . '/iframe-footer.php'); ?>
19
  <?php }
application/Backend/{phtml/metabox/post-metabox.phtml → tmpl/metabox/post-metabox.php} RENAMED
File without changes
application/Backend/tmpl/metabox/principal-iframe.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <?php echo $this->loadTemplate(__DIR__ . '/iframe-header.php', $params); ?>
5
+
6
+ <?php echo $this->loadTemplate(dirname(__DIR__) . '/page/subject-panel.php', $params); ?>
7
+
8
+ <!-- Additional attributes -->
9
+ <input type="hidden" id="aam-policy-id" value="<?php echo $params->policyId; ?>" />
10
+
11
+ <?php echo $this->loadTemplate(__DIR__ . '/iframe-footer.php', $params); ?>
12
+ <?php }
application/Backend/{phtml/metabox/term-metabox.phtml → tmpl/metabox/term-metabox.php} RENAMED
File without changes
application/Backend/tmpl/metabox/user-iframe.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <?php echo $this->loadTemplate(__DIR__ . '/iframe-header.php'); ?>
5
+
6
+ <div class="row" style="margin: 10px 0 0 0;">
7
+ <div class="col-sm-12">
8
+ <div id="aam-content">
9
+ <?php echo $this->loadPartial('loading-content'); ?>
10
+ </div>
11
+ </div>
12
+ </div>
13
+
14
+ <!-- User specific attributes -->
15
+ <input type="hidden" id="aam-subject-type" value="user" />
16
+ <input type="hidden" id="aam-subject-id" value="<?php echo $params->user->ID; ?>" />
17
+ <input type="hidden" id="aam-subject-name" value="<?php echo esc_js($params->user->display_name); ?>" />
18
+ <input type="hidden" id="aam-subject-level" value="<?php echo AAM_Core_API::maxLevel($params->user->allcaps); ?>" />
19
+
20
+ <?php echo $this->loadTemplate(__DIR__ . '/iframe-footer.php'); ?>
21
+ <?php }
application/Backend/tmpl/metabox/user-metabox.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <iframe src="<?php echo admin_url('admin.php?page=aam&aamframe=user&id=' . $params->user->ID); ?>" width="100%" height="550" style="border-bottom: 1px solid #e5e5e5; margin-top:10px;"></iframe>
5
+ <?php }
application/Backend/{phtml/page/addon-panel.phtml → tmpl/page/addon-panel.php} RENAMED
File without changes
application/Backend/{phtml/page/current-subject.phtml → tmpl/page/current-subject.php} RENAMED
File without changes
application/Backend/{phtml/page/main-panel.phtml → tmpl/page/main-panel.php} RENAMED
File without changes
application/Backend/{phtml/page/subject-panel-advanced.phtml → tmpl/page/subject-panel-advanced.php} RENAMED
File without changes
application/Backend/{phtml/page/subject-panel.phtml → tmpl/page/subject-panel.php} RENAMED
@@ -11,19 +11,19 @@
11
  <ul class="nav nav-tabs" role="tablist">
12
  <?php $active = 0; ?>
13
  <?php if (current_user_can('aam_manage_roles')) { ?>
14
- <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#roles" aria-controls="roles" role="tab" data-toggle="tab"><i class="icon-users"></i><br /><?php echo __('Roles', AAM_KEY); ?></a></li>
15
  <?php } ?>
16
  <?php if (current_user_can('aam_manage_users')) { ?>
17
- <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#users" aria-controls="users" role="tab" data-toggle="tab"><i class="icon-user"></i><br /><?php echo __('Users', AAM_KEY); ?></a></li>
18
  <?php } ?>
19
  <?php if (current_user_can('aam_manage_visitors')) { ?>
20
- <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#visitor" aria-controls="visitor" role="tab" data-toggle="tab"><i class="icon-user-secret"></i><br /><?php echo __('Visitor', AAM_KEY); ?></a></li>
21
  <?php } ?>
22
  <?php if (current_user_can('aam_manage_default')) { ?>
23
- <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#default" aria-controls="default" role="tab" data-toggle="tab" class="text-danger"><i class="icon-asterisk"></i><br /><?php echo __('Default', AAM_KEY); ?></a></li>
24
  <?php } ?>
25
  <?php if ($active === 0) { ?>
26
- <li role="presentation" class="active text-center"><a href="#none" aria-controls="none" role="tab" data-toggle="tab" class="text-muted"><i class="icon-asterisk"></i><br /><?php echo __('None', AAM_KEY); ?></a></li>
27
  <?php } ?>
28
  </ul>
29
  <div class="tab-content">
@@ -63,18 +63,12 @@
63
  <?php } ?>
64
  <?php if (current_user_can('aam_manage_visitors')) { ?>
65
  <div role="tabpanel" class="tab-pane<?php echo (!$active++ ? ' active' : ''); ?>" id="visitor">
66
- <div class="visitor-message">
67
- <span class="aam-bordered"><?php echo __('Manage access to your website for visitors (any user that is not authenticated)', AAM_KEY); ?>.</span>
68
- <button class="btn btn-primary btn-block" id="manage-visitor"><i class="icon-cog"></i> <?php echo __('Manage Visitors', AAM_KEY); ?></button>
69
- </div>
70
  </div>
71
  <?php } ?>
72
  <?php if (current_user_can('aam_manage_default')) { ?>
73
  <div role="tabpanel" class="tab-pane<?php echo (!$active++ ? ' active' : ''); ?>" id="default">
74
- <div class="visitor-message">
75
- <span class="aam-bordered"><?php echo __('Manage default access to your website resources for all users, roles and visitor. This includes Administrator role and your user', AAM_KEY); ?>.</span>
76
- <button class="btn btn-danger btn-block" id="manage-default"><i class="icon-cog"></i> <?php echo __('Manage Default Access', AAM_KEY); ?></button>
77
- </div>
78
  </div>
79
  <?php } ?>
80
  <?php if ($active === 0) { ?>
11
  <ul class="nav nav-tabs" role="tablist">
12
  <?php $active = 0; ?>
13
  <?php if (current_user_can('aam_manage_roles')) { ?>
14
+ <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#roles" aria-controls="roles" role="tab" data-toggle="tab"><i class="icon-users"></i><span class="aam-subject-title"><?php echo __('Roles', AAM_KEY); ?></span></a></li>
15
  <?php } ?>
16
  <?php if (current_user_can('aam_manage_users')) { ?>
17
+ <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#users" aria-controls="users" role="tab" data-toggle="tab"><i class="icon-user"></i><span class="aam-subject-title"><?php echo __('Users', AAM_KEY); ?></span></a></li>
18
  <?php } ?>
19
  <?php if (current_user_can('aam_manage_visitors')) { ?>
20
+ <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#visitor" aria-controls="visitor" role="tab" data-toggle="tab"><i class="icon-user-secret"></i><span class="aam-subject-title"><?php echo __('Visitor', AAM_KEY); ?></span></a></li>
21
  <?php } ?>
22
  <?php if (current_user_can('aam_manage_default')) { ?>
23
+ <li role="presentation" class="<?php echo (!$active++ ? 'active ' : ''); ?>text-center"><a href="#default" aria-controls="default" role="tab" data-toggle="tab" class="text-danger"><i class="icon-asterisk"></i><span class="aam-subject-title"><?php echo __('Default', AAM_KEY); ?></span></a></li>
24
  <?php } ?>
25
  <?php if ($active === 0) { ?>
26
+ <li role="presentation" class="active text-center"><a href="#none" aria-controls="none" role="tab" data-toggle="tab" class="text-muted"><i class="icon-asterisk"></i><span class="aam-subject-title"><?php echo __('None', AAM_KEY); ?></span></a></li>
27
  <?php } ?>
28
  </ul>
29
  <div class="tab-content">
63
  <?php } ?>
64
  <?php if (current_user_can('aam_manage_visitors')) { ?>
65
  <div role="tabpanel" class="tab-pane<?php echo (!$active++ ? ' active' : ''); ?>" id="visitor">
66
+ <?php echo apply_filters('aam_visitor_subject_tab_filter', $this->loadPartial('visitor-subject-tab', $params), $params); ?>
 
 
 
67
  </div>
68
  <?php } ?>
69
  <?php if (current_user_can('aam_manage_default')) { ?>
70
  <div role="tabpanel" class="tab-pane<?php echo (!$active++ ? ' active' : ''); ?>" id="default">
71
+ <?php echo apply_filters('aam_default_subject_tab_filter', $this->loadPartial('default-subject-tab', $params), $params); ?>
 
 
 
72
  </div>
73
  <?php } ?>
74
  <?php if ($active === 0) { ?>
application/Backend/tmpl/partial/default-principal-subject-tab.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <div class="visitor-message">
5
+ <p class="aam-notification">
6
+ <?php echo AAM_Backend_View_Helper::preparePhrase('This feature is allowed only with [Plus Package] addon.', 'b'); ?>
7
+ </p>
8
+ </div>
9
+ <?php }
application/Backend/tmpl/partial/default-subject-tab.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <div class="visitor-message">
5
+ <span class="aam-bordered"><?php echo __('Manage default access to your website resources for all users, roles and visitor. This includes Administrator role and your user', AAM_KEY); ?>.</span>
6
+ <button class="btn btn-danger btn-block" id="manage-default"><i class="icon-cog"></i> <?php echo __('Manage Default Access', AAM_KEY); ?></button>
7
+ </div>
8
+ <?php }
application/Backend/{phtml/partial/jwt-login-url.phtml → tmpl/partial/jwt-login-url.php} RENAMED
File without changes
application/Backend/tmpl/partial/loading-content.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <p class="alert alert-info text-larger text-center" id="aam-initial-load">
5
+ <?php echo AAM_Backend_View_Helper::preparePhrase('[Loading AAM UI]. Please wait. If content will not load within next 30 seconds, clear your browser cache and reload the page. If still nothing, it is most likely some sort of JavaScript or CSS conflict with one your active plugins or theme. Try to deactivate all plugins and switch to any default WordPress theme to find out what causes the issue.', 'strong'); ?>
6
+ </p>
7
+ <?php }
application/Backend/{phtml/partial/post-access-form.phtml → tmpl/partial/post-access-form.php} RENAMED
@@ -76,8 +76,17 @@
76
  <label><?php echo __('Access Limit Threshold', AAM_KEY); ?></label>
77
  <input type="number" class="form-control" placeholder="<?php echo __('Enter digital number', AAM_KEY); ?>" id="aam-access-threshold" value="<?php echo $params->object->get('limited.threshold'); ?>" />
78
  </div>
 
 
 
 
 
 
 
 
79
  </div>
80
  <div class="modal-footer">
 
81
  <button type="button" class="btn btn-success btn-save" id="save-limited-btn"><?php echo __('Save', AAM_KEY); ?></button>
82
  <button type="button" class="btn btn-default" data-dismiss="modal"><?php echo __('Close', AAM_KEY); ?></button>
83
  </div>
@@ -168,7 +177,7 @@
168
  <div class="modal-body">
169
  <div class="form-group">
170
  <label><?php echo __('Password', AAM_KEY); ?></label>
171
- <input type="text" class="form-control" placeholder="<?php echo __('Enter Password', AAM_KEY); ?>" id="aam-access-password" value="<?php echo AAM_Core_API::crypt($params->object->get('protected.password'), 'decrypt'); ?>" />
172
  </div>
173
  </div>
174
  <div class="modal-footer">
76
  <label><?php echo __('Access Limit Threshold', AAM_KEY); ?></label>
77
  <input type="number" class="form-control" placeholder="<?php echo __('Enter digital number', AAM_KEY); ?>" id="aam-access-threshold" value="<?php echo $params->object->get('limited.threshold'); ?>" />
78
  </div>
79
+ <?php if ($params->subject->isUser()) { ?>
80
+ <?php $counter = intval(get_user_meta($params->subject->getId(), sprintf(AAM_Service_Content::POST_COUNTER_DB_OPTION, $params->object->ID), true)); ?>
81
+ <?php $remaining = $params->object->get('limited.threshold') - $counter; ?>
82
+
83
+ <div class="form-group">
84
+ <p class="alert alert-info"><?php echo sprintf(AAM_Backend_View_Helper::preparePhrase('The user can access content [%d] times.', 'b'), $remaining >= 0 ? $remaining : 0); ?></p>
85
+ </div>
86
+ <?php } ?>
87
  </div>
88
  <div class="modal-footer">
89
+ <?php if (!empty($counter)) { ?><button type="button" class="btn btn-warning btn-save" id="reset-limited-btn"><?php echo __('Reset', AAM_KEY); ?></button><?php } ?>
90
  <button type="button" class="btn btn-success btn-save" id="save-limited-btn"><?php echo __('Save', AAM_KEY); ?></button>
91
  <button type="button" class="btn btn-default" data-dismiss="modal"><?php echo __('Close', AAM_KEY); ?></button>
92
  </div>
177
  <div class="modal-body">
178
  <div class="form-group">
179
  <label><?php echo __('Password', AAM_KEY); ?></label>
180
+ <input type="text" class="form-control" placeholder="<?php echo __('Enter Password', AAM_KEY); ?>" id="aam-access-password" value="<?php echo $params->object->get('protected.password'); ?>" />
181
  </div>
182
  </div>
183
  <div class="modal-footer">
application/Backend/{phtml/partial/posts-terms-help-tips.phtml → tmpl/partial/posts-terms-help-tips.php} RENAMED
File without changes
application/Backend/{phtml/partial/role-inheritance.phtml → tmpl/partial/role-inheritance.php} RENAMED
File without changes
application/Backend/{phtml/partial/taxonomy-access-form.phtml → tmpl/partial/taxonomy-access-form.php} RENAMED
File without changes
application/Backend/{phtml/partial/term-access-form.phtml → tmpl/partial/term-access-form.php} RENAMED
File without changes
application/Backend/{phtml/partial/type-access-form.phtml → tmpl/partial/type-access-form.php} RENAMED
File without changes
application/Backend/tmpl/partial/visitor-principal-subject-tab.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <div class="visitor-message">
5
+ <span class="aam-bordered"><?php echo __('Attach current access &amp; security policy to visitors (any user that is not authenticated)', AAM_KEY); ?>.</span>
6
+ <?php
7
+ $visitor = new AAM_Core_Subject_Visitor();
8
+ $hasPolicy = $visitor->getObject(AAM_Core_Object_Policy::OBJECT_TYPE)->has($params->policyId);
9
+ $btnStatus = $hasPolicy ? 'detach' : 'attach';
10
+ ?>
11
+ <?php if ($hasPolicy) { ?>
12
+ <button class="btn btn-primary btn-block" id="attach-policy-visitor" data-has="1" <?php echo ($btnStatus ? '' : ' disabled'); ?>><?php echo __('Detach Policy From Visitors', AAM_KEY); ?></button>
13
+ <?php } else { ?>
14
+ <button class="btn btn-primary btn-block" id="attach-policy-visitor" data-has="0" <?php echo ($btnStatus ? '' : ' disabled'); ?>><?php echo __('Attach Policy To Visitors', AAM_KEY); ?></button>
15
+ <?php } ?>
16
+ </div>
17
+ <?php }
application/Backend/tmpl/partial/visitor-subject-tab.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php /** @version 6.0.0 */ ?>
2
+
3
+ <?php if (defined('AAM_KEY')) { ?>
4
+ <div class="visitor-message">
5
+ <span class="aam-bordered"><?php echo __('Manage access to your website for visitors (any user that is not authenticated)', AAM_KEY); ?>.</span>
6
+ <button class="btn btn-primary btn-block" id="manage-visitor"><i class="icon-cog"></i> <?php echo __('Manage Visitors', AAM_KEY); ?></button>
7
+ </div>
8
+ <?php }
application/Backend/tmpl/policy/default-policy.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @version 6.0.0
10
+ */
11
+
12
+ global $wp_version;
13
+
14
+ return sprintf('{
15
+ "Version": "1.0.0",
16
+ "Dependency": {
17
+ "wordpress": ">=%s",
18
+ "advanced-access-manager": ">=%s"
19
+ },
20
+ "Statement": [
21
+ {
22
+ "Effect": "deny",
23
+ "Resource": [],
24
+ "Action": []
25
+ }
26
+ ]
27
+ }', $wp_version, AAM_VERSION);
application/Backend/{phtml/service/404redirect.phtml → tmpl/service/404redirect.php} RENAMED
File without changes
application/Backend/{phtml/service/capability.phtml → tmpl/service/capability.php} RENAMED
File without changes
application/Backend/{phtml/service/jwt.phtml → tmpl/service/jwt.php} RENAMED
File without changes
application/Backend/{phtml/service/login-redirect.phtml → tmpl/service/login-redirect.php} RENAMED
File without changes
application/Backend/{phtml/service/logout-redirect.phtml → tmpl/service/logout-redirect.php} RENAMED
File without changes
application/Backend/{phtml/service/menu.phtml → tmpl/service/menu.php} RENAMED
@@ -73,7 +73,7 @@
73
  <div class="col-xs-12 col-md-6 aam-submenu-item">
74
  <div class="aam-menu-details">
75
  <?php echo $submenu['name']; ?>
76
- <small><a href="#menu-details-modal" data-toggle="modal" data-uri="<?php echo urldecode($submenu['uri']); ?>" data-cap="<?php echo $submenu['capability']; ?>" data-name="<?php echo $submenu['name']; ?>" class="aam-menu-item"><?php echo __('more details', AAM_KEY); ?></a></small>
77
  </div>
78
  <input type="checkbox" class="aam-checkbox-danger" id="menu-item-<?php echo $i . $j; ?>" data-menu-id="<?php echo $submenu['id']; ?>" <?php echo ($submenu['checked'] ? ' checked="checked"' : ''); ?> />
79
  <label for="menu-item-<?php echo $i . $j; ?>" data-toggle="tooltip" title="<?php echo ($object->isRestricted($submenu['id']) ? __('Uncheck to allow', AAM_KEY) : __('Check to restrict', AAM_KEY)); ?>"></label>
@@ -159,6 +159,10 @@
159
  <th width="20%"><?php echo __('URI', AAM_KEY); ?></th>
160
  <td id="menu-item-uri"></td>
161
  </tr>
 
 
 
 
162
  </tbody>
163
  </table>
164
  </div>
73
  <div class="col-xs-12 col-md-6 aam-submenu-item">
74
  <div class="aam-menu-details">
75
  <?php echo $submenu['name']; ?>
76
+ <small><a href="#menu-details-modal" data-toggle="modal" data-uri="<?php echo urldecode($submenu['uri']); ?>" data-cap="<?php echo $submenu['capability']; ?>" data-name="<?php echo $submenu['name']; ?>" data-id="<?php echo $submenu['id']; ?>" class="aam-menu-item"><?php echo __('more details', AAM_KEY); ?></a></small>
77
  </div>
78
  <input type="checkbox" class="aam-checkbox-danger" id="menu-item-<?php echo $i . $j; ?>" data-menu-id="<?php echo $submenu['id']; ?>" <?php echo ($submenu['checked'] ? ' checked="checked"' : ''); ?> />
79
  <label for="menu-item-<?php echo $i . $j; ?>" data-toggle="tooltip" title="<?php echo ($object->isRestricted($submenu['id']) ? __('Uncheck to allow', AAM_KEY) : __('Check to restrict', AAM_KEY)); ?>"></label>
159
  <th width="20%"><?php echo __('URI', AAM_KEY); ?></th>
160
  <td id="menu-item-uri"></td>
161
  </tr>
162
+ <tr>
163
+ <th width="20%"><?php echo __('ID', AAM_KEY); ?></th>
164
+ <td id="menu-item-id"></td>
165
+ </tr>
166
  </tbody>
167
  </table>
168
  </div>
application/Backend/{phtml/service/metabox.phtml → tmpl/service/metabox.php} RENAMED
@@ -30,7 +30,7 @@
30
  global $wp_post_types;
31
 
32
  $first = false;
33
- $object = AAM_Backend_Subject::getInstance()->getObject('metabox');
34
  $metaboxList = $this->getMetaboxList();
35
  ?>
36
 
@@ -71,10 +71,10 @@
71
  <div class="col-xs-12 col-md-6 aam-submenu-item">
72
  <div class="aam-menu-details">
73
  <?php echo $metabox['title']; ?>
74
- <small><a href="#metabox-details-modal" data-toggle="modal" data-title="<?php echo $metabox['title']; ?>" data-screen="<?php echo $screen; ?>" class="aam-metabox-item"><?php echo __('more details', AAM_KEY); ?></a></small>
75
  </div>
76
 
77
- <input type="checkbox" class="aam-checkbox-danger" id="metabox-<?php echo $screen; ?>-<?php echo $metabox['id']; ?>" data-metabox="<?php echo $screen; ?>|<?php echo $metabox['id']; ?>" <?php echo ($object->isHidden($screen, $metabox['id']) ? ' checked="checked"' : ''); ?> />
78
  <label for="metabox-<?php echo $screen; ?>-<?php echo $metabox['id']; ?>" data-toggle="tooltip" title="<?php echo ($object->isHidden($screen, $metabox['id']) ? __('Uncheck to show', AAM_KEY) : __('Check to hide', AAM_KEY)); ?>"></label>
79
  </div>
80
  <?php } ?>
@@ -136,6 +136,10 @@
136
  <th width="20%"><?php echo __('Screen ID', AAM_KEY); ?></th>
137
  <td id="metabox-screen-id"></td>
138
  </tr>
 
 
 
 
139
  </tbody>
140
  </table>
141
  </div>
30
  global $wp_post_types;
31
 
32
  $first = false;
33
+ $object = AAM_Backend_Subject::getInstance()->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
34
  $metaboxList = $this->getMetaboxList();
35
  ?>
36
 
71
  <div class="col-xs-12 col-md-6 aam-submenu-item">
72
  <div class="aam-menu-details">
73
  <?php echo $metabox['title']; ?>
74
+ <small><a href="#metabox-details-modal" data-toggle="modal" data-title="<?php echo $metabox['title']; ?>" data-screen="<?php echo $screen; ?>" data-id="<?php echo strtolower($screen . '|' . $metabox['id']); ?>" class="aam-metabox-item"><?php echo __('more details', AAM_KEY); ?></a></small>
75
  </div>
76
 
77
+ <input type="checkbox" class="aam-checkbox-danger" id="metabox-<?php echo $screen; ?>-<?php echo $metabox['id']; ?>" data-metabox="<?php echo strtolower($screen . '|' . $metabox['id']); ?>" <?php echo ($object->isHidden($screen, $metabox['id']) ? ' checked="checked"' : ''); ?> />
78
  <label for="metabox-<?php echo $screen; ?>-<?php echo $metabox['id']; ?>" data-toggle="tooltip" title="<?php echo ($object->isHidden($screen, $metabox['id']) ? __('Uncheck to show', AAM_KEY) : __('Check to hide', AAM_KEY)); ?>"></label>
79
  </div>
80
  <?php } ?>
136
  <th width="20%"><?php echo __('Screen ID', AAM_KEY); ?></th>
137
  <td id="metabox-screen-id"></td>
138
  </tr>
139
+ <tr>
140
+ <th width="20%"><?php echo __('Internal ID', AAM_KEY); ?></th>
141
+ <td id="metabox-id"></td>
142
+ </tr>
143
  </tbody>
144
  </table>
145
  </div>
application/Backend/{phtml/service/policy.phtml → tmpl/service/policy.php} RENAMED
@@ -43,4 +43,4 @@
43
  </div>
44
  <?php } ?>
45
  </div>
46
- <?php }
43
  </div>
44
  <?php } ?>
45
  </div>
46
+ <?php }
application/Backend/{phtml/service/post.phtml → tmpl/service/post.php} RENAMED
File without changes
application/Backend/{phtml/service/redirect.phtml → tmpl/service/redirect.php} RENAMED
File without changes
application/Backend/{phtml/service/route.phtml → tmpl/service/route.php} RENAMED
File without changes
application/Backend/{phtml/service/toolbar.phtml → tmpl/service/toolbar.php} RENAMED
@@ -33,7 +33,7 @@
33
  <a role="button" data-toggle="collapse" data-parent="#toolbar-list" href="#toolbar-<?php echo $branch->id; ?>" aria-controls="toolbar-<?php echo $branch->id; ?>" <?php if (!$first) { echo 'aria-expanded="true"'; } ?>>
34
  <?php echo $this->normalizeTitle($branch); ?> <small class="aam-menu-capability"><?php echo str_replace(site_url(), '', $branch->href); ?></small>
35
  </a>
36
- <?php if ($object->isRestricted('toolbar-' . $branch->id)) { ?>
37
  <i class="aam-panel-title-icon icon-eye-off text-danger"></i>
38
  <?php } ?>
39
  </h4>
@@ -49,15 +49,15 @@
49
  <hr class="aam-divider" />
50
  <?php if (!empty($branch->children)) { ?>
51
  <div class="row aam-inner-tab">
52
- <?php echo ($object->isRestricted('toolbar-' . $branch->id) ? '<div class="aam-lock"></div>' : ''); ?>
53
  <?php foreach ($this->getAllChildren($branch) as $child) { ?>
54
  <div class="col-xs-12 col-md-6 aam-submenu-item">
55
  <div class="aam-menu-details">
56
  <?php echo $this->normalizeTitle($child); ?>
57
  <small><a href="#toolbar-details-modal" data-toggle="modal" data-uri="<?php echo urldecode(str_replace(site_url(), '', $child->href)); ?>" data-id="<?php echo esc_js($child->id); ?>" data-name="<?php echo esc_js($this->normalizeTitle($child)); ?>" class="aam-toolbar-item"><?php echo __('more details', AAM_KEY); ?></a></small>
58
  </div>
59
- <input type="checkbox" class="aam-checkbox-danger" id="toolbar-<?php echo $child->id; ?>" data-toolbar="<?php echo $child->id; ?>" <?php echo ($object->isRestricted($child->id) ? ' checked="checked"' : ''); ?> />
60
- <label for="toolbar-<?php echo $child->id; ?>" data-toggle="tooltip" title="<?php echo ($object->isRestricted($child->id) ? __('Uncheck to allow', AAM_KEY) : __('Check to restrict', AAM_KEY)); ?>"></label>
61
  </div>
62
  <?php } ?>
63
  </div>
@@ -65,7 +65,7 @@
65
  <?php } ?>
66
  <div class="row<?php echo (!empty($branch->children) ? ' aam-margin-top-xs' : ''); ?>">
67
  <div class="col-xs-10 col-md-6 col-xs-offset-1 col-md-offset-3">
68
- <?php if ($object->isRestricted('toolbar-' . $branch->id)) { ?>
69
  <a href="#" class="btn btn-primary btn-sm btn-block aam-restrict-toolbar" data-toolbar="toolbar-<?php echo $branch->id; ?>" data-target="#toolbar-<?php echo $branch->id; ?>">
70
  <i class="icon-eye"></i> <?php echo __('Show Menu', AAM_KEY); ?>
71
  </a>
33
  <a role="button" data-toggle="collapse" data-parent="#toolbar-list" href="#toolbar-<?php echo $branch->id; ?>" aria-controls="toolbar-<?php echo $branch->id; ?>" <?php if (!$first) { echo 'aria-expanded="true"'; } ?>>
34
  <?php echo $this->normalizeTitle($branch); ?> <small class="aam-menu-capability"><?php echo str_replace(site_url(), '', $branch->href); ?></small>
35
  </a>
36
+ <?php if ($object->isHidden('toolbar-' . $branch->id)) { ?>
37
  <i class="aam-panel-title-icon icon-eye-off text-danger"></i>
38
  <?php } ?>
39
  </h4>
49
  <hr class="aam-divider" />
50
  <?php if (!empty($branch->children)) { ?>
51
  <div class="row aam-inner-tab">
52
+ <?php echo ($object->isHidden('toolbar-' . $branch->id) ? '<div class="aam-lock"></div>' : ''); ?>
53
  <?php foreach ($this->getAllChildren($branch) as $child) { ?>
54
  <div class="col-xs-12 col-md-6 aam-submenu-item">
55
  <div class="aam-menu-details">
56
  <?php echo $this->normalizeTitle($child); ?>
57
  <small><a href="#toolbar-details-modal" data-toggle="modal" data-uri="<?php echo urldecode(str_replace(site_url(), '', $child->href)); ?>" data-id="<?php echo esc_js($child->id); ?>" data-name="<?php echo esc_js($this->normalizeTitle($child)); ?>" class="aam-toolbar-item"><?php echo __('more details', AAM_KEY); ?></a></small>
58
  </div>
59
+ <input type="checkbox" class="aam-checkbox-danger" id="toolbar-<?php echo $child->id; ?>" data-toolbar="<?php echo $child->id; ?>" <?php echo ($object->isHidden($child->id) ? ' checked="checked"' : ''); ?> />
60
+ <label for="toolbar-<?php echo $child->id; ?>" data-toggle="tooltip" title="<?php echo ($object->isHidden($child->id) ? __('Uncheck to allow', AAM_KEY) : __('Check to restrict', AAM_KEY)); ?>"></label>
61
  </div>
62
  <?php } ?>
63
  </div>
65
  <?php } ?>
66
  <div class="row<?php echo (!empty($branch->children) ? ' aam-margin-top-xs' : ''); ?>">
67
  <div class="col-xs-10 col-md-6 col-xs-offset-1 col-md-offset-3">
68
+ <?php if ($object->isHidden('toolbar-' . $branch->id)) { ?>
69
  <a href="#" class="btn btn-primary btn-sm btn-block aam-restrict-toolbar" data-toolbar="toolbar-<?php echo $branch->id; ?>" data-target="#toolbar-<?php echo $branch->id; ?>">
70
  <i class="icon-eye"></i> <?php echo __('Show Menu', AAM_KEY); ?>
71
  </a>
application/Backend/{phtml/service/uri.phtml → tmpl/service/uri.php} RENAMED
@@ -139,7 +139,6 @@
139
  <table id="uri-list" class="table table-striped table-bordered">
140
  <thead>
141
  <tr>
142
- <th>ID</th>
143
  <th width="60%"><?php echo __('URI', AAM_KEY); ?></th>
144
  <th width="20%"><?php echo __('Type', AAM_KEY); ?></th>
145
  <th>Type Details</th>
139
  <table id="uri-list" class="table table-striped table-bordered">
140
  <thead>
141
  <tr>
 
142
  <th width="60%"><?php echo __('URI', AAM_KEY); ?></th>
143
  <th width="20%"><?php echo __('Type', AAM_KEY); ?></th>
144
  <th>Type Details</th>
application/Backend/{phtml/service/welcome.phtml → tmpl/service/welcome.php} RENAMED
File without changes
application/Backend/{phtml/settings/configpress.phtml → tmpl/settings/configpress.php} RENAMED
File without changes
application/Backend/{phtml/settings/content.phtml → tmpl/settings/content.php} RENAMED
File without changes
application/Backend/{phtml/settings/core.phtml → tmpl/settings/core.php} RENAMED
File without changes
application/Backend/{phtml/settings/security.phtml → tmpl/settings/security.php} RENAMED
File without changes
application/Backend/{phtml/settings/service.phtml → tmpl/settings/service.php} RENAMED
@@ -13,6 +13,6 @@
13
  </thead>
14
  <tbody></tbody>
15
  </table>
16
- <div class="hidden" id="service-list-json"><?php echo wp_json_encode($this->getList()); ?></div>
17
  </div>
18
  <?php }
13
  </thead>
14
  <tbody></tbody>
15
  </table>
16
+ <div class="hidden" id="service-list-json"><?php echo wp_json_encode($this->getList(), JSON_HEX_QUOT); ?></div>
17
  </div>
18
  <?php }
application/Backend/{phtml/user/multiple-roles.phtml → tmpl/user/multiple-roles.php} RENAMED
File without changes
application/Backend/{phtml/widget/login-backend.phtml → tmpl/widget/login-backend.php} RENAMED
File without changes
application/Backend/{phtml/widget/login-frontend.phtml → tmpl/widget/login-frontend.php} RENAMED
File without changes
application/Core/API.php CHANGED
@@ -89,8 +89,9 @@ final class AAM_Core_API
89
  * @access public
90
  * @version 6.0.0
91
  */
92
- public static function updateOption($option, $data, $blog_id = null, $autoload = null)
93
- {
 
94
  if (is_multisite()) {
95
  if (is_null($blog_id)) {
96
  $blog = get_current_blog_id();
@@ -136,34 +137,6 @@ final class AAM_Core_API
136
  return $response;
137
  }
138
 
139
- /**
140
- * Check is currently logged in user is allowed to manage specified user level
141
- *
142
- * @param int $level
143
- *
144
- * @return boolean
145
- *
146
- * @access public
147
- */
148
- public static function isUserLevelAllowed($level)
149
- {
150
- $allow_equal_level = true;
151
-
152
- if (self::capExists('manage_same_user_level')) {
153
- $allow_equal_level = current_user_can('manage_same_user_level');
154
- }
155
-
156
- $user_level = AAM::getUser()->getMaxLevel();
157
-
158
- if ($allow_equal_level) {
159
- $allowed = $user_level >= $level;
160
- } else {
161
- $allowed = $user_level > $level;
162
- }
163
-
164
- return $allowed;
165
- }
166
-
167
  /**
168
  * Get role list
169
  *
@@ -252,8 +225,16 @@ final class AAM_Core_API
252
  */
253
  public static function capExists($cap)
254
  {
 
255
  $caps = self::getAllCapabilities();
256
 
 
 
 
 
 
 
 
257
  return (is_string($cap) && array_key_exists($cap, $caps));
258
  }
259
 
@@ -330,48 +311,6 @@ final class AAM_Core_API
330
  return $result;
331
  }
332
 
333
- /**
334
- * Encrypt secret information
335
- *
336
- * This method is used by Content & Access Policy services to encrypt passwords
337
- *
338
- * @param string $string
339
- * @param string $action
340
- *
341
- * @return string
342
- *
343
- * @access public
344
- * @version 6.0.0
345
- */
346
- public static function crypt($string, $action = 'encrypt')
347
- {
348
- if (function_exists('openssl_encrypt')) {
349
- $key = hash('sha256', SECURE_AUTH_KEY);
350
- $iv = substr(hash('sha256', SECURE_AUTH_SALT), 0, 16);
351
-
352
- if ($action === 'encrypt') {
353
- $string = openssl_encrypt($string, 'AES-256-CBC', $key, 0, $iv);
354
- $string = base64_encode($string);
355
- } else {
356
- $string = openssl_decrypt(
357
- base64_decode($string),
358
- 'AES-256-CBC',
359
- $key,
360
- 0,
361
- $iv
362
- );
363
- }
364
- } else {
365
- _doing_it_wrong(
366
- __CLASS__ . '::' . __METHOD__,
367
- 'OpenSSL PHP library is not installed',
368
- '6.0.0'
369
- );
370
- }
371
-
372
- return $string;
373
- }
374
-
375
  /**
376
  * Get WP core password hasher
377
  *
89
  * @access public
90
  * @version 6.0.0
91
  */
92
+ public static function updateOption(
93
+ $option, $data, $blog_id = null, $autoload = null
94
+ ) {
95
  if (is_multisite()) {
96
  if (is_null($blog_id)) {
97
  $blog = get_current_blog_id();
137
  return $response;
138
  }
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  /**
141
  * Get role list
142
  *
225
  */
226
  public static function capExists($cap)
227
  {
228
+ // Get list of all capabilities registered on the role levels
229
  $caps = self::getAllCapabilities();
230
 
231
+ // Get list of all capabilities that are assigned on the user level if user
232
+ // is authenticated
233
+ if (is_user_logged_in()) {
234
+ $user = wp_get_current_user();
235
+ $caps = array_merge($user->caps, $user->allcaps, $caps);
236
+ }
237
+
238
  return (is_string($cap) && array_key_exists($cap, $caps));
239
  }
240
 
311
  return $result;
312
  }
313
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  /**
315
  * Get WP core password hasher
316
  *
application/Core/ConfigPress/Reader.php CHANGED
@@ -95,7 +95,7 @@ class AAM_Core_ConfigPress_Reader
95
  {
96
  $config = array();
97
 
98
- foreach ($data as $section => $data) {
99
  //check if section has parent section or property
100
  if (preg_match('/[\s\w]{1}' . self::INHERIT_KEY . '[\s\w]{1}/', $section)) {
101
  $section = $this->inherit($section, $config);
@@ -110,10 +110,10 @@ class AAM_Core_ConfigPress_Reader
110
  }
111
  }
112
 
113
- if (is_array($data)) { //this is a INI section, build the nested tree
114
- $this->buildNestedSection($data, $config[$section]);
115
  } else { //single property, no need to do anything
116
- $config[$section] = $this->parseValue($data);
117
  }
118
  }
119
 
95
  {
96
  $config = array();
97
 
98
+ foreach ($data as $section => $block) {
99
  //check if section has parent section or property
100
  if (preg_match('/[\s\w]{1}' . self::INHERIT_KEY . '[\s\w]{1}/', $section)) {
101
  $section = $this->inherit($section, $config);
110
  }
111
  }
112
 
113
+ if (is_array($block)) { //this is a INI section, build the nested tree
114
+ $this->buildNestedSection($block, $config[$section]);
115
  } else { //single property, no need to do anything
116
+ $config[$section] = $this->parseValue($block);
117
  }
118
  }
119
 
application/Core/Contract/RequestTrait.php CHANGED
@@ -32,7 +32,13 @@ trait AAM_Core_Contract_RequestTrait
32
  */
33
  public function getFromPost($param, $filter = FILTER_DEFAULT, $options = null)
34
  {
35
- return filter_input(INPUT_POST, $param, $filter, $options);
 
 
 
 
 
 
36
  }
37
 
38
  /**
@@ -49,7 +55,13 @@ trait AAM_Core_Contract_RequestTrait
49
  */
50
  public function getFromQuery($param, $filter = FILTER_DEFAULT, $options = null)
51
  {
52
- return filter_input(INPUT_GET, $param, $filter, $options);
 
 
 
 
 
 
53
  }
54
 
55
  /**
@@ -66,7 +78,7 @@ trait AAM_Core_Contract_RequestTrait
66
  */
67
  public function getFromRequest($param, $filter = FILTER_DEFAULT, $options = null)
68
  {
69
- return filter_var(AAM_Core_Request::request($param), $filter, $options);
70
  }
71
 
72
  /**
@@ -83,7 +95,15 @@ trait AAM_Core_Contract_RequestTrait
83
  */
84
  public function getFromCookie($param, $filter = FILTER_DEFAULT, $options = null)
85
  {
86
- return filter_input(INPUT_COOKIE, $param, $filter, $options);
 
 
 
 
 
 
 
 
87
  }
88
 
89
  /**
@@ -104,10 +124,47 @@ trait AAM_Core_Contract_RequestTrait
104
 
105
  // Cover the unexpected server issues (e.g. FastCGI may cause unexpected null)
106
  if (empty($var)) {
107
- $var = filter_var(AAM_Core_Request::server($param), $filter, $options);
 
 
108
  }
109
 
110
  return $var;
111
  }
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  }
32
  */
33
  public function getFromPost($param, $filter = FILTER_DEFAULT, $options = null)
34
  {
35
+ $post = filter_input(INPUT_POST, $param, $filter, $options);
36
+
37
+ if (is_null($post)) {
38
+ $post = filter_var($this->readFromArray($_POST, $param), $filter, $options);
39
+ }
40
+
41
+ return $post;
42
  }
43
 
44
  /**
55
  */
56
  public function getFromQuery($param, $filter = FILTER_DEFAULT, $options = null)
57
  {
58
+ $get = filter_input(INPUT_GET, $param, $filter, $options);
59
+
60
+ if (is_null($get)) {
61
+ $get = filter_var($this->readFromArray($_GET, $param), $filter, $options);
62
+ }
63
+
64
+ return $get;
65
  }
66
 
67
  /**
78
  */
79
  public function getFromRequest($param, $filter = FILTER_DEFAULT, $options = null)
80
  {
81
+ return filter_var($this->readFromArray($_REQUEST, $param), $filter, $options);
82
  }
83
 
84
  /**
95
  */
96
  public function getFromCookie($param, $filter = FILTER_DEFAULT, $options = null)
97
  {
98
+ $cookie = filter_input(INPUT_COOKIE, $param, $filter, $options);
99
+
100
+ if (is_null($cookie)) {
101
+ $cookie = filter_var($this->readFromArray(
102
+ $_COOKIE, $param), $filter, $options
103
+ );
104
+ }
105
+
106
+ return $cookie;
107
  }
108
 
109
  /**
124
 
125
  // Cover the unexpected server issues (e.g. FastCGI may cause unexpected null)
126
  if (empty($var)) {
127
+ $var = filter_var(
128
+ $this->readFromArray($_SERVER, $param), $filter, $options
129
+ );
130
  }
131
 
132
  return $var;
133
  }
134
 
135
+ /**
136
+ * Check array for specified parameter and return the it's value or
137
+ * default one
138
+ *
139
+ * @param array $array Global array _GET, _POST etc
140
+ * @param string $param Array Parameter
141
+ * @param mixed $default Default value
142
+ *
143
+ * @return mixed
144
+ *
145
+ * @access protected
146
+ * @version 6.0.0
147
+ */
148
+ protected function readFromArray($array, $param, $default = null)
149
+ {
150
+ $value = $default;
151
+
152
+ if (is_null($param)) {
153
+ $value = $array;
154
+ } else {
155
+ $chunks = explode('.', $param);
156
+ $value = $array;
157
+ foreach ($chunks as $chunk) {
158
+ if (isset($value[$chunk])) {
159
+ $value = $value[$chunk];
160
+ } else {
161
+ $value = $default;
162
+ break;
163
+ }
164
+ }
165
+ }
166
+
167
+ return $value;
168
+ }
169
+
170
  }
application/Core/Gateway.php CHANGED
@@ -36,7 +36,7 @@ final class AAM_Core_Gateway
36
  _doing_it_wrong(
37
  __CLASS__ . '::' . __METHOD__,
38
  "The method {$name} is not defined in the AAM API",
39
- '6.0.0'
40
  );
41
  }
42
 
@@ -92,18 +92,18 @@ final class AAM_Core_Gateway
92
  *
93
  * If no $id specified, current user will be returned
94
  *
95
- * @param int $id Optional user id
96
- * @param boolean $init
97
  *
98
  * @return AAM_Core_Subject
99
  *
100
  * @access public
101
  * @version 6.0.0
102
  */
103
- public function getUser($id = null, $init = false)
104
  {
105
  if (!empty($id)) {
106
- $user = new AAM_Core_Subject_User($id, $init);
 
107
  } else {
108
  $user = AAM::getUser();
109
  }
36
  _doing_it_wrong(
37
  __CLASS__ . '::' . __METHOD__,
38
  "The method {$name} is not defined in the AAM API",
39
+ AAM_VERSION
40
  );
41
  }
42
 
92
  *
93
  * If no $id specified, current user will be returned
94
  *
95
+ * @param int $id
 
96
  *
97
  * @return AAM_Core_Subject
98
  *
99
  * @access public
100
  * @version 6.0.0
101
  */
102
+ public function getUser($id = null)
103
  {
104
  if (!empty($id)) {
105
+ $user = new AAM_Core_Subject_User($id);
106
+ $user->initialize();
107
  } else {
108
  $user = AAM::getUser();
109
  }
application/Core/Jwt/Issuer.php CHANGED
@@ -137,7 +137,7 @@ class AAM_Core_Jwt_Issuer
137
  _doing_it_wrong(
138
  __CLASS__ . '::' . __METHOD__,
139
  'Invalid JWT token: ' . $ex->getMessage(),
140
- '6.0.0'
141
  );
142
  }
143
 
@@ -167,7 +167,7 @@ class AAM_Core_Jwt_Issuer
167
  _doing_it_wrong(
168
  __CLASS__ . '::' . __METHOD__,
169
  'Invalid JWT token: ' . $ex->getMessage(),
170
- '6.0.0'
171
  );
172
  }
173
 
137
  _doing_it_wrong(
138
  __CLASS__ . '::' . __METHOD__,
139
  'Invalid JWT token: ' . $ex->getMessage(),
140
+ AAM_VERSION
141
  );
142
  }
143
 
167
  _doing_it_wrong(
168
  __CLASS__ . '::' . __METHOD__,
169
  'Invalid JWT token: ' . $ex->getMessage(),
170
+ AAM_VERSION
171
  );
172
  }
173
 
application/Core/Migration/2019_06_30-migrate-settings-to-6.0.0.php CHANGED
@@ -50,6 +50,9 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
50
  // TODO: aam-utilities => aam_config
51
 
52
  // TODO: aam-extensions => aam_addons
 
 
 
53
  }
54
 
55
  /**
@@ -188,7 +191,7 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
188
  } elseif ($k === 'protected') {
189
  $options['protected'] = array(
190
  'enabled' => self::_isTrue($value),
191
- 'password' => AAM_Core_API::crypt($legacy['password'])
192
  );
193
  } elseif ($k === 'expire') {
194
  $options['ceased'] = array(
50
  // TODO: aam-utilities => aam_config
51
 
52
  // TODO: aam-extensions => aam_addons
53
+
54
+ //clear schedules
55
+ wp_clear_scheduled_hook('aam-cron');
56
  }
57
 
58
  /**
191
  } elseif ($k === 'protected') {
192
  $options['protected'] = array(
193
  'enabled' => self::_isTrue($value),
194
+ 'password' => $legacy['password']
195
  );
196
  } elseif ($k === 'expire') {
197
  $options['ceased'] = array(
application/Core/Object.php CHANGED
@@ -55,6 +55,16 @@ abstract class AAM_Core_Object
55
  */
56
  private $_option = array();
57
 
 
 
 
 
 
 
 
 
 
 
58
  /**
59
  * Overwritten indicator
60
  *
@@ -129,7 +139,7 @@ abstract class AAM_Core_Object
129
  _doing_it_wrong(
130
  $function,
131
  sprintf(__('AAM object function %s is not defined', AAM_KEY), $function),
132
- '6.0.0'
133
  );
134
  }
135
 
@@ -219,6 +229,36 @@ abstract class AAM_Core_Object
219
  return $this->_option;
220
  }
221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  /**
223
  * Merge options based on merging preferences
224
  *
@@ -267,16 +307,37 @@ abstract class AAM_Core_Object
267
  /**
268
  * Set overwritten flat
269
  *
270
- * @param boolean $overwritten
271
  *
272
  * @return void
273
  *
274
  * @access public
275
  * @version 6.0.0
276
  */
277
- public function setOverwritten($overwritten = true)
278
  {
279
- $this->_overwritten = $overwritten;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  }
281
 
282
  /**
55
  */
56
  private $_option = array();
57
 
58
+ /**
59
+ * Explicit options (not inherited from parent subjects)
60
+ *
61
+ * @var array
62
+ *
63
+ * @access private
64
+ * @version 6.0.0
65
+ */
66
+ private $_explicitOption = array();
67
+
68
  /**
69
  * Overwritten indicator
70
  *
139
  _doing_it_wrong(
140
  $function,
141
  sprintf(__('AAM object function %s is not defined', AAM_KEY), $function),
142
+ AAM_VERSION
143
  );
144
  }
145
 
229
  return $this->_option;
230
  }
231
 
232
+ /**
233
+ * Get specific access property
234
+ *
235
+ * @param string $property
236
+ * @param mixed $default
237
+ *
238
+ * @return mixed
239
+ *
240
+ * @access public
241
+ * @version 5.0.0
242
+ */
243
+ public function get($property, $default = null)
244
+ {
245
+ $option = $this->getOption();
246
+
247
+ $chunks = explode('.', $property);
248
+ $value = (isset($option[$chunks[0]]) ? $option[$chunks[0]] : null);
249
+
250
+ foreach (array_slice($chunks, 1) as $chunk) {
251
+ if (isset($value[$chunk])) {
252
+ $value = $value[$chunk];
253
+ } else {
254
+ $value = $default;
255
+ break;
256
+ }
257
+ }
258
+
259
+ return (is_null($value) ? $default : $value);
260
+ }
261
+
262
  /**
263
  * Merge options based on merging preferences
264
  *
307
  /**
308
  * Set overwritten flat
309
  *
310
+ * @param array $option
311
  *
312
  * @return void
313
  *
314
  * @access public
315
  * @version 6.0.0
316
  */
317
+ public function determineOverwritten($option)
318
  {
319
+ $this->_overwritten = !empty($option);
320
+ $this->_explicitOption = $option;
321
+ }
322
+
323
+ public function isExplicit($property)
324
+ {
325
+ $option = $this->_explicitOption;
326
+ $explicit = true;
327
+
328
+ $chunks = explode('.', $property);
329
+ $value = (isset($option[$chunks[0]]) ? $option[$chunks[0]] : null);
330
+
331
+ foreach (array_slice($chunks, 1) as $chunk) {
332
+ if (isset($value[$chunk])) {
333
+ $value = $value[$chunk];
334
+ } else {
335
+ $explicit = false;
336
+ break;
337
+ }
338
+ }
339
+
340
+ return $explicit;
341
  }
342
 
343
  /**
application/Core/Object/LoginRedirect.php CHANGED
@@ -35,9 +35,7 @@ class AAM_Core_Object_LoginRedirect extends AAM_Core_Object
35
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
36
 
37
  // If options are defined, set the overwritten flag
38
- if (!empty($option)) {
39
- $this->setOverwritten(true);
40
- }
41
 
42
  $this->setOption(is_array($option) ? $option : array());
43
  }
35
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
36
 
37
  // If options are defined, set the overwritten flag
38
+ $this->determineOverwritten($option);
 
 
39
 
40
  $this->setOption(is_array($option) ? $option : array());
41
  }
application/Core/Object/LogoutRedirect.php CHANGED
@@ -35,9 +35,7 @@ class AAM_Core_Object_LogoutRedirect extends AAM_Core_Object
35
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
36
 
37
  // If options are defined, set the overwritten flag
38
- if (!empty($option)) {
39
- $this->setOverwritten(true);
40
- }
41
 
42
  $this->setOption(is_array($option) ? $option : array());
43
  }
35
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
36
 
37
  // If options are defined, set the overwritten flag
38
+ $this->determineOverwritten($option);
 
 
39
 
40
  $this->setOption(is_array($option) ? $option : array());
41
  }
application/Core/Object/Menu.php CHANGED
@@ -33,15 +33,11 @@ class AAM_Core_Object_Menu extends AAM_Core_Object
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
- if (!empty($option)) {
37
- $this->setOverwritten(true);
38
- }
39
 
40
  // Trigger custom functionality that may populate the menu options. For
41
  // example, this hooks is used by Access Policy service
42
- $option = apply_filters(
43
- 'aam_admin_menu_object_option_filter', $option, $this->getSubject()
44
- );
45
 
46
  $this->setOption(is_array($option) ? $option : array());
47
  }
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
+ $this->determineOverwritten($option);
 
 
37
 
38
  // Trigger custom functionality that may populate the menu options. For
39
  // example, this hooks is used by Access Policy service
40
+ $option = apply_filters('aam_menu_object_option_filter', $option, $this);
 
 
41
 
42
  $this->setOption(is_array($option) ? $option : array());
43
  }
application/Core/Object/Metabox.php CHANGED
@@ -33,15 +33,11 @@ class AAM_Core_Object_Metabox extends AAM_Core_Object
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
- if (!empty($option)) {
37
- $this->setOverwritten(true);
38
- }
39
 
40
  // Trigger custom functionality that may populate the menu options. For
41
  // example, this hooks is used by Access Policy service
42
- $option = apply_filters(
43
- 'aam_metabox_object_option_filter', $option, $this->getSubject()
44
- );
45
 
46
  $this->setOption(is_array($option) ? $option : array());
47
  }
@@ -51,26 +47,17 @@ class AAM_Core_Object_Metabox extends AAM_Core_Object
51
  *
52
  * @param string $screen
53
  * @param string $metaboxId
54
- * @param array $metabox
55
  *
56
  * @return boolean
57
  *
58
  * @access public
59
  * @version 6.0.0
60
  */
61
- public function isHidden($screen, $metaboxId, $metabox = null)
62
  {
63
  $option = $this->getOption();
64
 
65
- // TODO: In the Access Policy implement support for this hook. Do not forget
66
- // that $metabox['title'] has to be handled too
67
- return apply_filters(
68
- 'aam_metabox_is_hidden_filter',
69
- !empty($option["{$screen}|{$metaboxId}"]),
70
- $screen,
71
- $metaboxId,
72
- $metabox
73
- );
74
  }
75
 
76
  }
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
+ $this->determineOverwritten($option);
 
 
37
 
38
  // Trigger custom functionality that may populate the menu options. For
39
  // example, this hooks is used by Access Policy service
40
+ $option = apply_filters('aam_metabox_object_option_filter', $option, $this);
 
 
41
 
42
  $this->setOption(is_array($option) ? $option : array());
43
  }
47
  *
48
  * @param string $screen
49
  * @param string $metaboxId
 
50
  *
51
  * @return boolean
52
  *
53
  * @access public
54
  * @version 6.0.0
55
  */
56
+ public function isHidden($screen, $metaboxId)
57
  {
58
  $option = $this->getOption();
59
 
60
+ return !empty($option[strtolower("{$screen}|{$metaboxId}")]);
 
 
 
 
 
 
 
 
61
  }
62
 
63
  }
application/Core/Object/Policy.php CHANGED
@@ -5,51 +5,52 @@
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
  /**
11
  * Policy object
12
- *
13
  * @package AAM
14
- * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
  */
16
  class AAM_Core_Object_Policy extends AAM_Core_Object
17
  {
18
 
19
  /**
20
  * Type of object
 
 
21
  */
22
  const OBJECT_TYPE = 'policy';
23
 
24
  /**
25
  * Initialize the policy rules for current subject
26
- *
27
  * @return void
28
- *
29
  * @access protected
 
30
  */
31
  protected function initialize()
32
  {
33
- $subject = $this->getSubject();
34
- $option = $subject->readOption('policy');
35
 
36
- if (empty($option)) {
37
- $option = array();
38
- } else {
39
- $this->setOverwritten(true);
40
- }
41
 
42
  $this->setOption(is_array($option) ? $option : array());
43
  }
44
 
45
  /**
46
  * Check if policy attached
47
- *
48
  * @param int $id
49
- *
50
  * @return boolean
51
- *
52
  * @access public
 
53
  */
54
  public function has($id)
55
  {
@@ -58,21 +59,4 @@ class AAM_Core_Object_Policy extends AAM_Core_Object
58
  return !empty($option[$id]);
59
  }
60
 
61
- /**
62
- *
63
- * @param type $id
64
- *
65
- * @return type
66
- */
67
- public function delete($id)
68
- {
69
- $option = $this->getOption();
70
- if (isset($option[$id])) {
71
- unset($option[$id]);
72
- }
73
- $this->setOption($option);
74
-
75
- return $this->getSubject()->updateOption($this->getOption(), 'policy');
76
- }
77
-
78
- }
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
+ * @version 6.0.0
10
  */
11
 
12
  /**
13
  * Policy object
14
+ *
15
  * @package AAM
16
+ * @version 6.0.0
17
  */
18
  class AAM_Core_Object_Policy extends AAM_Core_Object
19
  {
20
 
21
  /**
22
  * Type of object
23
+ *
24
+ * @version 6.0.0
25
  */
26
  const OBJECT_TYPE = 'policy';
27
 
28
  /**
29
  * Initialize the policy rules for current subject
30
+ *
31
  * @return void
32
+ *
33
  * @access protected
34
+ * @version 6.0.0
35
  */
36
  protected function initialize()
37
  {
38
+ $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
 
39
 
40
+ $this->determineOverwritten($option);
 
 
 
 
41
 
42
  $this->setOption(is_array($option) ? $option : array());
43
  }
44
 
45
  /**
46
  * Check if policy attached
47
+ *
48
  * @param int $id
49
+ *
50
  * @return boolean
51
+ *
52
  * @access public
53
+ * @version 6.0.0
54
  */
55
  public function has($id)
56
  {
59
  return !empty($option[$id]);
60
  }
61
 
62
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
application/Core/Object/Post.php CHANGED
@@ -56,10 +56,16 @@ class AAM_Core_Object_Post extends AAM_Core_Object
56
  // This is done to remove redundant calls to the database on the backend view
57
  if (is_a($post, 'WP_Post')) {
58
  $this->setPost($post);
59
- $this->setId($post->ID);
60
  } elseif (is_numeric($post)) {
61
  $this->setPost(get_post($post));
62
- $this->setId($this->ID);
 
 
 
 
 
 
 
63
  }
64
 
65
  $this->initialize();
@@ -88,30 +94,19 @@ class AAM_Core_Object_Post extends AAM_Core_Object
88
  */
89
  protected function initialize()
90
  {
91
- $option = array();
92
-
93
- if ($this->suppressFilters() === false) {
94
- // Trigger custom functionality that may populate the post access options.
95
- // For example, this hooks is used by Access Policy service.
96
- $option = apply_filters('aam_pre_init_post_object_filter', $option, $this);
97
- }
98
-
99
  // Read direct access settings - those that are explicitly defined for the
100
  // post
101
- $direct = $this->getSubject()->readOption(
102
  self::OBJECT_TYPE, $this->ID . '|' . $this->post_type
103
  );
104
 
105
- if (!empty($direct) && is_array($direct)) {
106
- $this->setOverwritten(true);
107
- $option = array_replace($option, $direct); // merge access settings
108
- }
109
 
110
  if ($this->suppressFilters() === false) {
111
  // Trigger custom functionality that may populate the post access options
112
  // after initial setup. Typically is used by third party functionality and
113
  // premium AAM plugins.
114
- $option = apply_filters('aam_post_init_post_object_filter', $option, $this);
115
  }
116
 
117
  // Finally set the option for this object
@@ -199,36 +194,6 @@ class AAM_Core_Object_Post extends AAM_Core_Object
199
  return $this->is($property);
200
  }
201
 
202
- /**
203
- * Get specific access property
204
- *
205
- * @param string $property
206
- * @param mixed $default
207
- *
208
- * @return mixed
209
- *
210
- * @access public
211
- * @version 6.0.0
212
- */
213
- public function get($property, $default = null)
214
- {
215
- $option = $this->getOption();
216
-
217
- $chunks = explode('.', $property);
218
- $value = (isset($option[$chunks[0]]) ? $option[$chunks[0]] : null);
219
-
220
- foreach (array_slice($chunks, 1) as $chunk) {
221
- if (isset($value[$chunk])) {
222
- $value = $value[$chunk];
223
- } else {
224
- $value = $default;
225
- break;
226
- }
227
- }
228
-
229
- return (is_null($value) ? $default : $value);
230
- }
231
-
232
  /**
233
  * Get WP Post
234
  *
56
  // This is done to remove redundant calls to the database on the backend view
57
  if (is_a($post, 'WP_Post')) {
58
  $this->setPost($post);
 
59
  } elseif (is_numeric($post)) {
60
  $this->setPost(get_post($post));
61
+ }
62
+
63
+ // Making sure that we actually have post, otherwise just initiate with dummy
64
+ if (is_a($this->getPost(), 'WP_Post')) {
65
+ $this->setId($this->getPost()->ID);
66
+ } else {
67
+ $this->setPost(new WP_Post((object) array('ID' => 0)));
68
+ $this->setId(0);
69
  }
70
 
71
  $this->initialize();
94
  */
95
  protected function initialize()
96
  {
 
 
 
 
 
 
 
 
97
  // Read direct access settings - those that are explicitly defined for the
98
  // post
99
+ $option = $this->getSubject()->readOption(
100
  self::OBJECT_TYPE, $this->ID . '|' . $this->post_type
101
  );
102
 
103
+ $this->determineOverwritten($option);
 
 
 
104
 
105
  if ($this->suppressFilters() === false) {
106
  // Trigger custom functionality that may populate the post access options
107
  // after initial setup. Typically is used by third party functionality and
108
  // premium AAM plugins.
109
+ $option = apply_filters('aam_post_object_option_filter', $option, $this);
110
  }
111
 
112
  // Finally set the option for this object
194
  return $this->is($property);
195
  }
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  /**
198
  * Get WP Post
199
  *
application/Core/Object/Redirect.php CHANGED
@@ -33,10 +33,7 @@ class AAM_Core_Object_Redirect extends AAM_Core_Object
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
- //inherit from default Administrator role
37
- if (!empty($option)) {
38
- $this->setOverwritten(true);
39
- }
40
 
41
  $this->setOption(is_array($option) ? $option : array());
42
  }
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
+ $this->determineOverwritten($option);
 
 
 
37
 
38
  $this->setOption(is_array($option) ? $option : array());
39
  }
application/Core/Object/Route.php CHANGED
@@ -33,9 +33,7 @@ class AAM_Core_Object_Route extends AAM_Core_Object
33
  {
34
  $option = $this->getSubject()->readOption('route');
35
 
36
- if (!empty($option)) {
37
- $this->setOverwritten(true);
38
- }
39
 
40
  // Trigger custom functionality that may populate the menu options. For
41
  // example, this hooks is used by Access Policy service
33
  {
34
  $option = $this->getSubject()->readOption('route');
35
 
36
+ $this->determineOverwritten($option);
 
 
37
 
38
  // Trigger custom functionality that may populate the menu options. For
39
  // example, this hooks is used by Access Policy service
application/Core/Object/Toolbar.php CHANGED
@@ -33,15 +33,13 @@ class AAM_Core_Object_Toolbar extends AAM_Core_Object
33
  {
34
  $option = $this->getSubject()->readOption('toolbar');
35
 
36
- if (!empty($option)) {
37
- $this->setOverwritten(true);
38
- }
39
 
40
  // Trigger custom functionality that may populate the menu options. For
41
  // example, this hooks is used by Access Policy service
42
  if (empty($option)) {
43
  $option = apply_filters(
44
- 'aam_toolbar_object_option_filter', $option, $this->getSubject()
45
  );
46
  }
47
 
@@ -60,7 +58,7 @@ class AAM_Core_Object_Toolbar extends AAM_Core_Object
60
  * @access public
61
  * @version 6.0.0
62
  */
63
- public function isRestricted($item, $both = false)
64
  {
65
  $options = $this->getOption();
66
 
33
  {
34
  $option = $this->getSubject()->readOption('toolbar');
35
 
36
+ $this->determineOverwritten($option);
 
 
37
 
38
  // Trigger custom functionality that may populate the menu options. For
39
  // example, this hooks is used by Access Policy service
40
  if (empty($option)) {
41
  $option = apply_filters(
42
+ 'aam_toolbar_object_option_filter', $option, $this
43
  );
44
  }
45
 
58
  * @access public
59
  * @version 6.0.0
60
  */
61
+ public function isHidden($item, $both = false)
62
  {
63
  $options = $this->getOption();
64
 
application/Core/Object/Uri.php CHANGED
@@ -33,16 +33,12 @@ class AAM_Core_Object_Uri extends AAM_Core_Object
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
- if (!empty($option)) {
37
- $this->setOverwritten(true);
38
- }
39
 
40
  // Trigger custom functionality that may populate the menu options. For
41
  // example, this hooks is used by Access Policy service
42
  if (empty($option)) {
43
- $option = apply_filters(
44
- 'aam_uri_object_option_filter', $option, $this->getSubject()
45
- );
46
  }
47
 
48
  $this->setOption(is_array($option) ? $option : array());
@@ -63,18 +59,18 @@ class AAM_Core_Object_Uri extends AAM_Core_Object
63
  {
64
  $match = null;
65
 
66
- foreach ($this->getOption() as $rule) {
67
- $uri = wp_parse_url($rule['uri']);
68
- $out = array();
69
 
70
- if (!empty($uri['query'])) {
71
- parse_str($uri['query'], $out);
72
  }
73
 
74
  // Normalize the search and target URIs
75
- $s = rtrim($s, '/');
76
- $uri['path'] = rtrim((isset($uri['path']) ? $uri['path'] : ''), '/');
77
- $regex = '@^' . preg_quote($uri['path']) . '$@';
78
 
79
  // Perform the initial match for the base URI
80
  $uri_matched = apply_filters(
@@ -95,19 +91,19 @@ class AAM_Core_Object_Uri extends AAM_Core_Object
95
  /**
96
  * Delete specified URI rule
97
  *
98
- * @param string $id
99
  *
100
  * @return boolean
101
  *
102
  * @access public
103
  * @version 6.0.0
104
  */
105
- public function delete($id)
106
  {
107
  $option = $this->getOption();
108
 
109
- if (isset($option[$id])) {
110
- unset($option[$id]);
111
 
112
  $this->setOption($option);
113
 
@@ -134,18 +130,18 @@ class AAM_Core_Object_Uri extends AAM_Core_Object
134
  $merged = array();
135
  $pref = AAM::api()->getConfig('core.settings.uri.merge.preference', 'deny');
136
 
137
- foreach (array_merge($options, $this->getOption()) as $key => $options) {
138
  // If merging preference is "deny" and at least one of the access
139
  // settings is checked, then final merged array will have it set
140
  // to checked
141
- if (!isset($merged[$options['uri']])) {
142
- $merged[$key] = $options;
143
  } else {
144
  if (($pref === 'deny') && ($options['type'] !== 'allow')) {
145
- $merged[$key] = $options;
146
  break;
147
  } elseif ($pref === 'allow' && ($options['type'] === 'allow')) {
148
- $merged[$key] = $options;
149
  break;
150
  }
151
  }
33
  {
34
  $option = $this->getSubject()->readOption(self::OBJECT_TYPE);
35
 
36
+ $this->determineOverwritten($option);
 
 
37
 
38
  // Trigger custom functionality that may populate the menu options. For
39
  // example, this hooks is used by Access Policy service
40
  if (empty($option)) {
41
+ $option = apply_filters('aam_uri_object_option_filter', $option, $this);
 
 
42
  }
43
 
44
  $this->setOption(is_array($option) ? $option : array());
59
  {
60
  $match = null;
61
 
62
+ foreach ($this->getOption() as $uri => $rule) {
63
+ $meta = wp_parse_url($uri);
64
+ $out = array();
65
 
66
+ if (!empty($meta['query'])) {
67
+ parse_str($meta['query'], $out);
68
  }
69
 
70
  // Normalize the search and target URIs
71
+ $s = rtrim($s, '/');
72
+ $meta['path'] = rtrim(isset($meta['path']) ? $meta['path'] : '', '/');
73
+ $regex = '@^' . preg_quote($meta['path']) . '$@';
74
 
75
  // Perform the initial match for the base URI
76
  $uri_matched = apply_filters(
91
  /**
92
  * Delete specified URI rule
93
  *
94
+ * @param string $uri
95
  *
96
  * @return boolean
97
  *
98
  * @access public
99
  * @version 6.0.0
100
  */
101
+ public function delete($uri)
102
  {
103
  $option = $this->getOption();
104
 
105
+ if (isset($option[$uri])) {
106
+ unset($option[$uri]);
107
 
108
  $this->setOption($option);
109
 
130
  $merged = array();
131
  $pref = AAM::api()->getConfig('core.settings.uri.merge.preference', 'deny');
132
 
133
+ foreach (array_merge($options, $this->getOption()) as $uri => $options) {
134
  // If merging preference is "deny" and at least one of the access
135
  // settings is checked, then final merged array will have it set
136
  // to checked
137
+ if (!isset($merged[$uri])) {
138
+ $merged[$uri] = $options;
139
  } else {
140
  if (($pref === 'deny') && ($options['type'] !== 'allow')) {
141
+ $merged[$uri] = $options;
142
  break;
143
  } elseif ($pref === 'allow' && ($options['type'] === 'allow')) {
144
+ $merged[$uri] = $options;
145
  break;
146
  }
147
  }
application/Core/Object/Visibility.php CHANGED
@@ -25,6 +25,45 @@ class AAM_Core_Object_Visibility extends AAM_Core_Object
25
  */
26
  const OBJECT_TYPE = 'visibility';
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  /**
29
  * @inheritDoc
30
  * @version 6.0.0
@@ -37,9 +76,11 @@ class AAM_Core_Object_Visibility extends AAM_Core_Object
37
  $this->pushOptions('post', $id, $settings);
38
  }
39
 
40
- // Initialize post visibility option. This hooks is used by Access Policy
41
- // service as well as Plus Package to populate visibility list
42
- do_action('aam_visibility_object_init_action', $this);
 
 
43
  }
44
 
45
  /**
@@ -58,17 +99,17 @@ class AAM_Core_Object_Visibility extends AAM_Core_Object
58
  {
59
  $option = $this->getOption();
60
  $filtered = array();
61
- $listOptions = apply_filters('aam_visibility_options_filter', array('hidden'));
62
 
63
  foreach ($options as $key => $value) {
64
- if (in_array($key, $listOptions, true)) {
65
  $filtered[$key] = $value;
66
  }
67
  }
68
 
69
  if (empty($filtered)) {
70
  $filtered = array_combine(
71
- $listOptions, array_fill(0, count($listOptions), false)
 
72
  );
73
  }
74
 
25
  */
26
  const OBJECT_TYPE = 'visibility';
27
 
28
+ /**
29
+ * List of properties that are responsible for visibility
30
+ *
31
+ * @var array
32
+ *
33
+ * @access protected
34
+ * @version 6.0.0
35
+ */
36
+ protected $accessProperties = array();
37
+
38
+ /**
39
+ * Constructor
40
+ *
41
+ * @param AAM_Core_Subject $subject
42
+ * @param mixed $id
43
+ * @param boolean $setSuppressFilters
44
+ *
45
+ * @return void
46
+ *
47
+ * @access public
48
+ * @version 6.0.0
49
+ */
50
+ public function __construct(
51
+ AAM_Core_Subject $subject, $id = null, $suppressFilters = false
52
+ ) {
53
+ $this->setSubject($subject);
54
+ $this->setId($id);
55
+ $this->setSuppressFilters($suppressFilters);
56
+
57
+ // Determine post access properties that are responsible for the post
58
+ // visibility
59
+ $this->accessProperties = apply_filters(
60
+ 'aam_visibility_options_filter', array('hidden')
61
+ );
62
+
63
+ // Initialize the object
64
+ $this->initialize();
65
+ }
66
+
67
  /**
68
  * @inheritDoc
69
  * @version 6.0.0
76
  $this->pushOptions('post', $id, $settings);
77
  }
78
 
79
+ if ($this->suppressFilters() === false) {
80
+ // Initialize post visibility option. This hooks is used by Access Policy
81
+ // service as well as Plus Package to populate visibility list
82
+ do_action('aam_visibility_object_init_action', $this);
83
+ }
84
  }
85
 
86
  /**
99
  {
100
  $option = $this->getOption();
101
  $filtered = array();
 
102
 
103
  foreach ($options as $key => $value) {
104
+ if (in_array($key, $this->accessProperties, true)) {
105
  $filtered[$key] = $value;
106
  }
107
  }
108
 
109
  if (empty($filtered)) {
110
  $filtered = array_combine(
111
+ $this->accessProperties,
112
+ array_fill(0, count($this->accessProperties), false)
113
  );
114
  }
115
 
application/Core/Policy/Condition.php CHANGED
@@ -5,26 +5,20 @@
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
  /**
11
  * AAM core policy condition evaluator
12
  *
13
  * @package AAM
14
- * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
- * @since AAM v5.8.2
16
  */
17
- final class AAM_Core_Policy_Condition {
 
18
 
19
- /**
20
- * Single instance of itself
21
- *
22
- * @var AAM_Core_Policy_Condition
23
- *
24
- * @access protected
25
- * @static
26
- */
27
- protected static $instance = null;
28
 
29
  /**
30
  * Map between condition type and method that evaluates the
@@ -33,6 +27,7 @@ final class AAM_Core_Policy_Condition {
33
  * @var array
34
  *
35
  * @access protected
 
36
  */
37
  protected $map = array(
38
  'between' => 'evaluateBetweenConditions',
@@ -49,15 +44,6 @@ final class AAM_Core_Policy_Condition {
49
  'regex' => 'evaluateRegexConditions'
50
  );
51
 
52
- /**
53
- * Constructor
54
- *
55
- * @return void
56
- *
57
- * @access protected
58
- */
59
- protected function __construct() {}
60
-
61
  /**
62
  * Evaluate the group of conditions based on type
63
  *
@@ -67,11 +53,13 @@ final class AAM_Core_Policy_Condition {
67
  * @return boolean
68
  *
69
  * @access public
 
70
  */
71
- public function evaluate($conditions, $args = array()) {
72
- $result = true;
 
73
 
74
- foreach($conditions as $type => $conditions) {
75
  $type = strtolower($type);
76
 
77
  if (isset($this->map[$type])) {
@@ -79,19 +67,19 @@ final class AAM_Core_Policy_Condition {
79
 
80
  // Since v5.9.2 - if specific condition type is array, then combine
81
  // them with AND operation
82
- if (isset($conditions[0]) && is_array($conditions[0])) {
83
- foreach($conditions as $set) {
84
- $result = $result && call_user_func($callback, $set, $args);
85
  }
86
  } else {
87
- $result = $result && call_user_func($callback, $conditions, $args);
88
  }
89
  } else {
90
- $result = false;
91
  }
92
  }
93
 
94
- return $result;
95
  }
96
 
97
  /**
@@ -103,23 +91,25 @@ final class AAM_Core_Policy_Condition {
103
  * @return boolean
104
  *
105
  * @access protected
 
106
  */
107
- protected function evaluateBetweenConditions($conditions, $args) {
 
108
  $result = false;
109
 
110
- foreach($this->prepareConditions($conditions, $args) as $condition) {
111
  // Convert the right condition into the array of array to cover more
112
  // complex between conditions like [[0,8],[13,15]]
113
- if (is_array($condition['right'][0])) {
114
- $right = $condition['right'];
115
  } else {
116
- $right = array($condition['right']);
117
  }
118
- foreach($right as $subset) {
119
  $min = (is_array($subset) ? array_shift($subset) : $subset);
120
  $max = (is_array($subset) ? end($subset) : $subset);
121
 
122
- $result = $result || ($condition['left'] >= $min && $condition['left'] <= $max);
123
  }
124
  }
125
 
@@ -137,11 +127,13 @@ final class AAM_Core_Policy_Condition {
137
  * @return boolean
138
  *
139
  * @access protected
 
140
  */
141
- protected function evaluateEqualsConditions($conditions, $args) {
 
142
  $result = false;
143
 
144
- foreach($this->prepareConditions($conditions, $args) as $condition) {
145
  $result = $result || ($condition['left'] === $condition['right']);
146
  }
147
 
@@ -157,8 +149,10 @@ final class AAM_Core_Policy_Condition {
157
  * @return boolean
158
  *
159
  * @access protected
 
160
  */
161
- protected function evaluateNotEqualsConditions($conditions, $args) {
 
162
  return !$this->evaluateEqualsConditions($conditions, $args);
163
  }
164
 
@@ -171,11 +165,13 @@ final class AAM_Core_Policy_Condition {
171
  * @return boolean
172
  *
173
  * @access protected
 
174
  */
175
- protected function evaluateGreaterConditions($conditions, $args) {
 
176
  $result = false;
177
 
178
- foreach($this->prepareConditions($conditions, $args) as $condition) {
179
  $result = $result || ($condition['left'] > $condition['right']);
180
  }
181
 
@@ -191,11 +187,13 @@ final class AAM_Core_Policy_Condition {
191
  * @return boolean
192
  *
193
  * @access protected
 
194
  */
195
- protected function evaluateLessConditions($conditions, $args) {
 
196
  $result = false;
197
 
198
- foreach($this->prepareConditions($conditions, $args) as $condition) {
199
  $result = $result || ($condition['left'] < $condition['right']);
200
  }
201
 
@@ -211,11 +209,13 @@ final class AAM_Core_Policy_Condition {
211
  * @return boolean
212
  *
213
  * @access protected
 
214
  */
215
- protected function evaluateGreaterOrEqualsConditions($conditions, $args) {
 
216
  $result = false;
217
 
218
- foreach($this->prepareConditions($conditions, $args) as $condition) {
219
  $result = $result || ($condition['left'] >= $condition['right']);
220
  }
221
 
@@ -231,11 +231,13 @@ final class AAM_Core_Policy_Condition {
231
  * @return boolean
232
  *
233
  * @access protected
 
234
  */
235
- protected function evaluateLessOrEqualsConditions($conditions, $args) {
 
236
  $result = false;
237
 
238
- foreach($this->prepareConditions($conditions, $args) as $condition) {
239
  $result = $result || ($condition['left'] <= $condition['right']);
240
  }
241
 
@@ -251,12 +253,14 @@ final class AAM_Core_Policy_Condition {
251
  * @return boolean
252
  *
253
  * @access protected
 
254
  */
255
- protected function evaluateInConditions($conditions, $args) {
 
256
  $result = false;
257
 
258
- foreach($this->prepareConditions($conditions, $args) as $condition) {
259
- $result = $result || in_array($condition['left'], (array)$condition['right'], true);
260
  }
261
 
262
  return $result;
@@ -271,8 +275,10 @@ final class AAM_Core_Policy_Condition {
271
  * @return boolean
272
  *
273
  * @access protected
 
274
  */
275
- protected function evaluateNotInConditions($conditions, $args) {
 
276
  return !$this->evaluateInConditions($conditions, $args);
277
  }
278
 
@@ -285,14 +291,16 @@ final class AAM_Core_Policy_Condition {
285
  * @return boolean
286
  *
287
  * @access protected
 
288
  */
289
- protected function evaluateLikeConditions($conditions, $args) {
 
290
  $result = false;
291
 
292
- foreach($this->prepareConditions($conditions, $args) as $condition) {
293
- foreach((array)$condition['right'] as $el) {
294
  $sub = str_replace('\*', '.*', preg_quote($el));
295
- $result = $result || preg_match('@^' . $sub . '$@', $condition['left']);
296
  }
297
  }
298
 
@@ -308,8 +316,10 @@ final class AAM_Core_Policy_Condition {
308
  * @return boolean
309
  *
310
  * @access protected
 
311
  */
312
- protected function evaluateNotLikeConditions($conditions, $args) {
 
313
  return !$this->evaluateLikeConditions($conditions, $args);
314
  }
315
 
@@ -322,11 +332,13 @@ final class AAM_Core_Policy_Condition {
322
  * @return boolean
323
  *
324
  * @access protected
 
325
  */
326
- protected function evaluateRegexConditions($conditions, $args) {
 
327
  $result = false;
328
 
329
- foreach($this->prepareConditions($conditions, $args) as $condition) {
330
  $result = $result || preg_match($condition['right'], $condition['left']);
331
  }
332
 
@@ -342,12 +354,14 @@ final class AAM_Core_Policy_Condition {
342
  * @return array
343
  *
344
  * @access protected
 
345
  */
346
- protected function prepareConditions($conditions, $args) {
 
347
  $result = array();
348
 
349
  if (is_array($conditions)) {
350
- foreach($conditions as $left => $right) {
351
  $result[] = array(
352
  'left' => $this->parseExpression($left, $args),
353
  'right' => $this->parseExpression($right, $args)
@@ -367,19 +381,23 @@ final class AAM_Core_Policy_Condition {
367
  * @return mixed Prepared part of the condition or false on failure
368
  *
369
  * @access protected
 
370
  */
371
- protected function parseExpression($exp, $args) {
 
372
  if (is_scalar($exp)) {
373
  if (preg_match_all('/(\$\{[^}]+\})/', $exp, $match)) {
374
  $exp = AAM_Core_Policy_Token::evaluate($exp, $match[1], $args);
375
  }
376
 
 
 
377
  // If there is type scaling, perform it too
378
- if (preg_match('/^\(\*(string|ip|int|boolean|bool|array|null)\)(.*)/i', $exp, $scale)) {
379
- $exp = $this->scaleValue($scale[2], $scale[1]);
380
  }
381
  } elseif (is_array($exp) || is_object($exp)) {
382
- foreach($exp as &$value) {
383
  $value = $this->parseExpression($value, $args);
384
  }
385
  } elseif (is_null($exp) === false) {
@@ -390,7 +408,7 @@ final class AAM_Core_Policy_Condition {
390
  }
391
 
392
  /**
393
- * Scale value to specific type
394
  *
395
  * @param mixed $value
396
  * @param string $type
@@ -398,11 +416,13 @@ final class AAM_Core_Policy_Condition {
398
  * @return mixed
399
  *
400
  * @access protected
 
401
  */
402
- protected function scaleValue($value, $type) {
403
- switch(strtolower($type)) {
 
404
  case 'string':
405
- $value = (string)$value;
406
  break;
407
 
408
  case 'ip':
@@ -410,12 +430,12 @@ final class AAM_Core_Policy_Condition {
410
  break;
411
 
412
  case 'int':
413
- $value = (int)$value;
414
  break;
415
 
416
  case 'boolean':
417
  case 'bool':
418
- $value = (bool)$value;
419
  break;
420
 
421
  case 'array':
@@ -433,20 +453,4 @@ final class AAM_Core_Policy_Condition {
433
  return $value;
434
  }
435
 
436
- /**
437
- * Get single instance of itself
438
- *
439
- * @return AAM_Core_Policy_Condition
440
- *
441
- * @access public
442
- * @static
443
- */
444
- public static function getInstance() {
445
- if (is_null(self::$instance)) {
446
- self::$instance = new self;
447
- }
448
-
449
- return self::$instance;
450
- }
451
-
452
  }
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
+ * @version 6.0.0
10
  */
11
 
12
  /**
13
  * AAM core policy condition evaluator
14
  *
15
  * @package AAM
16
+ * @version 6.0.0
 
17
  */
18
+ class AAM_Core_Policy_Condition
19
+ {
20
 
21
+ use AAM_Core_Contract_SingletonTrait;
 
 
 
 
 
 
 
 
22
 
23
  /**
24
  * Map between condition type and method that evaluates the
27
  * @var array
28
  *
29
  * @access protected
30
+ * @version 6.0.0
31
  */
32
  protected $map = array(
33
  'between' => 'evaluateBetweenConditions',
44
  'regex' => 'evaluateRegexConditions'
45
  );
46
 
 
 
 
 
 
 
 
 
 
47
  /**
48
  * Evaluate the group of conditions based on type
49
  *
53
  * @return boolean
54
  *
55
  * @access public
56
+ * @version 6.0.0
57
  */
58
+ public function evaluate($conditions, $args = array())
59
+ {
60
+ $res = true;
61
 
62
+ foreach ($conditions as $type => $condition) {
63
  $type = strtolower($type);
64
 
65
  if (isset($this->map[$type])) {
67
 
68
  // Since v5.9.2 - if specific condition type is array, then combine
69
  // them with AND operation
70
+ if (isset($condition[0]) && is_array($condition[0])) {
71
+ foreach ($condition as $set) {
72
+ $res = $res && call_user_func($callback, $set, $args);
73
  }
74
  } else {
75
+ $res = $res && call_user_func($callback, $condition, $args);
76
  }
77
  } else {
78
+ $res = false;
79
  }
80
  }
81
 
82
+ return $res;
83
  }
84
 
85
  /**
91
  * @return boolean
92
  *
93
  * @access protected
94
+ * @version 6.0.0
95
  */
96
+ protected function evaluateBetweenConditions($conditions, $args)
97
+ {
98
  $result = false;
99
 
100
+ foreach ($this->prepareConditions($conditions, $args) as $cnd) {
101
  // Convert the right condition into the array of array to cover more
102
  // complex between conditions like [[0,8],[13,15]]
103
+ if (is_array($cnd['right'][0])) {
104
+ $right = $cnd['right'];
105
  } else {
106
+ $right = array($cnd['right']);
107
  }
108
+ foreach ($right as $subset) {
109
  $min = (is_array($subset) ? array_shift($subset) : $subset);
110
  $max = (is_array($subset) ? end($subset) : $subset);
111
 
112
+ $result = $result || ($cnd['left'] >= $min && $cnd['left'] <= $max);
113
  }
114
  }
115
 
127
  * @return boolean
128
  *
129
  * @access protected
130
+ * @version 6.0.0
131
  */
132
+ protected function evaluateEqualsConditions($conditions, $args)
133
+ {
134
  $result = false;
135
 
136
+ foreach ($this->prepareConditions($conditions, $args) as $condition) {
137
  $result = $result || ($condition['left'] === $condition['right']);
138
  }
139
 
149
  * @return boolean
150
  *
151
  * @access protected
152
+ * @version 6.0.0
153
  */
154
+ protected function evaluateNotEqualsConditions($conditions, $args)
155
+ {
156
  return !$this->evaluateEqualsConditions($conditions, $args);
157
  }
158
 
165
  * @return boolean
166
  *
167
  * @access protected
168
+ * @version 6.0.0
169
  */
170
+ protected function evaluateGreaterConditions($conditions, $args)
171
+ {
172
  $result = false;
173
 
174
+ foreach ($this->prepareConditions($conditions, $args) as $condition) {
175
  $result = $result || ($condition['left'] > $condition['right']);
176
  }
177
 
187
  * @return boolean
188
  *
189
  * @access protected
190
+ * @version 6.0.0
191
  */
192
+ protected function evaluateLessConditions($conditions, $args)
193
+ {
194
  $result = false;
195
 
196
+ foreach ($this->prepareConditions($conditions, $args) as $condition) {
197
  $result = $result || ($condition['left'] < $condition['right']);
198
  }
199
 
209
  * @return boolean
210
  *
211
  * @access protected
212
+ * @version 6.0.0
213
  */
214
+ protected function evaluateGreaterOrEqualsConditions($conditions, $args)
215
+ {
216
  $result = false;
217
 
218
+ foreach ($this->prepareConditions($conditions, $args) as $condition) {
219
  $result = $result || ($condition['left'] >= $condition['right']);
220
  }
221
 
231
  * @return boolean
232
  *
233
  * @access protected
234
+ * @version 6.0.0
235
  */
236
+ protected function evaluateLessOrEqualsConditions($conditions, $args)
237
+ {
238
  $result = false;
239
 
240
+ foreach ($this->prepareConditions($conditions, $args) as $condition) {
241
  $result = $result || ($condition['left'] <= $condition['right']);
242
  }
243
 
253
  * @return boolean
254
  *
255
  * @access protected
256
+ * @version 6.0.0
257
  */
258
+ protected function evaluateInConditions($conditions, $args)
259
+ {
260
  $result = false;
261
 
262
+ foreach ($this->prepareConditions($conditions, $args) as $cnd) {
263
+ $result = $result || in_array($cnd['left'], (array) $cnd['right'], true);
264
  }
265
 
266
  return $result;
275
  * @return boolean
276
  *
277
  * @access protected
278
+ * @version 6.0.0
279
  */
280
+ protected function evaluateNotInConditions($conditions, $args)
281
+ {
282
  return !$this->evaluateInConditions($conditions, $args);
283
  }
284
 
291
  * @return boolean
292
  *
293
  * @access protected
294
+ * @version 6.0.0
295
  */
296
+ protected function evaluateLikeConditions($conditions, $args)
297
+ {
298
  $result = false;
299
 
300
+ foreach ($this->prepareConditions($conditions, $args) as $cnd) {
301
+ foreach ((array) $cnd['right'] as $el) {
302
  $sub = str_replace('\*', '.*', preg_quote($el));
303
+ $result = $result || preg_match('@^' . $sub . '$@', $cnd['left']);
304
  }
305
  }
306
 
316
  * @return boolean
317
  *
318
  * @access protected
319
+ * @version 6.0.0
320
  */
321
+ protected function evaluateNotLikeConditions($conditions, $args)
322
+ {
323
  return !$this->evaluateLikeConditions($conditions, $args);
324
  }
325
 
332
  * @return boolean
333
  *
334
  * @access protected
335
+ * @version 6.0.0
336
  */
337
+ protected function evaluateRegexConditions($conditions, $args)
338
+ {
339
  $result = false;
340
 
341
+ foreach ($this->prepareConditions($conditions, $args) as $condition) {
342
  $result = $result || preg_match($condition['right'], $condition['left']);
343
  }
344
 
354
  * @return array
355
  *
356
  * @access protected
357
+ * @version 6.0.0
358
  */
359
+ protected function prepareConditions($conditions, $args)
360
+ {
361
  $result = array();
362
 
363
  if (is_array($conditions)) {
364
+ foreach ($conditions as $left => $right) {
365
  $result[] = array(
366
  'left' => $this->parseExpression($left, $args),
367
  'right' => $this->parseExpression($right, $args)
381
  * @return mixed Prepared part of the condition or false on failure
382
  *
383
  * @access protected
384
+ * @version 6.0.0
385
  */
386
+ protected function parseExpression($exp, $args)
387
+ {
388
  if (is_scalar($exp)) {
389
  if (preg_match_all('/(\$\{[^}]+\})/', $exp, $match)) {
390
  $exp = AAM_Core_Policy_Token::evaluate($exp, $match[1], $args);
391
  }
392
 
393
+ $types = 'string|ip|int|boolean|bool|array|null';
394
+
395
  // If there is type scaling, perform it too
396
+ if (preg_match('/^\(\*(' . $types . ')\)(.*)/i', $exp, $scale)) {
397
+ $exp = $this->castValue($scale[2], $scale[1]);
398
  }
399
  } elseif (is_array($exp) || is_object($exp)) {
400
+ foreach ($exp as &$value) {
401
  $value = $this->parseExpression($value, $args);
402
  }
403
  } elseif (is_null($exp) === false) {
408
  }
409
 
410
  /**
411
+ * Cast value to specific type
412
  *
413
  * @param mixed $value
414
  * @param string $type
416
  * @return mixed
417
  *
418
  * @access protected
419
+ * @version 6.0.0
420
  */
421
+ protected function castValue($value, $type)
422
+ {
423
+ switch (strtolower($type)) {
424
  case 'string':
425
+ $value = (string) $value;
426
  break;
427
 
428
  case 'ip':
430
  break;
431
 
432
  case 'int':
433
+ $value = (int) $value;
434
  break;
435
 
436
  case 'boolean':
437
  case 'bool':
438
+ $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
439
  break;
440
 
441
  case 'array':
453
  return $value;
454
  }
455
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  }
application/Core/Policy/Factory.php CHANGED
@@ -5,50 +5,68 @@
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
  /**
11
  * AAM core policy manager factory
12
- *
13
  * @package AAM
14
- * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
- * @since AAM v5.7.2
16
  */
17
- final class AAM_Core_Policy_Factory {
18
-
 
19
  /**
20
- * Collection of instances
21
- *
22
- * @var array
23
- *
24
  * @access private
25
- * @static
26
  */
27
  private static $_instances = array();
28
-
29
  /**
30
- * Get single instance of itself
31
- *
32
  * @param AAM_Core_Subject $subject
33
- *
34
  * @return AAM_Core_Policy_Manager
35
- *
36
  * @access public
37
- * @static
38
  */
39
- public static function get(AAM_Core_Subject $subject = null) {
 
40
  if (is_null($subject)) {
41
  $subject = AAM::getUser();
42
  }
43
-
44
- $id = $subject->getId();
45
  $sid = $subject::UID . (empty($id) ? '' : '_' . $id);
46
 
47
  if (!isset(self::$_instances[$sid])) {
48
  self::$_instances[$sid] = new AAM_Core_Policy_Manager($subject);
 
 
49
  }
50
-
51
  return self::$_instances[$sid];
52
  }
53
-
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
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
+ * @version 6.0.0
10
  */
11
 
12
  /**
13
  * AAM core policy manager factory
14
+ *
15
  * @package AAM
16
+ * @version 6.0.0
 
17
  */
18
+ final class AAM_Core_Policy_Factory
19
+ {
20
+
21
  /**
22
+ * Collection of policy manage instances
23
+ *
24
+ * @var array
25
+ *
26
  * @access private
27
+ * @version 6.0.0
28
  */
29
  private static $_instances = array();
30
+
31
  /**
32
+ * Get single instance of access manager
33
+ *
34
  * @param AAM_Core_Subject $subject
35
+ *
36
  * @return AAM_Core_Policy_Manager
37
+ *
38
  * @access public
39
+ * @version 6.0.0
40
  */
41
+ public static function get(AAM_Core_Subject $subject = null)
42
+ {
43
  if (is_null($subject)) {
44
  $subject = AAM::getUser();
45
  }
46
+
47
+ $id = $subject->getId();
48
  $sid = $subject::UID . (empty($id) ? '' : '_' . $id);
49
 
50
  if (!isset(self::$_instances[$sid])) {
51
  self::$_instances[$sid] = new AAM_Core_Policy_Manager($subject);
52
+ // Parse all attached to the user policies
53
+ self::$_instances[$sid]->initialize();
54
  }
55
+
56
  return self::$_instances[$sid];
57
  }
58
+
59
+ /**
60
+ * Reset internal cache
61
+ *
62
+ * @return void
63
+ *
64
+ * @access public
65
+ * @version 6.0.0
66
+ */
67
+ public static function reset()
68
+ {
69
+ self::$_instances = array();
70
+ }
71
+
72
  }
application/Core/Policy/Manager.php CHANGED
@@ -5,414 +5,449 @@
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
  /**
11
- * AAM core policy manager
12
- *
13
  * @package AAM
14
- * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
- * @since AAM v5.7.2
16
  */
17
- final class AAM_Core_Policy_Manager {
18
-
 
19
  /**
20
  * Policy core object
21
- *
22
  * @var AAM_Core_Object_Policy
23
- *
24
- * @access protected
 
25
  */
26
- protected $policyObject;
27
-
28
  /**
29
  * Current subject
30
- *
31
  * @var AAM_Core_Subject
32
- *
33
- * @access protected
 
34
  */
35
  protected $subject;
36
-
37
  /**
38
  * Parsed policy tree
39
- *
40
  * @var array
41
- *
42
- * @access protected
 
43
  */
44
- protected $tree = null;
45
-
 
 
 
46
  /**
47
  * Constructor
48
- *
49
  * @access protected
50
- *
51
  * @return void
 
52
  */
53
- public function __construct(AAM_Core_Subject $subject) {
54
- $this->policyObject = $subject->getObject('policy');
55
- $this->subject = $subject;
 
56
  }
57
 
58
  /**
59
- * Undocumented function
60
  *
61
- * @return void
62
- */
63
- public function initializePolicyTree() {
64
- $this->preparePolicyTree();
65
- }
66
-
67
- /**
68
- * Call policy object public methods
69
- *
70
  * @param string $name
71
  * @param array $args
72
- *
73
  * @return mixed
74
- *
75
  * @access public
 
76
  */
77
- public function __call($name, $args) {
78
- $result = null;
79
-
80
- if (method_exists($this->policyObject, $name)) {
81
- $result = call_user_func_array(array($this->policyObject, $name), $args);
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
-
84
- return $result;
85
  }
86
-
87
  /**
88
- * Find all the matching policies
89
- *
90
- * @param string $s RegEx
91
- * @param array $args Inline arguments
92
- * @param bool $single Single record only - the last record
93
- *
94
  * @return array
95
- *
96
  * @access public
 
97
  */
98
- public function find($s, $args = array(), $single = false) {
99
- $statements = array();
100
- $tree = $this->preparePolicyTree();
101
-
102
- foreach($tree['Statement'] as $key => $stm) {
103
- if (preg_match($s, $key) && $this->isApplicable($stm, $args)) {
104
- $statements[$this->strToLower($key)] = $stm;
105
- }
106
  }
107
-
108
- return ($single ? end($statements) : $statements);
109
- }
110
-
111
- /**
112
- * Check if specified action is allowed for resource
113
- *
114
- * This method is working with "Statement" array.
115
- *
116
- * @param string $resource Resource name
117
- * @param array $args Args that will be injected during condition evaluation
118
- *
119
- * @return boolean|null
120
- *
121
- * @access public
122
- */
123
- public function isAllowed($resource, $args = array()) {
124
- $allowed = null;
125
- $tree = $this->preparePolicyTree();
126
- $id = $this->strToLower($resource);
127
-
128
- if (isset($tree['Statement'][$id])) {
129
- $stm = $tree['Statement'][$id];
130
-
131
- if ($this->isApplicable($stm, $args)) {
132
- $effect = strtolower($stm['Effect']);
133
- $allowed = ($effect === 'allow');
134
  }
135
  }
136
-
137
- return $allowed;
138
  }
139
 
140
  /**
141
- * Convert string to lowercase
 
 
 
 
142
  *
143
- * @param string $str
144
- *
145
- * @return string
146
- *
147
  * @access protected
 
148
  */
149
- protected function strToLower($str) {
150
- if (function_exists('mb_strtolower')) {
151
- $result = mb_strtolower($str);
152
- } else {
153
- $result = strtolower($str);
 
 
 
 
 
 
 
 
 
 
 
154
  }
155
 
156
- return $result;
157
  }
158
 
159
  /**
160
- * Determine if resource is the boundary
161
- *
162
- * The Boundary is type of resource that is denied and is enforced so no other
163
- * statements can override it. For example edit_posts capability can be boundary
164
- * for any statement that user Role resource
 
165
  *
166
- * @param string $resource
167
- * @param array $args
168
- *
169
- * @return boolean
170
- *
171
  * @access public
 
 
172
  */
173
- public function isBoundary($resource, $args = array()) {
174
- $denied = false;
175
- $tree = $this->preparePolicyTree();
176
- $id = $this->strToLower($resource);
177
-
178
- if (isset($tree['Statement'][$id])) {
179
- $stm = $tree['Statement'][$id];
180
-
181
- if ($this->isApplicable($stm, $args)) {
182
- $effect = strtolower($stm['Effect']);
183
- $denied = ($effect === 'deny' && !empty($stm['Enforce']));
184
  }
185
  }
186
-
187
- return $denied;
188
  }
189
-
190
  /**
191
- * Get Policy Param
192
- *
193
- * @param string $name
194
- * @param array $args
195
- *
196
- * @return mixed
197
- *
 
 
198
  * @access public
 
199
  */
200
- public function getParam($id, $args = array()) {
201
- $value = null;
 
 
202
 
203
- if (isset($this->tree['Param'][$id])) {
204
- $param = $this->tree['Param'][$id];
205
-
206
- if ($this->isApplicable($param, $args)) {
207
- if (preg_match_all('/(\$\{[^}]+\})/', $param['Value'], $match)) {
208
- $value = AAM_Core_Policy_Token::evaluate($param['Value'], $match[1]);
209
- } else {
210
- $value = $param['Value'];
211
- }
212
  }
213
  }
214
-
215
- return $value;
216
  }
217
-
218
  /**
219
- * Check if current subject can toggle specific policy
220
- *
221
- * Verify that policy can be attached/detached
222
- *
223
- * @param int $id Policy ID
224
- * @param string $action Either "attach" or "detach"
225
- *
226
- * @return bool
227
- *
228
  * @access public
229
- * @since v5.9
230
  */
231
- public function canTogglePolicy($id, $action) {
232
- $post = get_post($id);
233
-
234
- // Verify that current user can perform following action
235
- $stm = $this->find(
236
- "/^post:{$post->post_type}:({$post->post_name}|{$post->ID}):{$action}/i",
237
- array('post' => $post),
238
- true
239
- );
240
-
241
- return (empty($stm['Effect']) || $stm['Effect'] === 'allow');
242
  }
243
-
244
  /**
245
- * Check if policy block is applicable
246
- *
247
- * @param array $block
248
- * @param array $args
249
- *
250
- * @return boolean
251
- *
252
- * @access protected
253
  */
254
- protected function isApplicable($block, $args = array()) {
255
- $result = true;
256
-
257
- if (!empty($block['Condition']) && !is_scalar($block['Condition'])) {
258
- $result = AAM_Core_Policy_Condition::getInstance()->evaluate(
259
- $block['Condition'], $args
260
- );
 
 
 
 
 
 
 
 
 
 
261
  }
262
-
263
- return $result;
264
  }
265
-
266
  /**
267
- * Prepare policy tree
268
- *
269
- * This is the lazy load for the policy tree. If tree has not been initialized,
270
- * trigger the process of parsing and merging statements and settings.
271
- *
272
  * @return array
273
- *
274
  * @access protected
 
275
  */
276
- protected function preparePolicyTree() {
277
- if (is_null($this->tree)) {
278
- $this->tree = array(
279
- 'Statement' => array(),
280
- 'Param' => array()
281
- );
282
-
283
- $ids = array_filter(
284
- $this->policyObject->getOption(),
285
- function($state) {
286
- return !empty($state);
287
- }
288
- );
289
-
290
- if (count($ids)) {
291
- $policies = get_posts(array(
292
- 'include' => array_keys($ids),
293
- 'post_status' => 'publish',
294
- 'post_type' => 'aam_policy'
295
- ));
296
-
297
- foreach($policies as $policy) {
298
- $this->extendTree(
299
- $this->tree, $this->parsePolicy($policy->post_content)
300
- );
301
- }
302
- }
303
- }
304
-
305
- return $this->tree;
306
  }
307
-
308
  /**
309
- * Parse policy post and extract Statements and Params
310
- *
311
- * @param string $policy
312
- *
313
  * @return array
314
- *
315
  * @access protected
 
316
  */
317
- protected function parsePolicy($policy) {
318
- $val = json_decode($policy, true);
319
-
 
320
  // Do not load the policy if any errors
321
  if (json_last_error() === JSON_ERROR_NONE) {
322
  $tree = array(
323
- 'Statement' => isset($val['Statement']) ? (array) $val['Statement'] : array(),
324
- 'Param' => isset($val['Param']) ? (array) $val['Param'] : array(),
325
  );
326
  } else {
327
  $tree = array('Statement' => array(), 'Param' => array());
 
 
 
 
 
 
 
 
 
328
  }
329
-
330
  return $tree;
331
  }
332
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  /**
334
  * Extend tree with additional statements and params
335
- *
336
  * @param array &$tree
337
  * @param array $addition
338
- *
339
  * @return array
340
- *
341
  * @access protected
 
342
  */
343
- protected function extendTree(&$tree, $addition) {
 
 
 
 
344
  // Step #1. If there are any statements, let's index them by resource:action
345
  // and insert into the list of statements
346
- foreach($addition['Statement'] as $stm) {
347
- $list = (isset($stm['Resource']) ? (array) $stm['Resource'] : array());
348
- $acts = (isset($stm['Action']) ? (array) $stm['Action'] : array(''));
349
-
350
- foreach($list as $res) {
351
- // Allow to build resource name dynamically.
352
  // e.g. "Term:category:${USERMETA.region}:posts"
353
  if (preg_match_all('/(\$\{[^}]+\})/', $res, $match)) {
354
  $res = AAM_Core_Policy_Token::evaluate($res, $match[1]);
355
  }
356
- foreach($acts as $act) {
357
- $id = $this->strToLower($res . (!empty($act) ? ":{$act}" : ''));
358
-
359
- if (!isset($tree['Statement'][$id]) || empty($tree['Statement'][$id]['Enforce'])) {
360
- $tree['Statement'][$id] = $this->removeKeys($stm, array('Resource', 'Action'));
 
361
  }
362
  }
363
  }
364
  }
365
 
366
- // Define closure function to handle options and site options
367
- $closure = function($res, $option) {
368
- $param = $this->tree['Param']["option:{$option}"];
369
-
370
- if ($this->isApplicable($param)) {
371
- if (is_array($res) && is_array($param['Value'])) {
372
- $res = array_merge($res, $param['Value']);
373
- } else {
374
- $res = $param['Value'];
375
- }
376
- }
377
-
378
- return $res;
379
- };
380
 
381
  // Step #2. If there are any params, let's index them and insert into the list
382
- foreach($addition['Param'] as $param) {
383
  if (!empty($param['Key'])) {
384
- $id = $param['Key'];
 
 
 
 
 
 
385
 
386
- if (!isset($tree['Param'][$id]) || empty($tree['Param'][$id]['Enforce'])) {
387
- $tree['Param'][$id] = $this->removeKeys($param, array('Key'));
388
 
389
  if (strpos($id, 'option:') === 0) {
390
- add_filter('option_' . substr($id, 7), $closure, 1, 2);
391
- add_filter('site_option_' . substr($id, 7), $closure, 1, 2);
 
 
 
392
  }
393
  }
394
  }
395
  }
396
  }
397
-
398
  /**
399
- * Remove unnecessary keys from array
400
- *
401
- * @param array $arr
402
- * @param array $keys
403
- *
404
- * @return array
405
- *
406
  * @access private
 
407
  */
408
- private function removeKeys($arr, $keys) {
409
- foreach($keys as $key) {
410
- if (isset($arr[$key])) {
411
- unset($arr[$key]);
 
 
 
 
412
  }
413
  }
414
-
415
- return $arr;
416
  }
417
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  }
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
+ * @version 6.0.0
10
  */
11
 
12
  /**
13
+ * AAM policy manager for a specific subject
14
+ *
15
  * @package AAM
16
+ * @version 6.0.0
 
17
  */
18
+ class AAM_Core_Policy_Manager
19
+ {
20
+
21
  /**
22
  * Policy core object
23
+ *
24
  * @var AAM_Core_Object_Policy
25
+ *
26
+ * @access protected
27
+ * @version 6.0.0
28
  */
29
+ protected $object;
30
+
31
  /**
32
  * Current subject
33
+ *
34
  * @var AAM_Core_Subject
35
+ *
36
+ * @access protected
37
+ * @version 6.0.0
38
  */
39
  protected $subject;
40
+
41
  /**
42
  * Parsed policy tree
43
+ *
44
  * @var array
45
+ *
46
+ * @access protected
47
+ * @version 6.0.0
48
  */
49
+ protected $tree = array(
50
+ 'Statement' => array(),
51
+ 'Param' => array()
52
+ );
53
+
54
  /**
55
  * Constructor
56
+ *
57
  * @access protected
58
+ *
59
  * @return void
60
+ * @version 6.0.0
61
  */
62
+ public function __construct(AAM_Core_Subject $subject)
63
+ {
64
+ $this->object = $subject->getObject(AAM_Core_Object_Policy::OBJECT_TYPE);
65
+ $this->subject = $subject;
66
  }
67
 
68
  /**
69
+ * Get policy parameter
70
  *
 
 
 
 
 
 
 
 
 
71
  * @param string $name
72
  * @param array $args
73
+ *
74
  * @return mixed
75
+ *
76
  * @access public
77
+ * @version 6.0.0
78
  */
79
+ public function getParam($id, $args = array())
80
+ {
81
+ $value = null;
82
+
83
+ if (isset($this->tree['Param'][$id])) {
84
+ $param = $this->tree['Param'][$id];
85
+
86
+ if ($this->isApplicable($param, $args)) {
87
+ if (preg_match_all('/(\$\{[^}]+\})/', $param['Value'], $match)) {
88
+ $value = AAM_Core_Policy_Token::evaluate(
89
+ $param['Value'], $match[1]
90
+ );
91
+ } else {
92
+ $value = $param['Value'];
93
+ }
94
+ }
95
  }
96
+
97
+ return $value;
98
  }
99
+
100
  /**
101
+ * Find all statements that match provided resource of list of resources
102
+ *
103
+ * @param string|array $s
104
+ * @param array $args
105
+ *
 
106
  * @return array
107
+ *
108
  * @access public
109
+ * @version 6.0.0
110
  */
111
+ public function getResources($s, $args = array())
112
+ {
113
+ if (is_array($s)) {
114
+ $regex = '/^(' . implode('|', $s) . '):/i';
115
+ } else {
116
+ $regex = "/^{$s}:/i";
 
 
117
  }
118
+
119
+ $statements = array();
120
+
121
+ foreach ($this->tree['Statement'] as $key => $stm) {
122
+ if (preg_match($regex, $key) && $this->isApplicable($stm, $args)) {
123
+ // Remove the resource type to keep it clean
124
+ $statements[preg_replace($regex, '', $key)] = $stm;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  }
126
  }
127
+
128
+ return $this->replaceTokens($statements);
129
  }
130
 
131
  /**
132
+ * Replace all the dynamic tokens recursively
133
+ *
134
+ * @param array $data
135
+ *
136
+ * @return array
137
  *
 
 
 
 
138
  * @access protected
139
+ * @version 6.0.0
140
  */
141
+ protected function replaceTokens($data)
142
+ {
143
+ $replaced = array();
144
+
145
+ foreach($data as $key => $value) {
146
+ if (preg_match_all('/(\$\{[^}]+\})/', $key, $match)) {
147
+ $key = AAM_Core_Policy_Token::evaluate($key, $match[1]);
148
+ }
149
+
150
+ if (is_array($value)) {
151
+ $replaced[$key] = $this->replaceTokens($value);
152
+ } elseif (preg_match_all('/(\$\{[^}]+\})/', $value, $match)) {
153
+ $replaced[$key] = AAM_Core_Policy_Token::evaluate($value, $match[1]);
154
+ } else {
155
+ $replaced[$key] = $value;
156
+ }
157
  }
158
 
159
+ return $replaced;
160
  }
161
 
162
  /**
163
+ * Hook into WP core function to override WP options
164
+ *
165
+ * @param mixed $res
166
+ * @param string $option
167
+ *
168
+ * @return mixed
169
  *
 
 
 
 
 
170
  * @access public
171
+ * @see AAM_Core_Policy_Manager::updatePolicyTree
172
+ * @version 6.0.0
173
  */
174
+ public function getOption($res, $option)
175
+ {
176
+ $param = $this->tree['Param']["option:{$option}"];
177
+
178
+ if ($this->isApplicable($param)) {
179
+ if (is_array($res) && is_array($param['Value'])) {
180
+ $res = array_merge($res, $param['Value']);
181
+ } else {
182
+ $res = $param['Value'];
 
 
183
  }
184
  }
185
+
186
+ return $res;
187
  }
188
+
189
  /**
190
+ * Check if specified action is allowed for resource
191
+ *
192
+ * This method is working with "Statement" array.
193
+ *
194
+ * @param string $resource Resource name
195
+ * @param array $args Args that will be injected during condition evaluation
196
+ *
197
+ * @return boolean|null
198
+ *
199
  * @access public
200
+ * @version 6.0.0
201
  */
202
+ public function isAllowed($resource, $args = array())
203
+ {
204
+ $allowed = null;
205
+ $id = strtolower($resource);
206
 
207
+ if (isset($this->tree['Statement'][$id])) {
208
+ $stm = $this->tree['Statement'][$id];
209
+
210
+ if ($this->isApplicable($stm, $args)) {
211
+ $allowed = (strtolower($stm['Effect']) === 'allow');
 
 
 
 
212
  }
213
  }
214
+
215
+ return $allowed;
216
  }
217
+
218
  /**
219
+ * Get parsed policy tree
220
+ *
221
+ * @return array
222
+ *
 
 
 
 
 
223
  * @access public
224
+ * @version 6.0.0
225
  */
226
+ public function getTree()
227
+ {
228
+ return $this->tree;
 
 
 
 
 
 
 
 
229
  }
230
+
231
  /**
232
+ * Parse all attached policies into the tree
233
+ *
234
+ * @return void
235
+ *
236
+ * @access public
237
+ * @version 6.0.0
 
 
238
  */
239
+ public function initialize()
240
+ {
241
+ // Get the list of all policies that are attached to the subject
242
+ $ids = array_filter($this->object->getOption(), function ($attached) {
243
+ return !empty($attached);
244
+ });
245
+
246
+ // If there is at least one policy attached and it is published, then
247
+ // parse into the tree
248
+ if (count($ids)) {
249
+ $policies = $this->fetchPolicies(array_keys($ids));
250
+
251
+ foreach ($policies as $policy) {
252
+ $this->updatePolicyTree($this->tree, $this->parsePolicy($policy));
253
+ }
254
+
255
+ $this->_cleanupTree();
256
  }
 
 
257
  }
258
+
259
  /**
260
+ * Fetch public policies by IDs
261
+ *
262
+ * @param array $ids
263
+ *
 
264
  * @return array
265
+ *
266
  * @access protected
267
+ * @version 6.0.0
268
  */
269
+ protected function fetchPolicies($ids)
270
+ {
271
+ return get_posts(array(
272
+ 'include' => $ids,
273
+ 'post_status' => 'publish',
274
+ 'suppress_filters' => true,
275
+ 'post_type' => AAM_Service_AccessPolicy::POLICY_CPT
276
+ ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
+
279
  /**
280
+ * Parse JSON policy and extract statements and params
281
+ *
282
+ * @param WP_Post $policy
283
+ *
284
  * @return array
285
+ *
286
  * @access protected
287
+ * @version 6.0.0
288
  */
289
+ protected function parsePolicy($policy)
290
+ {
291
+ $val = json_decode($policy->post_content, true);
292
+
293
  // Do not load the policy if any errors
294
  if (json_last_error() === JSON_ERROR_NONE) {
295
  $tree = array(
296
+ 'Statement' => $this->_getArrayOfArrays($val, 'Statement'),
297
+ 'Param' => $this->_getArrayOfArrays($val, 'Param'),
298
  );
299
  } else {
300
  $tree = array('Statement' => array(), 'Param' => array());
301
+
302
+ // Make sure that this is noticed
303
+ _doing_it_wrong(
304
+ __CLASS__ . '::' . __METHOD__,
305
+ sprintf(
306
+ 'Access policy %d error %s', $policy->ID, json_last_error_msg()
307
+ ),
308
+ AAM_VERSION
309
+ );
310
  }
311
+
312
  return $tree;
313
  }
314
+
315
+ /**
316
+ * Get array of array for Statement and Param policy props
317
+ *
318
+ * @param array $input
319
+ * @param string $prop
320
+ *
321
+ * @return array
322
+ *
323
+ * @access private
324
+ * @version 6.0.0
325
+ */
326
+ private function _getArrayOfArrays($input, $prop)
327
+ {
328
+ $response = array();
329
+
330
+ // Parse Statements and determine if it is multidimensional
331
+ if (array_key_exists($prop, $input)) {
332
+ if (!isset($input[$prop][0]) || !is_array($input[$prop][0])) {
333
+ $response = array($input[$prop]);
334
+ } else {
335
+ $response = $input[$prop];
336
+ }
337
+ }
338
+
339
+ return $response;
340
+ }
341
+
342
  /**
343
  * Extend tree with additional statements and params
344
+ *
345
  * @param array &$tree
346
  * @param array $addition
347
+ *
348
  * @return array
349
+ *
350
  * @access protected
351
+ * @version 6.0.0
352
  */
353
+ protected function updatePolicyTree(&$tree, $addition)
354
+ {
355
+ $stmts = &$tree['Statement'];
356
+ $params = &$tree['Param'];
357
+
358
  // Step #1. If there are any statements, let's index them by resource:action
359
  // and insert into the list of statements
360
+ foreach ($addition['Statement'] as $stm) {
361
+ $resources = (isset($stm['Resource']) ? (array) $stm['Resource'] : array());
362
+ $actions = (isset($stm['Action']) ? (array) $stm['Action'] : array(''));
363
+
364
+ foreach ($resources as $res) {
365
+ // Allow to build resource name dynamically.
366
  // e.g. "Term:category:${USERMETA.region}:posts"
367
  if (preg_match_all('/(\$\{[^}]+\})/', $res, $match)) {
368
  $res = AAM_Core_Policy_Token::evaluate($res, $match[1]);
369
  }
370
+
371
+ foreach ($actions as $act) {
372
+ $id = strtolower($res . (!empty($act) ? ":{$act}" : ''));
373
+
374
+ if (!isset($stmts[$id]) || empty($stmts[$id]['Enforce'])) {
375
+ $stmts[$id] = $stm;
376
  }
377
  }
378
  }
379
  }
380
 
381
+ $callback = array($this, 'getOption'); // Callback that hooks into get_option
 
 
 
 
 
 
 
 
 
 
 
 
 
382
 
383
  // Step #2. If there are any params, let's index them and insert into the list
384
+ foreach ($addition['Param'] as $param) {
385
  if (!empty($param['Key'])) {
386
+ // Allow to build param name dynamically.
387
+ // e.g. "${USERMETA.region}_posts"
388
+ if (preg_match_all('/(\$\{[^}]+\})/', $param['Key'], $match)) {
389
+ $id = AAM_Core_Policy_Token::evaluate($param['Key'], $match[1]);
390
+ } else {
391
+ $id = $param['Key'];
392
+ }
393
 
394
+ if (!isset($params[$id]) || empty($params[$id]['Enforce'])) {
395
+ $params[$id] = $param;
396
 
397
  if (strpos($id, 'option:') === 0) {
398
+ $name = substr($id, 7);
399
+
400
+ // Hook into the core
401
+ add_filter('pre_option_' . $name, $callback, 1, 2);
402
+ add_filter('pre_site_option_' . $name, $callback, 1, 2);
403
  }
404
  }
405
  }
406
  }
407
  }
408
+
409
  /**
410
+ * Perform some internal clean-up
411
+ *
412
+ * @return void
413
+ *
 
 
 
414
  * @access private
415
+ * @version 6.0.0
416
  */
417
+ private function _cleanupTree()
418
+ {
419
+ foreach($this->tree['Statement'] as $id => $stm) {
420
+ if (isset($stm['Resource'])) {
421
+ unset($this->tree['Statement'][$id]['Resource']);
422
+ }
423
+ if (isset($stm['Action'])) {
424
+ unset($this->tree['Statement'][$id]['Action']);
425
  }
426
  }
 
 
427
  }
428
+
429
+ /**
430
+ * Check if policy block is applicable
431
+ *
432
+ * @param array $block
433
+ * @param array $args
434
+ *
435
+ * @return boolean
436
+ *
437
+ * @access protected
438
+ * @version 6.0.0
439
+ */
440
+ protected function isApplicable($block, $args = array())
441
+ {
442
+ $result = true;
443
+
444
+ if (!empty($block['Condition']) && is_array($block['Condition'])) {
445
+ $result = AAM_Core_Policy_Condition::getInstance()->evaluate(
446
+ $block['Condition'], $args
447
+ );
448
+ }
449
+
450
+ return $result;
451
+ }
452
+
453
  }
application/Core/Policy/Resource.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
11
+ * AAM core policy resources
12
+ *
13
+ * @package AAM
14
+ * @version 6.0.0
15
+ */
16
+ class AAM_Core_Policy_Resource
17
+ {
18
+
19
+ /**
20
+ * Backend Menu resource
21
+ *
22
+ * @version 6.0.0
23
+ */
24
+ const MENU = 'BackendMenu';
25
+
26
+ /**
27
+ * Top admin bar resource
28
+ *
29
+ * @version 6.0.0
30
+ */
31
+ const TOOLBAR = 'Toolbar';
32
+
33
+ /**
34
+ * Backend & Frontend widget resource
35
+ *
36
+ * @version 6.0.0
37
+ */
38
+ const WIDGET = 'Widget';
39
+
40
+ /**
41
+ * Backend metabox resource
42
+ *
43
+ * @version 6.0.0
44
+ */
45
+ const METABOX = 'Metabox';
46
+
47
+ /**
48
+ * Capability resource
49
+ *
50
+ * @version 6.0.0
51
+ */
52
+ const CAPABILITY = 'Capability';
53
+
54
+ /**
55
+ * Role resource
56
+ *
57
+ * @version 6.0.0
58
+ */
59
+ const ROLE = 'Role';
60
+
61
+ /**
62
+ * Post resource
63
+ *
64
+ * @version 6.0.0
65
+ */
66
+ const POST = 'Post';
67
+
68
+ /**
69
+ * Uri resource
70
+ *
71
+ * @version 6.0.0
72
+ */
73
+ const URI = "URI";
74
+
75
+ /**
76
+ * Plugin resource
77
+ *
78
+ * @version 6.0.0
79
+ */
80
+ const PLUGIN = 'Plugin';
81
+
82
+ }
application/Core/Policy/Token.php CHANGED
@@ -5,16 +5,18 @@
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
  /**
11
  * AAM core policy token evaluator
12
  *
13
  * @package AAM
14
- * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
- * @since AAM v5.8.2
16
  */
17
- final class AAM_Core_Policy_Token {
 
18
 
19
  /**
20
  * Literal map token's type to the executable method that returns actual value
@@ -22,21 +24,21 @@ final class AAM_Core_Policy_Token {
22
  * @var array
23
  *
24
  * @access protected
25
- * @static
26
  */
27
  protected static $map = array(
28
- 'USER' => 'AAM_Core_Policy_Token::getUserValue',
29
- 'USERMETA' => 'AAM_Core_Policy_Token::getUserMetaValue',
30
- 'DATETIME' => 'AAM_Core_Policy_Token::getDateTimeValue',
31
- 'GET' => 'AAM_Core_Request::get',
32
- 'QUERY' => 'AAM_Core_Request::get',
33
- 'POST' => 'AAM_Core_Request::post',
34
- 'COOKIE' => 'AAM_Core_Request::cookie',
35
- 'SERVER' => 'AAM_Core_Request::server',
36
- 'ARGS' => 'AAM_Core_Policy_Token::getArgValue',
37
- 'CONST' => 'AAM_Core_Policy_Token::defined',
38
- 'JWT' => '', //TODO: Implement
39
- 'PARAM' => '' //TODO: Implement
40
  );
41
 
42
  /**
@@ -44,14 +46,16 @@ final class AAM_Core_Policy_Token {
44
  *
45
  * @param string $part String with tokens
46
  * @param array $tokens Extracted token
 
47
  *
48
  * @return string
49
  *
50
  * @access public
51
- * @static
52
  */
53
- public static function evaluate($part, array $tokens, array $args = array()) {
54
- foreach($tokens as $token) {
 
55
  $val = self::getValue(
56
  preg_replace('/^\$\{([^}]+)\}$/', '${1}', $token),
57
  $args
@@ -76,14 +80,15 @@ final class AAM_Core_Policy_Token {
76
  * @return mixed
77
  *
78
  * @access protected
79
- * @static
80
  */
81
- protected static function getValue($token, $args) {
 
82
  $value = null;
83
  $parts = explode('.', $token);
84
 
85
  if (isset(self::$map[$parts[0]])) {
86
- if ($parts[0] === 'ARG') {
87
  $value = call_user_func(self::$map[$parts[0]], $parts[1], $args);
88
  } else {
89
  $value = call_user_func(self::$map[$parts[0]], $parts[1]);
@@ -103,12 +108,13 @@ final class AAM_Core_Policy_Token {
103
  * @return mixed
104
  *
105
  * @access protected
106
- * @static
107
  */
108
- protected static function getUserValue($prop) {
 
109
  $user = AAM::getUser();
110
 
111
- switch(strtolower($prop)) {
112
  case 'ip':
113
  case 'ipaddress':
114
  $value = AAM_Core_Request::server('REMOTE_ADDR');
@@ -116,13 +122,12 @@ final class AAM_Core_Policy_Token {
116
 
117
  case 'authenticated':
118
  case 'isauthenticated':
119
- $value = $user::UID !== AAM_Core_Subject_Visitor;
120
  break;
121
 
122
  case 'capabilities':
123
  case 'caps':
124
- $value = array();
125
- foreach((array) $user->allcaps as $cap => $effect) {
126
  if (!empty($effect)) {
127
  $value[] = $cap;
128
  }
@@ -140,19 +145,20 @@ final class AAM_Core_Policy_Token {
140
  /**
141
  * Get user meta value(s)
142
  *
143
- * @param string $metakey
144
  *
145
  * @return void
146
  *
147
  * @access protected
148
- * @static
149
  */
150
- protected static function getUserMetaValue($metakey) {
 
151
  $value = null;
152
  $id = get_current_user_id();
153
 
154
  if (!empty($id)) { // Only authenticated users have some sort of meta
155
- $meta = get_user_meta($id, $metakey);
156
 
157
  // If $meta has only one value in the array, then extract it, otherwise
158
  // return the array of values
@@ -175,24 +181,26 @@ final class AAM_Core_Policy_Token {
175
  * @return mixed
176
  *
177
  * @access protected
178
- * @static
179
  */
180
- protected static function getArgValue($prop, $args) {
 
181
  return (isset($args[$prop]) ? $args[$prop] : null);
182
  }
183
 
184
  /**
185
- * Get current datetime value
186
  *
187
  * @param string $prop
188
  *
189
- * @return string
190
  *
191
  * @access protected
192
- * @static
193
  */
194
- protected static function getDateTimeValue($prop) {
195
- return date($prop);
 
196
  }
197
 
198
  /**
@@ -203,9 +211,10 @@ final class AAM_Core_Policy_Token {
203
  * @return mixed
204
  *
205
  * @access protected
206
- * @static
207
  */
208
- protected static function defined($const) {
 
209
  return (defined($const) ? constant($const) : null);
210
  }
211
 
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
+ * @version 6.0.0
10
  */
11
 
12
  /**
13
  * AAM core policy token evaluator
14
  *
15
  * @package AAM
16
+ * @version 6.0.0
 
17
  */
18
+ class AAM_Core_Policy_Token
19
+ {
20
 
21
  /**
22
  * Literal map token's type to the executable method that returns actual value
24
  * @var array
25
  *
26
  * @access protected
27
+ * @version 6.0.0
28
  */
29
  protected static $map = array(
30
+ 'USER' => 'AAM_Core_Policy_Token::getUserValue',
31
+ 'USER_META' => 'AAM_Core_Policy_Token::getUserMetaValue',
32
+ 'DATETIME' => 'date',
33
+ 'HTTP_GET' => 'AAM_Core_Request::get',
34
+ 'HTTP_QUERY' => 'AAM_Core_Request::get',
35
+ 'HTTP_POST' => 'AAM_Core_Request::post',
36
+ 'HTTP_COOKIE' => 'AAM_Core_Request::cookie',
37
+ 'PHP_SERVER' => 'AAM_Core_Request::server',
38
+ 'ARGS' => 'AAM_Core_Policy_Token::getArgValue',
39
+ 'CONST' => 'AAM_Core_Policy_Token::getConstant',
40
+ 'WP_OPTION' => 'AAM_Core_API::getOption',
41
+ 'JWT' => 'AAM_Core_Policy_Token::getJwtClaim'
42
  );
43
 
44
  /**
46
  *
47
  * @param string $part String with tokens
48
  * @param array $tokens Extracted token
49
+ * @param array $args Inline arguments
50
  *
51
  * @return string
52
  *
53
  * @access public
54
+ * @version 6.0.0
55
  */
56
+ public static function evaluate($part, array $tokens, array $args = array())
57
+ {
58
+ foreach ($tokens as $token) {
59
  $val = self::getValue(
60
  preg_replace('/^\$\{([^}]+)\}$/', '${1}', $token),
61
  $args
80
  * @return mixed
81
  *
82
  * @access protected
83
+ * @version 6.0.0
84
  */
85
+ protected static function getValue($token, $args)
86
+ {
87
  $value = null;
88
  $parts = explode('.', $token);
89
 
90
  if (isset(self::$map[$parts[0]])) {
91
+ if ($parts[0] === 'ARGS') {
92
  $value = call_user_func(self::$map[$parts[0]], $parts[1], $args);
93
  } else {
94
  $value = call_user_func(self::$map[$parts[0]], $parts[1]);
108
  * @return mixed
109
  *
110
  * @access protected
111
+ * @version 6.0.0
112
  */
113
+ protected static function getUserValue($prop)
114
+ {
115
  $user = AAM::getUser();
116
 
117
+ switch (strtolower($prop)) {
118
  case 'ip':
119
  case 'ipaddress':
120
  $value = AAM_Core_Request::server('REMOTE_ADDR');
122
 
123
  case 'authenticated':
124
  case 'isauthenticated':
125
+ $value = is_user_logged_in();
126
  break;
127
 
128
  case 'capabilities':
129
  case 'caps':
130
+ foreach ((array) $user->allcaps as $cap => $effect) {
 
131
  if (!empty($effect)) {
132
  $value[] = $cap;
133
  }
145
  /**
146
  * Get user meta value(s)
147
  *
148
+ * @param string $meta_key
149
  *
150
  * @return void
151
  *
152
  * @access protected
153
+ * @version 6.0.0
154
  */
155
+ protected static function getUserMetaValue($meta_key)
156
+ {
157
  $value = null;
158
  $id = get_current_user_id();
159
 
160
  if (!empty($id)) { // Only authenticated users have some sort of meta
161
+ $meta = get_user_meta($id, $meta_key);
162
 
163
  // If $meta has only one value in the array, then extract it, otherwise
164
  // return the array of values
181
  * @return mixed
182
  *
183
  * @access protected
184
+ * @version 6.0.0
185
  */
186
+ protected static function getArgValue($prop, $args)
187
+ {
188
  return (isset($args[$prop]) ? $args[$prop] : null);
189
  }
190
 
191
  /**
192
+ * Get JWT claim property
193
  *
194
  * @param string $prop
195
  *
196
+ * @return mixed
197
  *
198
  * @access protected
199
+ * @version 6.0.0
200
  */
201
+ protected static function getJwtClaim($prop)
202
+ {
203
+ return apply_filters('aam_get_jwt_claim', null, $prop);
204
  }
205
 
206
  /**
211
  * @return mixed
212
  *
213
  * @access protected
214
+ * @version 6.0.0
215
  */
216
+ protected static function getConstant($const)
217
+ {
218
  return (defined($const) ? constant($const) : null);
219
  }
220
 
application/Core/Policy/Validator.php CHANGED
@@ -5,122 +5,150 @@
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
  'isValidDependency', // #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 isValidDependency() {
 
 
 
 
 
 
 
 
 
 
 
118
  if (!empty($this->json['Dependency'])) {
119
- foreach($this->json['Dependency'] as $app => $constraints) {
120
  try {
121
  $satisfies = Semver::satisfies(
122
- $this->getAppVersion(strtolower($app)), $constraints
 
123
  );
 
124
  if ($satisfies === false) {
125
  throw new Exception(
126
  AAM_Backend_View_Helper::preparePhrase(
@@ -135,38 +163,63 @@ class AAM_Core_Policy_Validator {
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(
@@ -175,7 +228,8 @@ class AAM_Core_Policy_Validator {
175
  )
176
  );
177
  }
178
-
179
  return $version;
180
  }
 
181
  }
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
+ * @version 6.0.0
10
  */
11
 
12
  use Composer\Semver\Semver;
13
 
14
  /**
15
+ * AAM access policy validator
16
+ *
17
  * @package AAM
18
+ * @version 6.0.0
 
19
  */
20
+ class AAM_Core_Policy_Validator
21
+ {
22
+
23
  /**
24
  * Raw policy text
25
+ *
26
  * @var string
27
+ *
28
+ * @access protected
29
+ * @version 6.0.0
30
  */
31
  protected $policy;
32
+
33
  /**
34
  * Parsed JSON document
35
+ *
36
  * @var array
37
+ *
38
+ * @access protected
39
+ * @version 6.0.0
40
  */
41
  protected $json;
42
+
43
  /**
44
  * Collection of errors
45
+ *
46
  * @var array
47
+ *
48
+ * @access protected
49
+ * @version 6.0.0
50
  */
51
  protected $errors = array();
52
+
53
  /**
54
  * Constructor
55
+ *
56
  * @param string $policy
57
+ *
58
+ * @return void
59
+ *
60
  * @access public
61
+ * @version 6.0.0
62
  */
63
+ public function __construct($policy)
64
+ {
65
  $this->policy = trim($policy);
66
  $this->json = json_decode($policy, true);
67
  }
68
+
69
  /**
70
+ * Validate the policy by invoking several validation steps
71
+ *
72
  * @return array
73
+ *
74
  * @access public
75
+ * @version 6.0.0
76
  */
77
+ public function validate()
78
+ {
79
  $steps = array(
80
  'isJSON', // #1. Check if policy is valid JSON
81
  'isNotEmpty', // #2. Check if policy is not empty
82
  'isValidDependency', // #3. Check if all dependencies are defined properly
83
  );
84
+
85
+ foreach ($steps as $step) {
86
  if (call_user_func(array($this, $step)) === false) {
87
  break;
88
  }
89
  }
90
+
91
  return $this->errors;
92
  }
93
+
94
  /**
95
  * Check if policy is valid JSON
96
+ *
97
  * @return boolean
98
+ *
99
+ * @access protected
100
+ * @version 6.0.0
101
  */
102
+ protected function isJSON()
103
+ {
104
  $result = is_array($this->json);
105
+
106
  if ($result === false) {
107
  $this->errors[] = __('The policy is not valid JSON object', AAM_KEY);
108
  }
109
+
110
  return $result;
111
  }
112
+
113
  /**
114
  * Check if policy is empty
115
+ *
116
  * @return boolean
117
+ *
118
+ * @access protected
119
+ * @version 6.0.0
120
  */
121
+ protected function isNotEmpty()
122
+ {
123
  $result = !empty($this->policy) && !empty($this->json);
124
+
125
  if ($result === false) {
126
  $this->errors[] = __('The policy document is empty', AAM_KEY);
127
  }
128
+
129
  return $result;
130
  }
131
+
132
+ /**
133
+ * Check for the policy dependencies
134
+ *
135
+ * Make sure that depending plugins are installed and have proper versions
136
+ *
137
+ * @return void
138
+ *
139
+ * @access protected
140
+ * @version 6.0.0
141
+ */
142
+ protected function isValidDependency()
143
+ {
144
  if (!empty($this->json['Dependency'])) {
145
+ foreach ($this->json['Dependency'] as $app => $constraints) {
146
  try {
147
  $satisfies = Semver::satisfies(
148
+ $this->getAppVersion($app),
149
+ $constraints
150
  );
151
+
152
  if ($satisfies === false) {
153
  throw new Exception(
154
  AAM_Backend_View_Helper::preparePhrase(
163
  }
164
  }
165
  }
166
+
167
+ /**
168
+ * Get dependency's version
169
+ *
170
+ * @param string $app
171
+ *
172
+ * @return void
173
+ *
174
+ * @access protected
175
+ * @version 6.0.0
176
+ */
177
+ protected function getAppVersion($app)
178
+ {
179
  global $wp_version;
180
+
181
+ $slug = strtolower($app);
182
+
183
+ if ($slug === 'wordpress') {
184
  $version = $wp_version;
185
  } else {
186
+ $version = $this->getPluginVersion($slug);
187
  }
188
+
189
  return $version;
190
  }
191
+
192
+ /**
193
+ * Get plugin's version
194
+ *
195
+ * @param string $slug
196
+ *
197
+ * @return string
198
+ *
199
+ * @access protected
200
+ * @throws Exception
201
+ * @version 6.0.0
202
+ */
203
+ protected function getPluginVersion($slug)
204
+ {
205
  static $plugins = null;
206
+
207
  if (is_null($plugins)) {
208
  if (file_exists(ABSPATH . 'wp-admin/includes/plugin.php')) {
209
  require_once ABSPATH . 'wp-admin/includes/plugin.php';
210
  }
211
+
212
  $plugins = get_plugins();
213
  }
214
+
215
  $version = null;
216
+
217
+ foreach ($plugins as $plugin => $data) {
218
  if (stripos($plugin, $slug . '/') === 0) {
219
  $version = $data['Version'];
220
  }
221
  }
222
+
223
  if (is_null($version)) {
224
  throw new Exception(
225
  AAM_Backend_View_Helper::preparePhrase(
228
  )
229
  );
230
  }
231
+
232
  return $version;
233
  }
234
+
235
  }
application/Core/Subject.php CHANGED
@@ -105,7 +105,7 @@ abstract class AAM_Core_Subject
105
  _doing_it_wrong(
106
  static::class . '::' . $name,
107
  'Subject does not have method defined',
108
- '6.0.0'
109
  );
110
  }
111
 
@@ -334,7 +334,7 @@ abstract class AAM_Core_Subject
334
  )->getOption();
335
 
336
  // Merge access settings while reading hierarchical chain
337
- $option = array_replace($option, $object->getOption());
338
 
339
  // Merge access settings if multi-roles option is enabled
340
  $multi = AAM::api()->getConfig('core.settings.multiSubject', false);
105
  _doing_it_wrong(
106
  static::class . '::' . $name,
107
  'Subject does not have method defined',
108
+ AAM_VERSION
109
  );
110
  }
111
 
334
  )->getOption();
335
 
336
  // Merge access settings while reading hierarchical chain
337
+ $option = array_replace_recursive($option, $object->getOption());
338
 
339
  // Merge access settings if multi-roles option is enabled
340
  $multi = AAM::api()->getConfig('core.settings.multiSubject', false);
application/Core/Subject/Role.php CHANGED
@@ -35,6 +35,16 @@ class AAM_Core_Subject_Role extends AAM_Core_Subject
35
  */
36
  protected $name;
37
 
 
 
 
 
 
 
 
 
 
 
38
  /**
39
  * Constructor
40
  *
@@ -193,11 +203,6 @@ class AAM_Core_Subject_Role extends AAM_Core_Subject
193
  $has = $this->has_cap($cap);
194
  }
195
 
196
- // Override result if necessary
197
- if (apply_filters('aam_allowed_cap_filter', true, $cap) === false) {
198
- $has = false;
199
- }
200
-
201
  return $has;
202
  }
203
 
@@ -207,11 +212,15 @@ class AAM_Core_Subject_Role extends AAM_Core_Subject
207
  */
208
  public function getParent()
209
  {
210
- return apply_filters(
211
- 'aam_parent_role_filter',
212
- AAM_Core_Subject_Default::getInstance(),
213
- $this
214
- );
 
 
 
 
215
  }
216
 
217
  /**
35
  */
36
  protected $name;
37
 
38
+ /**
39
+ * Parent role's subject
40
+ *
41
+ * @var AAM_Core_Subject
42
+ *
43
+ * @access private
44
+ * @version 6.0.0
45
+ */
46
+ private $_parent = null;
47
+
48
  /**
49
  * Constructor
50
  *
203
  $has = $this->has_cap($cap);
204
  }
205
 
 
 
 
 
 
206
  return $has;
207
  }
208
 
212
  */
213
  public function getParent()
214
  {
215
+ if (is_null($this->_parent)) {
216
+ $this->_parent = apply_filters(
217
+ 'aam_parent_role_filter',
218
+ AAM_Core_Subject_Default::getInstance(),
219
+ $this
220
+ );
221
+ }
222
+
223
+ return $this->_parent;
224
  }
225
 
226
  /**
application/Core/Subject/User.php CHANGED
@@ -55,27 +55,35 @@ class AAM_Core_Subject_User extends AAM_Core_Subject
55
  /**
56
  * Constructor
57
  *
58
- * @param int $id
59
- * @param boolean $initialize
60
  *
61
  * @return void
62
  *
63
  * @access public
64
  * @version 6.0.0
65
  */
66
- public function __construct($id, $initialize = false)
67
  {
68
  // Set subject Id
69
  $this->setId(intval($id));
70
 
71
  // Retrieve underlining WP core principal
72
  $this->setPrincipal($this->retrievePrincipal());
 
73
 
74
- if ($initialize === true) {
75
- // Initialize current user. This hook is used by Access Policy service to
76
- // mutate the capability and role lists for current user
77
- do_action('aam_initialize_user_action', $this);
78
- }
 
 
 
 
 
 
 
 
79
  }
80
 
81
  /**
55
  /**
56
  * Constructor
57
  *
58
+ * @param int $id
 
59
  *
60
  * @return void
61
  *
62
  * @access public
63
  * @version 6.0.0
64
  */
65
+ public function __construct($id)
66
  {
67
  // Set subject Id
68
  $this->setId(intval($id));
69
 
70
  // Retrieve underlining WP core principal
71
  $this->setPrincipal($this->retrievePrincipal());
72
+ }
73
 
74
+ /**
75
+ * Initialize user subject
76
+ *
77
+ * @return void
78
+ *
79
+ * @access public
80
+ * @version 6.0.0
81
+ */
82
+ public function initialize()
83
+ {
84
+ // Initialize current user. This hook is used by Access Policy service to
85
+ // mutate the capability and role lists for current user
86
+ do_action('aam_initialize_user_action', $this);
87
  }
88
 
89
  /**
application/Service/AccessPolicy.php CHANGED
@@ -5,24 +5,42 @@
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
  /**
11
  * Access Policy service
12
  *
13
- * @since v6.0.0
 
14
  */
15
  class AAM_Service_AccessPolicy
16
  {
17
- use AAM_Core_Contract_ServiceTrait;
 
18
 
19
  /**
20
  * AAM configuration setting that is associated with the feature
 
 
21
  */
22
  const FEATURE_FLAG = 'core.service.access-policy.enabled';
23
 
 
 
 
 
 
 
 
24
  /**
25
  * Constructor
 
 
 
 
 
26
  */
27
  protected function __construct()
28
  {
@@ -33,15 +51,6 @@ class AAM_Service_AccessPolicy
33
  AAM_Backend_Feature_Main_Policy::register();
34
  }, 40);
35
 
36
- //manager Admin Menu
37
- if (is_multisite() && is_network_admin()) {
38
- //register AAM in the network admin panel
39
- add_action('_network_admin_menu', array($this, 'registerAdminMenu'));
40
- } else {
41
- add_action('_user_admin_menu', array($this, 'registerAdminMenu'));
42
- add_action('_admin_menu', array($this, 'registerAdminMenu'));
43
- }
44
-
45
  //register custom access control metabox
46
  add_action('add_meta_boxes', array($this, 'registerMetaboxes'));
47
 
@@ -55,7 +64,7 @@ class AAM_Service_AccessPolicy
55
  add_filter('aam_service_list_filter', function ($services) {
56
  $services[] = array(
57
  'title' => __('Access Policies', AAM_KEY),
58
- 'description' => __('Manage list of access policies for any user, role or visitors.', AAM_KEY),
59
  'setting' => self::FEATURE_FLAG
60
  );
61
 
@@ -68,6 +77,44 @@ class AAM_Service_AccessPolicy
68
  }
69
  }
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /**
72
  * Hook into policy submission and filter its content
73
  *
@@ -76,14 +123,15 @@ class AAM_Service_AccessPolicy
76
  * @return array
77
  *
78
  * @access public
 
79
  */
80
  public function managePolicyContent($data)
81
  {
82
- if (isset($data['post_type']) && ($data['post_type'] === 'aam_policy')) {
83
- $content = trim(filter_input(INPUT_POST, 'aam-policy'));
84
 
85
  if (empty($data['post_content'])) {
86
- $content = AAM_Backend_View_Helper::getDefaultPolicy();
87
  }
88
 
89
  // Reformat the policy content
@@ -109,6 +157,7 @@ class AAM_Service_AccessPolicy
109
  * @return void
110
  *
111
  * @access protected
 
112
  */
113
  protected function initializeHooks()
114
  {
@@ -117,13 +166,14 @@ class AAM_Service_AccessPolicy
117
  register_post_type('aam_policy', array(
118
  'label' => __('Access Policy', AAM_KEY),
119
  'labels' => array(
120
- 'name' => __('Access Policies', AAM_KEY),
121
- 'edit_item' => __('Edit Policy', AAM_KEY),
122
- 'add_new_item' => __('Add New Policy', AAM_KEY),
123
- 'new_item' => __('New Policy', AAM_KEY)
 
124
  ),
125
  'description' => __('Access and security policy', AAM_KEY),
126
- 'public' => true,
127
  'show_ui' => true,
128
  'show_in_menu' => false,
129
  'exclude_from_search' => true,
@@ -143,203 +193,191 @@ class AAM_Service_AccessPolicy
143
  ));
144
  });
145
 
 
 
 
 
 
 
 
 
 
146
  // Manage access to the Capabilities
147
- add_filter('aam_allowed_cap_filter', array($this, 'isCapAllowed'), 10, 3);
 
148
 
149
  // Manage access to the Plugin list and individual plugins
150
  add_filter('aam_allowed_plugin_action_filter', array($this, 'isPluginActionAllowed'), 10, 3);
151
-
152
- // Manage access settings for the Objects
153
- add_filter('aam_admin_menu_object_option_filter', array($this, 'initMenuObjectOptions'), 10, 2);
154
- add_filter('aam_metabox_object_option_filter', array($this, 'initMetaboxObjectOptions'), 10, 2);
155
- add_filter('aam-post-access-filter', array($this, 'initPostAccessOptions'), 10, 2);
156
- add_filter('aam_route_object_option_filter', array($this, 'initRouteObjectOptions'), 10, 2);
157
- add_filter('aam_toolbar_object_option_filter', array($this, 'initToolbarObjectOptions'), 10, 2);
158
- add_filter('aam_uri_object_option_filter', array($this, 'initUriObjectOptions'), 10, 2);
159
-
160
- // Visibility initialization process
161
- add_action('aam_visibility_object_init_action', array($this, 'initVisibilityObject'), 1);
162
-
163
- // User initialization process
164
- add_action('aam_initialize_user_action', array($this, 'initializeUser'));
165
-
166
- // Convert Access Policy settings into AAM core settings
167
- add_filter('aam-policy-posts-terms-settings-filter', array($this, 'convertContentSettings'), 10, 5);
168
  }
169
 
170
  /**
 
171
  *
172
- * @global WP_Post $post
 
 
 
 
 
 
 
173
  */
174
- public function registerMetaboxes()
175
  {
176
- global $post;
177
-
178
- if (is_a($post, 'WP_Post') && ($post->post_type === 'aam_policy')) {
179
- add_meta_box(
180
- 'aam-policy',
181
- __('Access Policy Document', AAM_KEY),
182
- function () {
183
- global $post;
184
-
185
- if (is_a($post, 'WP_Post')) {
186
- echo $this->renderPolicyMetabox($post);
187
- }
188
- },
189
- null,
190
- 'normal',
191
- 'high'
192
- );
193
- add_meta_box(
194
- 'aam-policy-attached',
195
- __('Access Policy Assignee', AAM_KEY),
196
- function () {
197
- global $post;
198
 
199
- if (is_a($post, 'WP_Post')) {
200
- echo $this->renderPolicyPrincipalMetabox($post);
201
- }
202
- },
203
- null,
204
- 'side'
205
- );
206
  }
207
- }
208
 
209
- /**
210
- *
211
- * @param type $post
212
- * @return type
213
- */
214
- public function renderPolicyMetabox($post)
215
- {
216
- return $this->loadTemplate(
217
- dirname(__FILE__) . '/phtml/metabox/policy-metabox.phtml',
218
- (object) array('post' => $post)
219
- );
220
  }
221
 
222
  /**
 
223
  *
224
- * @param type $post
225
- * @return type
226
- */
227
- public function renderPolicyPrincipalMetabox($post)
228
- {
229
- return $this->loadTemplate(
230
- dirname(__FILE__) . '/phtml/metabox/policy-principal-metabox.phtml',
231
- (object) array('post' => $post)
232
- );
233
- }
234
-
235
- /**
236
- * Check if specified action is allowed upon capability
237
- *
238
- * @param boolean $allowed
239
- * @param string $cap
240
- * @param string $action
241
  *
242
- * @return boolean
243
  *
244
  * @access public
245
- * @link https://aamplugin.com/reference/policy#capability
 
246
  */
247
- public function isCapAllowed($allowed, $cap, $action = null)
248
  {
249
- $manager = $this->getPolicyManager();
 
 
250
 
251
- if ($action !== null) {
252
- $result = $manager->isAllowed("Capability:{$cap}:AAM:{$action}");
253
- } else {
254
- $result = $manager->isAllowed("Capability:{$cap}");
255
  }
256
 
257
- return $result;
258
  }
259
 
260
  /**
261
- * Get policy manager
262
  *
263
- * @return AAM_Core_Policy_Manager
 
 
 
264
  *
265
  * @access public
 
 
266
  */
267
- public function getPolicyManager(AAM_Core_Subject $subject = null)
268
  {
269
- return AAM_Core_Policy_Factory::get(
270
- (is_null($subject) ? $this->getUser() : $subject)
271
- );
 
 
 
 
 
 
 
 
 
272
  }
273
 
274
  /**
275
- * Check if specific action is allowed upon all plugins or specified plugin
276
  *
277
- * @param boolean|null $allowed
278
- * @param string $action
279
- * @param string $slug
280
  *
281
  * @return boolean
282
  *
283
  * @access public
284
- * @link https://aamplugin.com/reference/policy#plugin
 
285
  */
286
- public function isPluginActionAllowed($allowed, $action, $slug = null)
287
  {
288
- $manager = $this->getPolicyManager();
 
289
 
290
- if ($slug === null) {
291
- $result = $manager->isAllowed("Plugin:WP:{$action}");
292
- } else {
293
- $result = $manager->isAllowed("Plugin:{$slug}:WP:{$action}");
294
- }
295
-
296
- return $result;
297
  }
298
 
299
  /**
300
- * Initialize Menu Object options
301
  *
302
- * @param array $option
303
- * @param AAM_Core_Subject $subject
304
  *
305
- * @return array
306
  *
307
  * @access public
308
- * @see https://aamplugin.com/reference/policy#backendmenu
 
 
309
  */
310
- public function initMenuObjectOptions($option, AAM_Core_Subject $subject)
311
  {
312
- $stms = $this->getPolicyManager($subject)->find("/^BackendMenu:/i");
 
313
 
314
- foreach ($stms as $key => $stm) {
315
- $chunks = explode(':', $key);
316
- $option[$chunks[1]] = ($stm['Effect'] === 'deny' ? 1 : 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  }
318
 
319
- return $option;
320
- }
 
 
 
 
 
 
321
 
322
- /**
323
- * Initialize Metabox Object options
324
- *
325
- * @param array $option
326
- * @param AAM_Core_Subject $subject
327
- *
328
- * @return array
329
- *
330
- * @access public
331
- * @see https://aamplugin.com/reference/policy#metabox
332
- */
333
- public function initMetaboxObjectOptions($option, AAM_Core_Subject $subject)
334
- {
335
- $stms = $this->getPolicyManager($subject)->find("/^(Metabox|Widget):/i");
336
 
337
- foreach ($stms as $key => $stm) {
338
- $chunks = explode(':', $key);
339
- $option[$chunks[1]] = ($stm['Effect'] === 'deny' ? 1 : 0);
 
340
  }
341
 
342
- return $option;
 
 
 
343
  }
344
 
345
  /**
@@ -353,153 +391,144 @@ class AAM_Service_AccessPolicy
353
  * @access public
354
  * @see https://aamplugin.com/reference/policy#post
355
  */
356
- public function initPostAccessOptions($option, AAM_Core_Object_Post $object)
357
  {
358
- $post = $object->getPost();
359
- $stms = $this->getPolicyManager($object->getSubject())->find(
360
- "/^post:{$post->post_type}:({$post->post_name}|{$post->ID}):/",
361
- array('post' => $post)
362
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
364
- $option = array();
 
 
365
 
366
- foreach ($stms as $key => $stm) {
367
- $chunks = explode(':', $key);
368
- $action = (isset($chunks[3]) ? $chunks[3] : 'read');
369
- $meta = (isset($stm['Metadata']) ? $stm['Metadata'] : array());
370
-
371
- $option = array_merge(
372
- $option,
373
- apply_filters(
374
- 'aam-policy-posts-terms-settings-filter',
375
- $action,
376
- $stm['Effect'] === 'deny',
377
- '',
378
- ($action === 'read' ? $meta : array()),
379
- array($post)
380
- )
381
- );
382
  }
383
 
384
- return $option;
385
  }
386
 
387
  /**
388
- * Initialize Route Object options
389
  *
390
- * @param array $option
391
- * @param AAM_Core_Subject $subject
 
392
  *
393
- * @return array
394
  *
395
- * @access public
396
- * @see https://aamplugin.com/reference/policy#route
397
  */
398
- public function initRouteObjectOptions($option, AAM_Core_Subject $subject)
399
  {
400
- $stms = $this->getPolicyManager($subject)->find("/^Route:/i");
401
-
402
- foreach ($stms as $key => $stm) {
403
- $chunks = explode(':', $key);
404
- $method = (isset($chunks[3]) ? $chunks[3] : 'post');
405
- $id = "{$chunks[1]}|{$chunks[2]}|{$method}";
406
-
407
- $option[$id] = ($stm['Effect'] === 'deny' ? 1 : 0);
408
- }
409
-
410
- return $option;
411
  }
412
 
413
  /**
414
- * Initialize Toolbar Object options
415
  *
416
- * @param array $option
417
- * @param AAM_Core_Subject $subject
418
  *
419
- * @return array
420
  *
421
- * @access public
422
- * @see https://aamplugin.com/reference/policy#toolbar
423
  */
424
- public function initToolbarObjectOptions($option, AAM_Core_Subject $subject)
425
  {
426
- $stms = $this->getPolicyManager($subject)->find("/^Toolbar:/i");
427
 
428
- foreach ($stms as $key => $stm) {
429
- $chunks = explode(':', $key);
430
- $option[$chunks[1]] = ($stm['Effect'] === 'deny' ? 1 : 0);
431
- }
432
 
433
- return $option;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  }
435
 
436
  /**
437
- * Initialize URI Object options
438
  *
439
- * @param array $option
440
- * @param AAM_Core_Subject $subject
441
  *
442
  * @return array
443
  *
444
- * @access public
445
- * @see https://aamplugin.com/reference/policy#uri
446
  */
447
- public function initUriObjectOptions($option, AAM_Core_Subject $subject)
448
  {
449
- $stms = $this->getPolicyManager($subject)->find("/^URI:/i");
 
 
 
450
 
451
- foreach ($stms as $key => $stm) {
452
- $chunks = explode(':', $key);
453
- $effect = ($stm['Effect'] === 'deny' ? 1 : 0);
454
- $type = $stm['Effect'];
455
- $destination = null;
456
- $code = null;
457
-
458
- if ($effect === 1 && !empty($stm['Metadata']['Redirect'])) {
459
- $redirect = $stm['Metadata']['Redirect'];
460
- $type = strtolower($redirect['Type']);
461
- $code = isset($redirect['Code']) ? $redirect['Code'] : 307;
462
-
463
- switch ($type) {
464
- case 'message':
465
- $destination = $redirect['Message'];
466
- break;
467
-
468
- case 'page':
469
- if (isset($redirect['Id'])) {
470
- $destination = intval($redirect['Id']);
471
- } elseif (isset($redirect['Slug'])) {
472
- $page = get_page_by_path($redirect['Slug'], OBJECT);
473
- $destination = (is_a($page, 'WP_Post') ? $page->ID : 0);
474
- }
475
- break;
476
-
477
- case 'url':
478
- $destination = filter_var(
479
- $redirect['URL'],
480
- FILTER_VALIDATE_URL
481
- );
482
- if (empty($destination)) {
483
- $type = 'message';
484
- $destination = "Invalid URL: [{$redirect['URL']}]";
485
- }
486
- break;
487
-
488
- case 'callback':
489
- $destination = $redirect['Callback'];
490
- break;
491
- }
492
- }
493
 
494
- $option[crc32($chunks[1] . $type . $destination)] = array(
495
- 'uri' => $chunks[1],
496
- 'type' => $type,
497
- 'action' => $destination,
498
- 'code' => $code
499
- );
 
 
 
 
 
500
  }
501
 
502
- return $option;
 
 
503
  }
504
 
505
  /**
@@ -511,289 +540,205 @@ class AAM_Service_AccessPolicy
511
  *
512
  * @access public
513
  */
514
- public function initVisibilityObject(AAM_Core_Object_Visibility $visibility)
515
  {
516
- $subject = $visibility->getSubject();
517
-
518
- // Read all the settings from the Access & Security Policies
519
- $stms = AAM_Core_Policy_Factory::get($subject)->find("/^post:(.*):list$/");
520
 
521
- foreach ($stms as $key => $stm) {
522
- $chunks = explode(':', $key);
 
523
 
524
- if (is_numeric($chunks[2])) {
525
- $postId = $chunks[2];
526
- } else {
527
- $post = get_page_by_path(
528
- $chunks[2],
529
- OBJECT,
530
- $chunks[1]
531
- );
532
- $postId = (is_a($post, 'WP_Post') ? $post->ID : 0);
533
- }
534
 
535
- // Cover the case when unknown slug is used
536
- if (!empty($postId)) {
537
- $this->pushOptions(
538
- 'post',
539
- "{$postId}|{$chunks[1]}",
540
- array(
541
- "hidden" => ($stm['Effect'] === 'deny' ? 1 : 0)
542
- )
543
- );
544
  }
545
  }
546
  }
547
 
548
  /**
549
- * Initialize user with policy capabilities and roles
550
  *
551
- * @param AAM_Core_Subject_User $user
 
552
  *
553
- * @return void
554
  *
555
  * @access public
556
- * @link https://aamplugin.com/reference/policy#capability
557
- * @link https://aamplugin.com/reference/policy#role
558
  */
559
- public function initializeUser(AAM_Core_Subject_User $user)
560
  {
561
- $subject = $user->getSubject();
562
- $manager = $this->getPolicyManager($user);
563
-
564
- // Retrieve all capabilities set in Access Policy
565
- // Load Capabilities from the policy
566
- $policyCaps = array();
567
 
568
- foreach ($manager->find("/^Capability:[\w]+$/i") as $key => $stm) {
569
- $chunks = explode(':', $key);
570
- $policyCaps[$chunks[1]] = ($stm['Effect'] === 'allow' ? 1 : 0);
571
- }
572
-
573
- // Load Roles from the policy
574
- $roles = (array)$subject->roles;
575
- $allRoles = AAM_Core_API::getRoles();
576
- $roleCaps = array();
577
-
578
- foreach ($manager->find("/^Role:/i") as $key => $stm) {
579
- $chunks = explode(':', $key);
580
 
581
- if ($stm['Effect'] === 'allow') {
582
- if (!in_array($chunks[1], $roles, true)) {
583
- if ($allRoles->is_role($chunks[1])) {
584
- $roleCaps = array_merge($roleCaps, $allRoles->get_role($chunks[1])->capabilities);
585
- $roleCaps[] = $chunks[1];
586
- }
587
- $roles[] = $chunks[1];
588
- }
589
- } elseif (in_array($chunks[1], $roles, true)) {
590
- // Make sure that we delete all instances of the role
591
- foreach ($roles as $i => $role) {
592
- if ($role === $chunks[1]) {
593
- unset($roles[$i]);
594
- }
595
- }
596
- }
597
- }
598
-
599
- //reset the user capabilities
600
- $subject->allcaps = array_merge($subject->allcaps, $roleCaps, $policyCaps);
601
-
602
- //make sure that no capabilities are going outside of define boundary
603
- $subject->allcaps = $this->applyCapabilityBoundaries($manager, $subject->allcaps);
604
- $subject->caps = $this->applyCapabilityBoundaries($manager, $subject->caps);
605
-
606
- // also delete all capabilities that are assigned to denied role ONLY
607
- // $diff contains the list of roles that were denied for user
608
- $diff = array_diff_key($subject->roles, $roles);
609
-
610
- // prepare the list of capabilities that potentially should be removed from
611
- // user
612
- $removeCaps = array();
613
- foreach ($diff as $role) {
614
- $removeCaps = array_merge($removeCaps, $allRoles->get_role($role)->capabilities);
615
- }
616
-
617
- // prepare the list of capabilities that should still be assigned to user
618
- $keepCaps = array();
619
- foreach ($roles as $role) {
620
- if ($allRoles->is_role($role)) {
621
- $keepCaps = array_merge($keepCaps, $allRoles->get_role($role)->capabilities);
622
- }
623
- }
624
-
625
- foreach (array_keys($removeCaps) as $key) {
626
- if (!array_key_exists($key, $keepCaps)) {
627
- unset($subject->allcaps[$key]);
628
- if (isset($subject->caps[$key])) {
629
- unset($subject->caps[$key]);
630
- }
631
  }
632
  }
633
 
634
- $subject->roles = $roles;
635
  }
636
 
637
  /**
638
- * Check if any of the capabilities going out of the defined boundary
639
  *
640
- * @param AAM_Core_Policy_Manager $manager
641
- * @param array $caps
642
  *
643
  * @return array
644
  *
645
  * @access protected
 
646
  */
647
- protected function applyCapabilityBoundaries(AAM_Core_Policy_Manager $manager, $caps)
648
  {
649
- $final = array();
 
 
 
 
 
 
 
 
 
 
 
 
650
 
651
- foreach ($caps as $key => $effect) {
652
- if ($manager->isBoundary("Capability:{$key}") === false) {
653
- $final[$key] = $effect;
654
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
655
  }
656
 
657
- return $final;
 
 
 
 
658
  }
659
 
660
  /**
661
- * Register Admin Menu
662
  *
663
- * @return void
 
 
 
 
664
  *
665
  * @access public
 
 
666
  */
667
- public function registerAdminMenu()
668
  {
669
- // Access policy page
670
- add_submenu_page(
671
- 'aam',
672
- 'Access Policies',
673
- 'Access Policies',
674
- AAM_Core_Config::get(
675
- 'policy.capability',
676
- (AAM_Core_API::capExists('aam_manage_policy') ? 'aam_manage_policy' : 'administrator')
677
- ),
678
- 'edit.php?post_type=aam_policy'
679
- );
680
 
681
- $type = get_post_type_object('aam_policy');
682
- if (current_user_can($type->cap->create_posts)) {
683
- add_submenu_page(
684
- 'aam',
685
- 'Add New Policies',
686
- 'Add New Policies',
687
- $type->cap->create_posts,
688
- 'post-new.php?post_type=aam_policy'
689
- );
690
  }
 
 
691
  }
692
 
693
  /**
694
- * Compatibility between post actions and policy actions
695
  *
696
- * @param string $action
697
- * @param bool|int $effect
698
- * @param string $prefix
699
- * @param array $meta
700
- * @param array $args
701
  *
702
  * @return array
 
 
 
703
  */
704
- public function convertContentSettings($action, $effect, $prefix = '', $meta = array(), $args = array())
705
  {
706
- $result = array();
 
707
 
708
- if (!empty($meta['Password']['Value'])) {
709
- $result = array(
710
- "{$prefix}password" => $meta['Password']['Value'],
711
- "{$prefix}protected" => true
712
- );
713
- }
714
 
715
- if (!empty($meta['Teaser']['Value'])) {
716
- if (preg_match_all('/(\$\{[^}]+\})/', $meta['Teaser']['Value'], $match)) {
717
- $res = AAM_Core_Policy_Token::evaluate($meta['Teaser']['Value'], $match[1], $args);
718
- } else {
719
- $res = $meta['Teaser']['Value'];
720
  }
721
-
722
- $result = array_merge($result, array(
723
- "{$prefix}teaser" => $res,
724
- "{$prefix}limit" => true,
725
- ));
726
  }
727
 
728
- if (!empty($meta['Redirect'])) {
729
- // Build the redirect location
730
- $type = (isset($meta['Redirect']['Type']) ? $meta['Redirect']['Type'] : 'message');
731
- switch ($type) {
732
- case 'page':
733
- if (isset($meta['Redirect']['Id'])) {
734
- $destination = intval($meta['Redirect']['Id']);
735
- } elseif (isset($meta['Redirect']['Slug'])) {
736
- $page = get_page_by_path(
737
- $meta['Redirect']['Slug'],
738
- OBJECT
739
- );
740
- $destination = (is_a($page, 'WP_Post') ? $page->ID : 0);
741
- }
742
- if (isset($meta['Redirect']['Code'])) {
743
- $destination .= "|{$meta['Redirect']['Code']}";
744
- } else {
745
- $destination .= "|307";
746
- }
747
- break;
748
-
749
- case 'url':
750
- $destination = filter_var(
751
- $meta['Redirect']['URL'],
752
- FILTER_VALIDATE_URL
753
- );
754
- if (empty($destination)) {
755
- $type = 'message';
756
- $destination = "Invalid URL: [{$meta['Redirect']['URL']}]";
757
- }
758
- if (isset($meta['Redirect']['Code'])) {
759
- $destination .= "|{$meta['Redirect']['Code']}";
760
- } else {
761
- $destination .= "|307";
762
- }
763
- break;
764
-
765
- case 'callback':
766
- $destination = $meta['Redirect']['Callback'];
767
- break;
768
-
769
- case 'login':
770
- $destination = null;
771
- break;
772
-
773
- default:
774
- $destination = $meta['Redirect']['Message'];
775
- break;
776
- }
777
 
778
- $result = array_merge($result, array(
779
- "{$prefix}redirect" => true,
780
- "{$prefix}location" => $type . (!empty($destination) ? "|{$destination}" : '')
781
- ));
782
- }
 
 
 
 
 
 
 
 
 
783
 
784
- if (empty($meta)) {
785
- $action = apply_filters('aam-policy-post-resource-action-filter', $action);
 
 
786
 
787
- $result = array_merge($result, array(
788
- "{$prefix}{$action}" => $effect
789
- ));
790
  }
791
 
792
- return $result;
793
  }
 
794
  }
795
 
796
  if (defined('AAM_KEY')) {
797
- // TODO: Enable the service bootstrap
798
- // AAM_Service_AccessPolicy::bootstrap();
799
- }
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
+ * @version 6.0.0
10
  */
11
 
12
  /**
13
  * Access Policy service
14
  *
15
+ * @package AAM
16
+ * @version 6.0.0
17
  */
18
  class AAM_Service_AccessPolicy
19
  {
20
+ use AAM_Core_Contract_ServiceTrait,
21
+ AAM_Core_Contract_RequestTrait;
22
 
23
  /**
24
  * AAM configuration setting that is associated with the feature
25
+ *
26
+ * @version 6.0.0
27
  */
28
  const FEATURE_FLAG = 'core.service.access-policy.enabled';
29
 
30
+ /**
31
+ * Access policy CPT
32
+ *
33
+ * @version 6.0.0
34
+ */
35
+ const POLICY_CPT = 'aam_policy';
36
+
37
  /**
38
  * Constructor
39
+ *
40
+ * @return void
41
+ *
42
+ * @access protected
43
+ * @version 6.0.0
44
  */
45
  protected function __construct()
46
  {
51
  AAM_Backend_Feature_Main_Policy::register();
52
  }, 40);
53
 
 
 
 
 
 
 
 
 
 
54
  //register custom access control metabox
55
  add_action('add_meta_boxes', array($this, 'registerMetaboxes'));
56
 
64
  add_filter('aam_service_list_filter', function ($services) {
65
  $services[] = array(
66
  'title' => __('Access Policies', AAM_KEY),
67
+ 'description' => __('Manage access to the website with well documented JSON access policies for any user, role or visitors. Keep the paper-trail of all the access changes with policy revisions.', AAM_KEY),
68
  'setting' => self::FEATURE_FLAG
69
  );
70
 
77
  }
78
  }
79
 
80
+ /**
81
+ * Register UI metaboxes for the Access Policy edit screen
82
+ *
83
+ * @global WP_Post $post
84
+ *
85
+ * @return void
86
+ *
87
+ * @access public
88
+ * @version 6.0.0
89
+ */
90
+ public function registerMetaboxes()
91
+ {
92
+ global $post;
93
+
94
+ if (is_a($post, 'WP_Post') && ($post->post_type === self::POLICY_CPT)) {
95
+ add_meta_box(
96
+ self::POLICY_CPT,
97
+ __('Access Policy Document', AAM_KEY),
98
+ function() {
99
+ echo AAM_Backend_View::getInstance()->renderPolicyMetabox();
100
+ },
101
+ null,
102
+ 'normal',
103
+ 'high'
104
+ );
105
+
106
+ add_meta_box(
107
+ 'aam-policy-assignee',
108
+ __('Access Policy Assignee', AAM_KEY),
109
+ function() {
110
+ echo AAM_Backend_View::getInstance()->renderPolicyPrincipalMetabox();
111
+ },
112
+ null,
113
+ 'side'
114
+ );
115
+ }
116
+ }
117
+
118
  /**
119
  * Hook into policy submission and filter its content
120
  *
123
  * @return array
124
  *
125
  * @access public
126
+ * @version 6.0.0
127
  */
128
  public function managePolicyContent($data)
129
  {
130
+ if (isset($data['post_type']) && ($data['post_type'] === self::POLICY_CPT)) {
131
+ $content = $this->getFromPost('aam-policy');
132
 
133
  if (empty($data['post_content'])) {
134
+ $content = AAM_Backend_Feature_Main_Policy::getDefaultPolicy();
135
  }
136
 
137
  // Reformat the policy content
157
  * @return void
158
  *
159
  * @access protected
160
+ * @version 6.0.0
161
  */
162
  protected function initializeHooks()
163
  {
166
  register_post_type('aam_policy', array(
167
  'label' => __('Access Policy', AAM_KEY),
168
  'labels' => array(
169
+ 'name' => __('Access Policies', AAM_KEY),
170
+ 'edit_item' => __('Edit Policy', AAM_KEY),
171
+ 'singular_name' => __('Policy', AAM_KEY),
172
+ 'add_new_item' => __('Add New Policy', AAM_KEY),
173
+ 'new_item' => __('New Policy', AAM_KEY)
174
  ),
175
  'description' => __('Access and security policy', AAM_KEY),
176
+ 'public' => false,
177
  'show_ui' => true,
178
  'show_in_menu' => false,
179
  'exclude_from_search' => true,
193
  ));
194
  });
195
 
196
+ // Hook into AAM core objects initialization
197
+ add_filter('aam_menu_object_option_filter', array($this, 'initializeMenu'), 10, 2);
198
+ add_filter('aam_metabox_object_option_filter', array($this, 'initializeMetabox'), 10, 2);
199
+ add_filter('aam_toolbar_object_option_filter', array($this, 'initializeToolbar'), 10, 2);
200
+ add_filter('aam_post_object_option_filter', array($this, 'initializePost'), 10, 2);
201
+ add_action('aam_visibility_object_init_action', array($this, 'initializeVisibility'));
202
+ add_filter('aam_uri_object_option_filter', array($this, 'initializeUri'), 10, 2);
203
+ //add_filter('aam_route_object_option_filter', array($this, 'initializeRoute'), 10, 2);
204
+
205
  // Manage access to the Capabilities
206
+ add_filter('aam_cap_can_filter', array($this, 'isCapabilityAllowed'), 10, 3);
207
+ add_action('aam_initialize_user_action', array($this, 'initializeUser'));
208
 
209
  // Manage access to the Plugin list and individual plugins
210
  add_filter('aam_allowed_plugin_action_filter', array($this, 'isPluginActionAllowed'), 10, 3);
211
+ add_filter('all_plugins', array($this, 'filterPlugins'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
 
214
  /**
215
+ * Initialize Admin Menu Object options
216
  *
217
+ * @param array $option
218
+ * @param AAM_Core_Object_Menu $object
219
+ *
220
+ * @return array
221
+ *
222
+ * @access public
223
+ * @see https://aamplugin.com/reference/policy#backendmenu
224
+ * @version 6.0.0
225
  */
226
+ public function initializeMenu($option, AAM_Core_Object_Menu $object)
227
  {
228
+ $manager = AAM_Core_Policy_Factory::get($object->getSubject());
229
+ $found = $manager->getResources(AAM_Core_Policy_Resource::MENU);
230
+ $parsed = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
 
232
+ foreach ($found as $key => $stm) {
233
+ $parsed[$key] = ($stm['Effect'] === 'deny' ? true : false);
 
 
 
 
 
234
  }
 
235
 
236
+ return array_replace($option, $parsed); // First-class citizen
 
 
 
 
 
 
 
 
 
 
237
  }
238
 
239
  /**
240
+ * Initialize Toolbar Object options
241
  *
242
+ * @param array $option
243
+ * @param AAM_Core_Object_Toolbar $object
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  *
245
+ * @return array
246
  *
247
  * @access public
248
+ * @see https://aamplugin.com/reference/policy#toolbar
249
+ * @version 6.0.0
250
  */
251
+ public function initializeToolbar($option, AAM_Core_Object_Toolbar $object)
252
  {
253
+ $manager = AAM_Core_Policy_Factory::get($object->getSubject());
254
+ $found = $manager->getResources(AAM_Core_Policy_Resource::TOOLBAR);
255
+ $parsed = array();
256
 
257
+ foreach ($found as $key => $stm) {
258
+ $parsed[$key] = ($stm['Effect'] === 'deny' ? true : false);
 
 
259
  }
260
 
261
+ return array_replace($option, $parsed); // First-class citizen
262
  }
263
 
264
  /**
265
+ * Initialize Metabox Object options
266
  *
267
+ * @param array $option
268
+ * @param AAM_Core_Object_Metabox $object
269
+ *
270
+ * @return array
271
  *
272
  * @access public
273
+ * @see https://aamplugin.com/reference/policy#metabox
274
+ * @version 6.0.0
275
  */
276
+ public function initializeMetabox($option, AAM_Core_Object_Metabox $object)
277
  {
278
+ $manager = AAM_Core_Policy_Factory::get($object->getSubject());
279
+ $found = $manager->getResources(array(
280
+ AAM_Core_Policy_Resource::METABOX, AAM_Core_Policy_Resource::WIDGET
281
+ ));
282
+
283
+ $parsed = array();
284
+
285
+ foreach ($found as $key => $stm) {
286
+ $parsed[$key] = ($stm['Effect'] === 'deny' ? true : false);
287
+ }
288
+
289
+ return array_replace($option, $parsed); // First-class citizen
290
  }
291
 
292
  /**
293
+ * Check if specified action is allowed upon capability
294
  *
295
+ * @param boolean $allowed
296
+ * @param string $cap
297
+ * @param string $action
298
  *
299
  * @return boolean
300
  *
301
  * @access public
302
+ * @link https://aamplugin.com/reference/policy#capability
303
+ * @version 6.0.0
304
  */
305
+ public function isCapabilityAllowed($allowed, $cap, $action)
306
  {
307
+ $manager = AAM_Core_Policy_Factory::get(AAM::getUser());
308
+ $result = $manager->isAllowed("Capability:{$cap}:AAM:{$action}");
309
 
310
+ return ($result === null ? $allowed : $result);
 
 
 
 
 
 
311
  }
312
 
313
  /**
314
+ * Initialize user with policy capabilities and roles
315
  *
316
+ * @param AAM_Core_Subject_User $subject
 
317
  *
318
+ * @return void
319
  *
320
  * @access public
321
+ * @link https://aamplugin.com/reference/policy#capability
322
+ * @link https://aamplugin.com/reference/policy#role
323
+ * @version 6.0.0
324
  */
325
+ public function initializeUser(AAM_Core_Subject_User $subject)
326
  {
327
+ $manager = AAM_Core_Policy_Factory::get($subject);
328
+ $wp_user = $subject->getPrincipal();
329
 
330
+ // Update user's list of roles if policy states so
331
+ $roles = $manager->getResources(AAM_Core_Policy_Resource::ROLE);
332
+
333
+ if (count($roles)) {
334
+ foreach($roles as $id => $statement) {
335
+ $effect = strtolower($statement['Effect']);
336
+ $exists = array_key_exists($id, $wp_user->caps);
337
+
338
+ if ($effect === 'allow') { // Add new
339
+ $wp_user->caps[$id] = true;
340
+ } elseif (($effect === 'deny') && $exists) { // Remove
341
+ unset($wp_user->caps[$id]);
342
+ }
343
+ }
344
+
345
+ // Re-index all user capabilities based on new set of roles
346
+ $wp_user->get_role_caps();
347
+
348
+ // Add siblings to the User subject
349
+ $user_roles = array_values($wp_user->roles);
350
+
351
+ if (count($user_roles) > 1) {
352
+ $subject->getParent()->setSiblings(array_map(function($id) {
353
+ return AAM::api()->getRole($id);
354
+ }, array_slice($user_roles, 1)));
355
+ }
356
  }
357
 
358
+ // Get all the capabilities that mentioned in the policies explicitly
359
+ $caps = array_filter(
360
+ $manager->getResources(AAM_Core_Policy_Resource::CAPABILITY),
361
+ function($stm, $res) {
362
+ return (strpos($res, ':') === false); // Exclude any :AAM: resources
363
+ },
364
+ ARRAY_FILTER_USE_BOTH
365
+ );
366
 
367
+ foreach($caps as $cap => $statement) {
368
+ $effect = (strtolower($statement['Effect']) === 'allow' ? true : false);
369
+ $wp_user->allcaps[$cap] = $effect;
 
 
 
 
 
 
 
 
 
 
 
370
 
371
+ // Also update user's specific cap if exists
372
+ if (array_key_exists($cap, $wp_user->caps)) {
373
+ $wp_user->caps[$cap] = $effect;
374
+ }
375
  }
376
 
377
+ // Finally update user level
378
+ $wp_user->user_level = array_reduce(
379
+ array_keys($wp_user->allcaps), array($wp_user, 'level_reduction'), 0
380
+ );
381
  }
382
 
383
  /**
391
  * @access public
392
  * @see https://aamplugin.com/reference/policy#post
393
  */
394
+ public function initializePost($option, AAM_Core_Object_Post $object)
395
  {
396
+ $manager = AAM_Core_Policy_Factory::get($object->getSubject());
397
+
398
+ $found = $manager->getResources(sprintf(
399
+ '%s:%s:(%d|%s)',
400
+ AAM_Core_Policy_Resource::POST,
401
+ $object->post_type,
402
+ $object->ID,
403
+ $object->post_name
404
+ ));
405
+
406
+ $parsed = array();
407
+
408
+ foreach($found as $action => $stmt) {
409
+ switch($action) {
410
+ case 'edit':
411
+ case 'delete':
412
+ case 'publish':
413
+ case 'comment':
414
+ $this->convertedPostSimpleAction($parsed, $action, $stmt);
415
+ break;
416
 
417
+ case 'list':
418
+ $this->convertedPostSimpleAction($parsed, 'hidden', $stmt);
419
+ break;
420
 
421
+ case 'read':
422
+ $this->convertedPostReadAction($parsed, $stmt);
423
+ break;
424
+
425
+ default:
426
+ $parsed = apply_filters(
427
+ 'aam_policy_post_conversion_filter', array(), $action, $stmt
428
+ );
429
+ break;
430
+ }
 
 
 
 
 
 
431
  }
432
 
433
+ return array_replace_recursive($option, $parsed); // First-class citizen
434
  }
435
 
436
  /**
437
+ * Covert simple post action to post object property
438
  *
439
+ * @param array &$options
440
+ * @param string $action
441
+ * @param array $statement
442
  *
443
+ * @return void
444
  *
445
+ * @access protected
446
+ * @version 6.0.0
447
  */
448
+ protected function convertedPostSimpleAction(&$options, $action, $statement)
449
  {
450
+ $options[$action] = strtolower($statement['Effect']) !== "allow";
 
 
 
 
 
 
 
 
 
 
451
  }
452
 
453
  /**
454
+ * Convert Post Read action based on metadata
455
  *
456
+ * @param array &$options
457
+ * @param array $statement
458
  *
459
+ * @return void
460
  *
461
+ * @access protected
 
462
  */
463
+ protected function convertedPostReadAction(&$options, $statement)
464
  {
465
+ $effect = strtolower($statement['Effect']) !== "allow";
466
 
467
+ if (array_key_exists('Metadata', $statement)) {
468
+ $metadata = $statement['Metadata'];
 
 
469
 
470
+ // Password Protected options
471
+ if(array_key_exists('Password', $metadata)) {
472
+ $options['protected'] = array(
473
+ 'enabled' => $effect,
474
+ 'password' => $metadata['Password']['Value']
475
+ );
476
+ }
477
+
478
+ // Teaser message is defined
479
+ if(array_key_exists('Teaser', $metadata)) {
480
+ $options['teaser'] = array(
481
+ 'enabled' => $effect,
482
+ 'message' => $metadata['Teaser']['Value']
483
+ );
484
+ }
485
+
486
+ // Redirect options
487
+ if(array_key_exists('Redirect', $metadata)) {
488
+ $redirect = $this->convertRedirectAction($metadata['Redirect']);
489
+ $redirect['enabled'] = $effect;
490
+ $options['redirected'] = $redirect;
491
+ }
492
+ } else { // Simply restrict access to read a post
493
+ $options['restricted'] = $effect;
494
+ }
495
  }
496
 
497
  /**
498
+ * Convert Redirect type of action
499
  *
500
+ * @param array $metadata
 
501
  *
502
  * @return array
503
  *
504
+ * @access protected
505
+ * @version 6.0.0
506
  */
507
+ protected function convertRedirectAction($metadata)
508
  {
509
+ $response = array(
510
+ 'type' => $metadata['Type'],
511
+ 'httpCode' => (int)(isset($metadata['Code']) ? $metadata['Code'] : 307)
512
+ );
513
 
514
+ $destination = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
 
516
+ if ($metadata['Type'] === 'page') {
517
+ if (isset($metadata['Id'])) {
518
+ $destination = intval($metadata['Id']);
519
+ } elseif (isset($metadata['Slug'])) {
520
+ $page = get_page_by_path($metadata['Slug'], OBJECT);
521
+ $destination = (is_a($page, 'WP_Post') ? $page->ID : 0);
522
+ }
523
+ } elseif ($metadata['Type'] === 'url') {
524
+ $destination = $metadata['URL'];
525
+ } elseif ($metadata['Type'] === 'callback') {
526
+ $destination = $metadata['Callback'];
527
  }
528
 
529
+ $response['destination'] = $destination;
530
+
531
+ return $response;
532
  }
533
 
534
  /**
540
  *
541
  * @access public
542
  */
543
+ public function initializeVisibility(AAM_Core_Object_Visibility $visibility)
544
  {
545
+ $manager = AAM_Core_Policy_Factory::get($visibility->getSubject());
546
+ $found = $manager->getResources(AAM_Core_Policy_Resource::POST);
 
 
547
 
548
+ foreach($found as $resource => $stm) {
549
+ $chunks = explode(':', $resource);
550
+ $effect = (strtolower($stm['Effect']) === 'allow' ? false : true);
551
 
552
+ // Take in consideration only visibility properties
553
+ if ($chunks[2] === 'list') {
554
+ if (is_numeric($chunks[1])) {
555
+ $id = intval($chunks[1]);
556
+ } else {
557
+ $post = get_page_by_path($chunks[1], OBJECT, $chunks[0]);
558
+ $id = (is_a($post, 'WP_Post') ? $post->ID : null);
559
+ }
 
 
560
 
561
+ // Making sure that we have at least numeric post ID
562
+ if (!empty($id)) {
563
+ $visibility->pushOptions('post', "{$id}|{$chunks[0]}", array(
564
+ 'hidden' => $effect
565
+ ));
566
+ }
 
 
 
567
  }
568
  }
569
  }
570
 
571
  /**
572
+ * Initialize URI Object options
573
  *
574
+ * @param array $option
575
+ * @param AAM_Core_Object_Uri $object
576
  *
577
+ * @return array
578
  *
579
  * @access public
580
+ * @see https://aamplugin.com/reference/policy#uri
 
581
  */
582
+ public function initializeUri($option, AAM_Core_Object_Uri $object)
583
  {
584
+ $manager = AAM_Core_Policy_Factory::get($object->getSubject());
585
+ $found = $manager->getResources(AAM_Core_Policy_Resource::URI);
586
+ $parsed = array();
 
 
 
587
 
588
+ foreach($found as $uri => $stm) {
589
+ $uri = rtrim($uri, '/'); // No need to honor the trailing forward slash
590
+ $effect = (strtolower($stm['Effect']) === 'allow' ? false : true);
 
 
 
 
 
 
 
 
 
591
 
592
+ if ($effect === false) {
593
+ $parsed[$uri] = array(
594
+ 'type' => 'allow'
595
+ );
596
+ } elseif (array_key_exists('Metadata', $stm)) {
597
+ $option[$uri] = $this->convertUriAction($stm['Metadata']);
598
+ } else {
599
+ $option[$uri] = array(
600
+ 'type' => 'default'
601
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
602
  }
603
  }
604
 
605
+ return array_merge($option, $parsed); //First-class citizen
606
  }
607
 
608
  /**
609
+ * Convert URI metadata to the URI access option
610
  *
611
+ * @param array $metadata
 
612
  *
613
  * @return array
614
  *
615
  * @access protected
616
+ * @version 6.0.0
617
  */
618
+ protected function convertUriAction($metadata)
619
  {
620
+ $type = strtolower($metadata['Type']);
621
+ $code = isset($metadata['Code']) ? $metadata['Code'] : 307;
622
+ $action = null;
623
+
624
+ switch($type) {
625
+ case 'page':
626
+ if (isset($metadata['Id'])) {
627
+ $action = intval($metadata['Id']);
628
+ } elseif (isset($metadata['Slug'])) {
629
+ $page = get_page_by_path($metadata['Slug'], OBJECT, 'page');
630
+ $action = (is_a($page, 'WP_Post') ? $page->ID : 0);
631
+ }
632
+ break;
633
 
634
+ case 'message':
635
+ $action = $metadata['Message'];
636
+ break;
637
+
638
+ case 'url':
639
+ $action = $metadata['URL'];
640
+ break;
641
+
642
+ case 'callback':
643
+ $action = $metadata['Callback'];
644
+ break;
645
+
646
+ case 'login':
647
+ $code = 401; //Unauthorized
648
+ break;
649
+
650
+ default:
651
+ break;
652
  }
653
 
654
+ return array(
655
+ 'type' => $type,
656
+ 'action' => $action,
657
+ 'code' => $code
658
+ );
659
  }
660
 
661
  /**
662
+ * Check if specific action is allowed upon all plugins or specified plugin
663
  *
664
+ * @param boolean|null $allowed
665
+ * @param string $action
666
+ * @param string $slug
667
+ *
668
+ * @return boolean
669
  *
670
  * @access public
671
+ * @link https://aamplugin.com/reference/policy#plugin
672
+ * @version 6.0.0
673
  */
674
+ public function isPluginActionAllowed($allowed, $action, $slug = null)
675
  {
676
+ $manager = AAM_Core_Policy_Factory::get(AAM::getUser());
 
 
 
 
 
 
 
 
 
 
677
 
678
+ if ($slug === null) {
679
+ $id = AAM_Core_Policy_Resource::PLUGIN . ":WP:{$action}";
680
+ } else {
681
+ $id = AAM_Core_Policy_Resource::PLUGIN . ":{$slug}:WP:{$action}";
 
 
 
 
 
682
  }
683
+
684
+ return $manager->isAllowed($id);
685
  }
686
 
687
  /**
688
+ * Filter out all the plugins that are not allowed to be listed
689
  *
690
+ * @param array $plugins
 
 
 
 
691
  *
692
  * @return array
693
+ *
694
+ * @access public
695
+ * @version 6.0.0
696
  */
697
+ public function filterPlugins($plugins)
698
  {
699
+ $manager = AAM_Core_Policy_Factory::get(AAM::getUser());
700
+ $filtered = array();
701
 
702
+ foreach($plugins as $id => $plugin) {
703
+ list($slug) = explode('/', $id);
704
+ $resource = AAM_Core_Policy_Resource::PLUGIN . ":{$slug}:WP:list";
 
 
 
705
 
706
+ if ($manager->isAllowed($resource) !== false) {
707
+ $filtered[$id] = $plugin;
 
 
 
708
  }
 
 
 
 
 
709
  }
710
 
711
+ return $filtered;
712
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
713
 
714
+ /**
715
+ * Initialize Route Object options
716
+ *
717
+ * @param array $option
718
+ * @param AAM_Core_Subject $subject
719
+ *
720
+ * @return array
721
+ *
722
+ * @access public
723
+ * @see https://aamplugin.com/reference/policy#route
724
+ */
725
+ public function initRouteObjectOptions($option, AAM_Core_Subject $subject)
726
+ {
727
+ $stms = $this->getPolicyManager($subject)->find("/^Route:/i");
728
 
729
+ foreach ($stms as $key => $stm) {
730
+ $chunks = explode(':', $key);
731
+ $method = (isset($chunks[3]) ? $chunks[3] : 'post');
732
+ $id = "{$chunks[1]}|{$chunks[2]}|{$method}";
733
 
734
+ $option[$id] = ($stm['Effect'] === 'deny' ? 1 : 0);
 
 
735
  }
736
 
737
+ return $option;
738
  }
739
+
740
  }
741
 
742
  if (defined('AAM_KEY')) {
743
+ AAM_Service_AccessPolicy::bootstrap();
744
+ }
 
application/Service/Content.php CHANGED
@@ -27,6 +27,13 @@ class AAM_Service_Content
27
  */
28
  const FEATURE_FLAG = 'core.service.content.enabled';
29
 
 
 
 
 
 
 
 
30
  /**
31
  * Constructor
32
  *
@@ -302,6 +309,8 @@ class AAM_Service_Content
302
  if ($auth->get_error_code() === 'post_access_redirected') {
303
  $response->set_headers(array('Location' => $data['location']));
304
  }
 
 
305
  }
306
 
307
  return $response;
@@ -404,9 +413,11 @@ class AAM_Service_Content
404
  if (is_wp_error($error)) {
405
  if ($error->get_error_code() === 'post_access_redirected') {
406
  AAM_Core_Redirect::execute('url', $error->get_error_data());
407
- } else {
408
- wp_die($error->get_error_message(), 'aam_access_denied');
409
  }
 
 
410
  }
411
  }
412
  }
@@ -569,7 +580,7 @@ class AAM_Service_Content
569
  }
570
 
571
  // Replace the [excerpt] placeholder with posts excerpt and do
572
- // shortcode evaluation
573
  $content = do_shortcode(
574
  str_replace('[excerpt]', $post->post_excerpt, $message)
575
  );
@@ -829,6 +840,25 @@ class AAM_Service_Content
829
  return $caps;
830
  }
831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
832
  /**
833
  * Check if current user is authorized to read the post
834
  *
@@ -854,7 +884,7 @@ class AAM_Service_Content
854
 
855
  // Prepare the pipeline of steps that AAM core will perform to check post's
856
  // accessibility
857
- $pipeline = apply_filters('aam_post_read_authorization_pipeline_filter', array(
858
  // Step #1. Check if access expired to the post
859
  array($this, 'checkPostExpiration'),
860
  // Step #2. Check if user has access to read the post
@@ -960,7 +990,7 @@ class AAM_Service_Content
960
  // Check current access counter only for authenticated users
961
  if ($user && $post->is('limited')) {
962
  $limited = $post->get('limited');
963
- $option = 'aam-post-' . $post->ID . '-access-counter';
964
  $counter = intval(get_user_meta($user, $option, true));
965
 
966
  if ($counter >= $limited['threshold']) {
@@ -1020,7 +1050,7 @@ class AAM_Service_Content
1020
  _doing_it_wrong(
1021
  __CLASS__ . '::' . __METHOD__,
1022
  'Callback is not invocable',
1023
- '6.0.0'
1024
  );
1025
  }
1026
  break;
@@ -1069,19 +1099,16 @@ class AAM_Service_Content
1069
  // protected posts/pages
1070
  if (empty($password)) {
1071
  $password = wp_unslash(
1072
- $this->getFromRequest('wp-postpass_' . COOKIEHASH)
1073
  );
1074
 
1075
  $isMatched = AAM_Core_API::prepareHasher()->CheckPassword(
1076
  $protected['password'], $password
1077
  );
1078
  } else {
1079
- $decrypted = AAM_Core_API::crypt($protected['password'], 'decrypt');
1080
- $isMatched = $decrypted === $password;
1081
  }
1082
 
1083
-
1084
-
1085
  if ($isMatched === false) {
1086
  $result = new WP_Error(
1087
  'post_access_protected',
27
  */
28
  const FEATURE_FLAG = 'core.service.content.enabled';
29
 
30
+ /**
31
+ * Post view counter
32
+ *
33
+ * @version 6.0.0
34
+ */
35
+ const POST_COUNTER_DB_OPTION = 'aam_post_%s_access_counter';
36
+
37
  /**
38
  * Constructor
39
  *
309
  if ($auth->get_error_code() === 'post_access_redirected') {
310
  $response->set_headers(array('Location' => $data['location']));
311
  }
312
+ } else {
313
+ $this->incrementPostReadCounter($object);
314
  }
315
 
316
  return $response;
413
  if (is_wp_error($error)) {
414
  if ($error->get_error_code() === 'post_access_redirected') {
415
  AAM_Core_Redirect::execute('url', $error->get_error_data());
416
+ } elseif ($error->get_error_code() !== 'post_access_protected') {
417
+ wp_die($error->get_error_message(), 'aam_access_denied');
418
  }
419
+ } else {
420
+ $this->incrementPostReadCounter($post);
421
  }
422
  }
423
  }
580
  }
581
 
582
  // Replace the [excerpt] placeholder with posts excerpt and do
583
+ // short-code evaluation
584
  $content = do_shortcode(
585
  str_replace('[excerpt]', $post->post_excerpt, $message)
586
  );
840
  return $caps;
841
  }
842
 
843
+ /**
844
+ * Increment user view counter is tracking is defined
845
+ *
846
+ * @param AAM_Core_Object_Post $post
847
+ *
848
+ * @return void
849
+ *
850
+ * @access protected
851
+ * @version 6.0.0
852
+ */
853
+ protected function incrementPostReadCounter($post)
854
+ {
855
+ if(is_user_logged_in() && $post->is('limited')) {
856
+ $option = sprintf(self::POST_COUNTER_DB_OPTION, $post->ID);
857
+ $counter = intval(get_user_meta(get_current_user_id(), $option, true));
858
+ update_user_meta(get_current_user_id(), $option, ++$counter);
859
+ }
860
+ }
861
+
862
  /**
863
  * Check if current user is authorized to read the post
864
  *
884
 
885
  // Prepare the pipeline of steps that AAM core will perform to check post's
886
  // accessibility
887
+ $pipeline = apply_filters('aam_post_read_access_pipeline_filter', array(
888
  // Step #1. Check if access expired to the post
889
  array($this, 'checkPostExpiration'),
890
  // Step #2. Check if user has access to read the post
990
  // Check current access counter only for authenticated users
991
  if ($user && $post->is('limited')) {
992
  $limited = $post->get('limited');
993
+ $option = sprintf(self::POST_COUNTER_DB_OPTION, $post->ID);
994
  $counter = intval(get_user_meta($user, $option, true));
995
 
996
  if ($counter >= $limited['threshold']) {
1050
  _doing_it_wrong(
1051
  __CLASS__ . '::' . __METHOD__,
1052
  'Callback is not invocable',
1053
+ AAM_VERSION
1054
  );
1055
  }
1056
  break;
1099
  // protected posts/pages
1100
  if (empty($password)) {
1101
  $password = wp_unslash(
1102
+ $this->getFromCookie('wp-postpass_' . COOKIEHASH)
1103
  );
1104
 
1105
  $isMatched = AAM_Core_API::prepareHasher()->CheckPassword(
1106
  $protected['password'], $password
1107
  );
1108
  } else {
1109
+ $isMatched = $protected['password'] === $password;
 
1110
  }
1111
 
 
 
1112
  if ($isMatched === false) {
1113
  $result = new WP_Error(
1114
  'post_access_protected',
application/Service/Core.php CHANGED
@@ -45,6 +45,19 @@ class AAM_Service_Core
45
  add_filter('self_admin_url', array($this, 'pluginUpdateDetails'), 10);
46
  }
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  // Check if user has ability to perform certain task based on provided
49
  // capability and meta data
50
  add_filter('map_meta_cap', array($this, 'mapMetaCaps'), 999, 4);
@@ -149,6 +162,21 @@ class AAM_Service_Core
149
  return $url;
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  /**
153
  * Eliminate XSS through translation file
154
  *
45
  add_filter('self_admin_url', array($this, 'pluginUpdateDetails'), 10);
46
  }
47
 
48
+ if (is_admin()) {
49
+ if (AAM_Core_Config::get('ui.settings.renderAccessMetabox', true)) {
50
+ add_action('show_user_profile', array($this, 'renderAccessWidget'));
51
+ add_action('edit_user_profile', array($this, 'renderAccessWidget'));
52
+ }
53
+
54
+ // Register UI elements
55
+ add_action('aam_init_ui_action', function () {
56
+ AAM_Backend_Feature_Subject_Role::register();
57
+ AAM_Backend_Feature_Subject_User::register();
58
+ });
59
+ }
60
+
61
  // Check if user has ability to perform certain task based on provided
62
  // capability and meta data
63
  add_filter('map_meta_cap', array($this, 'mapMetaCaps'), 999, 4);
162
  return $url;
163
  }
164
 
165
+ /**
166
+ * Render "Access Manager" widget on the user/profile edit screen
167
+ *
168
+ * @param WP_User $user
169
+ *
170
+ * @return void
171
+ *
172
+ * @access public
173
+ * @version 6.0.0
174
+ */
175
+ public function renderAccessWidget($user)
176
+ {
177
+ echo AAM_Backend_View::getInstance()->renderUserMetabox($user);
178
+ }
179
+
180
  /**
181
  * Eliminate XSS through translation file
182
  *
application/Service/ExtendedCapabilities.php CHANGED
@@ -19,7 +19,8 @@
19
  */
20
  class AAM_Service_ExtendedCapabilities
21
  {
22
- use AAM_Core_Contract_ServiceTrait;
 
23
 
24
  /**
25
  * AAM configuration setting that is associated with the service
@@ -239,7 +240,7 @@ class AAM_Service_ExtendedCapabilities
239
  */
240
  public function canUpdatePassword($login, &$password, &$password2)
241
  {
242
- $userId = AAM_Core_Request::post('user_id');
243
  $isProfile = $userId === get_current_user_id();
244
 
245
  if ($isProfile) {
19
  */
20
  class AAM_Service_ExtendedCapabilities
21
  {
22
+ use AAM_Core_Contract_ServiceTrait,
23
+ AAM_Core_Contract_RequestTrait;
24
 
25
  /**
26
  * AAM configuration setting that is associated with the service
240
  */
241
  public function canUpdatePassword($login, &$password, &$password2)
242
  {
243
+ $userId = $this->getFromPost('user_id', FILTER_VALIDATE_INT);
244
  $isProfile = $userId === get_current_user_id();
245
 
246
  if ($isProfile) {
application/Service/Jwt.php CHANGED
@@ -110,10 +110,21 @@ class AAM_Service_Jwt
110
 
111
  // Delete JWT cookie if it is set
112
  add_action('wp_logout', function() {
113
- if (filter_input(INPUT_COOKIE, 'aam_jwt_token')) {
114
- setcookie('aam_jwt_token', null, time() - 1, COOKIEPATH, COOKIE_DOMAIN);
 
 
 
 
 
 
 
 
115
  }
116
  });
 
 
 
117
  }
118
 
119
  /**
@@ -396,6 +407,32 @@ class AAM_Service_Jwt
396
  return $userId;
397
  }
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  /**
400
  * Authenticate user with JWT
401
  *
@@ -475,10 +512,10 @@ class AAM_Service_Jwt
475
  *
476
  * @return object|null
477
  *
478
- * @access public
479
  * @version 6.0.0
480
  */
481
- public function extractToken()
482
  {
483
  $container = explode(',', AAM_Core_Config::get(
484
  'authentication.jwt.container',
@@ -488,24 +525,24 @@ class AAM_Service_Jwt
488
  foreach ($container as $method) {
489
  switch (strtolower(trim($method))) {
490
  case 'header':
491
- $jwt = AAM_Core_Request::server('HTTP_AUTHENTICATION');
492
  break;
493
 
494
  case 'cookie':
495
- $jwt = AAM_Core_Request::cookie('aam_jwt_token');
496
  break;
497
 
498
  case 'post':
499
- $jwt = AAM_Core_Request::post('aam-jwt');
500
  break;
501
 
502
  case 'get':
503
  case 'query':
504
- $jwt = AAM_Core_Request::get('aam-jwt');
505
  break;
506
 
507
  default:
508
- $jwt = apply_filters('aam-get-jwt-filter', null, $method);
509
  break;
510
  }
511
 
110
 
111
  // Delete JWT cookie if it is set
112
  add_action('wp_logout', function() {
113
+ if ($this->getFromCookie('aam_jwt_token')) {
114
+ setcookie(
115
+ 'aam_jwt_token',
116
+ null,
117
+ time() - 1,
118
+ COOKIEPATH,
119
+ COOKIE_DOMAIN,
120
+ is_ssl(),
121
+ true
122
+ );
123
  }
124
  });
125
+
126
+ // Fetch specific claim from the JWT token if present
127
+ add_filter('aam_get_jwt_claim', array($this, 'getJwtClaim'), 20, 2);
128
  }
129
 
130
  /**
407
  return $userId;
408
  }
409
 
410
+ /**
411
+ * Get specific claim from the JWT token
412
+ *
413
+ * @param mixed $value
414
+ * @param string $prop
415
+ *
416
+ * @return mixed
417
+ *
418
+ * @access public
419
+ * @version 6.0.0
420
+ */
421
+ public function getJwtClaim($value, $prop)
422
+ {
423
+ $token = $this->extractToken();
424
+
425
+ if ($token) {
426
+ $claims = AAM_Core_Jwt_Issuer::getInstance()->extractTokenClaims(
427
+ $token->jwt
428
+ );
429
+
430
+ $value = (property_exists($claims, $prop) ? $claims->$prop : null);
431
+ }
432
+
433
+ return $value;
434
+ }
435
+
436
  /**
437
  * Authenticate user with JWT
438
  *
512
  *
513
  * @return object|null
514
  *
515
+ * @access protected
516
  * @version 6.0.0
517
  */
518
+ protected function extractToken()
519
  {
520
  $container = explode(',', AAM_Core_Config::get(
521
  'authentication.jwt.container',
525
  foreach ($container as $method) {
526
  switch (strtolower(trim($method))) {
527
  case 'header':
528
+ $jwt = $this->getFromServer('HTTP_AUTHENTICATION');
529
  break;
530
 
531
  case 'cookie':
532
+ $jwt = $this->getFromCookie('aam_jwt_token');
533
  break;
534
 
535
  case 'post':
536
+ $jwt = $this->getFromPost('aam-jwt');
537
  break;
538
 
539
  case 'get':
540
  case 'query':
541
+ $jwt = $this->getFromQuery('aam-jwt');
542
  break;
543
 
544
  default:
545
+ $jwt = apply_filters('aam_extract_jwt_filter', null, $method);
546
  break;
547
  }
548
 
application/Service/Metabox.php CHANGED
@@ -250,8 +250,8 @@ class AAM_Service_Metabox
250
  {
251
  $object = AAM::getUser()->getObject('metabox');
252
 
253
- foreach ($metaboxes as $id => $metabox) {
254
- if ($object->isHidden($screen_id, $id, $metabox)) {
255
  remove_meta_box($id, $screen_id, $zone);
256
  }
257
  }
250
  {
251
  $object = AAM::getUser()->getObject('metabox');
252
 
253
+ foreach (array_keys($metaboxes) as $id) {
254
+ if ($object->isHidden($screen_id, $id)) {
255
  remove_meta_box($id, $screen_id, $zone);
256
  }
257
  }
application/Service/Route.php CHANGED
@@ -213,7 +213,7 @@ class AAM_Service_Route
213
  if ($object->isRestricted('restful', $route, $method)) {
214
  $response = new WP_Error(
215
  'rest_access_denied',
216
- __('Access denied', AAM_KEY),
217
  array('status' => 401)
218
  );
219
  break;
213
  if ($object->isRestricted('restful', $route, $method)) {
214
  $response = new WP_Error(
215
  'rest_access_denied',
216
+ __('Access Denied', AAM_KEY),
217
  array('status' => 401)
218
  );
219
  break;
application/Service/SecureLogin.php CHANGED
@@ -95,15 +95,14 @@ class AAM_Service_SecureLogin
95
  add_action('wp_login_failed', array($this, 'trackFailedLoginAttempt'));
96
 
97
  // AAM UI controls
98
- add_filter('aam_user_row_actions_filter', function($actions, $user, $allowed) {
99
  // Move this to the Secure Login Service
100
  if (current_user_can('aam_toggle_users')) {
101
- $prefix = $allowed ? '' : 'no-';
102
- $actions[] = $prefix . ($user->user_status ? 'unlock' : 'lock');
103
  }
104
 
105
  return $actions;
106
- }, 10, 3);
107
  add_filter('aam_ajax_filter', array($this, 'handleAjax'), 10, 3);
108
  add_filter('aam_user_expiration_actions_filter', function($actions) {
109
  $actions['lock'] = __('Block User Account', AAM_KEY);
@@ -434,7 +433,7 @@ class AAM_Service_SecureLogin
434
  $result = false;
435
 
436
  if (current_user_can('aam_toggle_users') && current_user_can('edit_users')) {
437
- if (AAM_Core_API::isUserLevelAllowed($user->getMaxLevel())) {
438
  // User is not allowed to lock himself
439
  if (intval($user->getId()) !== get_current_user_id()) {
440
  $result = $this->changeUserStatus(
95
  add_action('wp_login_failed', array($this, 'trackFailedLoginAttempt'));
96
 
97
  // AAM UI controls
98
+ add_filter('aam_user_row_actions_filter', function($actions, $user) {
99
  // Move this to the Secure Login Service
100
  if (current_user_can('aam_toggle_users')) {
101
+ $actions[] = ($user->user_status ? 'unlock' : 'lock');
 
102
  }
103
 
104
  return $actions;
105
+ }, 10, 2);
106
  add_filter('aam_ajax_filter', array($this, 'handleAjax'), 10, 3);
107
  add_filter('aam_user_expiration_actions_filter', function($actions) {
108
  $actions['lock'] = __('Block User Account', AAM_KEY);
433
  $result = false;
434
 
435
  if (current_user_can('aam_toggle_users') && current_user_can('edit_users')) {
436
+ if (apply_filters('aam_user_can_manage_level_filter', true, $user->getMaxLevel())) {
437
  // User is not allowed to lock himself
438
  if (intval($user->getId()) !== get_current_user_id()) {
439
  $result = $this->changeUserStatus(
application/Service/Settings.php CHANGED
@@ -36,6 +36,7 @@ class AAM_Service_Settings
36
  AAM_Backend_Feature_Settings_Core::register();
37
  AAM_Backend_Feature_Settings_Content::register();
38
  AAM_Backend_Feature_Settings_ConfigPress::register();
 
39
  }, 1);
40
  }
41
  }
36
  AAM_Backend_Feature_Settings_Core::register();
37
  AAM_Backend_Feature_Settings_Content::register();
38
  AAM_Backend_Feature_Settings_ConfigPress::register();
39
+ AAM_Backend_Feature_Settings_Manager::register();
40
  }, 1);
41
  }
42
  }
application/Service/Toolbar.php CHANGED
@@ -119,7 +119,7 @@ class AAM_Service_Toolbar
119
  _doing_it_wrong(
120
  __CLASS__ . '::' . __METHOD__,
121
  'Toolbar object does not have "nodes" property',
122
- '6.0.0'
123
  );
124
  }
125
 
@@ -159,7 +159,7 @@ class AAM_Service_Toolbar
159
  $nodes = $wp_admin_bar->get_nodes();
160
 
161
  foreach ((is_array($nodes) ? $nodes : array()) as $id => $node) {
162
- if ($toolbar->isRestricted($id, true)) {
163
  if (!empty($node->parent)) { // update parent node with # link
164
  $parent = $wp_admin_bar->get_node($node->parent);
165
  if ($parent && ($parent->href === $node->href)) {
119
  _doing_it_wrong(
120
  __CLASS__ . '::' . __METHOD__,
121
  'Toolbar object does not have "nodes" property',
122
+ AAM_VERSION
123
  );
124
  }
125
 
159
  $nodes = $wp_admin_bar->get_nodes();
160
 
161
  foreach ((is_array($nodes) ? $nodes : array()) as $id => $node) {
162
+ if ($toolbar->isHidden($id, true)) {
163
  if (!empty($node->parent)) { // update parent node with # link
164
  $parent = $wp_admin_bar->get_node($node->parent);
165
  if ($parent && ($parent->href === $node->href)) {
application/Service/UserLevelFilter.php CHANGED
@@ -75,6 +75,41 @@ class AAM_Service_UserLevelFilter
75
 
76
  // Check if user has ability to perform certain task on other users
77
  add_filter('map_meta_cap', array($this, 'mapMetaCaps'), 999, 4);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
 
80
  /**
@@ -97,7 +132,7 @@ class AAM_Service_UserLevelFilter
97
  $levels[$id] = AAM_Core_API::maxLevel($role['capabilities']);
98
  }
99
 
100
- if (!AAM_Core_API::isUserLevelAllowed($levels[$id])) {
101
  unset($roles[$id]);
102
  }
103
  }
@@ -127,7 +162,7 @@ class AAM_Service_UserLevelFilter
127
  foreach ($roles->role_objects as $id => $role) {
128
  $roleMax = AAM_Core_API::maxLevel($role->capabilities);
129
 
130
- if (!AAM_Core_API::isUserLevelAllowed($roleMax)) {
131
  $exclude[] = $id;
132
  }
133
  }
@@ -151,7 +186,7 @@ class AAM_Service_UserLevelFilter
151
 
152
  foreach ($roles->role_objects as $id => $role) {
153
  $roleMax = AAM_Core_API::maxLevel($role->capabilities);
154
- if (isset($views[$id]) && !AAM_Core_API::isUserLevelAllowed($roleMax)) {
155
  unset($views[$id]);
156
  }
157
  }
@@ -179,21 +214,10 @@ class AAM_Service_UserLevelFilter
179
  */
180
  public function mapMetaCaps($caps, $cap, $user_id, $args)
181
  {
182
- $objectId = (isset($args[0]) ? $args[0] : null);
183
-
184
- switch ($cap) {
185
- case 'edit_user':
186
- case 'delete_user':
187
- // Some plugins or themes simply do not provide the the user ID for
188
- // these capabilities. I did not find in WP core any place were they
189
- // violate this rule
190
- if (!empty($objectId)) {
191
- $caps = $this->authorizeUserUpdate($caps, $objectId);
192
- }
193
- break;
194
 
195
- default:
196
- break;
197
  }
198
 
199
  return $caps;
@@ -215,7 +239,7 @@ class AAM_Service_UserLevelFilter
215
  $user = AAM::api()->getUser($userId);
216
  $userLevel = AAM_Core_API::maxLevel($user->allcaps);
217
 
218
- if (AAM_Core_API::isUserLevelAllowed($userLevel)) {
219
  $caps[] = 'do_not_allow';
220
  }
221
 
75
 
76
  // Check if user has ability to perform certain task on other users
77
  add_filter('map_meta_cap', array($this, 'mapMetaCaps'), 999, 4);
78
+
79
+ // Determine if current user is allowed to manage specific user level
80
+ add_filter(
81
+ 'aam_user_can_manage_level_filter', array($this, 'isUserLevelAllowed'), 10, 2
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Determine if current user is allowed to manage provided user level
87
+ *
88
+ * @param boolean $allowed
89
+ * @param int $level
90
+ *
91
+ * @return boolean
92
+ *
93
+ * @access public
94
+ * @version 6.0.0
95
+ */
96
+ public function isUserLevelAllowed($allowed, $level)
97
+ {
98
+ $allow_equal_level = true;
99
+
100
+ if (AAM_Core_API::capExists('manage_same_user_level')) {
101
+ $allow_equal_level = current_user_can('manage_same_user_level');
102
+ }
103
+
104
+ $user_level = AAM::getUser()->getMaxLevel();
105
+
106
+ if ($allow_equal_level) {
107
+ $allowed = $user_level >= $level;
108
+ } else {
109
+ $allowed = $user_level > $level;
110
+ }
111
+
112
+ return $allowed;
113
  }
114
 
115
  /**
132
  $levels[$id] = AAM_Core_API::maxLevel($role['capabilities']);
133
  }
134
 
135
+ if (!$this->isUserLevelAllowed(true, $levels[$id])) {
136
  unset($roles[$id]);
137
  }
138
  }
162
  foreach ($roles->role_objects as $id => $role) {
163
  $roleMax = AAM_Core_API::maxLevel($role->capabilities);
164
 
165
+ if (!$this->isUserLevelAllowed(true, $roleMax)) {
166
  $exclude[] = $id;
167
  }
168
  }
186
 
187
  foreach ($roles->role_objects as $id => $role) {
188
  $roleMax = AAM_Core_API::maxLevel($role->capabilities);
189
+ if (isset($views[$id]) && !$this->isUserLevelAllowed(true, $roleMax)) {
190
  unset($views[$id]);
191
  }
192
  }
214
  */
215
  public function mapMetaCaps($caps, $cap, $user_id, $args)
216
  {
217
+ $id = (isset($args[0]) ? $args[0] : null);
 
 
 
 
 
 
 
 
 
 
 
218
 
219
+ if (in_array($cap, array('edit_user', 'delete_user')) && !empty($id)) {
220
+ $caps = $this->authorizeUserUpdate($caps, $id);
221
  }
222
 
223
  return $caps;
239
  $user = AAM::api()->getUser($userId);
240
  $userLevel = AAM_Core_API::maxLevel($user->allcaps);
241
 
242
+ if (!$this->isUserLevelAllowed(true, $userLevel)) {
243
  $caps[] = 'do_not_allow';
244
  }
245
 
application/Shortcode/Factory.php CHANGED
@@ -55,7 +55,7 @@ class AAM_Shortcode_Factory
55
  }
56
 
57
  /**
58
- * Process the shortcode
59
  *
60
  * @return string
61
  *
@@ -72,7 +72,7 @@ class AAM_Shortcode_Factory
72
  _doing_it_wrong(
73
  __CLASS__ . '::' . __METHOD__,
74
  'No valid strategy found for the given context',
75
- '6.0.0'
76
  );
77
  }
78
 
55
  }
56
 
57
  /**
58
+ * Process the short-code
59
  *
60
  * @return string
61
  *
72
  _doing_it_wrong(
73
  __CLASS__ . '::' . __METHOD__,
74
  'No valid strategy found for the given context',
75
+ AAM_VERSION
76
  );
77
  }
78
 
lang/advanced-access-manager-en_US.po CHANGED
@@ -1,7 +1,7 @@
1
  msgid ""
2
  msgstr ""
3
  "Project-Id-Version: Advanced Access Manager\n"
4
- "POT-Creation-Date: 2019-10-04 11:08-0400\n"
5
  "PO-Revision-Date: \n"
6
  "Last-Translator: \n"
7
  "Language-Team: AAMPlugin <support@aamplugin.com>\n"
@@ -24,6 +24,34 @@ msgstr ""
24
  msgid "WP 4.7.0 or higher is required."
25
  msgstr ""
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  #: application/Backend/Feature/Main/404Redirect.php:71
28
  #: application/Service/NotFoundRedirect.php:52
29
  msgid "404 Redirect"
@@ -37,32 +65,32 @@ msgstr ""
37
  msgid "Permission denied to delete this capability"
38
  msgstr ""
39
 
40
- #: application/Backend/Feature/Main/Capability.php:320
41
- #: application/Backend/Feature/Main/Capability.php:341
42
  msgid "System"
43
  msgstr ""
44
 
45
- #: application/Backend/Feature/Main/Capability.php:321
46
- #: application/Backend/Feature/Main/Capability.php:343
47
  msgid "Posts & Pages"
48
  msgstr ""
49
 
50
- #: application/Backend/Feature/Main/Capability.php:322
51
- #: application/Backend/Feature/Main/Capability.php:345
52
  msgid "Backend"
53
  msgstr ""
54
 
55
- #: application/Backend/Feature/Main/Capability.php:323
56
- #: application/Backend/Feature/Main/Capability.php:347
57
  msgid "AAM Interface"
58
  msgstr ""
59
 
60
- #: application/Backend/Feature/Main/Capability.php:324
61
- #: application/Backend/Feature/Main/Capability.php:349
62
  msgid "Miscellaneous"
63
  msgstr ""
64
 
65
- #: application/Backend/Feature/Main/Capability.php:368
66
  #: application/Service/Capability.php:52
67
  msgid "Capabilities"
68
  msgstr ""
@@ -93,22 +121,18 @@ msgstr ""
93
  msgid "Backend Menu"
94
  msgstr ""
95
 
96
- #: application/Backend/Feature/Main/Metabox.php:269
97
  #: application/Service/Metabox.php:42
98
  msgid "Metaboxes & Widgets"
99
  msgstr ""
100
 
101
- #: application/Backend/Feature/Main/Policy.php:67
102
- msgid "Failed to fetch policy. Please try again."
103
- msgstr ""
104
-
105
- #: application/Backend/Feature/Main/Policy.php:179
106
  msgid "(no title)"
107
  msgstr ""
108
 
109
- #: application/Backend/Feature/Main/Policy.php:224
110
- #: application/Service/AccessPolicy.php:57
111
- #: application/Service/AccessPolicy.php:120
112
  msgid "Access Policies"
113
  msgstr ""
114
 
@@ -117,12 +141,12 @@ msgid "307 - Temporary Redirect (Default)"
117
  msgstr ""
118
 
119
  #: application/Backend/Feature/Main/Post.php:215
120
- #: application/Backend/phtml/service/uri.phtml:95
121
  msgid "301 - Moved Permanently"
122
  msgstr ""
123
 
124
  #: application/Backend/Feature/Main/Post.php:216
125
- #: application/Backend/phtml/service/uri.phtml:97
126
  msgid "303 - See Other"
127
  msgstr ""
128
 
@@ -131,22 +155,22 @@ msgstr ""
131
  msgid "%d times"
132
  msgstr ""
133
 
134
- #: application/Backend/Feature/Main/Post.php:335
135
  #, php-format
136
  msgid "\"%s\" page"
137
  msgstr ""
138
 
139
- #: application/Backend/Feature/Main/Post.php:341
140
  #, php-format
141
  msgid "%s URL"
142
  msgstr ""
143
 
144
- #: application/Backend/Feature/Main/Post.php:345
145
  msgid "Login page"
146
  msgstr ""
147
 
148
- #: application/Backend/Feature/Main/Post.php:988
149
- #: application/Service/Content.php:65
150
  msgid "Posts & Terms"
151
  msgstr ""
152
 
@@ -165,7 +189,7 @@ msgstr ""
165
  msgid "Admin Toolbar"
166
  msgstr ""
167
 
168
- #: application/Backend/Feature/Main/Uri.php:141 application/Service/Uri.php:53
169
  msgid "URI Access"
170
  msgstr ""
171
 
@@ -250,15 +274,15 @@ msgstr ""
250
  msgid "Services"
251
  msgstr ""
252
 
253
- #: application/Backend/Feature/Subject/Role.php:121
254
- #: application/Backend/Feature/Subject/Role.php:190
255
- #: application/Backend/Feature/Subject/Role.php:268
256
- #: application/Backend/Feature/Subject/Role.php:294
257
  #: application/Backend/Feature/Subject/User.php:80
258
  msgid "Unauthorized operation"
259
  msgstr ""
260
 
261
- #: application/Backend/Feature/Subject/Role.php:291
262
  msgid "Failed to delete the role"
263
  msgstr ""
264
 
@@ -266,9 +290,9 @@ msgstr ""
266
  msgid "Cannot manage yourself"
267
  msgstr ""
268
 
269
- #: application/Backend/Feature/Subject/User.php:209
270
- #: application/Backend/Feature/Subject/User.php:234
271
- #: application/Backend/View/Localization.php:141 media/js/aam.js:4665
272
  msgid "Unexpected application error"
273
  msgstr ""
274
 
@@ -280,28 +304,23 @@ msgstr ""
280
  msgid "You are not allowed to manage AAM subjects"
281
  msgstr ""
282
 
283
- #: application/Backend/View.php:158 application/Backend/View.php:160
284
- #: application/Service/Route.php:215
285
- msgid "Access denied"
286
- msgstr ""
287
-
288
- #: application/Backend/View/Localization.php:32 media/js/aam.js:2000
289
  msgid "Search Capability"
290
  msgstr ""
291
 
292
- #: application/Backend/View/Localization.php:33 media/js/aam.js:2001
293
  msgid "_TOTAL_ capability(s)"
294
  msgstr ""
295
 
296
- #: application/Backend/View/Localization.php:34 media/js/aam.js:421
297
- #: media/js/aam.js:482 media/js/aam.js:1040 media/js/aam.js:2148
298
- #: media/js/aam.js:2190 media/js/aam.js:2389 media/js/aam.js:2408
299
- #: media/js/aam.js:2434 media/js/aam.js:2456 media/js/aam.js:2475
300
- #: media/js/aam.js:3447
301
  msgid "Saving..."
302
  msgstr ""
303
 
304
- #: application/Backend/View/Localization.php:35 media/js/aam.js:2156
305
  msgid "Failed to add new capability"
306
  msgstr ""
307
 
@@ -309,37 +328,39 @@ msgstr ""
309
  msgid "Application error"
310
  msgstr ""
311
 
312
- #: application/Backend/View/Localization.php:37 media/js/aam.js:2164
313
  msgid "Add Capability"
314
  msgstr ""
315
 
316
- #: application/Backend/View/Localization.php:38 media/js/aam.js:2206
317
- #: application/Backend/phtml/service/capability.phtml:76
318
  msgid "Update Capability"
319
  msgstr ""
320
 
321
- #: application/Backend/View/Localization.php:39 media/js/aam.js:1487
322
- #: media/js/aam.js:1617 application/Backend/phtml/service/menu.phtml:93
323
- #: application/Backend/phtml/service/toolbar.phtml:70
 
324
  msgid "Show Menu"
325
  msgstr ""
326
 
327
- #: application/Backend/View/Localization.php:40 media/js/aam.js:1497
328
- #: media/js/aam.js:1627 application/Backend/phtml/service/menu.phtml:97
329
- #: application/Backend/phtml/service/toolbar.phtml:74
 
330
  msgid "Restrict Menu"
331
  msgstr ""
332
 
333
- #: application/Backend/View/Localization.php:41 media/js/aam.js:1797
334
  msgid "Failed to retrieve mataboxes"
335
  msgstr ""
336
 
337
- #: application/Backend/View/Localization.php:42 media/js/aam.js:2614
338
- #: media/js/aam.js:3334 media/js/aam.js:3525 media/js/aam.js:3752
339
  msgid "Search"
340
  msgstr ""
341
 
342
- #: application/Backend/View/Localization.php:43 media/js/aam.js:2615
343
  msgid "_TOTAL_ object(s)"
344
  msgstr ""
345
 
@@ -347,16 +368,16 @@ msgstr ""
347
  msgid "Failed"
348
  msgstr ""
349
 
350
- #: application/Backend/View/Localization.php:45 media/js/aam.js:59
351
- #: media/js/aam.js:4309
352
  msgid "Loading..."
353
  msgstr ""
354
 
355
- #: application/Backend/View/Localization.php:46 media/js/aam.js:64
356
  msgid "No role"
357
  msgstr ""
358
 
359
- #: application/Backend/View/Localization.php:47 media/js/aam.js:144
360
  msgid "Create New Role"
361
  msgstr ""
362
 
@@ -364,21 +385,21 @@ msgstr ""
364
  msgid "Search Role"
365
  msgstr ""
366
 
367
- #: application/Backend/View/Localization.php:49 media/js/aam.js:128
368
  msgid "_TOTAL_ role(s)"
369
  msgstr ""
370
 
371
- #: application/Backend/View/Localization.php:50 media/js/aam.js:1330
372
- #: media/js/aam.js:3536 media/js/aam.js:3767 media/js/aam.js:3852
373
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:19
374
- #: application/Backend/phtml/service/capability.phtml:30
375
- #: application/Backend/phtml/service/capability.phtml:64
376
- #: application/Backend/phtml/service/jwt.phtml:84
377
  msgid "Create"
378
  msgstr ""
379
 
380
- #: application/Backend/View/Localization.php:51 media/js/aam.js:166
381
- #: application/Backend/phtml/page/subject-panel.phtml:17
382
  msgid "Users"
383
  msgstr ""
384
 
@@ -390,18 +411,20 @@ msgstr ""
390
  msgid "Add Role"
391
  msgstr ""
392
 
393
- #: application/Backend/View/Localization.php:54 media/js/aam.js:489
394
  msgid "Failed to update role"
395
  msgstr ""
396
 
397
- #: application/Backend/View/Localization.php:55 media/js/aam.js:498
398
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:41
399
- #: application/Backend/phtml/service/capability.phtml:90
400
  msgid "Update"
401
  msgstr ""
402
 
403
- #: application/Backend/View/Localization.php:56 media/js/aam.js:1087
404
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:110
 
 
405
  msgid "Reset"
406
  msgstr ""
407
 
@@ -409,373 +432,383 @@ msgstr ""
409
  msgid "Update..."
410
  msgstr ""
411
 
412
- #: application/Backend/View/Localization.php:58 media/js/aam.js:521
413
- #: media/js/aam.js:1949 media/js/aam.js:3484 media/js/aam.js:3870
414
  msgid "Deleting..."
415
  msgstr ""
416
 
417
- #: application/Backend/View/Localization.php:59 media/js/aam.js:527
418
  msgid "Failed to delete role"
419
  msgstr ""
420
 
421
  #: application/Backend/View/Localization.php:60
422
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:53
423
  msgid "Delete Role"
424
  msgstr ""
425
 
426
- #: application/Backend/View/Localization.php:61 media/js/aam.js:621
427
  msgid "Failed to lock user"
428
  msgstr ""
429
 
430
- #: application/Backend/View/Localization.php:62 media/js/aam.js:712
431
  msgid "Search user"
432
  msgstr ""
433
 
434
- #: application/Backend/View/Localization.php:63 media/js/aam.js:713
 
 
 
 
435
  msgid "_TOTAL_ user(s)"
436
  msgstr ""
437
 
438
- #: application/Backend/View/Localization.php:64 media/js/aam.js:728
439
  msgid "Create New User"
440
  msgstr ""
441
 
442
- #: application/Backend/View/Localization.php:65 media/js/aam.js:776
443
- #: application/Backend/phtml/page/subject-panel.phtml:38
444
  msgid "Role"
445
  msgstr ""
446
 
447
- #: application/Backend/View/Localization.php:66
448
- #: application/Core/Subject/Default.php:36 media/js/aam.js:1200
449
  msgid "All Users, Roles and Visitor"
450
  msgstr ""
451
 
452
- #: application/Backend/View/Localization.php:67 media/js/aam.js:1171
453
- #: media/js/aam.js:1246 media/js/aam.js:4278
454
  msgid "Failed to apply policy changes"
455
  msgstr ""
456
 
457
- #: application/Backend/View/Localization.php:68 media/js/aam.js:1165
458
- #: media/js/aam.js:1174
 
459
  msgid "Attach Policy To Visitors"
460
  msgstr ""
461
 
462
- #: application/Backend/View/Localization.php:69 media/js/aam.js:1163
463
- #: media/js/aam.js:1176
 
464
  msgid "Detach Policy From Visitors"
465
  msgstr ""
466
 
467
- #: application/Backend/View/Localization.php:70
468
- #: application/Backend/View/Localization.php:124 media/js/aam.js:659
469
- #: media/js/aam.js:3659
470
  msgid "Generating URL..."
471
  msgstr ""
472
 
473
- #: application/Backend/View/Localization.php:71
474
- #: application/Core/Subject/Visitor.php:43 media/js/aam.js:1126
475
  msgid "Anonymous"
476
  msgstr ""
477
 
478
- #: application/Backend/View/Localization.php:72 media/js/aam.js:1152
479
- #: media/js/aam.js:1227 media/js/aam.js:1817 media/js/aam.js:4124
480
  msgid "Processing..."
481
  msgstr ""
482
 
483
- #: application/Backend/View/Localization.php:73 media/js/aam.js:737
484
  msgid "Loading roles..."
485
  msgstr ""
486
 
487
- #: application/Backend/View/Localization.php:74
488
- #: application/Backend/View/Localization.php:125 media/js/aam.js:669
489
- #: media/js/aam.js:3669
490
  msgid "Failed to generate JWT token"
491
  msgstr ""
492
 
493
- #: application/Backend/View/Localization.php:75
 
 
 
 
494
  msgid "Current user"
495
  msgstr ""
496
 
497
- #: application/Backend/View/Localization.php:76
498
  msgid "Current role"
499
  msgstr ""
500
 
501
- #: application/Backend/View/Localization.php:77 media/js/aam.js:2813
502
  msgid "Manage Access"
503
  msgstr ""
504
 
505
- #: application/Backend/View/Localization.php:78 media/js/aam.js:754
506
  msgid "Filter by role"
507
  msgstr ""
508
 
509
- #: application/Backend/View/Localization.php:79
510
- #: application/Backend/View/PostOptionList.php:76 media/js/aam.js:2825
511
  msgid "Edit"
512
  msgstr ""
513
 
514
- #: application/Backend/View/Localization.php:80 media/js/aam.js:1054
515
- #: media/js/aam.js:3463
516
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:111
517
- #: application/Backend/phtml/partial/post-access-form.phtml:60
518
- #: application/Backend/phtml/partial/post-access-form.phtml:81
519
- #: application/Backend/phtml/partial/post-access-form.phtml:154
520
- #: application/Backend/phtml/partial/post-access-form.phtml:175
521
- #: application/Backend/phtml/partial/post-access-form.phtml:196
522
- #: application/Backend/phtml/service/uri.phtml:108
523
  msgid "Save"
524
  msgstr ""
525
 
526
- #: application/Backend/View/Localization.php:81 media/js/aam.js:212
527
  msgid "Manage role"
528
  msgstr ""
529
 
530
- #: application/Backend/View/Localization.php:82 media/js/aam.js:233
531
  msgid "Edit role"
532
  msgstr ""
533
 
534
- #: application/Backend/View/Localization.php:83 media/js/aam.js:286
535
- #: media/js/aam.js:535
536
  msgid "Delete role"
537
  msgstr ""
538
 
539
- #: application/Backend/View/Localization.php:84 media/js/aam.js:257
540
  msgid "Clone role"
541
  msgstr ""
542
 
543
- #: application/Backend/View/Localization.php:85 media/js/aam.js:815
544
  msgid "Manage user"
545
  msgstr ""
546
 
547
- #: application/Backend/View/Localization.php:86 media/js/aam.js:864
548
  msgid "Edit user"
549
  msgstr ""
550
 
551
- #: application/Backend/View/Localization.php:87 media/js/aam.js:616
552
- #: media/js/aam.js:617 media/js/aam.js:885 media/js/aam.js:896
553
  msgid "Lock user"
554
  msgstr ""
555
 
556
- #: application/Backend/View/Localization.php:88 media/js/aam.js:610
557
- #: media/js/aam.js:611 media/js/aam.js:909 media/js/aam.js:920
558
  msgid "Unlock user"
559
  msgstr ""
560
 
561
- #: application/Backend/View/Localization.php:89 media/js/aam.js:1912
562
  msgid "WordPress core does not allow to grant this capability"
563
  msgstr ""
564
 
565
- #: application/Backend/View/Localization.php:90 media/js/aam.js:1238
566
- #: media/js/aam.js:1251
567
  msgid "Detach Policy From Everybody"
568
  msgstr ""
569
 
570
- #: application/Backend/View/Localization.php:91 media/js/aam.js:1240
571
- #: media/js/aam.js:1249
572
  msgid "Attach Policy To Everybody"
573
  msgstr ""
574
 
575
- #: application/Backend/View/Localization.php:92 media/js/aam.js:1319
576
  msgid "Search Policy"
577
  msgstr ""
578
 
579
- #: application/Backend/View/Localization.php:93 media/js/aam.js:1320
580
  msgid "_TOTAL_ Policies"
581
  msgstr ""
582
 
583
- #: application/Backend/View/Localization.php:94 media/js/aam.js:1353
584
  msgid "Apply Policy"
585
  msgstr ""
586
 
587
- #: application/Backend/View/Localization.php:95 media/js/aam.js:1373
588
  msgid "Revoke Policy"
589
  msgstr ""
590
 
591
- #: application/Backend/View/Localization.php:96
592
- #: application/Service/AccessPolicy.php:121 media/js/aam.js:1390
593
  msgid "Edit Policy"
594
  msgstr ""
595
 
596
- #: application/Backend/View/Localization.php:97 media/js/aam.js:1526
597
- #: application/Backend/phtml/service/menu.phtml:79
598
- #: application/Backend/phtml/service/toolbar.phtml:60
599
  msgid "Uncheck to allow"
600
  msgstr ""
601
 
602
- #: application/Backend/View/Localization.php:98 media/js/aam.js:1528
603
- #: application/Backend/phtml/service/menu.phtml:79
604
- #: application/Backend/phtml/service/toolbar.phtml:60
605
  msgid "Check to restrict"
606
  msgstr ""
607
 
608
- #: application/Backend/View/Localization.php:99 media/js/aam.js:1662
609
- #: media/js/aam.js:1850 application/Backend/phtml/service/metabox.phtml:78
 
610
  msgid "Uncheck to show"
611
  msgstr ""
612
 
613
- #: application/Backend/View/Localization.php:100 media/js/aam.js:1664
614
- #: media/js/aam.js:1852 application/Backend/phtml/service/metabox.phtml:78
 
615
  msgid "Check to hide"
616
  msgstr ""
617
 
618
- #: application/Backend/View/Localization.php:101 media/js/aam.js:1820
619
- #: application/Backend/phtml/service/metabox.phtml:114
620
  msgid "Initialize"
621
  msgstr ""
622
 
623
- #: application/Backend/View/Localization.php:102 media/js/aam.js:2003
624
  msgid "No capabilities"
625
  msgstr ""
626
 
627
- #: application/Backend/View/Localization.php:103 media/js/aam.js:2636
628
  msgid "Post Type"
629
  msgstr ""
630
 
631
- #: application/Backend/View/Localization.php:104 media/js/aam.js:2641
632
  msgid "Hierarchical Taxonomy"
633
  msgstr ""
634
 
635
- #: application/Backend/View/Localization.php:105 media/js/aam.js:2646
636
  msgid "Hierarchical Term"
637
  msgstr ""
638
 
639
- #: application/Backend/View/Localization.php:106 media/js/aam.js:2651
640
  msgid "Tag Taxonomy"
641
  msgstr ""
642
 
643
- #: application/Backend/View/Localization.php:107 media/js/aam.js:2656
644
  msgid "Tag"
645
  msgstr ""
646
 
647
- #: application/Backend/View/Localization.php:108 media/js/aam.js:2667
648
  msgid "Customized Settings"
649
  msgstr ""
650
 
651
- #: application/Backend/View/Localization.php:109 media/js/aam.js:2741
652
- #: media/js/aam.js:2744 media/js/aam.js:2766 media/js/aam.js:2769
653
  msgid "Parent"
654
  msgstr ""
655
 
656
- #: application/Backend/View/Localization.php:110 media/js/aam.js:2799
657
  msgid "Drill-Down"
658
  msgstr ""
659
 
660
- #: application/Backend/View/Localization.php:111 media/js/aam.js:3335
661
  msgid "_TOTAL_ route(s)"
662
  msgstr ""
663
 
664
- #: application/Backend/View/Localization.php:112 media/js/aam.js:3337
665
  msgid "No API endpoints found. You might have APIs disabled."
666
  msgstr ""
667
 
668
- #: application/Backend/View/Localization.php:113 media/js/aam.js:3338
669
- #: media/js/aam.js:3756 media/js/aam.js:4078
670
  msgid "Nothing to show"
671
  msgstr ""
672
 
673
- #: application/Backend/View/Localization.php:114 media/js/aam.js:3454
674
  msgid "Failed to save URI rule"
675
  msgstr ""
676
 
677
- #: application/Backend/View/Localization.php:115 media/js/aam.js:3490
678
  msgid "Failed to delete URI rule"
679
  msgstr ""
680
 
681
- #: application/Backend/View/Localization.php:116 media/js/aam.js:3526
682
  msgid "_TOTAL_ URI(s)"
683
  msgstr ""
684
 
685
- #: application/Backend/View/Localization.php:117 media/js/aam.js:3567
686
  msgid "Edit Rule"
687
  msgstr ""
688
 
689
- #: application/Backend/View/Localization.php:118 media/js/aam.js:3579
690
  msgid "Delete Rule"
691
  msgstr ""
692
 
693
- #: application/Backend/View/Localization.php:119 media/js/aam.js:3594
694
  msgid "Denied"
695
  msgstr ""
696
 
697
- #: application/Backend/View/Localization.php:120 media/js/aam.js:3601
698
  msgid "Redirected"
699
  msgstr ""
700
 
701
- #: application/Backend/View/Localization.php:121 media/js/aam.js:3606
702
  msgid "Callback"
703
  msgstr ""
704
 
705
- #: application/Backend/View/Localization.php:122 media/js/aam.js:3611
706
  msgid "Allowed"
707
  msgstr ""
708
 
709
- #: application/Backend/View/Localization.php:123 media/js/aam.js:3658
710
  msgid "Generating token..."
711
  msgstr ""
712
 
713
- #: application/Backend/View/Localization.php:126 media/js/aam.js:3753
714
  msgid "_TOTAL_ token(s)"
715
  msgstr ""
716
 
717
- #: application/Backend/View/Localization.php:127 media/js/aam.js:3755
718
  msgid "No JWT tokens have been generated."
719
  msgstr ""
720
 
721
- #: application/Backend/View/Localization.php:128 media/js/aam.js:3800
722
  msgid "Delete Token"
723
  msgstr ""
724
 
725
- #: application/Backend/View/Localization.php:129 media/js/aam.js:3813
726
  msgid "View Token"
727
  msgstr ""
728
 
729
- #: application/Backend/View/Localization.php:130 media/js/aam.js:3838
730
  msgid "Creating..."
731
  msgstr ""
732
 
733
- #: application/Backend/View/Localization.php:131 media/js/aam.js:4075
734
  msgid "Search Service"
735
  msgstr ""
736
 
737
- #: application/Backend/View/Localization.php:132 media/js/aam.js:4076
738
  msgid "_TOTAL_ service(s)"
739
  msgstr ""
740
 
741
- #: application/Backend/View/Localization.php:133 media/js/aam.js:4088
742
- #: application/Backend/phtml/settings/content.phtml:16
743
- #: application/Backend/phtml/settings/core.phtml:16
744
- #: application/Backend/phtml/settings/security.phtml:16
745
  msgid "Enabled"
746
  msgstr ""
747
 
748
- #: application/Backend/View/Localization.php:134 media/js/aam.js:4088
749
- #: application/Backend/phtml/settings/content.phtml:16
750
- #: application/Backend/phtml/settings/core.phtml:16
751
- #: application/Backend/phtml/settings/security.phtml:16
752
  msgid "Disabled"
753
  msgstr ""
754
 
755
- #: application/Backend/View/Localization.php:135 media/js/aam.js:4130
756
  msgid "All settings has been cleared successfully"
757
  msgstr ""
758
 
759
- #: application/Backend/View/Localization.php:136 media/js/aam.js:4142
760
- #: application/Backend/phtml/index.phtml:94
761
  msgid "Clear"
762
  msgstr ""
763
 
764
- #: application/Backend/View/Localization.php:137 media/js/aam.js:4314
765
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:102
766
- #: application/Backend/phtml/partial/role-inheritance.phtml:7
767
  msgid "Select Role"
768
  msgstr ""
769
 
770
- #: application/Backend/View/Localization.php:138 media/js/aam.js:4587
771
  msgid "Data has been saved to clipboard"
772
  msgstr ""
773
 
774
- #: application/Backend/View/Localization.php:139 media/js/aam.js:4591
775
  msgid "Failed to save data to clipboard"
776
  msgstr ""
777
 
778
- #: application/Backend/View/Localization.php:140 media/js/aam.js:4661
779
  msgid "Operation completed successfully"
780
  msgstr ""
781
 
@@ -800,7 +833,7 @@ msgid ""
800
  msgstr ""
801
 
802
  #: application/Backend/View/PostOptionList.php:41
803
- #: application/Backend/phtml/partial/post-access-form.phtml:50
804
  msgid "Teaser Message"
805
  msgstr ""
806
 
@@ -853,13 +886,13 @@ msgid ""
853
  msgstr ""
854
 
855
  #: application/Backend/View/PostOptionList.php:64
856
- #: application/Backend/phtml/partial/post-access-form.phtml:166
857
  msgid "Password Protected"
858
  msgstr ""
859
 
860
  #: application/Backend/View/PostOptionList.php:65
861
- #: application/Backend/phtml/partial/post-access-form.phtml:170
862
- #: application/Backend/phtml/widget/login-frontend.phtml:29
863
  msgid "Password"
864
  msgstr ""
865
 
@@ -893,11 +926,11 @@ msgstr ""
893
  msgid "Restrict access to edit the post."
894
  msgstr ""
895
 
896
- #: application/Backend/View/PostOptionList.php:81 media/js/aam.js:3498
897
- #: media/js/aam.js:3884
898
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:59
899
- #: application/Backend/phtml/service/jwt.phtml:136
900
- #: application/Backend/phtml/service/uri.phtml:130
901
  msgid "Delete"
902
  msgstr ""
903
 
@@ -931,810 +964,492 @@ msgstr ""
931
  msgid "Howdy, %username%"
932
  msgstr ""
933
 
934
- #: application/Core/Jwt/Issuer.php:55
935
- msgid "Token has been revoked"
936
  msgstr ""
937
 
938
- #: application/Core/Object.php:131
939
- #, php-format
940
- msgid "AAM object function %s is not defined"
941
  msgstr ""
942
 
943
- #: application/Core/Policy/Validator.php:94
944
- msgid "The policy is not valid JSON object"
945
  msgstr ""
946
 
947
- #: application/Core/Policy/Validator.php:111
948
- msgid "The policy document is empty"
949
  msgstr ""
950
 
951
- #: application/Core/Redirect.php:74
952
- #: application/Service/ExtendedCapabilities.php:124
953
- msgid "Access Denied"
954
  msgstr ""
955
 
956
- #: application/Service/AccessPolicy.php:58
957
- msgid "Manage list of access policies for any user, role or visitors."
958
  msgstr ""
959
 
960
- #: application/Service/AccessPolicy.php:118
961
- msgid "Access Policy"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
962
  msgstr ""
963
 
964
- #: application/Service/AccessPolicy.php:122
965
- msgid "Add New Policy"
966
  msgstr ""
967
 
968
- #: application/Service/AccessPolicy.php:123
969
- msgid "New Policy"
970
  msgstr ""
971
 
972
- #: application/Service/AccessPolicy.php:125
973
- msgid "Access and security policy"
974
  msgstr ""
975
 
976
- #: application/Service/AccessPolicy.php:181
977
- msgid "Access Policy Document"
 
 
978
  msgstr ""
979
 
980
- #: application/Service/AccessPolicy.php:195
981
- msgid "Access Policy Assignee"
 
982
  msgstr ""
983
 
984
- #: application/Service/AdminMenu.php:59
985
- msgid "Admin Menu"
 
 
 
986
  msgstr ""
987
 
988
- #: application/Service/AdminMenu.php:60
 
 
 
 
989
  msgid ""
990
- "Manage access to the admin (backend) main menu for any role or individual "
991
- "user. The service removes restricted menu items and protects direct access "
992
- "to them."
 
 
 
 
993
  msgstr ""
994
 
995
- #: application/Service/AdminMenu.php:275
996
- msgid "Sorry, you are not allowed to view this page."
997
  msgstr ""
998
 
999
- #: application/Service/Capability.php:53
1000
- msgid ""
1001
- "Manage list of all the registered with WordPress core capabilities for any "
1002
- "role or individual user. The service allows to create new or update and "
1003
- "delete existing capabilities. Very powerful set of tools for more advanced "
1004
- "user/role access management."
1005
  msgstr ""
1006
 
1007
- #: application/Service/Content.php:66
1008
- msgid ""
1009
- "Manage access to your website content for any user, role or visitor. This "
1010
- "include access to posts, pages, media attachment, custom post types, "
1011
- "categories, tags, custom taxonomies and terms."
1012
  msgstr ""
1013
 
1014
- #: application/Service/Content.php:111
1015
- msgid "Access Manager"
1016
  msgstr ""
1017
 
1018
- #: application/Service/Content.php:578
1019
- msgid "[No teaser message provided]"
 
1020
  msgstr ""
1021
 
1022
- #: application/Service/DeniedRedirect.php:64
1023
- msgid ""
1024
- "Manage the default access denied redirect when access gets denied for any "
1025
- "protected website resource. The service hooks into the WordPress core wp_die "
1026
- "function and redirect any frontend or backend denied requests accordingly."
1027
  msgstr ""
1028
 
1029
- #: application/Service/ExtendedCapabilities.php:47
1030
- msgid "Additional Caps"
1031
  msgstr ""
1032
 
1033
- #: application/Service/ExtendedCapabilities.php:48
1034
- msgid ""
1035
- "Extend the WordPress core collection of capabilities that allow more "
1036
- "granular access control to the backend core features."
1037
  msgstr ""
1038
 
1039
- #: application/Service/Jwt.php:54
1040
- msgid ""
1041
- "Manage the website authentication with JWT Bearer token. The service "
1042
- "facilitates the ability to manage the list of issued JWT token for any user, "
1043
- "revoke them or issue new on demand."
1044
  msgstr ""
1045
 
1046
- #: application/Service/Jwt.php:99
1047
- msgid "Issue JWT Token"
 
 
1048
  msgstr ""
1049
 
1050
- #: application/Service/Jwt.php:135 application/Service/Jwt.php:147
1051
- msgid "JWT token."
1052
  msgstr ""
1053
 
1054
- #: application/Service/Jwt.php:214
1055
- msgid "JWT token is not refreshable"
1056
  msgstr ""
1057
 
1058
- #: application/Service/LoginRedirect.php:53
 
1059
  msgid ""
1060
- "Manage login redirect for any group of users or individual user when "
1061
- "authentication is completed successfully."
 
1062
  msgstr ""
1063
 
1064
- #: application/Service/LogoutRedirect.php:53
1065
- msgid ""
1066
- "Manage logout redirect for any group of users or individual user after user "
1067
- "logged out successfully."
1068
  msgstr ""
1069
 
1070
- #: application/Service/Metabox.php:43
1071
- msgid ""
1072
- "Manage visibility for the classic (not Gutenberg blocks) backend metaboxes, "
1073
- "dashboard and frontend widgets for any role, user or visitors. The service "
1074
- "ONLY removes unwanted metaboxes and widgets and does not prevent from direct "
1075
- "data spoofing."
1076
  msgstr ""
1077
 
1078
- #: application/Service/NotFoundRedirect.php:53
1079
- msgid ""
1080
- "Manage frontend 404 (Not Found) redirect for any group of users or "
1081
- "individual user."
1082
  msgstr ""
1083
 
1084
- #: application/Service/Route.php:67
1085
- msgid ""
1086
- "Manage access to any individual RESTful endpoint for any role, user or "
1087
- "unauthenticated application request. The service works great with JWT "
1088
- "service that authenticate requests with JWT Bearer token."
1089
  msgstr ""
1090
 
1091
- #: application/Service/Route.php:94
1092
- msgid "XML-RPC WordPress API"
1093
  msgstr ""
1094
 
1095
- #: application/Service/Route.php:95
1096
  #, php-format
1097
- msgid ""
1098
- "Remote procedure call (RPC) interface is used to manage WordPress website "
1099
- "content and features. For more information check %sXML-RPC Support%s article."
1100
  msgstr ""
1101
 
1102
- #: application/Service/Route.php:99
1103
- msgid "RESTful WordPress API"
1104
  msgstr ""
1105
 
1106
- #: application/Service/Route.php:100
1107
- #, php-format
1108
  msgid ""
1109
- "RESTful interface that is used to manage WordPress website content and "
1110
- "features. For more information check %sREST API handbook%s."
1111
  msgstr ""
1112
 
1113
- #: application/Service/Route.php:126
1114
- msgid "RESTful API is disabled"
1115
  msgstr ""
1116
 
1117
- #: application/Service/SecureLogin.php:46
1118
- msgid "Secure Login"
1119
  msgstr ""
1120
 
1121
- #: application/Service/SecureLogin.php:47
1122
- msgid ""
1123
- "Enhance default WordPress authentication process with more secure login "
1124
- "mechanism. The service registers frontend AJAX Login widget as well as "
1125
- "additional endpoints for the RESTful API authentication."
1126
  msgstr ""
1127
 
1128
- #: application/Service/SecureLogin.php:109
1129
- msgid "Block User Account"
1130
  msgstr ""
1131
 
1132
- #: application/Service/SecureLogin.php:140
1133
- msgid "Valid username."
1134
  msgstr ""
1135
 
1136
- #: application/Service/SecureLogin.php:144
1137
- msgid "Valid password."
1138
  msgstr ""
1139
 
1140
- #: application/Service/SecureLogin.php:148
1141
- msgid "Redirect URL after authentication."
1142
  msgstr ""
1143
 
1144
- #: application/Service/SecureLogin.php:152
1145
- msgid "Prolong the user session."
1146
  msgstr ""
1147
 
1148
- #: application/Service/SecureLogin.php:316
1149
- msgid "Exceeded maximum number for authentication attempts. Try again later."
1150
  msgstr ""
1151
 
1152
- #: application/Service/SecureLogin.php:344
1153
- msgid "[ERROR]: User is locked. Contact website administrator."
1154
  msgstr ""
1155
 
1156
- #: application/Service/SecureLogin.php:369
1157
- #, php-format
1158
- msgid "%sAccess is restricted. Login to get access.%s"
1159
  msgstr ""
1160
 
1161
- #: application/Service/Shortcode.php:45
1162
- msgid "Shortcodes"
 
1163
  msgstr ""
1164
 
1165
- #: application/Service/Shortcode.php:46
1166
- msgid ""
1167
- "Classic WordPress shortcodes that allow to manage access to parts of a "
1168
- "frontent content as well as some UI helpers."
1169
  msgstr ""
1170
 
1171
- #: application/Service/Toolbar.php:67
1172
  msgid ""
1173
- "Manage access to the top admin toolbar items for any role or individual "
1174
- "user. The service only removes restricted items but does not actually "
1175
- "protect from direct access via link."
1176
  msgstr ""
1177
 
1178
- #: application/Service/Uri.php:54
 
 
 
 
1179
  msgid ""
1180
- "Manage direct access to the website URIs for any role or individual user. "
1181
- "Define either explicit URI or wildcard (with Plus Package addon) as well as "
1182
- "how to manage user request (allow, deny, redirect, etc.)."
1183
  msgstr ""
1184
 
1185
- #: application/Service/UserLevelFilter.php:45
1186
- msgid "User Level Filter"
1187
  msgstr ""
1188
 
1189
- #: application/Service/UserLevelFilter.php:46
1190
- msgid ""
1191
- "Extend default WordPress core users and roles handling, and make sure that "
1192
- "users with lower user level cannot see or manager users and roles with "
1193
- "higher level."
1194
  msgstr ""
1195
 
1196
- #: application/Service/Welcome.php:53
1197
- msgid ""
1198
- "Introduction panel to the AAM functionality. This is just a simple tab that "
1199
- "contains some introductory material to the AAM plugin and its capabilities."
 
 
1200
  msgstr ""
1201
 
1202
- #: application/Shortcode/Handler/LoginRedirect.php:86
1203
- msgid "Login to continue"
1204
  msgstr ""
1205
 
1206
- #: media/js/aam.js:127
1207
- msgid "Search role"
1208
  msgstr ""
1209
 
1210
- #: media/js/aam.js:444
1211
- msgid "Add role"
 
 
 
1212
  msgstr ""
1213
 
1214
- #: media/js/aam.js:776 application/Backend/phtml/service/toolbar.phtml:101
1215
- msgid "ID"
 
 
 
 
 
1216
  msgstr ""
1217
 
1218
- #: media/js/aam.js:1073 media/js/aam.js:2371 media/js/aam.js:4701
1219
- msgid "Resetting..."
 
 
 
 
 
 
 
1220
  msgstr ""
1221
 
1222
- #: media/js/aam.js:2631
1223
- msgid "Post"
 
 
 
 
 
 
 
 
1224
  msgstr ""
1225
 
1226
- #: media/js/aam.js:2699
1227
- msgid "post type"
1228
  msgstr ""
1229
 
1230
- #: media/js/aam.js:2703 media/js/aam.js:2731 media/js/aam.js:2747
1231
- #: media/js/aam.js:2756 media/js/aam.js:2772
1232
- msgid "ID:"
1233
  msgstr ""
1234
 
1235
- #: media/js/aam.js:2728
1236
- msgid "taxonomy"
1237
  msgstr ""
1238
 
1239
- #: media/js/vendor.js:597
1240
- msgid ": "
1241
- msgstr ""
1242
-
1243
- #: tests/Addon/PlusPackage/ContentAccessTest.php:185
1244
- msgid "AAM Test"
1245
- msgstr ""
1246
-
1247
- #: tests/Addon/PlusPackage/ContentAccessTest.php:186
1248
- msgid "Just for testing purposes"
1249
- msgstr ""
1250
-
1251
- #: application/Backend/phtml/index.phtml:12
1252
- msgid ""
1253
- "[Loading AAM UI]. Please wait. If content will not load within next 30 "
1254
- "seconds, clear your browser cache and reload the page. If still nothing, it "
1255
- "is most likely some sort of JavaScript or CSS conflict with one your active "
1256
- "plugins or theme. Try to deactivate all plugins and switch to any default "
1257
- "WordPress theme to find out what causes the issue."
1258
- msgstr ""
1259
-
1260
- #: application/Backend/phtml/index.phtml:25
1261
- msgid "Notifications"
1262
- msgstr ""
1263
-
1264
- #: application/Backend/phtml/index.phtml:46
1265
- msgid "Access"
1266
- msgstr ""
1267
-
1268
- #: application/Backend/phtml/index.phtml:51
1269
- msgid "Settings"
1270
- msgstr ""
1271
-
1272
- #: application/Backend/phtml/index.phtml:57
1273
- msgid "Add-Ons"
1274
- msgstr ""
1275
-
1276
- #: application/Backend/phtml/index.phtml:63
1277
- msgid "Help"
1278
- msgstr ""
1279
-
1280
- #: application/Backend/phtml/index.phtml:77
1281
- msgid "Reset AAM Settings"
1282
- msgstr ""
1283
-
1284
- #: application/Backend/phtml/index.phtml:87
1285
- #: application/Backend/phtml/page/addon-panel.phtml:70
1286
- #: application/Backend/phtml/page/addon-panel.phtml:81
1287
- #: application/Backend/phtml/page/addon-panel.phtml:91
1288
- #: application/Backend/phtml/page/addon-panel.phtml:104
1289
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:8
1290
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:20
1291
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:30
1292
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:42
1293
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:52
1294
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:60
1295
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:70
1296
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:112
1297
- #: application/Backend/phtml/partial/post-access-form.phtml:49
1298
- #: application/Backend/phtml/partial/post-access-form.phtml:61
1299
- #: application/Backend/phtml/partial/post-access-form.phtml:71
1300
- #: application/Backend/phtml/partial/post-access-form.phtml:82
1301
- #: application/Backend/phtml/partial/post-access-form.phtml:92
1302
- #: application/Backend/phtml/partial/post-access-form.phtml:155
1303
- #: application/Backend/phtml/partial/post-access-form.phtml:165
1304
- #: application/Backend/phtml/partial/post-access-form.phtml:176
1305
- #: application/Backend/phtml/partial/post-access-form.phtml:186
1306
- #: application/Backend/phtml/partial/post-access-form.phtml:197
1307
- #: application/Backend/phtml/service/capability.phtml:49
1308
- #: application/Backend/phtml/service/capability.phtml:65
1309
- #: application/Backend/phtml/service/capability.phtml:75
1310
- #: application/Backend/phtml/service/capability.phtml:91
1311
- #: application/Backend/phtml/service/capability.phtml:101
1312
- #: application/Backend/phtml/service/capability.phtml:110
1313
- #: application/Backend/phtml/service/jwt.phtml:36
1314
- #: application/Backend/phtml/service/jwt.phtml:85
1315
- #: application/Backend/phtml/service/jwt.phtml:95
1316
- #: application/Backend/phtml/service/jwt.phtml:119
1317
- #: application/Backend/phtml/service/jwt.phtml:129
1318
- #: application/Backend/phtml/service/jwt.phtml:137
1319
- #: application/Backend/phtml/service/menu.phtml:124
1320
- #: application/Backend/phtml/service/menu.phtml:144
1321
- #: application/Backend/phtml/service/menu.phtml:166
1322
- #: application/Backend/phtml/service/metabox.phtml:101
1323
- #: application/Backend/phtml/service/metabox.phtml:115
1324
- #: application/Backend/phtml/service/metabox.phtml:125
1325
- #: application/Backend/phtml/service/metabox.phtml:143
1326
- #: application/Backend/phtml/service/toolbar.phtml:86
1327
- #: application/Backend/phtml/service/toolbar.phtml:108
1328
- #: application/Backend/phtml/service/uri.phtml:26
1329
- #: application/Backend/phtml/service/uri.phtml:109
1330
- #: application/Backend/phtml/service/uri.phtml:119
1331
- #: application/Backend/phtml/service/uri.phtml:131
1332
- msgid "Close"
1333
- msgstr ""
1334
-
1335
- #: application/Backend/phtml/index.phtml:88
1336
- msgid "Clear all settings"
1337
- msgstr ""
1338
-
1339
- #: application/Backend/phtml/index.phtml:91
1340
- msgid "All AAM settings will be removed."
1341
- msgstr ""
1342
-
1343
- #: application/Backend/phtml/index.phtml:95
1344
- msgid "Cancel"
1345
- msgstr ""
1346
-
1347
- #: application/Backend/phtml/index.phtml:107
1348
- msgid ""
1349
- "With the [Enterprise Package] get our dedicated support channel and all the "
1350
- "premium add-ons for [100+ live websites]"
1351
- msgstr ""
1352
-
1353
- #: application/Backend/phtml/index.phtml:108
1354
- #: application/Backend/phtml/page/addon-panel.phtml:55
1355
- msgid "Read More"
1356
- msgstr ""
1357
-
1358
- #: application/Backend/phtml/metabox/policy-metabox.phtml:390
1359
- #, php-format
1360
- msgid ""
1361
- "To learn more about Access &amp; Security policy document, please check "
1362
- "[%sAccess &amp; Security Policy%s] page."
1363
- msgstr ""
1364
-
1365
- #: application/Backend/phtml/metabox/policy-metabox.phtml:419
1366
- msgid "Syntax Error"
1367
- msgstr ""
1368
-
1369
- #: application/Backend/phtml/page/addon-panel.phtml:8
1370
- msgid ""
1371
- "By purchasing any of the premium addon(s) below, you obtain the license that "
1372
- "allows you to install and use AAM software for one physical WordPress "
1373
- "installation only. Exceptions are websites where URL is either [localhost] "
1374
- "or starts with [dev.], [staging.], [test.] or [demo.] They are considered as "
1375
- "development websites and you can use the purchased license unlimited number "
1376
- "of times before it is activated on a production website. [Money back "
1377
- "guaranteed] within 30 day from the time of purchase."
1378
- msgstr ""
1379
-
1380
- #: application/Backend/phtml/page/addon-panel.phtml:13
1381
- msgid "Download Addon"
1382
- msgstr ""
1383
-
1384
- #: application/Backend/phtml/page/addon-panel.phtml:17
1385
- msgid "Enter The License Key"
1386
- msgstr ""
1387
-
1388
- #: application/Backend/phtml/page/addon-panel.phtml:21
1389
- msgid "Download"
1390
- msgstr ""
1391
-
1392
- #: application/Backend/phtml/page/addon-panel.phtml:29
1393
- msgid "Premium"
1394
- msgstr ""
1395
-
1396
- #: application/Backend/phtml/page/addon-panel.phtml:41
1397
- #: application/Backend/phtml/page/addon-panel.phtml:43
1398
- msgid "License"
1399
- msgstr ""
1400
-
1401
- #: application/Backend/phtml/page/addon-panel.phtml:43
1402
- msgid "unregistered version"
1403
- msgstr ""
1404
-
1405
- #: application/Backend/phtml/page/addon-panel.phtml:51
1406
- msgid "Active"
1407
- msgstr ""
1408
-
1409
- #: application/Backend/phtml/page/addon-panel.phtml:53
1410
- msgid "Inactive"
1411
- msgstr ""
1412
-
1413
- #: application/Backend/phtml/page/addon-panel.phtml:71
1414
- msgid "License Key Info"
1415
- msgstr ""
1416
-
1417
- #: application/Backend/phtml/page/addon-panel.phtml:75
1418
- msgid ""
1419
- "Insert license key that you received after the payment (find the email "
1420
- "example below). It might take up to 2 hours to process the payment."
1421
- msgstr ""
1422
-
1423
- #: application/Backend/phtml/page/addon-panel.phtml:92
1424
- msgid "Plugin Installation"
1425
- msgstr ""
1426
-
1427
- #: application/Backend/phtml/page/addon-panel.phtml:96
1428
- msgid "The plugin has been successfully downloaded from our server."
1429
- msgstr ""
1430
-
1431
- #: application/Backend/phtml/page/addon-panel.phtml:100
1432
- #, php-format
1433
- msgid ""
1434
- "With AAM v6.0.0 or higher, all premium addons are [regular WordPress "
1435
- "plugins] that you can upload by going to the %sPlugins%s page or extract "
1436
- "downloaded ZIP archive to the [/wp-content/plugins] folder."
1437
- msgstr ""
1438
-
1439
- #: application/Backend/phtml/page/main-panel.phtml:33
1440
- msgid "You are not allowed to manage any of the existing services."
1441
- msgstr ""
1442
-
1443
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:9
1444
- msgid "Create Role"
1445
- msgstr ""
1446
-
1447
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:13
1448
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:35
1449
- msgid "Role Name"
1450
- msgstr ""
1451
-
1452
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:14
1453
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:36
1454
- msgid "Enter Role Name"
1455
- msgstr ""
1456
-
1457
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:31
1458
- msgid "Update Role"
1459
- msgstr ""
1460
-
1461
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:56
1462
- #, php-format
1463
- msgid "Are you sure that you want to delete the %s role?"
1464
- msgstr ""
1465
-
1466
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:71
1467
- msgid "Manage User"
1468
- msgstr ""
1469
-
1470
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:74
1471
- msgid ""
1472
- "Define for how long user can access the website and what action needs to be "
1473
- "taken after access expires."
1474
- msgstr ""
1475
-
1476
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:83
1477
- msgid "Action After Expiration"
1478
- msgstr ""
1479
-
1480
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:86
1481
- msgid "Select Action"
1482
- msgstr ""
1483
-
1484
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:87
1485
- msgid "Logout User"
1486
- msgstr ""
1487
-
1488
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:88
1489
- msgid "Delete Account"
1490
- msgstr ""
1491
-
1492
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:89
1493
- msgid "Change User Role"
1494
- msgstr ""
1495
-
1496
- #: application/Backend/phtml/page/subject-panel-advanced.phtml:100
1497
- msgid "Change To Role"
1498
- msgstr ""
1499
-
1500
- #: application/Backend/phtml/page/subject-panel.phtml:7
1501
- msgid "Users/Roles Manager"
1502
- msgstr ""
1503
-
1504
- #: application/Backend/phtml/page/subject-panel.phtml:14
1505
- msgid "Roles"
1506
- msgstr ""
1507
-
1508
- #: application/Backend/phtml/page/subject-panel.phtml:20
1509
- msgid "Visitor"
1510
- msgstr ""
1511
-
1512
- #: application/Backend/phtml/page/subject-panel.phtml:23
1513
- msgid "Default"
1514
- msgstr ""
1515
-
1516
- #: application/Backend/phtml/page/subject-panel.phtml:26
1517
- msgid "None"
1518
- msgstr ""
1519
-
1520
- #: application/Backend/phtml/page/subject-panel.phtml:39
1521
- #: application/Backend/phtml/page/subject-panel.phtml:55
1522
- msgid "Action"
1523
- msgstr ""
1524
-
1525
- #: application/Backend/phtml/page/subject-panel.phtml:54
1526
- msgid "Username"
1527
- msgstr ""
1528
-
1529
- #: application/Backend/phtml/page/subject-panel.phtml:67
1530
- msgid ""
1531
- "Manage access to your website for visitors (any user that is not "
1532
- "authenticated)"
1533
- msgstr ""
1534
-
1535
- #: application/Backend/phtml/page/subject-panel.phtml:68
1536
- msgid "Manage Visitors"
1537
- msgstr ""
1538
-
1539
- #: application/Backend/phtml/page/subject-panel.phtml:75
1540
- msgid ""
1541
- "Manage default access to your website resources for all users, roles and "
1542
- "visitor. This includes Administrator role and your user"
1543
- msgstr ""
1544
-
1545
- #: application/Backend/phtml/page/subject-panel.phtml:76
1546
- msgid "Manage Default Access"
1547
- msgstr ""
1548
-
1549
- #: application/Backend/phtml/page/subject-panel.phtml:82
1550
- msgid ""
1551
- "You are not allowed to manage any of the existing users, roles, visitors or "
1552
- "default access settings."
1553
- msgstr ""
1554
-
1555
- #: application/Backend/phtml/partial/jwt-login-url.phtml:6
1556
- msgid "Login with URL"
1557
- msgstr ""
1558
-
1559
- #: application/Backend/phtml/partial/jwt-login-url.phtml:7
1560
- #: application/Backend/phtml/service/jwt.phtml:67
1561
- #: application/Backend/phtml/service/jwt.phtml:77
1562
- #: application/Backend/phtml/service/jwt.phtml:102
1563
- #: application/Backend/phtml/service/jwt.phtml:112
1564
- msgid "Copy to clipboard"
1565
- msgstr ""
1566
-
1567
- #: application/Backend/phtml/partial/jwt-login-url.phtml:10
1568
- msgid "Login URL has not been requested"
1569
- msgstr ""
1570
-
1571
- #: application/Backend/phtml/partial/jwt-login-url.phtml:12
1572
- msgid "Request URL"
1573
- msgstr ""
1574
-
1575
- #: application/Backend/phtml/partial/jwt-login-url.phtml:16
1576
- msgid ""
1577
- "With this URL user will be automatically logged in until defined date and "
1578
- "time. The JWT token associated with URL is [revokable] however not "
1579
- "[refreshable]."
1580
- msgstr ""
1581
-
1582
- #: application/Backend/phtml/partial/post-access-form.phtml:5
1583
- #: application/Backend/phtml/service/login-redirect.phtml:17
1584
- #: application/Backend/phtml/service/logout-redirect.phtml:17
1585
- #: application/Backend/phtml/service/menu.phtml:17
1586
- #: application/Backend/phtml/service/metabox.phtml:23
1587
- #: application/Backend/phtml/service/redirect.phtml:19
1588
- #: application/Backend/phtml/service/toolbar.phtml:16
1589
- #: application/Backend/phtml/service/uri.phtml:16
1590
- msgid "Settings are customized"
1591
- msgstr ""
1592
-
1593
- #: application/Backend/phtml/partial/post-access-form.phtml:6
1594
- #: application/Backend/phtml/service/login-redirect.phtml:18
1595
- #: application/Backend/phtml/service/logout-redirect.phtml:18
1596
- #: application/Backend/phtml/service/menu.phtml:18
1597
- #: application/Backend/phtml/service/metabox.phtml:24
1598
- #: application/Backend/phtml/service/redirect.phtml:20
1599
- #: application/Backend/phtml/service/route.phtml:19
1600
- #: application/Backend/phtml/service/toolbar.phtml:17
1601
- #: application/Backend/phtml/service/uri.phtml:17
1602
- msgid "Reset to default"
1603
- msgstr ""
1604
-
1605
- #: application/Backend/phtml/partial/post-access-form.phtml:23
1606
- msgid "change"
1607
- msgstr ""
1608
-
1609
- #: application/Backend/phtml/partial/post-access-form.phtml:54
1610
- msgid "Plain text or valid HTML"
1611
- msgstr ""
1612
-
1613
- #: application/Backend/phtml/partial/post-access-form.phtml:55
1614
- msgid "Enter your teaser message..."
1615
- msgstr ""
1616
-
1617
- #: application/Backend/phtml/partial/post-access-form.phtml:56
1618
  msgid ""
1619
  "Use [&#91;excerpt&#93;] shortcode to insert post excerpt to the teaser "
1620
  "message."
1621
  msgstr ""
1622
 
1623
- #: application/Backend/phtml/partial/post-access-form.phtml:72
1624
  msgid "Define Access Limit"
1625
  msgstr ""
1626
 
1627
- #: application/Backend/phtml/partial/post-access-form.phtml:76
1628
  msgid "Access Limit Threshold"
1629
  msgstr ""
1630
 
1631
- #: application/Backend/phtml/partial/post-access-form.phtml:77
1632
  msgid "Enter digital number"
1633
  msgstr ""
1634
 
1635
- #: application/Backend/phtml/partial/post-access-form.phtml:93
 
 
 
 
 
1636
  msgid "Access Redirect"
1637
  msgstr ""
1638
 
1639
- #: application/Backend/phtml/partial/post-access-form.phtml:96
1640
  msgid ""
1641
  "Use REDIRECT option only if you want to redirect user to a different "
1642
  "location either temporary or permanently. Do not use it as a way to protect "
1643
  "access to avoid inconsistent user experience."
1644
  msgstr ""
1645
 
1646
- #: application/Backend/phtml/partial/post-access-form.phtml:101
1647
- #: application/Backend/phtml/service/404redirect.phtml:26
1648
- #: application/Backend/phtml/service/login-redirect.phtml:33
1649
- #: application/Backend/phtml/service/logout-redirect.phtml:33
1650
- #: application/Backend/phtml/service/redirect.phtml:54
1651
- #: application/Backend/phtml/service/uri.phtml:57
1652
  msgid "Redirected to existing page [(select from the drop-down)]"
1653
  msgstr ""
1654
 
1655
- #: application/Backend/phtml/partial/post-access-form.phtml:105
1656
- #: application/Backend/phtml/service/logout-redirect.phtml:37
1657
  msgid "Redirected to the URL [(enter full URL starting from http or https)]"
1658
  msgstr ""
1659
 
1660
- #: application/Backend/phtml/partial/post-access-form.phtml:110
1661
- #: application/Backend/phtml/service/redirect.phtml:49
1662
- #: application/Backend/phtml/service/uri.phtml:52
1663
  msgid ""
1664
  "Redirect to the login page [(after login, user will be redirected back to "
1665
  "the restricted page)]"
1666
  msgstr ""
1667
 
1668
- #: application/Backend/phtml/partial/post-access-form.phtml:115
1669
- #: application/Backend/phtml/service/404redirect.phtml:34
1670
- #: application/Backend/phtml/service/login-redirect.phtml:41
1671
- #: application/Backend/phtml/service/logout-redirect.phtml:41
1672
- #: application/Backend/phtml/service/redirect.phtml:62
1673
- #: application/Backend/phtml/service/redirect.phtml:114
1674
- #: application/Backend/phtml/service/uri.phtml:65
1675
  #, php-format
1676
  msgid "Trigger PHP callback function [(valid %sPHP callback%s is required)]"
1677
  msgstr ""
1678
 
1679
- #: application/Backend/phtml/partial/post-access-form.phtml:119
1680
- #: application/Backend/phtml/service/404redirect.phtml:38
1681
- #: application/Backend/phtml/service/login-redirect.phtml:45
1682
- #: application/Backend/phtml/service/logout-redirect.phtml:45
1683
- #: application/Backend/phtml/service/redirect.phtml:71
1684
- #: application/Backend/phtml/service/redirect.phtml:123
1685
- #: application/Backend/phtml/service/uri.phtml:74
1686
  msgid "Existing Page"
1687
  msgstr ""
1688
 
1689
- #: application/Backend/phtml/partial/post-access-form.phtml:127
1690
- #: application/Backend/phtml/service/404redirect.phtml:47
1691
- #: application/Backend/phtml/service/login-redirect.phtml:54
1692
- #: application/Backend/phtml/service/logout-redirect.phtml:54
1693
- #: application/Backend/phtml/service/redirect.phtml:80
1694
- #: application/Backend/phtml/service/redirect.phtml:132
1695
- #: application/Backend/phtml/service/uri.phtml:81
1696
  msgid "-- Select Page --"
1697
  msgstr ""
1698
 
1699
- #: application/Backend/phtml/partial/post-access-form.phtml:133
1700
- #: application/Backend/phtml/service/404redirect.phtml:53
1701
- #: application/Backend/phtml/service/login-redirect.phtml:60
1702
- #: application/Backend/phtml/service/logout-redirect.phtml:60
1703
- #: application/Backend/phtml/service/redirect.phtml:86
1704
- #: application/Backend/phtml/service/redirect.phtml:138
1705
  msgid "The URL"
1706
  msgstr ""
1707
 
1708
- #: application/Backend/phtml/partial/post-access-form.phtml:138
1709
- #: application/Backend/phtml/service/404redirect.phtml:58
1710
- #: application/Backend/phtml/service/login-redirect.phtml:65
1711
- #: application/Backend/phtml/service/logout-redirect.phtml:65
1712
- #: application/Backend/phtml/service/redirect.phtml:91
1713
- #: application/Backend/phtml/service/redirect.phtml:143
1714
- #: application/Backend/phtml/service/uri.phtml:103
1715
  msgid "PHP Callback Function"
1716
  msgstr ""
1717
 
1718
- #: application/Backend/phtml/partial/post-access-form.phtml:139
1719
- #: application/Backend/phtml/service/redirect.phtml:92
1720
- #: application/Backend/phtml/service/redirect.phtml:144
1721
  msgid "Enter valid callback"
1722
  msgstr ""
1723
 
1724
- #: application/Backend/phtml/partial/post-access-form.phtml:143
1725
- #: application/Backend/phtml/service/uri.phtml:92
1726
  msgid "HTTP Redirect Code"
1727
  msgstr ""
1728
 
1729
- #: application/Backend/phtml/partial/post-access-form.phtml:171
1730
  msgid "Enter Password"
1731
  msgstr ""
1732
 
1733
- #: application/Backend/phtml/partial/post-access-form.phtml:187
1734
  msgid "Expiration Date/Time"
1735
  msgstr ""
1736
 
1737
- #: application/Backend/phtml/partial/posts-terms-help-tips.phtml:7
1738
  #, php-format
1739
  msgid ""
1740
  "You are allowed to manage access to unlimited number of posts, pages or "
@@ -1745,16 +1460,16 @@ msgid ""
1745
  "to manage access to the WordPress content%s."
1746
  msgstr ""
1747
 
1748
- #: application/Backend/phtml/partial/role-inheritance.phtml:5
1749
- msgid "Inherit Capabilities From"
1750
  msgstr ""
1751
 
1752
- #: application/Backend/phtml/partial/role-inheritance.phtml:13
1753
  msgid ""
1754
  "Also clone all AAM access settings (admin menu, metaboxes, redirects, etc.)"
1755
  msgstr ""
1756
 
1757
- #: application/Backend/phtml/partial/taxonomy-access-form.phtml:8
1758
  #, php-format
1759
  msgid ""
1760
  "Managing access to the taxonomy \"%s\" is available with the premium %s[Plus "
@@ -1763,7 +1478,7 @@ msgid ""
1763
  "Package add-on."
1764
  msgstr ""
1765
 
1766
- #: application/Backend/phtml/partial/term-access-form.phtml:8
1767
  #, php-format
1768
  msgid ""
1769
  "Managing access to the %s \"%s\" is available with the premium %s[Plus "
@@ -1772,42 +1487,58 @@ msgid ""
1772
  "add-on."
1773
  msgstr ""
1774
 
1775
- #: application/Backend/phtml/partial/term-access-form.phtml:9
1776
- #: application/Backend/phtml/partial/term-access-form.phtml:13
1777
  msgid "category"
1778
  msgstr ""
1779
 
1780
- #: application/Backend/phtml/partial/term-access-form.phtml:9
1781
- #: application/Backend/phtml/partial/term-access-form.phtml:13
1782
  msgid "tag"
1783
  msgstr ""
1784
 
1785
- #: application/Backend/phtml/partial/type-access-form.phtml:8
1786
  #, php-format
1787
  msgid ""
1788
  "Manage default access to all posts that belong to the post type %s. This "
1789
  "feature is available only with the premium %s[Plus Package]%s add-on."
1790
  msgstr ""
1791
 
1792
- #: application/Backend/phtml/service/404redirect.phtml:9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1793
  msgid "Setup [default] 404 redirect for all none-existing pages."
1794
  msgstr ""
1795
 
1796
- #: application/Backend/phtml/service/404redirect.phtml:22
1797
  msgid "Default WordPress 404 handler"
1798
  msgstr ""
1799
 
1800
- #: application/Backend/phtml/service/404redirect.phtml:30
1801
  msgid "Redirected to the URL [(enter valid URL starting from http or https)]"
1802
  msgstr ""
1803
 
1804
- #: application/Backend/phtml/service/404redirect.phtml:62
1805
  msgid ""
1806
  "You cannot setup 404 redirect for specific user, role or visitors. Switch to "
1807
  "[Manage Default Access] and define default 404 redirect for everybody."
1808
  msgstr ""
1809
 
1810
- #: application/Backend/phtml/service/capability.phtml:11
1811
  #, php-format
1812
  msgid ""
1813
  "[Be careful!] On this tab, you can manage capabilities for [%s]. Any changes "
@@ -1817,69 +1548,69 @@ msgid ""
1817
  "article."
1818
  msgstr ""
1819
 
1820
- #: application/Backend/phtml/service/capability.phtml:20
1821
  msgid "Filter"
1822
  msgstr ""
1823
 
1824
- #: application/Backend/phtml/service/capability.phtml:27
1825
  msgid "All Capabilities"
1826
  msgstr ""
1827
 
1828
- #: application/Backend/phtml/service/capability.phtml:37
1829
  msgid "Category"
1830
  msgstr ""
1831
 
1832
- #: application/Backend/phtml/service/capability.phtml:38
1833
- #: application/Backend/phtml/service/capability.phtml:54
1834
- #: application/Backend/phtml/service/capability.phtml:80
1835
- #: application/Backend/phtml/service/menu.phtml:155
1836
  msgid "Capability"
1837
  msgstr ""
1838
 
1839
- #: application/Backend/phtml/service/capability.phtml:39
1840
- #: application/Backend/phtml/service/jwt.phtml:24
1841
- #: application/Backend/phtml/service/policy.phtml:28
1842
- #: application/Backend/phtml/service/post.phtml:22
1843
- #: application/Backend/phtml/service/uri.phtml:147
1844
  msgid "Actions"
1845
  msgstr ""
1846
 
1847
- #: application/Backend/phtml/service/capability.phtml:50
1848
  msgid "Create Capability"
1849
  msgstr ""
1850
 
1851
- #: application/Backend/phtml/service/capability.phtml:55
1852
- #: application/Backend/phtml/service/capability.phtml:81
1853
  msgid "Enter Capability"
1854
  msgstr ""
1855
 
1856
- #: application/Backend/phtml/service/capability.phtml:59
1857
  msgid "Also assign this capability to me"
1858
  msgstr ""
1859
 
1860
- #: application/Backend/phtml/service/capability.phtml:85
1861
  msgid "Update this capability for me too"
1862
  msgstr ""
1863
 
1864
- #: application/Backend/phtml/service/capability.phtml:102
1865
  msgid "Delete Capability"
1866
  msgstr ""
1867
 
1868
- #: application/Backend/phtml/service/capability.phtml:105
1869
  msgid ""
1870
  "You are about to delete the %s capability. Any functionality that depends on "
1871
  "this capability will no longer be accessible by %n."
1872
  msgstr ""
1873
 
1874
- #: application/Backend/phtml/service/capability.phtml:108
1875
  msgid "Delete For %n Only"
1876
  msgstr ""
1877
 
1878
- #: application/Backend/phtml/service/capability.phtml:109
1879
  msgid "Delete For All Roles"
1880
  msgstr ""
1881
 
1882
- #: application/Backend/phtml/service/jwt.phtml:10
1883
  #, php-format
1884
  msgid ""
1885
  "Manage list of all valid JWT tokens to the website for [%s] account. For "
@@ -1887,74 +1618,74 @@ msgid ""
1887
  "WordPress JWT authentication%s article."
1888
  msgstr ""
1889
 
1890
- #: application/Backend/phtml/service/jwt.phtml:23
1891
  msgid "Expires"
1892
  msgstr ""
1893
 
1894
- #: application/Backend/phtml/service/jwt.phtml:37
1895
  msgid "Create JWT Token"
1896
  msgstr ""
1897
 
1898
- #: application/Backend/phtml/service/jwt.phtml:42
1899
  msgid "JWT Expires"
1900
  msgstr ""
1901
 
1902
- #: application/Backend/phtml/service/jwt.phtml:52
1903
  msgid "Is token refreshable?"
1904
  msgstr ""
1905
 
1906
- #: application/Backend/phtml/service/jwt.phtml:54
1907
  msgid ""
1908
  "Whether this token, before expires, can be used to obtain a new token for "
1909
  "the same time duration or not."
1910
  msgstr ""
1911
 
1912
- #: application/Backend/phtml/service/jwt.phtml:66
1913
- #: application/Backend/phtml/service/jwt.phtml:101
1914
- msgid "JWT Token (for any API calls)"
1915
  msgstr ""
1916
 
1917
- #: application/Backend/phtml/service/jwt.phtml:76
1918
  msgid "Passwordless Login URL"
1919
  msgstr ""
1920
 
1921
- #: application/Backend/phtml/service/jwt.phtml:80
1922
  msgid ""
1923
  "With this URL account will be automatically logged in as long as JWT token "
1924
  "is valid."
1925
  msgstr ""
1926
 
1927
- #: application/Backend/phtml/service/jwt.phtml:96
1928
  msgid "View JWT Token"
1929
  msgstr ""
1930
 
1931
- #: application/Backend/phtml/service/jwt.phtml:111
1932
  msgid "Passwordless Login URL (with JWT token)"
1933
  msgstr ""
1934
 
1935
- #: application/Backend/phtml/service/jwt.phtml:115
1936
  msgid ""
1937
  "Use this URL to authenticate account without the need to enter username/"
1938
  "password."
1939
  msgstr ""
1940
 
1941
- #: application/Backend/phtml/service/jwt.phtml:130
1942
  msgid "Delete JWT Token"
1943
  msgstr ""
1944
 
1945
- #: application/Backend/phtml/service/jwt.phtml:133
1946
  msgid ""
1947
  "You are about to delete already issued JWT token. Any application or user "
1948
  "that has this token, will no longer be able to use it. Please confirm."
1949
  msgstr ""
1950
 
1951
- #: application/Backend/phtml/service/login-redirect.phtml:9
1952
  msgid ""
1953
  "Define the [default] login redirect for all the users and roles when "
1954
  "authentication is completed successfully."
1955
  msgstr ""
1956
 
1957
- #: application/Backend/phtml/service/login-redirect.phtml:13
1958
  #, php-format
1959
  msgid ""
1960
  "Customize login redirect for [%s] when the authentication is completed "
@@ -1963,63 +1694,63 @@ msgid ""
1963
  "login solutions."
1964
  msgstr ""
1965
 
1966
- #: application/Backend/phtml/service/login-redirect.phtml:29
1967
- #: application/Backend/phtml/service/logout-redirect.phtml:29
1968
  msgid "WordPress default behavior"
1969
  msgstr ""
1970
 
1971
- #: application/Backend/phtml/service/login-redirect.phtml:37
1972
  msgid ""
1973
  "Redirected to the local URL [(enter full URL starting from http or https)]"
1974
  msgstr ""
1975
 
1976
- #: application/Backend/phtml/service/logout-redirect.phtml:9
1977
  msgid "Define the [default] logout redirect for all the users and roles."
1978
  msgstr ""
1979
 
1980
- #: application/Backend/phtml/service/logout-redirect.phtml:13
1981
  #, php-format
1982
  msgid "Customize logout redirect for [%s]."
1983
  msgstr ""
1984
 
1985
- #: application/Backend/phtml/service/menu.phtml:9
1986
  #, php-format
1987
  msgid ""
1988
  "Manage access to the backend main menu for [%s]. For more information check "
1989
  "%sHow to manage WordPress backend menu%s."
1990
  msgstr ""
1991
 
1992
- #: application/Backend/phtml/service/menu.phtml:56
1993
  msgid "Menu URI:"
1994
  msgstr ""
1995
 
1996
- #: application/Backend/phtml/service/menu.phtml:76
1997
- #: application/Backend/phtml/service/metabox.phtml:74
1998
- #: application/Backend/phtml/service/toolbar.phtml:57
1999
  msgid "more details"
2000
  msgstr ""
2001
 
2002
- #: application/Backend/phtml/service/menu.phtml:103
2003
  msgid ""
2004
  "Dashboard menu cannot be restricted because it is the default page all users "
2005
  "are redirected after login. You can restrict only Dashboard submenus if any."
2006
  msgstr ""
2007
 
2008
- #: application/Backend/phtml/service/menu.phtml:113
2009
  msgid ""
2010
  "Current user does not have enough capabilities to access any available "
2011
  "backend menu."
2012
  msgstr ""
2013
 
2014
- #: application/Backend/phtml/service/menu.phtml:125
2015
  msgid "Dashboard Lockdown"
2016
  msgstr ""
2017
 
2018
- #: application/Backend/phtml/service/menu.phtml:129
2019
  msgid "You cannot restrict access to the Dashboard Home page."
2020
  msgstr ""
2021
 
2022
- #: application/Backend/phtml/service/menu.phtml:130
2023
  #, php-format
2024
  msgid ""
2025
  "The [Dashboard Home] is the default page that every user is redirected to "
@@ -2027,58 +1758,63 @@ msgid ""
2027
  "lockdown WordPress backend%s article."
2028
  msgstr ""
2029
 
2030
- #: application/Backend/phtml/service/menu.phtml:134
2031
  msgid "OK"
2032
  msgstr ""
2033
 
2034
- #: application/Backend/phtml/service/menu.phtml:145
2035
  msgid "Menu Details"
2036
  msgstr ""
2037
 
2038
- #: application/Backend/phtml/service/menu.phtml:151
2039
- #: application/Backend/phtml/service/toolbar.phtml:93
2040
  msgid "Name"
2041
  msgstr ""
2042
 
2043
- #: application/Backend/phtml/service/menu.phtml:159
2044
- #: application/Backend/phtml/service/toolbar.phtml:97
2045
- #: application/Backend/phtml/service/uri.phtml:143
2046
  msgid "URI"
2047
  msgstr ""
2048
 
2049
- #: application/Backend/phtml/service/metabox.phtml:9
2050
- #, php-format
 
 
 
 
 
2051
  msgid ""
2052
  "Manage classic (not Gutenberg) metaboxes and widgets visibility for [%s]. "
2053
  "For more information please check %sHow to hide WordPress metaboxes and "
2054
  "widgets%s."
2055
  msgstr ""
2056
 
2057
- #: application/Backend/phtml/service/metabox.phtml:16
2058
  msgid "Refresh"
2059
  msgstr ""
2060
 
2061
- #: application/Backend/phtml/service/metabox.phtml:17
2062
  msgid "Init URL"
2063
  msgstr ""
2064
 
2065
- #: application/Backend/phtml/service/metabox.phtml:49
2066
  msgid "Dashboard Widgets"
2067
  msgstr ""
2068
 
2069
- #: application/Backend/phtml/service/metabox.phtml:53
2070
  msgid "Frontend Widgets [(including Appearance->Widgets)]"
2071
  msgstr ""
2072
 
2073
- #: application/Backend/phtml/service/metabox.phtml:91
2074
  msgid "The list is not initialized. Click Refresh button above."
2075
  msgstr ""
2076
 
2077
- #: application/Backend/phtml/service/metabox.phtml:102
2078
  msgid "Initialize URL"
2079
  msgstr ""
2080
 
2081
- #: application/Backend/phtml/service/metabox.phtml:106
2082
  msgid ""
2083
  "Some metaboxes are \"conditional\" and appear on the edit screen when "
2084
  "certain conditions are met. For example metabox \"Comments\" appears only "
@@ -2087,69 +1823,74 @@ msgid ""
2087
  "appears."
2088
  msgstr ""
2089
 
2090
- #: application/Backend/phtml/service/metabox.phtml:109
2091
  msgid "Backend page URL"
2092
  msgstr ""
2093
 
2094
- #: application/Backend/phtml/service/metabox.phtml:110
2095
  msgid "Insert valid URL"
2096
  msgstr ""
2097
 
2098
- #: application/Backend/phtml/service/metabox.phtml:126
2099
  msgid "Metabox/Widget Details"
2100
  msgstr ""
2101
 
2102
- #: application/Backend/phtml/service/metabox.phtml:132
2103
- #: application/Backend/phtml/service/post.phtml:21
2104
  msgid "Title"
2105
  msgstr ""
2106
 
2107
- #: application/Backend/phtml/service/metabox.phtml:136
2108
  msgid "Screen ID"
2109
  msgstr ""
2110
 
2111
- #: application/Backend/phtml/service/policy.phtml:7
 
 
 
 
2112
  #, php-format
2113
  msgid ""
2114
  "Manage access and security policies for [%s]. For more information check "
2115
  "%sAccess &amp; Security Policy%s page."
2116
  msgstr ""
2117
 
2118
- #: application/Backend/phtml/service/policy.phtml:15
2119
  msgid "Policies are customized"
2120
  msgstr ""
2121
 
2122
- #: application/Backend/phtml/service/policy.phtml:16
2123
  msgid "Reset To Default"
2124
  msgstr ""
2125
 
2126
- #: application/Backend/phtml/service/policy.phtml:27
 
2127
  msgid "Policy"
2128
  msgstr ""
2129
 
2130
- #: application/Backend/phtml/service/policy.phtml:40
2131
  #, php-format
2132
  msgid ""
2133
  "%s[AAM Plus Package]%s extension is required in order to apply Access &amp; "
2134
  "Security Policies to everybody all together."
2135
  msgstr ""
2136
 
2137
- #: application/Backend/phtml/service/post.phtml:11
2138
  msgid "Root"
2139
  msgstr ""
2140
 
2141
- #: application/Backend/phtml/service/post.phtml:31
2142
- #: application/Backend/phtml/service/post.phtml:34
2143
  msgid "Go Back"
2144
  msgstr ""
2145
 
2146
- #: application/Backend/phtml/service/redirect.phtml:11
2147
  msgid ""
2148
  "Define the [default] redirect for all users, roles and visitors when access "
2149
  "is denied to any restricted resources on your website."
2150
  msgstr ""
2151
 
2152
- #: application/Backend/phtml/service/redirect.phtml:15
2153
  #, php-format
2154
  msgid ""
2155
  "Customize redirect for %s when access is denied to restricted resources like "
@@ -2157,48 +1898,48 @@ msgid ""
2157
  "please check %sHow to redirect WordPress user when access is denied%s."
2158
  msgstr ""
2159
 
2160
- #: application/Backend/phtml/service/redirect.phtml:29
2161
  msgid "Frontend Redirect"
2162
  msgstr ""
2163
 
2164
- #: application/Backend/phtml/service/redirect.phtml:30
2165
  msgid "Backend Redirect"
2166
  msgstr ""
2167
 
2168
- #: application/Backend/phtml/service/redirect.phtml:40
2169
- #: application/Backend/phtml/service/redirect.phtml:98
2170
  msgid "Default [(\"Access Denied\" message)]"
2171
  msgstr ""
2172
 
2173
- #: application/Backend/phtml/service/redirect.phtml:44
2174
- #: application/Backend/phtml/service/redirect.phtml:102
2175
- #: application/Backend/phtml/service/uri.phtml:47
2176
  msgid "Show customized message [(plain text or HTML)]"
2177
  msgstr ""
2178
 
2179
- #: application/Backend/phtml/service/redirect.phtml:58
2180
- #: application/Backend/phtml/service/redirect.phtml:110
2181
- #: application/Backend/phtml/service/uri.phtml:61
2182
  msgid "Redirected to local URL [(enter valid URL starting from http or https)]"
2183
  msgstr ""
2184
 
2185
- #: application/Backend/phtml/service/redirect.phtml:66
2186
- #: application/Backend/phtml/service/redirect.phtml:118
2187
- #: application/Backend/phtml/service/uri.phtml:69
2188
  msgid "Customized Message"
2189
  msgstr ""
2190
 
2191
- #: application/Backend/phtml/service/redirect.phtml:67
2192
- #: application/Backend/phtml/service/redirect.phtml:119
2193
- #: application/Backend/phtml/service/uri.phtml:70
2194
  msgid "Enter message..."
2195
  msgstr ""
2196
 
2197
- #: application/Backend/phtml/service/redirect.phtml:106
2198
  msgid "Redirected to existing frontend page [(select from the drop-down)]"
2199
  msgstr ""
2200
 
2201
- #: application/Backend/phtml/service/route.phtml:10
2202
  #, php-format
2203
  msgid ""
2204
  "Manage access to the website API routes for [%s]. For the full RESTful API "
@@ -2206,23 +1947,23 @@ msgid ""
2206
  "in AAM."
2207
  msgstr ""
2208
 
2209
- #: application/Backend/phtml/service/route.phtml:18
2210
  msgid "Routes are customized"
2211
  msgstr ""
2212
 
2213
- #: application/Backend/phtml/service/route.phtml:29
2214
  msgid "Method"
2215
  msgstr ""
2216
 
2217
- #: application/Backend/phtml/service/route.phtml:30
2218
  msgid "Route"
2219
  msgstr ""
2220
 
2221
- #: application/Backend/phtml/service/route.phtml:31
2222
  msgid "Deny"
2223
  msgstr ""
2224
 
2225
- #: application/Backend/phtml/service/toolbar.phtml:8
2226
  msgid ""
2227
  "[Note!] Admin Toolbar service is not intended to restrict direct access to "
2228
  "linked pages. It used only to remove unnecessary items from the top admin "
@@ -2230,19 +1971,19 @@ msgid ""
2230
  "or utilize the great power of capabilities."
2231
  msgstr ""
2232
 
2233
- #: application/Backend/phtml/service/toolbar.phtml:46
2234
  msgid "Item ID:"
2235
  msgstr ""
2236
 
2237
- #: application/Backend/phtml/service/toolbar.phtml:87
2238
  msgid "Item Details"
2239
  msgstr ""
2240
 
2241
- #: application/Backend/phtml/service/toolbar.phtml:118
2242
  msgid "The list of top admin bar items is not initialized. Reload the page."
2243
  msgstr ""
2244
 
2245
- #: application/Backend/phtml/service/uri.phtml:8
2246
  #, php-format
2247
  msgid ""
2248
  "Manage access to the website URL(s) for the [%s]. Note! All entered URLs "
@@ -2251,55 +1992,55 @@ msgid ""
2251
  "website URL%s."
2252
  msgstr ""
2253
 
2254
- #: application/Backend/phtml/service/uri.phtml:27
2255
  msgid "URI Access Rule"
2256
  msgstr ""
2257
 
2258
- #: application/Backend/phtml/service/uri.phtml:31
2259
  msgid "Enter URL [(wildcard * is available with Plus Package extension)]"
2260
  msgstr ""
2261
 
2262
- #: application/Backend/phtml/service/uri.phtml:35
2263
  msgid "How to redirect user when match?"
2264
  msgstr ""
2265
 
2266
- #: application/Backend/phtml/service/uri.phtml:39
2267
  msgid "Allow Access"
2268
  msgstr ""
2269
 
2270
- #: application/Backend/phtml/service/uri.phtml:43
2271
  msgid "Deny Access [(show \"Access Denied\" message)]"
2272
  msgstr ""
2273
 
2274
- #: application/Backend/phtml/service/uri.phtml:87
2275
  msgid "The Valid Redirect URL"
2276
  msgstr ""
2277
 
2278
- #: application/Backend/phtml/service/uri.phtml:94
2279
  msgid "HTTP Code (Default 307)"
2280
  msgstr ""
2281
 
2282
- #: application/Backend/phtml/service/uri.phtml:96
2283
  msgid "302 - Found"
2284
  msgstr ""
2285
 
2286
- #: application/Backend/phtml/service/uri.phtml:98
2287
  msgid "307 - Temporary Redirect"
2288
  msgstr ""
2289
 
2290
- #: application/Backend/phtml/service/uri.phtml:120
2291
  msgid "Delete URI Rule"
2292
  msgstr ""
2293
 
2294
- #: application/Backend/phtml/service/uri.phtml:125
2295
  msgid "You are about to delete the URI Rule. Please confirm!"
2296
  msgstr ""
2297
 
2298
- #: application/Backend/phtml/service/uri.phtml:144
2299
  msgid "Type"
2300
  msgstr ""
2301
 
2302
- #: application/Backend/phtml/service/welcome.phtml:11
2303
  msgid ""
2304
  "Thank you for using the Advanced Access Manager (aka AAM) plugin. With "
2305
  "strong knowledge and experience in WordPress core, AAM becomes a very "
@@ -2307,11 +2048,11 @@ msgid ""
2307
  "backend, and RESTful API."
2308
  msgstr ""
2309
 
2310
- #: application/Backend/phtml/service/welcome.phtml:12
2311
  msgid "Note!"
2312
  msgstr ""
2313
 
2314
- #: application/Backend/phtml/service/welcome.phtml:12
2315
  #, php-format
2316
  msgid ""
2317
  "Power comes with responsibility. Make sure you have a good understanding of "
@@ -2322,7 +2063,7 @@ msgid ""
2322
  "server and never did."
2323
  msgstr ""
2324
 
2325
- #: application/Backend/phtml/service/welcome.phtml:13
2326
  msgid ""
2327
  "AAM is thoroughly tested on the fresh installation of the latest WordPress "
2328
  "and in the latest versions of Chrome, Safari, IE, and Firefox. If you have "
@@ -2330,7 +2071,7 @@ msgid ""
2330
  "themes."
2331
  msgstr ""
2332
 
2333
- #: application/Backend/phtml/service/welcome.phtml:14
2334
  #, php-format
2335
  msgid ""
2336
  "If you are not sure where to start, please check our %s\"Get Started\"%s "
@@ -2338,68 +2079,406 @@ msgid ""
2338
  "your WordPress website more effectively."
2339
  msgstr ""
2340
 
2341
- #: application/Backend/phtml/service/welcome.phtml:16
2342
  msgid "Go To The \"Get Started\" Page"
2343
  msgstr ""
2344
 
2345
- #: application/Backend/phtml/settings/configpress.phtml:8
2346
  #, php-format
2347
  msgid ""
2348
  "Fore more information about AAM configurations check %sAAM Configurations%s "
2349
  "article."
2350
  msgstr ""
2351
 
2352
- #: application/Backend/phtml/settings/service.phtml:10
 
 
 
 
2353
  msgid "Service Name/Description"
2354
  msgstr ""
2355
 
2356
- #: application/Backend/phtml/settings/service.phtml:11
2357
  msgid "Status"
2358
  msgstr ""
2359
 
2360
- #: application/Backend/phtml/widget/login-backend.phtml:5
2361
  msgid "Login Title"
2362
  msgstr ""
2363
 
2364
- #: application/Backend/phtml/widget/login-backend.phtml:10
2365
  msgid "Logged In Title"
2366
  msgstr ""
2367
 
2368
- #: application/Backend/phtml/widget/login-backend.phtml:15
2369
  #, php-format
2370
  msgid ""
2371
  "For more advanced setup like login/logout redirects, security enhancement or "
2372
  "custom styling, please refer to %sHow does AAM Secure Login works%s article."
2373
  msgstr ""
2374
 
2375
- #: application/Backend/phtml/widget/login-frontend.phtml:23
2376
  msgid "Username or Email Address"
2377
  msgstr ""
2378
 
2379
- #: application/Backend/phtml/widget/login-frontend.phtml:38
2380
  msgid "Remember Me"
2381
  msgstr ""
2382
 
2383
- #: application/Backend/phtml/widget/login-frontend.phtml:43
2384
  msgid "Log In"
2385
  msgstr ""
2386
 
2387
- #: application/Backend/phtml/widget/login-frontend.phtml:51
2388
  msgid "Register"
2389
  msgstr ""
2390
 
2391
- #: application/Backend/phtml/widget/login-frontend.phtml:56
2392
  msgid "Lost your password?"
2393
  msgstr ""
2394
 
2395
- #: application/Backend/phtml/widget/login-frontend.phtml:102
2396
  msgid "Dashboard"
2397
  msgstr ""
2398
 
2399
- #: application/Backend/phtml/widget/login-frontend.phtml:103
2400
  msgid "Edit My Profile"
2401
  msgstr ""
2402
 
2403
- #: application/Backend/phtml/widget/login-frontend.phtml:105
2404
  msgid "Log Out"
2405
  msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  msgid ""
2
  msgstr ""
3
  "Project-Id-Version: Advanced Access Manager\n"
4
+ "POT-Creation-Date: 2019-10-24 20:16-0400\n"
5
  "PO-Revision-Date: \n"
6
  "Last-Translator: \n"
7
  "Language-Team: AAMPlugin <support@aamplugin.com>\n"
24
  msgid "WP 4.7.0 or higher is required."
25
  msgstr ""
26
 
27
+ #: application/Addon/Repository.php:108
28
+ msgid ""
29
+ "Manage access to your WordPress website posts, pages, media, custom post "
30
+ "types, categories, tags and custom taxonomies for any role, individual user, "
31
+ "visitors or even define default access for everybody; and do this separately "
32
+ "for frontend, backend or API levels."
33
+ msgstr ""
34
+
35
+ #: application/Addon/Repository.php:113
36
+ msgid ""
37
+ "Manage access to your WordPress website by users IP address or referred host "
38
+ "and completely lock down the entire website if necessary. Define the "
39
+ "unlimited number of whitelisted or blacklisted IPs or hosts."
40
+ msgstr ""
41
+
42
+ #: application/Addon/Repository.php:118
43
+ msgid ""
44
+ "Define and manage complex WordPress role hierarchy where all the access "
45
+ "settings are propagated down the tree with the ability to override any "
46
+ "settings for any specific role."
47
+ msgstr ""
48
+
49
+ #: application/Addon/Repository.php:133
50
+ msgid ""
51
+ "Get the complete list of all premium AAM addons in one package and all "
52
+ "future premium addons will be included for now additional cost."
53
+ msgstr ""
54
+
55
  #: application/Backend/Feature/Main/404Redirect.php:71
56
  #: application/Service/NotFoundRedirect.php:52
57
  msgid "404 Redirect"
65
  msgid "Permission denied to delete this capability"
66
  msgstr ""
67
 
68
+ #: application/Backend/Feature/Main/Capability.php:335
69
+ #: application/Backend/Feature/Main/Capability.php:356
70
  msgid "System"
71
  msgstr ""
72
 
73
+ #: application/Backend/Feature/Main/Capability.php:336
74
+ #: application/Backend/Feature/Main/Capability.php:358
75
  msgid "Posts & Pages"
76
  msgstr ""
77
 
78
+ #: application/Backend/Feature/Main/Capability.php:337
79
+ #: application/Backend/Feature/Main/Capability.php:360
80
  msgid "Backend"
81
  msgstr ""
82
 
83
+ #: application/Backend/Feature/Main/Capability.php:338
84
+ #: application/Backend/Feature/Main/Capability.php:362
85
  msgid "AAM Interface"
86
  msgstr ""
87
 
88
+ #: application/Backend/Feature/Main/Capability.php:339
89
+ #: application/Backend/Feature/Main/Capability.php:364
90
  msgid "Miscellaneous"
91
  msgstr ""
92
 
93
+ #: application/Backend/Feature/Main/Capability.php:383
94
  #: application/Service/Capability.php:52
95
  msgid "Capabilities"
96
  msgstr ""
121
  msgid "Backend Menu"
122
  msgstr ""
123
 
124
+ #: application/Backend/Feature/Main/Metabox.php:263
125
  #: application/Service/Metabox.php:42
126
  msgid "Metaboxes & Widgets"
127
  msgstr ""
128
 
129
+ #: application/Backend/Feature/Main/Policy.php:214
 
 
 
 
130
  msgid "(no title)"
131
  msgstr ""
132
 
133
+ #: application/Backend/Feature/Main/Policy.php:307
134
+ #: application/Service/AccessPolicy.php:66
135
+ #: application/Service/AccessPolicy.php:169
136
  msgid "Access Policies"
137
  msgstr ""
138
 
141
  msgstr ""
142
 
143
  #: application/Backend/Feature/Main/Post.php:215
144
+ #: application/Backend/tmpl/service/uri.php:95
145
  msgid "301 - Moved Permanently"
146
  msgstr ""
147
 
148
  #: application/Backend/Feature/Main/Post.php:216
149
+ #: application/Backend/tmpl/service/uri.php:97
150
  msgid "303 - See Other"
151
  msgstr ""
152
 
155
  msgid "%d times"
156
  msgstr ""
157
 
158
+ #: application/Backend/Feature/Main/Post.php:333
159
  #, php-format
160
  msgid "\"%s\" page"
161
  msgstr ""
162
 
163
+ #: application/Backend/Feature/Main/Post.php:339
164
  #, php-format
165
  msgid "%s URL"
166
  msgstr ""
167
 
168
+ #: application/Backend/Feature/Main/Post.php:343
169
  msgid "Login page"
170
  msgstr ""
171
 
172
+ #: application/Backend/Feature/Main/Post.php:1000
173
+ #: application/Service/Content.php:72
174
  msgid "Posts & Terms"
175
  msgstr ""
176
 
189
  msgid "Admin Toolbar"
190
  msgstr ""
191
 
192
+ #: application/Backend/Feature/Main/Uri.php:135 application/Service/Uri.php:53
193
  msgid "URI Access"
194
  msgstr ""
195
 
274
  msgid "Services"
275
  msgstr ""
276
 
277
+ #: application/Backend/Feature/Subject/Role.php:118
278
+ #: application/Backend/Feature/Subject/Role.php:187
279
+ #: application/Backend/Feature/Subject/Role.php:271
280
+ #: application/Backend/Feature/Subject/Role.php:297
281
  #: application/Backend/Feature/Subject/User.php:80
282
  msgid "Unauthorized operation"
283
  msgstr ""
284
 
285
+ #: application/Backend/Feature/Subject/Role.php:294
286
  msgid "Failed to delete the role"
287
  msgstr ""
288
 
290
  msgid "Cannot manage yourself"
291
  msgstr ""
292
 
293
+ #: application/Backend/Feature/Subject/User.php:210
294
+ #: application/Backend/Feature/Subject/User.php:235
295
+ #: application/Backend/View/Localization.php:141 media/js/aam.js:4694
296
  msgid "Unexpected application error"
297
  msgstr ""
298
 
304
  msgid "You are not allowed to manage AAM subjects"
305
  msgstr ""
306
 
307
+ #: application/Backend/View/Localization.php:32 media/js/aam.js:1992
 
 
 
 
 
308
  msgid "Search Capability"
309
  msgstr ""
310
 
311
+ #: application/Backend/View/Localization.php:33 media/js/aam.js:1993
312
  msgid "_TOTAL_ capability(s)"
313
  msgstr ""
314
 
315
+ #: application/Backend/View/Localization.php:34 media/js/aam.js:410
316
+ #: media/js/aam.js:471 media/js/aam.js:1029 media/js/aam.js:2140
317
+ #: media/js/aam.js:2182 media/js/aam.js:2381 media/js/aam.js:2400
318
+ #: media/js/aam.js:2470 media/js/aam.js:2492 media/js/aam.js:2511
319
+ #: media/js/aam.js:3477
320
  msgid "Saving..."
321
  msgstr ""
322
 
323
+ #: application/Backend/View/Localization.php:35 media/js/aam.js:2148
324
  msgid "Failed to add new capability"
325
  msgstr ""
326
 
328
  msgid "Application error"
329
  msgstr ""
330
 
331
+ #: application/Backend/View/Localization.php:37 media/js/aam.js:2156
332
  msgid "Add Capability"
333
  msgstr ""
334
 
335
+ #: application/Backend/View/Localization.php:38
336
+ #: application/Backend/tmpl/service/capability.php:76 media/js/aam.js:2198
337
  msgid "Update Capability"
338
  msgstr ""
339
 
340
+ #: application/Backend/View/Localization.php:39
341
+ #: application/Backend/tmpl/service/menu.php:93
342
+ #: application/Backend/tmpl/service/toolbar.php:70 media/js/aam.js:1476
343
+ #: media/js/aam.js:1607
344
  msgid "Show Menu"
345
  msgstr ""
346
 
347
+ #: application/Backend/View/Localization.php:40
348
+ #: application/Backend/tmpl/service/menu.php:97
349
+ #: application/Backend/tmpl/service/toolbar.php:74 media/js/aam.js:1486
350
+ #: media/js/aam.js:1617
351
  msgid "Restrict Menu"
352
  msgstr ""
353
 
354
+ #: application/Backend/View/Localization.php:41 media/js/aam.js:1787
355
  msgid "Failed to retrieve mataboxes"
356
  msgstr ""
357
 
358
+ #: application/Backend/View/Localization.php:42 media/js/aam.js:2651
359
+ #: media/js/aam.js:3364 media/js/aam.js:3556 media/js/aam.js:3779
360
  msgid "Search"
361
  msgstr ""
362
 
363
+ #: application/Backend/View/Localization.php:43 media/js/aam.js:2652
364
  msgid "_TOTAL_ object(s)"
365
  msgstr ""
366
 
368
  msgid "Failed"
369
  msgstr ""
370
 
371
+ #: application/Backend/View/Localization.php:45 media/js/aam.js:64
372
+ #: media/js/aam.js:4336
373
  msgid "Loading..."
374
  msgstr ""
375
 
376
+ #: application/Backend/View/Localization.php:46 media/js/aam.js:69
377
  msgid "No role"
378
  msgstr ""
379
 
380
+ #: application/Backend/View/Localization.php:47 media/js/aam.js:149
381
  msgid "Create New Role"
382
  msgstr ""
383
 
385
  msgid "Search Role"
386
  msgstr ""
387
 
388
+ #: application/Backend/View/Localization.php:49 media/js/aam.js:133
389
  msgid "_TOTAL_ role(s)"
390
  msgstr ""
391
 
392
+ #: application/Backend/View/Localization.php:50
393
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:19
394
+ #: application/Backend/tmpl/service/capability.php:30
395
+ #: application/Backend/tmpl/service/capability.php:64
396
+ #: application/Backend/tmpl/service/jwt.php:84 media/js/aam.js:1319
397
+ #: media/js/aam.js:3567 media/js/aam.js:3794 media/js/aam.js:3879
398
  msgid "Create"
399
  msgstr ""
400
 
401
+ #: application/Backend/View/Localization.php:51
402
+ #: application/Backend/tmpl/page/subject-panel.php:17 media/js/aam.js:171
403
  msgid "Users"
404
  msgstr ""
405
 
411
  msgid "Add Role"
412
  msgstr ""
413
 
414
+ #: application/Backend/View/Localization.php:54 media/js/aam.js:478
415
  msgid "Failed to update role"
416
  msgstr ""
417
 
418
+ #: application/Backend/View/Localization.php:55
419
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:41
420
+ #: application/Backend/tmpl/service/capability.php:90 media/js/aam.js:487
421
  msgid "Update"
422
  msgstr ""
423
 
424
+ #: application/Backend/View/Localization.php:56
425
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:110
426
+ #: application/Backend/tmpl/partial/post-access-form.php:89
427
+ #: media/js/aam.js:1076 media/js/aam.js:2454
428
  msgid "Reset"
429
  msgstr ""
430
 
432
  msgid "Update..."
433
  msgstr ""
434
 
435
+ #: application/Backend/View/Localization.php:58 media/js/aam.js:510
436
+ #: media/js/aam.js:1941 media/js/aam.js:3515 media/js/aam.js:3897
437
  msgid "Deleting..."
438
  msgstr ""
439
 
440
+ #: application/Backend/View/Localization.php:59 media/js/aam.js:516
441
  msgid "Failed to delete role"
442
  msgstr ""
443
 
444
  #: application/Backend/View/Localization.php:60
445
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:53
446
  msgid "Delete Role"
447
  msgstr ""
448
 
449
+ #: application/Backend/View/Localization.php:61 media/js/aam.js:610
450
  msgid "Failed to lock user"
451
  msgstr ""
452
 
453
+ #: application/Backend/View/Localization.php:62 media/js/aam.js:701
454
  msgid "Search user"
455
  msgstr ""
456
 
457
+ #: application/Backend/View/Localization.php:63 media/js/aam.js:2443
458
+ msgid "Counter was reset successfully"
459
+ msgstr ""
460
+
461
+ #: application/Backend/View/Localization.php:64 media/js/aam.js:702
462
  msgid "_TOTAL_ user(s)"
463
  msgstr ""
464
 
465
+ #: application/Backend/View/Localization.php:65 media/js/aam.js:717
466
  msgid "Create New User"
467
  msgstr ""
468
 
469
+ #: application/Backend/View/Localization.php:66
470
+ #: application/Backend/tmpl/page/subject-panel.php:38 media/js/aam.js:765
471
  msgid "Role"
472
  msgstr ""
473
 
474
+ #: application/Backend/View/Localization.php:67
475
+ #: application/Core/Subject/Default.php:36 media/js/aam.js:1189
476
  msgid "All Users, Roles and Visitor"
477
  msgstr ""
478
 
479
+ #: application/Backend/View/Localization.php:68 media/js/aam.js:1160
480
+ #: media/js/aam.js:1235 media/js/aam.js:4305
481
  msgid "Failed to apply policy changes"
482
  msgstr ""
483
 
484
+ #: application/Backend/View/Localization.php:69
485
+ #: application/Backend/tmpl/partial/visitor-principal-subject-tab.php:14
486
+ #: media/js/aam.js:1154 media/js/aam.js:1163
487
  msgid "Attach Policy To Visitors"
488
  msgstr ""
489
 
490
+ #: application/Backend/View/Localization.php:70
491
+ #: application/Backend/tmpl/partial/visitor-principal-subject-tab.php:12
492
+ #: media/js/aam.js:1152 media/js/aam.js:1165
493
  msgid "Detach Policy From Visitors"
494
  msgstr ""
495
 
496
+ #: application/Backend/View/Localization.php:71 media/js/aam.js:648
497
+ #: media/js/aam.js:3686
 
498
  msgid "Generating URL..."
499
  msgstr ""
500
 
501
+ #: application/Backend/View/Localization.php:72
502
+ #: application/Core/Subject/Visitor.php:43 media/js/aam.js:1115
503
  msgid "Anonymous"
504
  msgstr ""
505
 
506
+ #: application/Backend/View/Localization.php:73 media/js/aam.js:1141
507
+ #: media/js/aam.js:1216 media/js/aam.js:1807 media/js/aam.js:4151
508
  msgid "Processing..."
509
  msgstr ""
510
 
511
+ #: application/Backend/View/Localization.php:74 media/js/aam.js:726
512
  msgid "Loading roles..."
513
  msgstr ""
514
 
515
+ #: application/Backend/View/Localization.php:75 media/js/aam.js:658
516
+ #: media/js/aam.js:3696
 
517
  msgid "Failed to generate JWT token"
518
  msgstr ""
519
 
520
+ #: application/Backend/View/Localization.php:76 media/js/aam.js:1909
521
+ msgid "Failed to process request"
522
+ msgstr ""
523
+
524
+ #: application/Backend/View/Localization.php:77
525
  msgid "Current user"
526
  msgstr ""
527
 
528
+ #: application/Backend/View/Localization.php:78
529
  msgid "Current role"
530
  msgstr ""
531
 
532
+ #: application/Backend/View/Localization.php:79 media/js/aam.js:2841
533
  msgid "Manage Access"
534
  msgstr ""
535
 
536
+ #: application/Backend/View/Localization.php:80 media/js/aam.js:743
537
  msgid "Filter by role"
538
  msgstr ""
539
 
540
+ #: application/Backend/View/Localization.php:81
541
+ #: application/Backend/View/PostOptionList.php:76 media/js/aam.js:2853
542
  msgid "Edit"
543
  msgstr ""
544
 
545
+ #: application/Backend/View/Localization.php:82
546
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:111
547
+ #: application/Backend/tmpl/partial/post-access-form.php:60
548
+ #: application/Backend/tmpl/partial/post-access-form.php:90
549
+ #: application/Backend/tmpl/partial/post-access-form.php:163
550
+ #: application/Backend/tmpl/partial/post-access-form.php:184
551
+ #: application/Backend/tmpl/partial/post-access-form.php:205
552
+ #: application/Backend/tmpl/service/uri.php:108 media/js/aam.js:1043
553
+ #: media/js/aam.js:3494
554
  msgid "Save"
555
  msgstr ""
556
 
557
+ #: application/Backend/View/Localization.php:83 media/js/aam.js:217
558
  msgid "Manage role"
559
  msgstr ""
560
 
561
+ #: application/Backend/View/Localization.php:84 media/js/aam.js:238
562
  msgid "Edit role"
563
  msgstr ""
564
 
565
+ #: application/Backend/View/Localization.php:85 media/js/aam.js:291
566
+ #: media/js/aam.js:524
567
  msgid "Delete role"
568
  msgstr ""
569
 
570
+ #: application/Backend/View/Localization.php:86 media/js/aam.js:262
571
  msgid "Clone role"
572
  msgstr ""
573
 
574
+ #: application/Backend/View/Localization.php:87 media/js/aam.js:804
575
  msgid "Manage user"
576
  msgstr ""
577
 
578
+ #: application/Backend/View/Localization.php:88 media/js/aam.js:853
579
  msgid "Edit user"
580
  msgstr ""
581
 
582
+ #: application/Backend/View/Localization.php:89 media/js/aam.js:605
583
+ #: media/js/aam.js:606 media/js/aam.js:874 media/js/aam.js:885
584
  msgid "Lock user"
585
  msgstr ""
586
 
587
+ #: application/Backend/View/Localization.php:90 media/js/aam.js:599
588
+ #: media/js/aam.js:600 media/js/aam.js:898 media/js/aam.js:909
589
  msgid "Unlock user"
590
  msgstr ""
591
 
592
+ #: application/Backend/View/Localization.php:91 media/js/aam.js:1903
593
  msgid "WordPress core does not allow to grant this capability"
594
  msgstr ""
595
 
596
+ #: application/Backend/View/Localization.php:92 media/js/aam.js:1227
597
+ #: media/js/aam.js:1240
598
  msgid "Detach Policy From Everybody"
599
  msgstr ""
600
 
601
+ #: application/Backend/View/Localization.php:93 media/js/aam.js:1229
602
+ #: media/js/aam.js:1238
603
  msgid "Attach Policy To Everybody"
604
  msgstr ""
605
 
606
+ #: application/Backend/View/Localization.php:94 media/js/aam.js:1308
607
  msgid "Search Policy"
608
  msgstr ""
609
 
610
+ #: application/Backend/View/Localization.php:95 media/js/aam.js:1309
611
  msgid "_TOTAL_ Policies"
612
  msgstr ""
613
 
614
+ #: application/Backend/View/Localization.php:96 media/js/aam.js:1342
615
  msgid "Apply Policy"
616
  msgstr ""
617
 
618
+ #: application/Backend/View/Localization.php:97 media/js/aam.js:1362
619
  msgid "Revoke Policy"
620
  msgstr ""
621
 
622
+ #: application/Backend/View/Localization.php:98
623
+ #: application/Service/AccessPolicy.php:170 media/js/aam.js:1379
624
  msgid "Edit Policy"
625
  msgstr ""
626
 
627
+ #: application/Backend/View/Localization.php:99
628
+ #: application/Backend/tmpl/service/menu.php:79
629
+ #: application/Backend/tmpl/service/toolbar.php:60 media/js/aam.js:1516
630
  msgid "Uncheck to allow"
631
  msgstr ""
632
 
633
+ #: application/Backend/View/Localization.php:100
634
+ #: application/Backend/tmpl/service/menu.php:79
635
+ #: application/Backend/tmpl/service/toolbar.php:60 media/js/aam.js:1518
636
  msgid "Check to restrict"
637
  msgstr ""
638
 
639
+ #: application/Backend/View/Localization.php:101
640
+ #: application/Backend/tmpl/service/metabox.php:78 media/js/aam.js:1652
641
+ #: media/js/aam.js:1841
642
  msgid "Uncheck to show"
643
  msgstr ""
644
 
645
+ #: application/Backend/View/Localization.php:102
646
+ #: application/Backend/tmpl/service/metabox.php:78 media/js/aam.js:1654
647
+ #: media/js/aam.js:1843
648
  msgid "Check to hide"
649
  msgstr ""
650
 
651
+ #: application/Backend/View/Localization.php:103
652
+ #: application/Backend/tmpl/service/metabox.php:114 media/js/aam.js:1810
653
  msgid "Initialize"
654
  msgstr ""
655
 
656
+ #: application/Backend/View/Localization.php:104 media/js/aam.js:1995
657
  msgid "No capabilities"
658
  msgstr ""
659
 
660
+ #: application/Backend/View/Localization.php:105 media/js/aam.js:2673
661
  msgid "Post Type"
662
  msgstr ""
663
 
664
+ #: application/Backend/View/Localization.php:106 media/js/aam.js:2678
665
  msgid "Hierarchical Taxonomy"
666
  msgstr ""
667
 
668
+ #: application/Backend/View/Localization.php:107 media/js/aam.js:2683
669
  msgid "Hierarchical Term"
670
  msgstr ""
671
 
672
+ #: application/Backend/View/Localization.php:108 media/js/aam.js:2688
673
  msgid "Tag Taxonomy"
674
  msgstr ""
675
 
676
+ #: application/Backend/View/Localization.php:109 media/js/aam.js:2693
677
  msgid "Tag"
678
  msgstr ""
679
 
680
+ #: application/Backend/View/Localization.php:110 media/js/aam.js:2704
681
  msgid "Customized Settings"
682
  msgstr ""
683
 
684
+ #: application/Backend/View/Localization.php:111 media/js/aam.js:2774
685
+ #: media/js/aam.js:2796
686
  msgid "Parent"
687
  msgstr ""
688
 
689
+ #: application/Backend/View/Localization.php:112 media/js/aam.js:2827
690
  msgid "Drill-Down"
691
  msgstr ""
692
 
693
+ #: application/Backend/View/Localization.php:113 media/js/aam.js:3365
694
  msgid "_TOTAL_ route(s)"
695
  msgstr ""
696
 
697
+ #: application/Backend/View/Localization.php:114 media/js/aam.js:3367
698
  msgid "No API endpoints found. You might have APIs disabled."
699
  msgstr ""
700
 
701
+ #: application/Backend/View/Localization.php:115 media/js/aam.js:3368
702
+ #: media/js/aam.js:3783 media/js/aam.js:4105
703
  msgid "Nothing to show"
704
  msgstr ""
705
 
706
+ #: application/Backend/View/Localization.php:116 media/js/aam.js:3485
707
  msgid "Failed to save URI rule"
708
  msgstr ""
709
 
710
+ #: application/Backend/View/Localization.php:117 media/js/aam.js:3521
711
  msgid "Failed to delete URI rule"
712
  msgstr ""
713
 
714
+ #: application/Backend/View/Localization.php:118 media/js/aam.js:3557
715
  msgid "_TOTAL_ URI(s)"
716
  msgstr ""
717
 
718
+ #: application/Backend/View/Localization.php:119 media/js/aam.js:3596
719
  msgid "Edit Rule"
720
  msgstr ""
721
 
722
+ #: application/Backend/View/Localization.php:120 media/js/aam.js:3608
723
  msgid "Delete Rule"
724
  msgstr ""
725
 
726
+ #: application/Backend/View/Localization.php:121 media/js/aam.js:3623
727
  msgid "Denied"
728
  msgstr ""
729
 
730
+ #: application/Backend/View/Localization.php:122 media/js/aam.js:3630
731
  msgid "Redirected"
732
  msgstr ""
733
 
734
+ #: application/Backend/View/Localization.php:123 media/js/aam.js:3635
735
  msgid "Callback"
736
  msgstr ""
737
 
738
+ #: application/Backend/View/Localization.php:124 media/js/aam.js:3640
739
  msgid "Allowed"
740
  msgstr ""
741
 
742
+ #: application/Backend/View/Localization.php:125 media/js/aam.js:3685
743
  msgid "Generating token..."
744
  msgstr ""
745
 
746
+ #: application/Backend/View/Localization.php:126 media/js/aam.js:3780
747
  msgid "_TOTAL_ token(s)"
748
  msgstr ""
749
 
750
+ #: application/Backend/View/Localization.php:127 media/js/aam.js:3782
751
  msgid "No JWT tokens have been generated."
752
  msgstr ""
753
 
754
+ #: application/Backend/View/Localization.php:128 media/js/aam.js:3827
755
  msgid "Delete Token"
756
  msgstr ""
757
 
758
+ #: application/Backend/View/Localization.php:129 media/js/aam.js:3840
759
  msgid "View Token"
760
  msgstr ""
761
 
762
+ #: application/Backend/View/Localization.php:130 media/js/aam.js:3865
763
  msgid "Creating..."
764
  msgstr ""
765
 
766
+ #: application/Backend/View/Localization.php:131 media/js/aam.js:4102
767
  msgid "Search Service"
768
  msgstr ""
769
 
770
+ #: application/Backend/View/Localization.php:132 media/js/aam.js:4103
771
  msgid "_TOTAL_ service(s)"
772
  msgstr ""
773
 
774
+ #: application/Backend/View/Localization.php:133
775
+ #: application/Backend/tmpl/settings/content.php:19
776
+ #: application/Backend/tmpl/settings/core.php:16
777
+ #: application/Backend/tmpl/settings/security.php:16 media/js/aam.js:4115
778
  msgid "Enabled"
779
  msgstr ""
780
 
781
+ #: application/Backend/View/Localization.php:134
782
+ #: application/Backend/tmpl/settings/content.php:19
783
+ #: application/Backend/tmpl/settings/core.php:16
784
+ #: application/Backend/tmpl/settings/security.php:16 media/js/aam.js:4115
785
  msgid "Disabled"
786
  msgstr ""
787
 
788
+ #: application/Backend/View/Localization.php:135 media/js/aam.js:4157
789
  msgid "All settings has been cleared successfully"
790
  msgstr ""
791
 
792
+ #: application/Backend/View/Localization.php:136
793
+ #: application/Backend/tmpl/index.php:92 media/js/aam.js:4169
794
  msgid "Clear"
795
  msgstr ""
796
 
797
+ #: application/Backend/View/Localization.php:137
798
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:102
799
+ #: application/Backend/tmpl/partial/role-inheritance.php:7 media/js/aam.js:4341
800
  msgid "Select Role"
801
  msgstr ""
802
 
803
+ #: application/Backend/View/Localization.php:138 media/js/aam.js:4616
804
  msgid "Data has been saved to clipboard"
805
  msgstr ""
806
 
807
+ #: application/Backend/View/Localization.php:139 media/js/aam.js:4620
808
  msgid "Failed to save data to clipboard"
809
  msgstr ""
810
 
811
+ #: application/Backend/View/Localization.php:140 media/js/aam.js:4690
812
  msgid "Operation completed successfully"
813
  msgstr ""
814
 
833
  msgstr ""
834
 
835
  #: application/Backend/View/PostOptionList.php:41
836
+ #: application/Backend/tmpl/partial/post-access-form.php:50
837
  msgid "Teaser Message"
838
  msgstr ""
839
 
886
  msgstr ""
887
 
888
  #: application/Backend/View/PostOptionList.php:64
889
+ #: application/Backend/tmpl/partial/post-access-form.php:175
890
  msgid "Password Protected"
891
  msgstr ""
892
 
893
  #: application/Backend/View/PostOptionList.php:65
894
+ #: application/Backend/tmpl/partial/post-access-form.php:179
895
+ #: application/Backend/tmpl/widget/login-frontend.php:29
896
  msgid "Password"
897
  msgstr ""
898
 
926
  msgid "Restrict access to edit the post."
927
  msgstr ""
928
 
929
+ #: application/Backend/View/PostOptionList.php:81
930
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:59
931
+ #: application/Backend/tmpl/service/jwt.php:136
932
+ #: application/Backend/tmpl/service/uri.php:130 media/js/aam.js:3529
933
+ #: media/js/aam.js:3911
934
  msgid "Delete"
935
  msgstr ""
936
 
964
  msgid "Howdy, %username%"
965
  msgstr ""
966
 
967
+ #: application/Backend/tmpl/index.php:23
968
+ msgid "Notifications"
969
  msgstr ""
970
 
971
+ #: application/Backend/tmpl/index.php:44
972
+ msgid "Access"
 
973
  msgstr ""
974
 
975
+ #: application/Backend/tmpl/index.php:49
976
+ msgid "Settings"
977
  msgstr ""
978
 
979
+ #: application/Backend/tmpl/index.php:55
980
+ msgid "Add-Ons"
981
  msgstr ""
982
 
983
+ #: application/Backend/tmpl/index.php:61
984
+ msgid "Help"
 
985
  msgstr ""
986
 
987
+ #: application/Backend/tmpl/index.php:75
988
+ msgid "Reset AAM Settings"
989
  msgstr ""
990
 
991
+ #: application/Backend/tmpl/index.php:85
992
+ #: application/Backend/tmpl/page/addon-panel.php:70
993
+ #: application/Backend/tmpl/page/addon-panel.php:81
994
+ #: application/Backend/tmpl/page/addon-panel.php:91
995
+ #: application/Backend/tmpl/page/addon-panel.php:104
996
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:8
997
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:20
998
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:30
999
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:42
1000
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:52
1001
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:60
1002
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:70
1003
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:112
1004
+ #: application/Backend/tmpl/partial/post-access-form.php:49
1005
+ #: application/Backend/tmpl/partial/post-access-form.php:61
1006
+ #: application/Backend/tmpl/partial/post-access-form.php:71
1007
+ #: application/Backend/tmpl/partial/post-access-form.php:91
1008
+ #: application/Backend/tmpl/partial/post-access-form.php:101
1009
+ #: application/Backend/tmpl/partial/post-access-form.php:164
1010
+ #: application/Backend/tmpl/partial/post-access-form.php:174
1011
+ #: application/Backend/tmpl/partial/post-access-form.php:185
1012
+ #: application/Backend/tmpl/partial/post-access-form.php:195
1013
+ #: application/Backend/tmpl/partial/post-access-form.php:206
1014
+ #: application/Backend/tmpl/service/capability.php:49
1015
+ #: application/Backend/tmpl/service/capability.php:65
1016
+ #: application/Backend/tmpl/service/capability.php:75
1017
+ #: application/Backend/tmpl/service/capability.php:91
1018
+ #: application/Backend/tmpl/service/capability.php:101
1019
+ #: application/Backend/tmpl/service/capability.php:110
1020
+ #: application/Backend/tmpl/service/jwt.php:36
1021
+ #: application/Backend/tmpl/service/jwt.php:85
1022
+ #: application/Backend/tmpl/service/jwt.php:95
1023
+ #: application/Backend/tmpl/service/jwt.php:119
1024
+ #: application/Backend/tmpl/service/jwt.php:129
1025
+ #: application/Backend/tmpl/service/jwt.php:137
1026
+ #: application/Backend/tmpl/service/menu.php:124
1027
+ #: application/Backend/tmpl/service/menu.php:144
1028
+ #: application/Backend/tmpl/service/menu.php:170
1029
+ #: application/Backend/tmpl/service/metabox.php:101
1030
+ #: application/Backend/tmpl/service/metabox.php:115
1031
+ #: application/Backend/tmpl/service/metabox.php:125
1032
+ #: application/Backend/tmpl/service/metabox.php:147
1033
+ #: application/Backend/tmpl/service/toolbar.php:86
1034
+ #: application/Backend/tmpl/service/toolbar.php:108
1035
+ #: application/Backend/tmpl/service/uri.php:26
1036
+ #: application/Backend/tmpl/service/uri.php:109
1037
+ #: application/Backend/tmpl/service/uri.php:119
1038
+ #: application/Backend/tmpl/service/uri.php:131
1039
+ msgid "Close"
1040
  msgstr ""
1041
 
1042
+ #: application/Backend/tmpl/index.php:86
1043
+ msgid "Clear all settings"
1044
  msgstr ""
1045
 
1046
+ #: application/Backend/tmpl/index.php:89
1047
+ msgid "All AAM settings will be removed."
1048
  msgstr ""
1049
 
1050
+ #: application/Backend/tmpl/index.php:93
1051
+ msgid "Cancel"
1052
  msgstr ""
1053
 
1054
+ #: application/Backend/tmpl/index.php:105
1055
+ msgid ""
1056
+ "With the [Enterprise Package] get our dedicated support channel and all the "
1057
+ "premium add-ons for [50+ live websites]"
1058
  msgstr ""
1059
 
1060
+ #: application/Backend/tmpl/index.php:106
1061
+ #: application/Backend/tmpl/page/addon-panel.php:55
1062
+ msgid "Read More"
1063
  msgstr ""
1064
 
1065
+ #: application/Backend/tmpl/metabox/policy-metabox.php:23
1066
+ #, php-format
1067
+ msgid ""
1068
+ "To learn more about Access &amp; Security policy document, please check "
1069
+ "[%sAccess &amp; Security Policy%s] page."
1070
  msgstr ""
1071
 
1072
+ #: application/Backend/tmpl/metabox/policy-metabox.php:51
1073
+ msgid "Syntax Error"
1074
+ msgstr ""
1075
+
1076
+ #: application/Backend/tmpl/page/addon-panel.php:8
1077
  msgid ""
1078
+ "By purchasing any of the premium addon(s) below, you obtain the license that "
1079
+ "allows you to install and use AAM software for one physical WordPress "
1080
+ "installation only. Exceptions are websites where URL is either [localhost] "
1081
+ "or starts with [dev.], [staging.], [test.] or [demo.] They are considered as "
1082
+ "development websites and you can use the purchased license unlimited number "
1083
+ "of times before it is activated on a production website. [Money back "
1084
+ "guaranteed] within 30 day from the time of purchase."
1085
  msgstr ""
1086
 
1087
+ #: application/Backend/tmpl/page/addon-panel.php:13
1088
+ msgid "Download Addon"
1089
  msgstr ""
1090
 
1091
+ #: application/Backend/tmpl/page/addon-panel.php:17
1092
+ msgid "Enter The License Key"
 
 
 
 
1093
  msgstr ""
1094
 
1095
+ #: application/Backend/tmpl/page/addon-panel.php:21
1096
+ msgid "Download"
 
 
 
1097
  msgstr ""
1098
 
1099
+ #: application/Backend/tmpl/page/addon-panel.php:29
1100
+ msgid "Premium"
1101
  msgstr ""
1102
 
1103
+ #: application/Backend/tmpl/page/addon-panel.php:41
1104
+ #: application/Backend/tmpl/page/addon-panel.php:43
1105
+ msgid "License"
1106
  msgstr ""
1107
 
1108
+ #: application/Backend/tmpl/page/addon-panel.php:43
1109
+ msgid "unregistered version"
 
 
 
1110
  msgstr ""
1111
 
1112
+ #: application/Backend/tmpl/page/addon-panel.php:51
1113
+ msgid "Active"
1114
  msgstr ""
1115
 
1116
+ #: application/Backend/tmpl/page/addon-panel.php:53
1117
+ msgid "Inactive"
 
 
1118
  msgstr ""
1119
 
1120
+ #: application/Backend/tmpl/page/addon-panel.php:71
1121
+ msgid "License Key Info"
 
 
 
1122
  msgstr ""
1123
 
1124
+ #: application/Backend/tmpl/page/addon-panel.php:75
1125
+ msgid ""
1126
+ "Insert license key that you received after the payment (find the email "
1127
+ "example below). It might take up to 2 hours to process the payment."
1128
  msgstr ""
1129
 
1130
+ #: application/Backend/tmpl/page/addon-panel.php:92
1131
+ msgid "Plugin Installation"
1132
  msgstr ""
1133
 
1134
+ #: application/Backend/tmpl/page/addon-panel.php:96
1135
+ msgid "The plugin has been successfully downloaded from our server."
1136
  msgstr ""
1137
 
1138
+ #: application/Backend/tmpl/page/addon-panel.php:100
1139
+ #, php-format
1140
  msgid ""
1141
+ "With AAM v6.0.0 or higher, all premium addons are [regular WordPress "
1142
+ "plugins] that you can upload by going to the %sPlugins%s page or extract "
1143
+ "downloaded ZIP archive to the [/wp-content/plugins] folder."
1144
  msgstr ""
1145
 
1146
+ #: application/Backend/tmpl/page/main-panel.php:33
1147
+ msgid "You are not allowed to manage any of the existing services."
 
 
1148
  msgstr ""
1149
 
1150
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:9
1151
+ msgid "Create Role"
 
 
 
 
1152
  msgstr ""
1153
 
1154
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:13
1155
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:35
1156
+ msgid "Role Name"
 
1157
  msgstr ""
1158
 
1159
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:14
1160
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:36
1161
+ msgid "Enter Role Name"
 
 
1162
  msgstr ""
1163
 
1164
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:31
1165
+ msgid "Update Role"
1166
  msgstr ""
1167
 
1168
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:56
1169
  #, php-format
1170
+ msgid "Are you sure that you want to delete the %s role?"
 
 
1171
  msgstr ""
1172
 
1173
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:71
1174
+ msgid "Manage User"
1175
  msgstr ""
1176
 
1177
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:74
 
1178
  msgid ""
1179
+ "Define for how long user can access the website and what action needs to be "
1180
+ "taken after access expires."
1181
  msgstr ""
1182
 
1183
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:83
1184
+ msgid "Action After Expiration"
1185
  msgstr ""
1186
 
1187
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:86
1188
+ msgid "Select Action"
1189
  msgstr ""
1190
 
1191
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:87
1192
+ msgid "Logout User"
 
 
 
1193
  msgstr ""
1194
 
1195
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:88
1196
+ msgid "Delete Account"
1197
  msgstr ""
1198
 
1199
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:89
1200
+ msgid "Change User Role"
1201
  msgstr ""
1202
 
1203
+ #: application/Backend/tmpl/page/subject-panel-advanced.php:100
1204
+ msgid "Change To Role"
1205
  msgstr ""
1206
 
1207
+ #: application/Backend/tmpl/page/subject-panel.php:7
1208
+ msgid "Users/Roles Manager"
1209
  msgstr ""
1210
 
1211
+ #: application/Backend/tmpl/page/subject-panel.php:14
1212
+ msgid "Roles"
1213
  msgstr ""
1214
 
1215
+ #: application/Backend/tmpl/page/subject-panel.php:20
1216
+ msgid "Visitor"
1217
  msgstr ""
1218
 
1219
+ #: application/Backend/tmpl/page/subject-panel.php:23
1220
+ msgid "Default"
1221
  msgstr ""
1222
 
1223
+ #: application/Backend/tmpl/page/subject-panel.php:26
1224
+ msgid "None"
 
1225
  msgstr ""
1226
 
1227
+ #: application/Backend/tmpl/page/subject-panel.php:39
1228
+ #: application/Backend/tmpl/page/subject-panel.php:55
1229
+ msgid "Action"
1230
  msgstr ""
1231
 
1232
+ #: application/Backend/tmpl/page/subject-panel.php:54
1233
+ msgid "Username"
 
 
1234
  msgstr ""
1235
 
1236
+ #: application/Backend/tmpl/page/subject-panel.php:76
1237
  msgid ""
1238
+ "You are not allowed to manage any of the existing users, roles, visitors or "
1239
+ "default access settings."
 
1240
  msgstr ""
1241
 
1242
+ #: application/Backend/tmpl/partial/default-principal-subject-tab.php:6
1243
+ msgid "This feature is allowed only with [Plus Package] addon."
1244
+ msgstr ""
1245
+
1246
+ #: application/Backend/tmpl/partial/default-subject-tab.php:5
1247
  msgid ""
1248
+ "Manage default access to your website resources for all users, roles and "
1249
+ "visitor. This includes Administrator role and your user"
 
1250
  msgstr ""
1251
 
1252
+ #: application/Backend/tmpl/partial/default-subject-tab.php:6
1253
+ msgid "Manage Default Access"
1254
  msgstr ""
1255
 
1256
+ #: application/Backend/tmpl/partial/jwt-login-url.php:6
1257
+ msgid "Login with URL"
 
 
 
1258
  msgstr ""
1259
 
1260
+ #: application/Backend/tmpl/partial/jwt-login-url.php:7
1261
+ #: application/Backend/tmpl/service/jwt.php:67
1262
+ #: application/Backend/tmpl/service/jwt.php:77
1263
+ #: application/Backend/tmpl/service/jwt.php:102
1264
+ #: application/Backend/tmpl/service/jwt.php:112
1265
+ msgid "Copy to clipboard"
1266
  msgstr ""
1267
 
1268
+ #: application/Backend/tmpl/partial/jwt-login-url.php:10
1269
+ msgid "Login URL has not been requested"
1270
  msgstr ""
1271
 
1272
+ #: application/Backend/tmpl/partial/jwt-login-url.php:12
1273
+ msgid "Request URL"
1274
  msgstr ""
1275
 
1276
+ #: application/Backend/tmpl/partial/jwt-login-url.php:16
1277
+ msgid ""
1278
+ "With this URL user will be automatically logged in until defined date and "
1279
+ "time. The JWT token associated with URL is [revokable] however not "
1280
+ "[refreshable]."
1281
  msgstr ""
1282
 
1283
+ #: application/Backend/tmpl/partial/loading-content.php:5
1284
+ msgid ""
1285
+ "[Loading AAM UI]. Please wait. If content will not load within next 30 "
1286
+ "seconds, clear your browser cache and reload the page. If still nothing, it "
1287
+ "is most likely some sort of JavaScript or CSS conflict with one your active "
1288
+ "plugins or theme. Try to deactivate all plugins and switch to any default "
1289
+ "WordPress theme to find out what causes the issue."
1290
  msgstr ""
1291
 
1292
+ #: application/Backend/tmpl/partial/post-access-form.php:5
1293
+ #: application/Backend/tmpl/service/login-redirect.php:17
1294
+ #: application/Backend/tmpl/service/logout-redirect.php:17
1295
+ #: application/Backend/tmpl/service/menu.php:17
1296
+ #: application/Backend/tmpl/service/metabox.php:23
1297
+ #: application/Backend/tmpl/service/redirect.php:19
1298
+ #: application/Backend/tmpl/service/toolbar.php:16
1299
+ #: application/Backend/tmpl/service/uri.php:16
1300
+ msgid "Settings are customized"
1301
  msgstr ""
1302
 
1303
+ #: application/Backend/tmpl/partial/post-access-form.php:6
1304
+ #: application/Backend/tmpl/service/login-redirect.php:18
1305
+ #: application/Backend/tmpl/service/logout-redirect.php:18
1306
+ #: application/Backend/tmpl/service/menu.php:18
1307
+ #: application/Backend/tmpl/service/metabox.php:24
1308
+ #: application/Backend/tmpl/service/redirect.php:20
1309
+ #: application/Backend/tmpl/service/route.php:19
1310
+ #: application/Backend/tmpl/service/toolbar.php:17
1311
+ #: application/Backend/tmpl/service/uri.php:17
1312
+ msgid "Reset to default"
1313
  msgstr ""
1314
 
1315
+ #: application/Backend/tmpl/partial/post-access-form.php:23
1316
+ msgid "change"
1317
  msgstr ""
1318
 
1319
+ #: application/Backend/tmpl/partial/post-access-form.php:54
1320
+ msgid "Plain text or valid HTML"
 
1321
  msgstr ""
1322
 
1323
+ #: application/Backend/tmpl/partial/post-access-form.php:55
1324
+ msgid "Enter your teaser message..."
1325
  msgstr ""
1326
 
1327
+ #: application/Backend/tmpl/partial/post-access-form.php:56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1328
  msgid ""
1329
  "Use [&#91;excerpt&#93;] shortcode to insert post excerpt to the teaser "
1330
  "message."
1331
  msgstr ""
1332
 
1333
+ #: application/Backend/tmpl/partial/post-access-form.php:72
1334
  msgid "Define Access Limit"
1335
  msgstr ""
1336
 
1337
+ #: application/Backend/tmpl/partial/post-access-form.php:76
1338
  msgid "Access Limit Threshold"
1339
  msgstr ""
1340
 
1341
+ #: application/Backend/tmpl/partial/post-access-form.php:77
1342
  msgid "Enter digital number"
1343
  msgstr ""
1344
 
1345
+ #: application/Backend/tmpl/partial/post-access-form.php:84
1346
+ #, php-format
1347
+ msgid "The user can access content [%d] times."
1348
+ msgstr ""
1349
+
1350
+ #: application/Backend/tmpl/partial/post-access-form.php:102
1351
  msgid "Access Redirect"
1352
  msgstr ""
1353
 
1354
+ #: application/Backend/tmpl/partial/post-access-form.php:105
1355
  msgid ""
1356
  "Use REDIRECT option only if you want to redirect user to a different "
1357
  "location either temporary or permanently. Do not use it as a way to protect "
1358
  "access to avoid inconsistent user experience."
1359
  msgstr ""
1360
 
1361
+ #: application/Backend/tmpl/partial/post-access-form.php:110
1362
+ #: application/Backend/tmpl/service/404redirect.php:26
1363
+ #: application/Backend/tmpl/service/login-redirect.php:33
1364
+ #: application/Backend/tmpl/service/logout-redirect.php:33
1365
+ #: application/Backend/tmpl/service/redirect.php:54
1366
+ #: application/Backend/tmpl/service/uri.php:57
1367
  msgid "Redirected to existing page [(select from the drop-down)]"
1368
  msgstr ""
1369
 
1370
+ #: application/Backend/tmpl/partial/post-access-form.php:114
1371
+ #: application/Backend/tmpl/service/logout-redirect.php:37
1372
  msgid "Redirected to the URL [(enter full URL starting from http or https)]"
1373
  msgstr ""
1374
 
1375
+ #: application/Backend/tmpl/partial/post-access-form.php:119
1376
+ #: application/Backend/tmpl/service/redirect.php:49
1377
+ #: application/Backend/tmpl/service/uri.php:52
1378
  msgid ""
1379
  "Redirect to the login page [(after login, user will be redirected back to "
1380
  "the restricted page)]"
1381
  msgstr ""
1382
 
1383
+ #: application/Backend/tmpl/partial/post-access-form.php:124
1384
+ #: application/Backend/tmpl/service/404redirect.php:34
1385
+ #: application/Backend/tmpl/service/login-redirect.php:41
1386
+ #: application/Backend/tmpl/service/logout-redirect.php:41
1387
+ #: application/Backend/tmpl/service/redirect.php:62
1388
+ #: application/Backend/tmpl/service/redirect.php:114
1389
+ #: application/Backend/tmpl/service/uri.php:65
1390
  #, php-format
1391
  msgid "Trigger PHP callback function [(valid %sPHP callback%s is required)]"
1392
  msgstr ""
1393
 
1394
+ #: application/Backend/tmpl/partial/post-access-form.php:128
1395
+ #: application/Backend/tmpl/service/404redirect.php:38
1396
+ #: application/Backend/tmpl/service/login-redirect.php:45
1397
+ #: application/Backend/tmpl/service/logout-redirect.php:45
1398
+ #: application/Backend/tmpl/service/redirect.php:71
1399
+ #: application/Backend/tmpl/service/redirect.php:123
1400
+ #: application/Backend/tmpl/service/uri.php:74
1401
  msgid "Existing Page"
1402
  msgstr ""
1403
 
1404
+ #: application/Backend/tmpl/partial/post-access-form.php:136
1405
+ #: application/Backend/tmpl/service/404redirect.php:47
1406
+ #: application/Backend/tmpl/service/login-redirect.php:54
1407
+ #: application/Backend/tmpl/service/logout-redirect.php:54
1408
+ #: application/Backend/tmpl/service/redirect.php:80
1409
+ #: application/Backend/tmpl/service/redirect.php:132
1410
+ #: application/Backend/tmpl/service/uri.php:81
1411
  msgid "-- Select Page --"
1412
  msgstr ""
1413
 
1414
+ #: application/Backend/tmpl/partial/post-access-form.php:142
1415
+ #: application/Backend/tmpl/service/404redirect.php:53
1416
+ #: application/Backend/tmpl/service/login-redirect.php:60
1417
+ #: application/Backend/tmpl/service/logout-redirect.php:60
1418
+ #: application/Backend/tmpl/service/redirect.php:86
1419
+ #: application/Backend/tmpl/service/redirect.php:138
1420
  msgid "The URL"
1421
  msgstr ""
1422
 
1423
+ #: application/Backend/tmpl/partial/post-access-form.php:147
1424
+ #: application/Backend/tmpl/service/404redirect.php:58
1425
+ #: application/Backend/tmpl/service/login-redirect.php:65
1426
+ #: application/Backend/tmpl/service/logout-redirect.php:65
1427
+ #: application/Backend/tmpl/service/redirect.php:91
1428
+ #: application/Backend/tmpl/service/redirect.php:143
1429
+ #: application/Backend/tmpl/service/uri.php:103
1430
  msgid "PHP Callback Function"
1431
  msgstr ""
1432
 
1433
+ #: application/Backend/tmpl/partial/post-access-form.php:148
1434
+ #: application/Backend/tmpl/service/redirect.php:92
1435
+ #: application/Backend/tmpl/service/redirect.php:144
1436
  msgid "Enter valid callback"
1437
  msgstr ""
1438
 
1439
+ #: application/Backend/tmpl/partial/post-access-form.php:152
1440
+ #: application/Backend/tmpl/service/uri.php:92
1441
  msgid "HTTP Redirect Code"
1442
  msgstr ""
1443
 
1444
+ #: application/Backend/tmpl/partial/post-access-form.php:180
1445
  msgid "Enter Password"
1446
  msgstr ""
1447
 
1448
+ #: application/Backend/tmpl/partial/post-access-form.php:196
1449
  msgid "Expiration Date/Time"
1450
  msgstr ""
1451
 
1452
+ #: application/Backend/tmpl/partial/posts-terms-help-tips.php:7
1453
  #, php-format
1454
  msgid ""
1455
  "You are allowed to manage access to unlimited number of posts, pages or "
1460
  "to manage access to the WordPress content%s."
1461
  msgstr ""
1462
 
1463
+ #: application/Backend/tmpl/partial/role-inheritance.php:5
1464
+ msgid "Inherit capabilities from"
1465
  msgstr ""
1466
 
1467
+ #: application/Backend/tmpl/partial/role-inheritance.php:13
1468
  msgid ""
1469
  "Also clone all AAM access settings (admin menu, metaboxes, redirects, etc.)"
1470
  msgstr ""
1471
 
1472
+ #: application/Backend/tmpl/partial/taxonomy-access-form.php:8
1473
  #, php-format
1474
  msgid ""
1475
  "Managing access to the taxonomy \"%s\" is available with the premium %s[Plus "
1478
  "Package add-on."
1479
  msgstr ""
1480
 
1481
+ #: application/Backend/tmpl/partial/term-access-form.php:8
1482
  #, php-format
1483
  msgid ""
1484
  "Managing access to the %s \"%s\" is available with the premium %s[Plus "
1487
  "add-on."
1488
  msgstr ""
1489
 
1490
+ #: application/Backend/tmpl/partial/term-access-form.php:9
1491
+ #: application/Backend/tmpl/partial/term-access-form.php:13
1492
  msgid "category"
1493
  msgstr ""
1494
 
1495
+ #: application/Backend/tmpl/partial/term-access-form.php:9
1496
+ #: application/Backend/tmpl/partial/term-access-form.php:13
1497
  msgid "tag"
1498
  msgstr ""
1499
 
1500
+ #: application/Backend/tmpl/partial/type-access-form.php:8
1501
  #, php-format
1502
  msgid ""
1503
  "Manage default access to all posts that belong to the post type %s. This "
1504
  "feature is available only with the premium %s[Plus Package]%s add-on."
1505
  msgstr ""
1506
 
1507
+ #: application/Backend/tmpl/partial/visitor-principal-subject-tab.php:5
1508
+ msgid ""
1509
+ "Attach current access &amp; security policy to visitors (any user that is "
1510
+ "not authenticated)"
1511
+ msgstr ""
1512
+
1513
+ #: application/Backend/tmpl/partial/visitor-subject-tab.php:5
1514
+ msgid ""
1515
+ "Manage access to your website for visitors (any user that is not "
1516
+ "authenticated)"
1517
+ msgstr ""
1518
+
1519
+ #: application/Backend/tmpl/partial/visitor-subject-tab.php:6
1520
+ msgid "Manage Visitors"
1521
+ msgstr ""
1522
+
1523
+ #: application/Backend/tmpl/service/404redirect.php:9
1524
  msgid "Setup [default] 404 redirect for all none-existing pages."
1525
  msgstr ""
1526
 
1527
+ #: application/Backend/tmpl/service/404redirect.php:22
1528
  msgid "Default WordPress 404 handler"
1529
  msgstr ""
1530
 
1531
+ #: application/Backend/tmpl/service/404redirect.php:30
1532
  msgid "Redirected to the URL [(enter valid URL starting from http or https)]"
1533
  msgstr ""
1534
 
1535
+ #: application/Backend/tmpl/service/404redirect.php:62
1536
  msgid ""
1537
  "You cannot setup 404 redirect for specific user, role or visitors. Switch to "
1538
  "[Manage Default Access] and define default 404 redirect for everybody."
1539
  msgstr ""
1540
 
1541
+ #: application/Backend/tmpl/service/capability.php:11
1542
  #, php-format
1543
  msgid ""
1544
  "[Be careful!] On this tab, you can manage capabilities for [%s]. Any changes "
1548
  "article."
1549
  msgstr ""
1550
 
1551
+ #: application/Backend/tmpl/service/capability.php:20
1552
  msgid "Filter"
1553
  msgstr ""
1554
 
1555
+ #: application/Backend/tmpl/service/capability.php:27
1556
  msgid "All Capabilities"
1557
  msgstr ""
1558
 
1559
+ #: application/Backend/tmpl/service/capability.php:37
1560
  msgid "Category"
1561
  msgstr ""
1562
 
1563
+ #: application/Backend/tmpl/service/capability.php:38
1564
+ #: application/Backend/tmpl/service/capability.php:54
1565
+ #: application/Backend/tmpl/service/capability.php:80
1566
+ #: application/Backend/tmpl/service/menu.php:155
1567
  msgid "Capability"
1568
  msgstr ""
1569
 
1570
+ #: application/Backend/tmpl/service/capability.php:39
1571
+ #: application/Backend/tmpl/service/jwt.php:24
1572
+ #: application/Backend/tmpl/service/policy.php:28
1573
+ #: application/Backend/tmpl/service/post.php:22
1574
+ #: application/Backend/tmpl/service/uri.php:146
1575
  msgid "Actions"
1576
  msgstr ""
1577
 
1578
+ #: application/Backend/tmpl/service/capability.php:50
1579
  msgid "Create Capability"
1580
  msgstr ""
1581
 
1582
+ #: application/Backend/tmpl/service/capability.php:55
1583
+ #: application/Backend/tmpl/service/capability.php:81
1584
  msgid "Enter Capability"
1585
  msgstr ""
1586
 
1587
+ #: application/Backend/tmpl/service/capability.php:59
1588
  msgid "Also assign this capability to me"
1589
  msgstr ""
1590
 
1591
+ #: application/Backend/tmpl/service/capability.php:85
1592
  msgid "Update this capability for me too"
1593
  msgstr ""
1594
 
1595
+ #: application/Backend/tmpl/service/capability.php:102
1596
  msgid "Delete Capability"
1597
  msgstr ""
1598
 
1599
+ #: application/Backend/tmpl/service/capability.php:105
1600
  msgid ""
1601
  "You are about to delete the %s capability. Any functionality that depends on "
1602
  "this capability will no longer be accessible by %n."
1603
  msgstr ""
1604
 
1605
+ #: application/Backend/tmpl/service/capability.php:108
1606
  msgid "Delete For %n Only"
1607
  msgstr ""
1608
 
1609
+ #: application/Backend/tmpl/service/capability.php:109
1610
  msgid "Delete For All Roles"
1611
  msgstr ""
1612
 
1613
+ #: application/Backend/tmpl/service/jwt.php:10
1614
  #, php-format
1615
  msgid ""
1616
  "Manage list of all valid JWT tokens to the website for [%s] account. For "
1618
  "WordPress JWT authentication%s article."
1619
  msgstr ""
1620
 
1621
+ #: application/Backend/tmpl/service/jwt.php:23
1622
  msgid "Expires"
1623
  msgstr ""
1624
 
1625
+ #: application/Backend/tmpl/service/jwt.php:37
1626
  msgid "Create JWT Token"
1627
  msgstr ""
1628
 
1629
+ #: application/Backend/tmpl/service/jwt.php:42
1630
  msgid "JWT Expires"
1631
  msgstr ""
1632
 
1633
+ #: application/Backend/tmpl/service/jwt.php:52
1634
  msgid "Is token refreshable?"
1635
  msgstr ""
1636
 
1637
+ #: application/Backend/tmpl/service/jwt.php:54
1638
  msgid ""
1639
  "Whether this token, before expires, can be used to obtain a new token for "
1640
  "the same time duration or not."
1641
  msgstr ""
1642
 
1643
+ #: application/Backend/tmpl/service/jwt.php:66
1644
+ #: application/Backend/tmpl/service/jwt.php:101
1645
+ msgid "JWT Token (for API request)"
1646
  msgstr ""
1647
 
1648
+ #: application/Backend/tmpl/service/jwt.php:76
1649
  msgid "Passwordless Login URL"
1650
  msgstr ""
1651
 
1652
+ #: application/Backend/tmpl/service/jwt.php:80
1653
  msgid ""
1654
  "With this URL account will be automatically logged in as long as JWT token "
1655
  "is valid."
1656
  msgstr ""
1657
 
1658
+ #: application/Backend/tmpl/service/jwt.php:96
1659
  msgid "View JWT Token"
1660
  msgstr ""
1661
 
1662
+ #: application/Backend/tmpl/service/jwt.php:111
1663
  msgid "Passwordless Login URL (with JWT token)"
1664
  msgstr ""
1665
 
1666
+ #: application/Backend/tmpl/service/jwt.php:115
1667
  msgid ""
1668
  "Use this URL to authenticate account without the need to enter username/"
1669
  "password."
1670
  msgstr ""
1671
 
1672
+ #: application/Backend/tmpl/service/jwt.php:130
1673
  msgid "Delete JWT Token"
1674
  msgstr ""
1675
 
1676
+ #: application/Backend/tmpl/service/jwt.php:133
1677
  msgid ""
1678
  "You are about to delete already issued JWT token. Any application or user "
1679
  "that has this token, will no longer be able to use it. Please confirm."
1680
  msgstr ""
1681
 
1682
+ #: application/Backend/tmpl/service/login-redirect.php:9
1683
  msgid ""
1684
  "Define the [default] login redirect for all the users and roles when "
1685
  "authentication is completed successfully."
1686
  msgstr ""
1687
 
1688
+ #: application/Backend/tmpl/service/login-redirect.php:13
1689
  #, php-format
1690
  msgid ""
1691
  "Customize login redirect for [%s] when the authentication is completed "
1694
  "login solutions."
1695
  msgstr ""
1696
 
1697
+ #: application/Backend/tmpl/service/login-redirect.php:29
1698
+ #: application/Backend/tmpl/service/logout-redirect.php:29
1699
  msgid "WordPress default behavior"
1700
  msgstr ""
1701
 
1702
+ #: application/Backend/tmpl/service/login-redirect.php:37
1703
  msgid ""
1704
  "Redirected to the local URL [(enter full URL starting from http or https)]"
1705
  msgstr ""
1706
 
1707
+ #: application/Backend/tmpl/service/logout-redirect.php:9
1708
  msgid "Define the [default] logout redirect for all the users and roles."
1709
  msgstr ""
1710
 
1711
+ #: application/Backend/tmpl/service/logout-redirect.php:13
1712
  #, php-format
1713
  msgid "Customize logout redirect for [%s]."
1714
  msgstr ""
1715
 
1716
+ #: application/Backend/tmpl/service/menu.php:9
1717
  #, php-format
1718
  msgid ""
1719
  "Manage access to the backend main menu for [%s]. For more information check "
1720
  "%sHow to manage WordPress backend menu%s."
1721
  msgstr ""
1722
 
1723
+ #: application/Backend/tmpl/service/menu.php:56
1724
  msgid "Menu URI:"
1725
  msgstr ""
1726
 
1727
+ #: application/Backend/tmpl/service/menu.php:76
1728
+ #: application/Backend/tmpl/service/metabox.php:74
1729
+ #: application/Backend/tmpl/service/toolbar.php:57
1730
  msgid "more details"
1731
  msgstr ""
1732
 
1733
+ #: application/Backend/tmpl/service/menu.php:103
1734
  msgid ""
1735
  "Dashboard menu cannot be restricted because it is the default page all users "
1736
  "are redirected after login. You can restrict only Dashboard submenus if any."
1737
  msgstr ""
1738
 
1739
+ #: application/Backend/tmpl/service/menu.php:113
1740
  msgid ""
1741
  "Current user does not have enough capabilities to access any available "
1742
  "backend menu."
1743
  msgstr ""
1744
 
1745
+ #: application/Backend/tmpl/service/menu.php:125
1746
  msgid "Dashboard Lockdown"
1747
  msgstr ""
1748
 
1749
+ #: application/Backend/tmpl/service/menu.php:129
1750
  msgid "You cannot restrict access to the Dashboard Home page."
1751
  msgstr ""
1752
 
1753
+ #: application/Backend/tmpl/service/menu.php:130
1754
  #, php-format
1755
  msgid ""
1756
  "The [Dashboard Home] is the default page that every user is redirected to "
1758
  "lockdown WordPress backend%s article."
1759
  msgstr ""
1760
 
1761
+ #: application/Backend/tmpl/service/menu.php:134
1762
  msgid "OK"
1763
  msgstr ""
1764
 
1765
+ #: application/Backend/tmpl/service/menu.php:145
1766
  msgid "Menu Details"
1767
  msgstr ""
1768
 
1769
+ #: application/Backend/tmpl/service/menu.php:151
1770
+ #: application/Backend/tmpl/service/toolbar.php:93
1771
  msgid "Name"
1772
  msgstr ""
1773
 
1774
+ #: application/Backend/tmpl/service/menu.php:159
1775
+ #: application/Backend/tmpl/service/toolbar.php:97
1776
+ #: application/Backend/tmpl/service/uri.php:142
1777
  msgid "URI"
1778
  msgstr ""
1779
 
1780
+ #: application/Backend/tmpl/service/menu.php:163
1781
+ #: application/Backend/tmpl/service/toolbar.php:101 media/js/aam.js:765
1782
+ msgid "ID"
1783
+ msgstr ""
1784
+
1785
+ #: application/Backend/tmpl/service/metabox.php:9
1786
+ #, php-format
1787
  msgid ""
1788
  "Manage classic (not Gutenberg) metaboxes and widgets visibility for [%s]. "
1789
  "For more information please check %sHow to hide WordPress metaboxes and "
1790
  "widgets%s."
1791
  msgstr ""
1792
 
1793
+ #: application/Backend/tmpl/service/metabox.php:16
1794
  msgid "Refresh"
1795
  msgstr ""
1796
 
1797
+ #: application/Backend/tmpl/service/metabox.php:17
1798
  msgid "Init URL"
1799
  msgstr ""
1800
 
1801
+ #: application/Backend/tmpl/service/metabox.php:49
1802
  msgid "Dashboard Widgets"
1803
  msgstr ""
1804
 
1805
+ #: application/Backend/tmpl/service/metabox.php:53
1806
  msgid "Frontend Widgets [(including Appearance->Widgets)]"
1807
  msgstr ""
1808
 
1809
+ #: application/Backend/tmpl/service/metabox.php:91
1810
  msgid "The list is not initialized. Click Refresh button above."
1811
  msgstr ""
1812
 
1813
+ #: application/Backend/tmpl/service/metabox.php:102
1814
  msgid "Initialize URL"
1815
  msgstr ""
1816
 
1817
+ #: application/Backend/tmpl/service/metabox.php:106
1818
  msgid ""
1819
  "Some metaboxes are \"conditional\" and appear on the edit screen when "
1820
  "certain conditions are met. For example metabox \"Comments\" appears only "
1823
  "appears."
1824
  msgstr ""
1825
 
1826
+ #: application/Backend/tmpl/service/metabox.php:109
1827
  msgid "Backend page URL"
1828
  msgstr ""
1829
 
1830
+ #: application/Backend/tmpl/service/metabox.php:110
1831
  msgid "Insert valid URL"
1832
  msgstr ""
1833
 
1834
+ #: application/Backend/tmpl/service/metabox.php:126
1835
  msgid "Metabox/Widget Details"
1836
  msgstr ""
1837
 
1838
+ #: application/Backend/tmpl/service/metabox.php:132
1839
+ #: application/Backend/tmpl/service/post.php:21
1840
  msgid "Title"
1841
  msgstr ""
1842
 
1843
+ #: application/Backend/tmpl/service/metabox.php:136
1844
  msgid "Screen ID"
1845
  msgstr ""
1846
 
1847
+ #: application/Backend/tmpl/service/metabox.php:140
1848
+ msgid "Internal ID"
1849
+ msgstr ""
1850
+
1851
+ #: application/Backend/tmpl/service/policy.php:7
1852
  #, php-format
1853
  msgid ""
1854
  "Manage access and security policies for [%s]. For more information check "
1855
  "%sAccess &amp; Security Policy%s page."
1856
  msgstr ""
1857
 
1858
+ #: application/Backend/tmpl/service/policy.php:15
1859
  msgid "Policies are customized"
1860
  msgstr ""
1861
 
1862
+ #: application/Backend/tmpl/service/policy.php:16
1863
  msgid "Reset To Default"
1864
  msgstr ""
1865
 
1866
+ #: application/Backend/tmpl/service/policy.php:27
1867
+ #: application/Service/AccessPolicy.php:171
1868
  msgid "Policy"
1869
  msgstr ""
1870
 
1871
+ #: application/Backend/tmpl/service/policy.php:40
1872
  #, php-format
1873
  msgid ""
1874
  "%s[AAM Plus Package]%s extension is required in order to apply Access &amp; "
1875
  "Security Policies to everybody all together."
1876
  msgstr ""
1877
 
1878
+ #: application/Backend/tmpl/service/post.php:11
1879
  msgid "Root"
1880
  msgstr ""
1881
 
1882
+ #: application/Backend/tmpl/service/post.php:31
1883
+ #: application/Backend/tmpl/service/post.php:34
1884
  msgid "Go Back"
1885
  msgstr ""
1886
 
1887
+ #: application/Backend/tmpl/service/redirect.php:11
1888
  msgid ""
1889
  "Define the [default] redirect for all users, roles and visitors when access "
1890
  "is denied to any restricted resources on your website."
1891
  msgstr ""
1892
 
1893
+ #: application/Backend/tmpl/service/redirect.php:15
1894
  #, php-format
1895
  msgid ""
1896
  "Customize redirect for %s when access is denied to restricted resources like "
1898
  "please check %sHow to redirect WordPress user when access is denied%s."
1899
  msgstr ""
1900
 
1901
+ #: application/Backend/tmpl/service/redirect.php:29
1902
  msgid "Frontend Redirect"
1903
  msgstr ""
1904
 
1905
+ #: application/Backend/tmpl/service/redirect.php:30
1906
  msgid "Backend Redirect"
1907
  msgstr ""
1908
 
1909
+ #: application/Backend/tmpl/service/redirect.php:40
1910
+ #: application/Backend/tmpl/service/redirect.php:98
1911
  msgid "Default [(\"Access Denied\" message)]"
1912
  msgstr ""
1913
 
1914
+ #: application/Backend/tmpl/service/redirect.php:44
1915
+ #: application/Backend/tmpl/service/redirect.php:102
1916
+ #: application/Backend/tmpl/service/uri.php:47
1917
  msgid "Show customized message [(plain text or HTML)]"
1918
  msgstr ""
1919
 
1920
+ #: application/Backend/tmpl/service/redirect.php:58
1921
+ #: application/Backend/tmpl/service/redirect.php:110
1922
+ #: application/Backend/tmpl/service/uri.php:61
1923
  msgid "Redirected to local URL [(enter valid URL starting from http or https)]"
1924
  msgstr ""
1925
 
1926
+ #: application/Backend/tmpl/service/redirect.php:66
1927
+ #: application/Backend/tmpl/service/redirect.php:118
1928
+ #: application/Backend/tmpl/service/uri.php:69
1929
  msgid "Customized Message"
1930
  msgstr ""
1931
 
1932
+ #: application/Backend/tmpl/service/redirect.php:67
1933
+ #: application/Backend/tmpl/service/redirect.php:119
1934
+ #: application/Backend/tmpl/service/uri.php:70
1935
  msgid "Enter message..."
1936
  msgstr ""
1937
 
1938
+ #: application/Backend/tmpl/service/redirect.php:106
1939
  msgid "Redirected to existing frontend page [(select from the drop-down)]"
1940
  msgstr ""
1941
 
1942
+ #: application/Backend/tmpl/service/route.php:10
1943
  #, php-format
1944
  msgid ""
1945
  "Manage access to the website API routes for [%s]. For the full RESTful API "
1947
  "in AAM."
1948
  msgstr ""
1949
 
1950
+ #: application/Backend/tmpl/service/route.php:18
1951
  msgid "Routes are customized"
1952
  msgstr ""
1953
 
1954
+ #: application/Backend/tmpl/service/route.php:29
1955
  msgid "Method"
1956
  msgstr ""
1957
 
1958
+ #: application/Backend/tmpl/service/route.php:30
1959
  msgid "Route"
1960
  msgstr ""
1961
 
1962
+ #: application/Backend/tmpl/service/route.php:31
1963
  msgid "Deny"
1964
  msgstr ""
1965
 
1966
+ #: application/Backend/tmpl/service/toolbar.php:8
1967
  msgid ""
1968
  "[Note!] Admin Toolbar service is not intended to restrict direct access to "
1969
  "linked pages. It used only to remove unnecessary items from the top admin "
1971
  "or utilize the great power of capabilities."
1972
  msgstr ""
1973
 
1974
+ #: application/Backend/tmpl/service/toolbar.php:46
1975
  msgid "Item ID:"
1976
  msgstr ""
1977
 
1978
+ #: application/Backend/tmpl/service/toolbar.php:87
1979
  msgid "Item Details"
1980
  msgstr ""
1981
 
1982
+ #: application/Backend/tmpl/service/toolbar.php:118
1983
  msgid "The list of top admin bar items is not initialized. Reload the page."
1984
  msgstr ""
1985
 
1986
+ #: application/Backend/tmpl/service/uri.php:8
1987
  #, php-format
1988
  msgid ""
1989
  "Manage access to the website URL(s) for the [%s]. Note! All entered URLs "
1992
  "website URL%s."
1993
  msgstr ""
1994
 
1995
+ #: application/Backend/tmpl/service/uri.php:27
1996
  msgid "URI Access Rule"
1997
  msgstr ""
1998
 
1999
+ #: application/Backend/tmpl/service/uri.php:31
2000
  msgid "Enter URL [(wildcard * is available with Plus Package extension)]"
2001
  msgstr ""
2002
 
2003
+ #: application/Backend/tmpl/service/uri.php:35
2004
  msgid "How to redirect user when match?"
2005
  msgstr ""
2006
 
2007
+ #: application/Backend/tmpl/service/uri.php:39
2008
  msgid "Allow Access"
2009
  msgstr ""
2010
 
2011
+ #: application/Backend/tmpl/service/uri.php:43
2012
  msgid "Deny Access [(show \"Access Denied\" message)]"
2013
  msgstr ""
2014
 
2015
+ #: application/Backend/tmpl/service/uri.php:87
2016
  msgid "The Valid Redirect URL"
2017
  msgstr ""
2018
 
2019
+ #: application/Backend/tmpl/service/uri.php:94
2020
  msgid "HTTP Code (Default 307)"
2021
  msgstr ""
2022
 
2023
+ #: application/Backend/tmpl/service/uri.php:96
2024
  msgid "302 - Found"
2025
  msgstr ""
2026
 
2027
+ #: application/Backend/tmpl/service/uri.php:98
2028
  msgid "307 - Temporary Redirect"
2029
  msgstr ""
2030
 
2031
+ #: application/Backend/tmpl/service/uri.php:120
2032
  msgid "Delete URI Rule"
2033
  msgstr ""
2034
 
2035
+ #: application/Backend/tmpl/service/uri.php:125
2036
  msgid "You are about to delete the URI Rule. Please confirm!"
2037
  msgstr ""
2038
 
2039
+ #: application/Backend/tmpl/service/uri.php:143
2040
  msgid "Type"
2041
  msgstr ""
2042
 
2043
+ #: application/Backend/tmpl/service/welcome.php:11
2044
  msgid ""
2045
  "Thank you for using the Advanced Access Manager (aka AAM) plugin. With "
2046
  "strong knowledge and experience in WordPress core, AAM becomes a very "
2048
  "backend, and RESTful API."
2049
  msgstr ""
2050
 
2051
+ #: application/Backend/tmpl/service/welcome.php:12
2052
  msgid "Note!"
2053
  msgstr ""
2054
 
2055
+ #: application/Backend/tmpl/service/welcome.php:12
2056
  #, php-format
2057
  msgid ""
2058
  "Power comes with responsibility. Make sure you have a good understanding of "
2063
  "server and never did."
2064
  msgstr ""
2065
 
2066
+ #: application/Backend/tmpl/service/welcome.php:13
2067
  msgid ""
2068
  "AAM is thoroughly tested on the fresh installation of the latest WordPress "
2069
  "and in the latest versions of Chrome, Safari, IE, and Firefox. If you have "
2071
  "themes."
2072
  msgstr ""
2073
 
2074
+ #: application/Backend/tmpl/service/welcome.php:14
2075
  #, php-format
2076
  msgid ""
2077
  "If you are not sure where to start, please check our %s\"Get Started\"%s "
2079
  "your WordPress website more effectively."
2080
  msgstr ""
2081
 
2082
+ #: application/Backend/tmpl/service/welcome.php:16
2083
  msgid "Go To The \"Get Started\" Page"
2084
  msgstr ""
2085
 
2086
+ #: application/Backend/tmpl/settings/configpress.php:8
2087
  #, php-format
2088
  msgid ""
2089
  "Fore more information about AAM configurations check %sAAM Configurations%s "
2090
  "article."
2091
  msgstr ""
2092
 
2093
+ #: application/Backend/tmpl/settings/content.php:24
2094
+ msgid "There are no settings associated with content service."
2095
+ msgstr ""
2096
+
2097
+ #: application/Backend/tmpl/settings/service.php:10
2098
  msgid "Service Name/Description"
2099
  msgstr ""
2100
 
2101
+ #: application/Backend/tmpl/settings/service.php:11
2102
  msgid "Status"
2103
  msgstr ""
2104
 
2105
+ #: application/Backend/tmpl/widget/login-backend.php:5
2106
  msgid "Login Title"
2107
  msgstr ""
2108
 
2109
+ #: application/Backend/tmpl/widget/login-backend.php:10
2110
  msgid "Logged In Title"
2111
  msgstr ""
2112
 
2113
+ #: application/Backend/tmpl/widget/login-backend.php:15
2114
  #, php-format
2115
  msgid ""
2116
  "For more advanced setup like login/logout redirects, security enhancement or "
2117
  "custom styling, please refer to %sHow does AAM Secure Login works%s article."
2118
  msgstr ""
2119
 
2120
+ #: application/Backend/tmpl/widget/login-frontend.php:23
2121
  msgid "Username or Email Address"
2122
  msgstr ""
2123
 
2124
+ #: application/Backend/tmpl/widget/login-frontend.php:38
2125
  msgid "Remember Me"
2126
  msgstr ""
2127
 
2128
+ #: application/Backend/tmpl/widget/login-frontend.php:43
2129
  msgid "Log In"
2130
  msgstr ""
2131
 
2132
+ #: application/Backend/tmpl/widget/login-frontend.php:51
2133
  msgid "Register"
2134
  msgstr ""
2135
 
2136
+ #: application/Backend/tmpl/widget/login-frontend.php:56
2137
  msgid "Lost your password?"
2138
  msgstr ""
2139
 
2140
+ #: application/Backend/tmpl/widget/login-frontend.php:102
2141
  msgid "Dashboard"
2142
  msgstr ""
2143
 
2144
+ #: application/Backend/tmpl/widget/login-frontend.php:103
2145
  msgid "Edit My Profile"
2146
  msgstr ""
2147
 
2148
+ #: application/Backend/tmpl/widget/login-frontend.php:105
2149
  msgid "Log Out"
2150
  msgstr ""
2151
+
2152
+ #: application/Core/Jwt/Issuer.php:55
2153
+ msgid "Token has been revoked"
2154
+ msgstr ""
2155
+
2156
+ #: application/Core/Object.php:141
2157
+ #, php-format
2158
+ msgid "AAM object function %s is not defined"
2159
+ msgstr ""
2160
+
2161
+ #: application/Core/Policy/Validator.php:107
2162
+ #: tests/Service/AccessPolicy/PolicyValidationTest.php:54
2163
+ msgid "The policy is not valid JSON object"
2164
+ msgstr ""
2165
+
2166
+ #: application/Core/Policy/Validator.php:126
2167
+ #: tests/Service/AccessPolicy/PolicyValidationTest.php:37
2168
+ msgid "The policy document is empty"
2169
+ msgstr ""
2170
+
2171
+ #: application/Core/Redirect.php:74
2172
+ #: application/Service/ExtendedCapabilities.php:126
2173
+ #: application/Service/Route.php:216
2174
+ msgid "Access Denied"
2175
+ msgstr ""
2176
+
2177
+ #: application/Service/AccessPolicy.php:67
2178
+ msgid ""
2179
+ "Manage access to the website with well documented JSON access policies for "
2180
+ "any user, role or visitors. Keep the paper-trail of all the access changes "
2181
+ "with policy revisions."
2182
+ msgstr ""
2183
+
2184
+ #: application/Service/AccessPolicy.php:97
2185
+ msgid "Access Policy Document"
2186
+ msgstr ""
2187
+
2188
+ #: application/Service/AccessPolicy.php:108
2189
+ msgid "Access Policy Assignee"
2190
+ msgstr ""
2191
+
2192
+ #: application/Service/AccessPolicy.php:167
2193
+ msgid "Access Policy"
2194
+ msgstr ""
2195
+
2196
+ #: application/Service/AccessPolicy.php:172
2197
+ msgid "Add New Policy"
2198
+ msgstr ""
2199
+
2200
+ #: application/Service/AccessPolicy.php:173
2201
+ msgid "New Policy"
2202
+ msgstr ""
2203
+
2204
+ #: application/Service/AccessPolicy.php:175
2205
+ msgid "Access and security policy"
2206
+ msgstr ""
2207
+
2208
+ #: application/Service/AdminMenu.php:59
2209
+ msgid "Admin Menu"
2210
+ msgstr ""
2211
+
2212
+ #: application/Service/AdminMenu.php:60
2213
+ msgid ""
2214
+ "Manage access to the admin (backend) main menu for any role or individual "
2215
+ "user. The service removes restricted menu items and protects direct access "
2216
+ "to them."
2217
+ msgstr ""
2218
+
2219
+ #: application/Service/AdminMenu.php:275
2220
+ msgid "Sorry, you are not allowed to view this page."
2221
+ msgstr ""
2222
+
2223
+ #: application/Service/Capability.php:53
2224
+ msgid ""
2225
+ "Manage list of all the registered with WordPress core capabilities for any "
2226
+ "role or individual user. The service allows to create new or update and "
2227
+ "delete existing capabilities. Very powerful set of tools for more advanced "
2228
+ "user/role access management."
2229
+ msgstr ""
2230
+
2231
+ #: application/Service/Content.php:73
2232
+ msgid ""
2233
+ "Manage access to your website content for any user, role or visitor. This "
2234
+ "include access to posts, pages, media attachment, custom post types, "
2235
+ "categories, tags, custom taxonomies and terms."
2236
+ msgstr ""
2237
+
2238
+ #: application/Service/Content.php:118
2239
+ msgid "Access Manager"
2240
+ msgstr ""
2241
+
2242
+ #: application/Service/Content.php:579
2243
+ msgid "[No teaser message provided]"
2244
+ msgstr ""
2245
+
2246
+ #: application/Service/DeniedRedirect.php:64
2247
+ msgid ""
2248
+ "Manage the default access denied redirect when access gets denied for any "
2249
+ "protected website resource. The service hooks into the WordPress core wp_die "
2250
+ "function and redirect any frontend or backend denied requests accordingly."
2251
+ msgstr ""
2252
+
2253
+ #: application/Service/ExtendedCapabilities.php:48
2254
+ msgid "Additional Caps"
2255
+ msgstr ""
2256
+
2257
+ #: application/Service/ExtendedCapabilities.php:49
2258
+ msgid ""
2259
+ "Extend the WordPress core collection of capabilities that allow more "
2260
+ "granular access control to the backend core features."
2261
+ msgstr ""
2262
+
2263
+ #: application/Service/Jwt.php:54
2264
+ msgid ""
2265
+ "Manage the website authentication with JWT Bearer token. The service "
2266
+ "facilitates the ability to manage the list of issued JWT token for any user, "
2267
+ "revoke them or issue new on demand."
2268
+ msgstr ""
2269
+
2270
+ #: application/Service/Jwt.php:99
2271
+ msgid "Issue JWT Token"
2272
+ msgstr ""
2273
+
2274
+ #: application/Service/Jwt.php:146 application/Service/Jwt.php:158
2275
+ msgid "JWT token."
2276
+ msgstr ""
2277
+
2278
+ #: application/Service/Jwt.php:225
2279
+ msgid "JWT token is not refreshable"
2280
+ msgstr ""
2281
+
2282
+ #: application/Service/LoginRedirect.php:53
2283
+ msgid ""
2284
+ "Manage login redirect for any group of users or individual user when "
2285
+ "authentication is completed successfully."
2286
+ msgstr ""
2287
+
2288
+ #: application/Service/LogoutRedirect.php:53
2289
+ msgid ""
2290
+ "Manage logout redirect for any group of users or individual user after user "
2291
+ "logged out successfully."
2292
+ msgstr ""
2293
+
2294
+ #: application/Service/Metabox.php:43
2295
+ msgid ""
2296
+ "Manage visibility for the classic (not Gutenberg blocks) backend metaboxes, "
2297
+ "dashboard and frontend widgets for any role, user or visitors. The service "
2298
+ "ONLY removes unwanted metaboxes and widgets and does not prevent from direct "
2299
+ "data spoofing."
2300
+ msgstr ""
2301
+
2302
+ #: application/Service/NotFoundRedirect.php:53
2303
+ msgid ""
2304
+ "Manage frontend 404 (Not Found) redirect for any group of users or "
2305
+ "individual user."
2306
+ msgstr ""
2307
+
2308
+ #: application/Service/Route.php:67
2309
+ msgid ""
2310
+ "Manage access to any individual RESTful endpoint for any role, user or "
2311
+ "unauthenticated application request. The service works great with JWT "
2312
+ "service that authenticate requests with JWT Bearer token."
2313
+ msgstr ""
2314
+
2315
+ #: application/Service/Route.php:95
2316
+ msgid "XML-RPC WordPress API"
2317
+ msgstr ""
2318
+
2319
+ #: application/Service/Route.php:96
2320
+ #, php-format
2321
+ msgid ""
2322
+ "Remote procedure call (RPC) interface is used to manage WordPress website "
2323
+ "content and features. For more information check %sXML-RPC Support%s article."
2324
+ msgstr ""
2325
+
2326
+ #: application/Service/Route.php:100
2327
+ msgid "RESTful WordPress API"
2328
+ msgstr ""
2329
+
2330
+ #: application/Service/Route.php:101
2331
+ #, php-format
2332
+ msgid ""
2333
+ "RESTful interface that is used to manage WordPress website content and "
2334
+ "features. For more information check %sREST API handbook%s."
2335
+ msgstr ""
2336
+
2337
+ #: application/Service/Route.php:127
2338
+ msgid "RESTful API is disabled"
2339
+ msgstr ""
2340
+
2341
+ #: application/Service/SecureLogin.php:46
2342
+ msgid "Secure Login"
2343
+ msgstr ""
2344
+
2345
+ #: application/Service/SecureLogin.php:47
2346
+ msgid ""
2347
+ "Enhance default WordPress authentication process with more secure login "
2348
+ "mechanism. The service registers frontend AJAX Login widget as well as "
2349
+ "additional endpoints for the RESTful API authentication."
2350
+ msgstr ""
2351
+
2352
+ #: application/Service/SecureLogin.php:108
2353
+ msgid "Block User Account"
2354
+ msgstr ""
2355
+
2356
+ #: application/Service/SecureLogin.php:139
2357
+ msgid "Valid username."
2358
+ msgstr ""
2359
+
2360
+ #: application/Service/SecureLogin.php:143
2361
+ msgid "Valid password."
2362
+ msgstr ""
2363
+
2364
+ #: application/Service/SecureLogin.php:147
2365
+ msgid "Redirect URL after authentication."
2366
+ msgstr ""
2367
+
2368
+ #: application/Service/SecureLogin.php:151
2369
+ msgid "Prolong the user session."
2370
+ msgstr ""
2371
+
2372
+ #: application/Service/SecureLogin.php:315
2373
+ msgid "Exceeded maximum number for authentication attempts. Try again later."
2374
+ msgstr ""
2375
+
2376
+ #: application/Service/SecureLogin.php:342
2377
+ msgid "[ERROR]: User is locked. Contact website administrator."
2378
+ msgstr ""
2379
+
2380
+ #: application/Service/SecureLogin.php:365
2381
+ #, php-format
2382
+ msgid "%sAccess is restricted. Login to get access.%s"
2383
+ msgstr ""
2384
+
2385
+ #: application/Service/Shortcode.php:45
2386
+ msgid "Shortcodes"
2387
+ msgstr ""
2388
+
2389
+ #: application/Service/Shortcode.php:46
2390
+ msgid ""
2391
+ "Classic WordPress shortcodes that allow to manage access to parts of a "
2392
+ "frontent content as well as some UI helpers."
2393
+ msgstr ""
2394
+
2395
+ #: application/Service/Toolbar.php:67
2396
+ msgid ""
2397
+ "Manage access to the top admin toolbar items for any role or individual "
2398
+ "user. The service only removes restricted items but does not actually "
2399
+ "protect from direct access via link."
2400
+ msgstr ""
2401
+
2402
+ #: application/Service/Uri.php:54
2403
+ msgid ""
2404
+ "Manage direct access to the website URIs for any role or individual user. "
2405
+ "Define either explicit URI or wildcard (with Plus Package addon) as well as "
2406
+ "how to manage user request (allow, deny, redirect, etc.)."
2407
+ msgstr ""
2408
+
2409
+ #: application/Service/UserLevelFilter.php:45
2410
+ msgid "User Level Filter"
2411
+ msgstr ""
2412
+
2413
+ #: application/Service/UserLevelFilter.php:46
2414
+ msgid ""
2415
+ "Extend default WordPress core users and roles handling, and make sure that "
2416
+ "users with lower user level cannot see or manager users and roles with "
2417
+ "higher level."
2418
+ msgstr ""
2419
+
2420
+ #: application/Service/Welcome.php:53
2421
+ msgid ""
2422
+ "Introduction panel to the AAM functionality. This is just a simple tab that "
2423
+ "contains some introductory material to the AAM plugin and its capabilities."
2424
+ msgstr ""
2425
+
2426
+ #: application/Shortcode/Handler/LoginRedirect.php:86
2427
+ msgid "Login to continue"
2428
+ msgstr ""
2429
+
2430
+ #: media/js/aam.js:132
2431
+ msgid "Search role"
2432
+ msgstr ""
2433
+
2434
+ #: media/js/aam.js:433
2435
+ msgid "Add role"
2436
+ msgstr ""
2437
+
2438
+ #: media/js/aam.js:1062 media/js/aam.js:2363 media/js/aam.js:2434
2439
+ #: media/js/aam.js:4730
2440
+ msgid "Resetting..."
2441
+ msgstr ""
2442
+
2443
+ #: media/js/aam.js:2668
2444
+ msgid "Post"
2445
+ msgstr ""
2446
+
2447
+ #: media/js/aam.js:2734
2448
+ msgid "post type"
2449
+ msgstr ""
2450
+
2451
+ #: media/js/aam.js:2739 media/js/aam.js:2765 media/js/aam.js:2778
2452
+ #: media/js/aam.js:2787 media/js/aam.js:2800
2453
+ msgid "ID:"
2454
+ msgstr ""
2455
+
2456
+ #: media/js/aam.js:2761
2457
+ msgid "taxonomy"
2458
+ msgstr ""
2459
+
2460
+ #: media/js/vendor.js:597
2461
+ msgid ": "
2462
+ msgstr ""
2463
+
2464
+ #: tests/Addon/PlusPackage/ContentAccessTest.php:185
2465
+ msgid "AAM Test"
2466
+ msgstr ""
2467
+
2468
+ #: tests/Addon/PlusPackage/ContentAccessTest.php:186
2469
+ msgid "Just for testing purposes"
2470
+ msgstr ""
2471
+
2472
+ #: tests/Service/AccessPolicy/PolicyValidationTest.php:76
2473
+ msgid "The plugin [advanced-access-manager-x] is required by the policy"
2474
+ msgstr ""
2475
+
2476
+ #: tests/Service/AccessPolicy/PolicyValidationTest.php:100
2477
+ msgid ""
2478
+ "The dependency [advanced-access-manager] does not satisfy version "
2479
+ "requirement by the policy"
2480
+ msgstr ""
2481
+
2482
+ #: tests/Service/Core/CoreServiceTest.php:39
2483
+ msgid "<script>alert(1);</script>"
2484
+ msgstr ""
media/css/aam.css CHANGED
@@ -275,6 +275,10 @@ html, body {
275
  position: relative;
276
  }
277
 
 
 
 
 
278
  a:focus, a:active {
279
  outline: none;
280
  box-shadow: none;
@@ -463,6 +467,18 @@ a.btn:focus, a.btn:active {
463
  color: #3c763d;
464
  }
465
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  .aam-api-method.post {
467
  color: #F5AA2E;
468
  }
275
  position: relative;
276
  }
277
 
278
+ .tooltip{
279
+ z-index:9999;
280
+ }
281
+
282
  a:focus, a:active {
283
  outline: none;
284
  box-shadow: none;
467
  color: #3c763d;
468
  }
469
 
470
+ .aam-iframe .aam-subject-title {
471
+ display: none;
472
+ }
473
+
474
+ .aam-iframe .postbox .hndle {
475
+ display: none;
476
+ }
477
+
478
+ .aam-subject-title {
479
+ margin-left: 4px;
480
+ }
481
+
482
  .aam-api-method.post {
483
  color: #F5AA2E;
484
  }
media/css/vendor.min.css CHANGED
@@ -3,7 +3,7 @@
3
  * Copyright 2011-2015 Twitter, Inc.
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5
  *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
6
- html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
7
 
8
 
9
  /*
@@ -121,11 +121,11 @@ div.dataTables_scrollFoot table {
121
  border-top: none;
122
  margin-top: 0 !important;
123
  }
124
- .table > tbody > tr > td small,
125
- .table > tbody > tr > th small,
126
- .table > tfoot > tr > td small,
127
- .table > tfoot > tr > th small,
128
- .table > thead > tr > td small,
129
  .table > thead > tr > th small{
130
  line-height: 1.2em;
131
  display: block;
3
  * Copyright 2011-2015 Twitter, Inc.
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5
  *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
6
+ html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:9999;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
7
 
8
 
9
  /*
121
  border-top: none;
122
  margin-top: 0 !important;
123
  }
124
+ .table > tbody > tr > td small,
125
+ .table > tbody > tr > th small,
126
+ .table > tfoot > tr > td small,
127
+ .table > tfoot > tr > th small,
128
+ .table > thead > tr > td small,
129
  .table > thead > tr > th small{
130
  line-height: 1.2em;
131
  display: block;
media/js/aam.js CHANGED
@@ -120,7 +120,7 @@
120
  subject: getAAM().getSubject().type,
121
  subjectId: getAAM().getSubject().id,
122
  ui: getLocal().ui,
123
- id: $('#object-id').val()
124
  }
125
  },
126
  columnDefs: [
@@ -311,7 +311,7 @@
311
  type: 'role',
312
  id: data[0]
313
  },
314
- $('#object-id').val(),
315
  ($(this).hasClass('icon-check-empty') ? 1 : 0),
316
  this
317
  );
@@ -319,14 +319,6 @@
319
  }
320
  break;
321
 
322
- case 'no-attach':
323
- if (getAAM().isUI('principal')) {
324
- $(container).append($('<i/>', {
325
- 'class': 'aam-row-action icon-check-empty text-muted'
326
- }));
327
- }
328
- break;
329
-
330
  case 'detach':
331
  if (getAAM().isUI('principal')) {
332
  $(container).append($('<i/>', {
@@ -337,7 +329,7 @@
337
  type: 'role',
338
  id: data[0]
339
  },
340
- $('#object-id').val(),
341
  ($(this).hasClass('icon-check') ? 0 : 1),
342
  this
343
  );
@@ -345,14 +337,6 @@
345
  }
346
  break;
347
 
348
- case 'no-detach':
349
- if (getAAM().isUI('principal')) {
350
- $(container).append($('<i/>', {
351
- 'class': 'aam-row-action icon-check text-muted'
352
- }));
353
- }
354
- break;
355
-
356
  default:
357
  if (getAAM().isUI('main')) {
358
  getAAM().triggerHook('role-action', {
@@ -703,7 +687,7 @@
703
  params.subject = getAAM().getSubject().type;
704
  params.subjectId = getAAM().getSubject().id;
705
  params.ui = getLocal().ui;
706
- params.id = $('#object-id').val();
707
 
708
  return params;
709
  }
@@ -937,7 +921,7 @@
937
  type: 'user',
938
  id: data[0]
939
  },
940
- $('#object-id').val(),
941
  1,
942
  this
943
  );
@@ -955,7 +939,7 @@
955
  type: 'user',
956
  id: data[0]
957
  },
958
- $('#object-id').val(),
959
  0,
960
  this
961
  );
@@ -1160,7 +1144,7 @@
1160
  {
1161
  type: 'visitor'
1162
  },
1163
- $('#object-id').val(),
1164
  effect,
1165
  function (response) {
1166
  if (response.status === 'success') {
@@ -1235,7 +1219,7 @@
1235
  {
1236
  type: 'default'
1237
  },
1238
- $('#object-id').val(),
1239
  effect,
1240
  function (response) {
1241
  if (response.status === 'success') {
@@ -1515,6 +1499,7 @@
1515
  $('#menu-item-name').html($(this).data('name'));
1516
  $('#menu-item-cap').html($(this).data('cap'));
1517
  $('#menu-item-uri').html($(this).data('uri'));
 
1518
  });
1519
  });
1520
 
@@ -1833,6 +1818,7 @@
1833
  $(this).bind('click', function () {
1834
  $('#metabox-title').html($(this).data('title'));
1835
  $('#metabox-screen-id').html($(this).data('screen'));
 
1836
  });
1837
  });
1838
 
@@ -1920,6 +1906,7 @@
1920
  } else {
1921
  $(btn).attr('class', 'aam-row-action text-success icon-check');
1922
  }
 
1923
  }
1924
  },
1925
  error: function () {
@@ -2427,6 +2414,50 @@
2427
  );
2428
  });
2429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2430
  // Initialize the "Access Redirect" modal
2431
  $('.post-redirect-type').each(function () {
2432
  $(this).bind('click', function () {
@@ -3439,11 +3470,12 @@
3439
  uri: uri,
3440
  type: type,
3441
  value: val,
3442
- code: code,
3443
- id: $('#uri-save-btn').attr('data-id')
3444
  },
3445
  beforeSend: function () {
3446
- $('#uri-save-btn').text(getAAM().__('Saving...')).attr('disabled', true);
 
 
3447
  },
3448
  success: function (response) {
3449
  if (response.status === 'success') {
@@ -3477,7 +3509,7 @@
3477
  _ajax_nonce: getLocal().nonce,
3478
  subject: getAAM().getSubject().type,
3479
  subjectId: getAAM().getSubject().id,
3480
- id: $('#uri-delete-btn').data('id')
3481
  },
3482
  beforeSend: function () {
3483
  $('#uri-delete-btn').text(getAAM().__('Deleting...')).attr('disabled', true);
@@ -3526,7 +3558,7 @@
3526
  infoFiltered: ''
3527
  },
3528
  columnDefs: [
3529
- { visible: false, targets: [0, 3, 4] }
3530
  ],
3531
  initComplete: function () {
3532
  var create = $('<a/>', {
@@ -3537,14 +3569,13 @@
3537
  $('.form-clearable', '#uri-model').val('');
3538
  $('.aam-uri-access-action').hide();
3539
  $('input[type="radio"]', '#uri-model').prop('checked', false);
3540
- $('#uri-save-btn').removeAttr('data-id');
3541
  $('#uri-model').modal('show');
3542
  });
3543
 
3544
  $('.dataTables_filter', '#uri-list_wrapper').append(create);
3545
  },
3546
  createdRow: function (row, data) {
3547
- var actions = data[5].split(',');
3548
 
3549
  var container = $('<div/>', { 'class': 'aam-row-actions' });
3550
  $.each(actions, function (i, action) {
@@ -3555,11 +3586,10 @@
3555
  }).bind('click', function () {
3556
  $('.form-clearable', '#uri-model').val('');
3557
  $('.aam-uri-access-action').hide();
3558
- $('#uri-rule').val(data[1]);
3559
- $('input[value="' + data[2] + '"]', '#uri-model').prop('checked', true).trigger('click');
3560
- $('#uri-access-deny-' + data[2] + '-value').val(data[3]);
3561
- $('#uri-access-deny-redirect-code-value').val(data[4]);
3562
- $('#uri-save-btn').attr('data-id', data[0]);
3563
  $('#uri-model').modal('show');
3564
  }).attr({
3565
  'data-toggle': "tooltip",
@@ -3571,7 +3601,7 @@
3571
  $(container).append($('<i/>', {
3572
  'class': 'aam-row-action icon-trash-empty text-danger'
3573
  }).bind('click', function () {
3574
- $('#uri-delete-btn').attr('data-id', data[0]);
3575
  $('#uri-delete-model').modal('show');
3576
  }).attr({
3577
  'data-toggle': "tooltip",
@@ -3587,7 +3617,7 @@
3587
  // Decorate the type of access
3588
  var type = $('<span/>');
3589
 
3590
- switch(data[2]) {
3591
  case 'default':
3592
  case 'message':
3593
  type.html(getAAM().__('Denied'));
@@ -3615,8 +3645,6 @@
3615
  $('td:eq(2)', row).html(container);
3616
 
3617
  $('td:eq(1)', row).html(type);
3618
-
3619
- $('td:eq(0)', row).html(data[1]);
3620
  }
3621
  });
3622
  }
@@ -4354,18 +4382,12 @@
4354
  AAM.prototype.fetchContent = function (view) {
4355
  var _this = this;
4356
 
4357
- //referred object ID like post, page or any custom post type
4358
- var object = window.location.search.match(/&oid\=([^&]*)/);
4359
- var type = window.location.search.match(/&otype\=([^&]*)/);
4360
-
4361
  var data = {
4362
  action: 'aamc',
4363
  _ajax_nonce: getLocal().nonce,
4364
  partial: view,
4365
  subject: this.getSubject().type,
4366
- subjectId: this.getSubject().id,
4367
- oid: object ? object[1] : null,
4368
- otype: type ? type[1] : null
4369
  };
4370
 
4371
  $.ajax(getLocal().url.site, {
@@ -4522,8 +4544,16 @@
4522
  * @returns {undefined}
4523
  */
4524
  AAM.prototype.initialize = function () {
4525
- //read default subject and set it for AAM object
4526
- if (getLocal().subject.type) {
 
 
 
 
 
 
 
 
4527
  this.setSubject(
4528
  getLocal().subject.type,
4529
  getLocal().subject.id,
120
  subject: getAAM().getSubject().type,
121
  subjectId: getAAM().getSubject().id,
122
  ui: getLocal().ui,
123
+ policyId: $('#aam-policy-id').val()
124
  }
125
  },
126
  columnDefs: [
311
  type: 'role',
312
  id: data[0]
313
  },
314
+ $('#aam-policy-id').val(),
315
  ($(this).hasClass('icon-check-empty') ? 1 : 0),
316
  this
317
  );
319
  }
320
  break;
321
 
 
 
 
 
 
 
 
 
322
  case 'detach':
323
  if (getAAM().isUI('principal')) {
324
  $(container).append($('<i/>', {
329
  type: 'role',
330
  id: data[0]
331
  },
332
+ $('#aam-policy-id').val(),
333
  ($(this).hasClass('icon-check') ? 0 : 1),
334
  this
335
  );
337
  }
338
  break;
339
 
 
 
 
 
 
 
 
 
340
  default:
341
  if (getAAM().isUI('main')) {
342
  getAAM().triggerHook('role-action', {
687
  params.subject = getAAM().getSubject().type;
688
  params.subjectId = getAAM().getSubject().id;
689
  params.ui = getLocal().ui;
690
+ params.policyId = $('#aam-policy-id').val();
691
 
692
  return params;
693
  }
921
  type: 'user',
922
  id: data[0]
923
  },
924
+ $('#aam-policy-id').val(),
925
  1,
926
  this
927
  );
939
  type: 'user',
940
  id: data[0]
941
  },
942
+ $('#aam-policy-id').val(),
943
  0,
944
  this
945
  );
1144
  {
1145
  type: 'visitor'
1146
  },
1147
+ $('#aam-policy-id').val(),
1148
  effect,
1149
  function (response) {
1150
  if (response.status === 'success') {
1219
  {
1220
  type: 'default'
1221
  },
1222
+ $('#aam-policy-id').val(),
1223
  effect,
1224
  function (response) {
1225
  if (response.status === 'success') {
1499
  $('#menu-item-name').html($(this).data('name'));
1500
  $('#menu-item-cap').html($(this).data('cap'));
1501
  $('#menu-item-uri').html($(this).data('uri'));
1502
+ $('#menu-item-id').html($(this).data('id'));
1503
  });
1504
  });
1505
 
1818
  $(this).bind('click', function () {
1819
  $('#metabox-title').html($(this).data('title'));
1820
  $('#metabox-screen-id').html($(this).data('screen'));
1821
+ $('#metabox-id').html($(this).data('id'));
1822
  });
1823
  });
1824
 
1906
  } else {
1907
  $(btn).attr('class', 'aam-row-action text-success icon-check');
1908
  }
1909
+ getAAM().notification(getAAM().__('Failed to process request'));
1910
  }
1911
  },
1912
  error: function () {
2414
  );
2415
  });
2416
 
2417
+ // Reset LIMIT counter
2418
+ $('#reset-limited-btn').bind('click', function() {
2419
+ getAAM().queueRequest(function () {
2420
+ $.ajax(getLocal().ajaxurl, {
2421
+ type: 'POST',
2422
+ dataType: 'json',
2423
+ data: {
2424
+ action: 'aam',
2425
+ sub_action: 'Main_Post.resetCounter',
2426
+ _ajax_nonce: getLocal().nonce,
2427
+ subject: getAAM().getSubject().type,
2428
+ subjectId: getAAM().getSubject().id,
2429
+ object: object,
2430
+ objectId: id
2431
+ },
2432
+ beforeSend: function() {
2433
+ $('#reset-limited-btn').text(
2434
+ getAAM().__('Resetting...')
2435
+ ).attr('disabled', true);
2436
+ },
2437
+ success: function (response) {
2438
+ if (response.status === 'failure') {
2439
+ getAAM().notification('danger', response.error);
2440
+ } else {
2441
+ getAAM().notification(
2442
+ 'success',
2443
+ getAAM().__('Counter was reset successfully')
2444
+ );
2445
+ $('#modal-limited').modal('hide');
2446
+ getAAM().loadAccessForm(object, id);
2447
+ }
2448
+ },
2449
+ error: function () {
2450
+ getAAM().notification('danger');
2451
+ },
2452
+ complete: function() {
2453
+ $('#reset-limited-btn').text(
2454
+ getAAM().__('Reset')
2455
+ ).attr('disabled', false);
2456
+ }
2457
+ });
2458
+ });
2459
+ });
2460
+
2461
  // Initialize the "Access Redirect" modal
2462
  $('.post-redirect-type').each(function () {
2463
  $(this).bind('click', function () {
3470
  uri: uri,
3471
  type: type,
3472
  value: val,
3473
+ code: code
 
3474
  },
3475
  beforeSend: function () {
3476
+ $('#uri-save-btn').text(
3477
+ getAAM().__('Saving...')
3478
+ ).attr('disabled', true);
3479
  },
3480
  success: function (response) {
3481
  if (response.status === 'success') {
3509
  _ajax_nonce: getLocal().nonce,
3510
  subject: getAAM().getSubject().type,
3511
  subjectId: getAAM().getSubject().id,
3512
+ uri: $('#uri-delete-btn').data('uri')
3513
  },
3514
  beforeSend: function () {
3515
  $('#uri-delete-btn').text(getAAM().__('Deleting...')).attr('disabled', true);
3558
  infoFiltered: ''
3559
  },
3560
  columnDefs: [
3561
+ { visible: false, targets: [2, 3] }
3562
  ],
3563
  initComplete: function () {
3564
  var create = $('<a/>', {
3569
  $('.form-clearable', '#uri-model').val('');
3570
  $('.aam-uri-access-action').hide();
3571
  $('input[type="radio"]', '#uri-model').prop('checked', false);
 
3572
  $('#uri-model').modal('show');
3573
  });
3574
 
3575
  $('.dataTables_filter', '#uri-list_wrapper').append(create);
3576
  },
3577
  createdRow: function (row, data) {
3578
+ var actions = data[4].split(',');
3579
 
3580
  var container = $('<div/>', { 'class': 'aam-row-actions' });
3581
  $.each(actions, function (i, action) {
3586
  }).bind('click', function () {
3587
  $('.form-clearable', '#uri-model').val('');
3588
  $('.aam-uri-access-action').hide();
3589
+ $('#uri-rule').val(data[0]);
3590
+ $('input[value="' + data[1] + '"]', '#uri-model').prop('checked', true).trigger('click');
3591
+ $('#uri-access-deny-' + data[1] + '-value').val(data[2]);
3592
+ $('#uri-access-deny-redirect-code-value').val(data[3]);
 
3593
  $('#uri-model').modal('show');
3594
  }).attr({
3595
  'data-toggle': "tooltip",
3601
  $(container).append($('<i/>', {
3602
  'class': 'aam-row-action icon-trash-empty text-danger'
3603
  }).bind('click', function () {
3604
+ $('#uri-delete-btn').attr('data-uri', data[0]);
3605
  $('#uri-delete-model').modal('show');
3606
  }).attr({
3607
  'data-toggle': "tooltip",
3617
  // Decorate the type of access
3618
  var type = $('<span/>');
3619
 
3620
+ switch(data[1]) {
3621
  case 'default':
3622
  case 'message':
3623
  type.html(getAAM().__('Denied'));
3645
  $('td:eq(2)', row).html(container);
3646
 
3647
  $('td:eq(1)', row).html(type);
 
 
3648
  }
3649
  });
3650
  }
4382
  AAM.prototype.fetchContent = function (view) {
4383
  var _this = this;
4384
 
 
 
 
 
4385
  var data = {
4386
  action: 'aamc',
4387
  _ajax_nonce: getLocal().nonce,
4388
  partial: view,
4389
  subject: this.getSubject().type,
4390
+ subjectId: this.getSubject().id
 
 
4391
  };
4392
 
4393
  $.ajax(getLocal().url.site, {
4544
  * @returns {undefined}
4545
  */
4546
  AAM.prototype.initialize = function () {
4547
+ // Read default subject and set it for AAM object
4548
+ if ($('#aam-subject-type').length > 0) {
4549
+ console.log('Here');
4550
+ this.setSubject(
4551
+ $('#aam-subject-type').val(),
4552
+ $('#aam-subject-id').val(),
4553
+ $('#aam-subject-name').val(),
4554
+ $('#aam-subject-level').val()
4555
+ );
4556
+ } else if (getLocal().subject.type) {
4557
  this.setSubject(
4558
  getLocal().subject.type,
4559
  getLocal().subject.id,
tests/Addon/IpCheck/IpCheckTest.php ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Addon\IpCheck;
11
+
12
+ use AAM,
13
+ AAM_Service_Content,
14
+ AAM_Core_Object_Post,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait,
17
+ AAM\AddOn\IPCheck\Object\IPCheck as IPCheckObject;
18
+
19
+ /**
20
+ * Test cases for the IP Check addon
21
+ *
22
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
23
+ * @version 6.0.0
24
+ */
25
+ class IpCheckTest extends TestCase
26
+ {
27
+ use ResetTrait;
28
+
29
+ /**
30
+ * Test that entire website is restricted when IP matched
31
+ *
32
+ * @return void
33
+ *
34
+ * @access public
35
+ * @version 6.0.0
36
+ */
37
+ public function testEntireWebsiteRestricted()
38
+ {
39
+ // Override the default handlers so we can suppress die exit
40
+ add_filter('wp_die_handler', function() {
41
+ return function($message, $title) {
42
+ _default_wp_die_handler($message, $title, array('exit' => false));
43
+ };
44
+ }, PHP_INT_MAX);
45
+
46
+ // Fake the IP address
47
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
48
+
49
+ $object = AAM::getUser()->getObject(IPCheckObject::OBJECT_TYPE);
50
+ $this->assertTrue($object->updateOptionItem('ip|127.0.0.1', true)->save());
51
+
52
+ // Capture the WP Die message
53
+ ob_start();
54
+ do_action('wp');
55
+ $content = ob_get_contents();
56
+ ob_end_clean();
57
+
58
+ $this->assertStringContainsString('Access Denied', $content);
59
+
60
+ // Reset WP Query
61
+ remove_all_filters('wp_die_handler', PHP_INT_MAX);
62
+ unset($_SERVER['REMOTE_ADDR']);
63
+ }
64
+
65
+ /**
66
+ * Test that access is denied based on user IP address
67
+ *
68
+ * @return void
69
+ *
70
+ * @access public
71
+ * @version 6.0.0
72
+ */
73
+ public function testPageRestrictedByIp()
74
+ {
75
+ $object = AAM::getUser()->getObject(
76
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
77
+ );
78
+
79
+ // Set restriction
80
+ $this->assertTrue($object->updateOptionItem('selective', array(
81
+ 'rules' => array(
82
+ 'ip|127.0.0.1' => true,
83
+ ),
84
+ 'enabled' => true
85
+ ))->save());
86
+
87
+ // Reset all internal cache
88
+ $this->_resetSubjects();
89
+
90
+ // Verify that access is denied by IP address
91
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
92
+
93
+ $post = AAM::getUser()->getObject(
94
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
95
+ );
96
+
97
+ $result = AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post);
98
+ $this->assertEquals('WP_Error', get_class($result));
99
+ $this->assertEquals(
100
+ 'User is unauthorized to access this post. Access Denied.',
101
+ $result->get_error_message()
102
+ );
103
+
104
+ // Reset original state
105
+ unset($_SERVER['REMOTE_ADDR']);
106
+ }
107
+
108
+ /**
109
+ * Test that access is denied for wildcard IP address
110
+ *
111
+ * @return void
112
+ *
113
+ * @access public
114
+ * @version 6.0.0
115
+ */
116
+ public function testPageRestrictedByIpWildcard()
117
+ {
118
+ $object = AAM::getUser()->getObject(
119
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
120
+ );
121
+
122
+ // Set restriction
123
+ $this->assertTrue($object->updateOptionItem('selective', array(
124
+ 'rules' => array(
125
+ 'ip|127.0.0.*' => true,
126
+ ),
127
+ 'enabled' => true
128
+ ))->save());
129
+
130
+ // Reset all internal cache
131
+ $this->_resetSubjects();
132
+
133
+ // Verify that access is denied by IP address
134
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.3';
135
+
136
+ $post = AAM::getUser()->getObject(
137
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
138
+ );
139
+
140
+ $result = AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post);
141
+ $this->assertEquals('WP_Error', get_class($result));
142
+ $this->assertEquals(
143
+ 'User is unauthorized to access this post. Access Denied.',
144
+ $result->get_error_message()
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Test that access is denied for the IP range
150
+ *
151
+ * @return void
152
+ *
153
+ * @access public
154
+ * @version 6.0.0
155
+ */
156
+ public function testPageRestrictedByIpRange()
157
+ {
158
+ $object = AAM::getUser()->getObject(
159
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
160
+ );
161
+
162
+ // Set restriction
163
+ $this->assertTrue($object->updateOptionItem('selective', array(
164
+ 'rules' => array(
165
+ 'ip|127.0.0.0-20' => true,
166
+ ),
167
+ 'enabled' => true
168
+ ))->save());
169
+
170
+ // Reset all internal cache
171
+ $this->_resetSubjects();
172
+
173
+ // Verify that access is denied by IP address
174
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.5';
175
+
176
+ $post = AAM::getUser()->getObject(
177
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
178
+ );
179
+
180
+ $result = AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post);
181
+ $this->assertEquals('WP_Error', get_class($result));
182
+ $this->assertEquals(
183
+ 'User is unauthorized to access this post. Access Denied.',
184
+ $result->get_error_message()
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Test that access is denied by the referred host
190
+ *
191
+ * @return void
192
+ *
193
+ * @access public
194
+ * @version 6.0.0
195
+ */
196
+ public function testPageRestrictedByHost()
197
+ {
198
+ $object = AAM::getUser()->getObject(
199
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
200
+ );
201
+
202
+ // Set restriction
203
+ $this->assertTrue($object->updateOptionItem('selective', array(
204
+ 'rules' => array(
205
+ 'host|example.local' => true,
206
+ ),
207
+ 'enabled' => true
208
+ ))->save());
209
+
210
+ // Reset all internal cache
211
+ $this->_resetSubjects();
212
+
213
+ // Verify that access is denied by referred host
214
+ $_SERVER['HTTP_REFERER'] = 'https://example.local';
215
+
216
+ $post = AAM::getUser()->getObject(
217
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
218
+ );
219
+
220
+ $result = AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post);
221
+ $this->assertEquals('WP_Error', get_class($result));
222
+ $this->assertEquals(
223
+ 'User is unauthorized to access this post. Access Denied.',
224
+ $result->get_error_message()
225
+ );
226
+ }
227
+
228
+ /**
229
+ * Test that access is denied by query param
230
+ *
231
+ * @return void
232
+ *
233
+ * @access public
234
+ * @version 6.0.0
235
+ */
236
+ public function testPageRestrictedByRef()
237
+ {
238
+ $object = AAM::getUser()->getObject(
239
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
240
+ );
241
+
242
+ // Set restriction
243
+ $this->assertTrue($object->updateOptionItem('selective', array(
244
+ 'rules' => array(
245
+ 'ref|test' => true,
246
+ ),
247
+ 'enabled' => true
248
+ ))->save());
249
+
250
+ // Reset all internal cache
251
+ $this->_resetSubjects();
252
+
253
+ // Verify that access is denied by ref
254
+ $_GET['ref'] = 'test';
255
+
256
+ $post = AAM::getUser()->getObject(
257
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
258
+ );
259
+
260
+ $result = AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post);
261
+ $this->assertEquals('WP_Error', get_class($result));
262
+ $this->assertEquals(
263
+ 'User is unauthorized to access this post. Access Denied.',
264
+ $result->get_error_message()
265
+ );
266
+ }
267
+
268
+ /**
269
+ * Test that cookie with JWT is sent when access is granted
270
+ *
271
+ * @return void
272
+ *
273
+ * @access public
274
+ * @version 6.0.0
275
+ */
276
+ public function testWebsiteAccessCookieSetup()
277
+ {
278
+ // Fake the IP address
279
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
280
+
281
+ $object = AAM::getUser()->getObject(IPCheckObject::OBJECT_TYPE);
282
+ $this->assertTrue($object->updateOptionItem('ip|127.0.0.1', false)->save());
283
+
284
+ // Capture the WP Die message
285
+ ob_start();
286
+ do_action('wp');
287
+ ob_end_clean();
288
+
289
+ $this->assertCount(1, array_filter(xdebug_get_headers(), function($m) {
290
+ return (strpos($m, 'aam_ipcheck_jwt=') !== false);
291
+ }));
292
+
293
+ // Reset WP Query
294
+ unset($_SERVER['REMOTE_ADDR']);
295
+ header_remove('Set-Cookie');
296
+ }
297
+
298
+ /**
299
+ * Test that cookie with JWT is sent when access to page is granted
300
+ *
301
+ * @return void
302
+ *
303
+ * @access public
304
+ * @version 6.0.0
305
+ */
306
+ public function testPageAccessCookieSetup()
307
+ {
308
+ $object = AAM::getUser()->getObject(
309
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
310
+ );
311
+
312
+ // Set restriction
313
+ $this->assertTrue($object->updateOptionItem('selective', array(
314
+ 'rules' => array(
315
+ 'ip|127.0.0.0-20' => false,
316
+ ),
317
+ 'enabled' => true
318
+ ))->save());
319
+
320
+ // Reset all internal cache
321
+ $this->_resetSubjects();
322
+
323
+ // Verify that access is denied by IP address
324
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.5';
325
+
326
+ $post = AAM::getUser()->getObject(
327
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
328
+ );
329
+
330
+ $this->assertTrue(
331
+ AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post)
332
+ );
333
+
334
+ // Note! 2 is because there is no way to clear sent headers with xdebug_*
335
+ $this->assertCount(2, array_filter(xdebug_get_headers(), function($m) {
336
+ return (strpos($m, 'aam_ipcheck_jwt=') !== false);
337
+ }));
338
+
339
+ // Reset WP Query
340
+ unset($_SERVER['REMOTE_ADDR']);
341
+ }
342
+
343
+ }
tests/Addon/PlusPackage/ContentAccessTest.php ADDED
@@ -0,0 +1,451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Addon\PlusPackage;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Post,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait,
17
+ AAM\AddOn\PlusPackage\Object\Term,
18
+ AAM\AddOn\PlusPackage\Object\Type,
19
+ AAM\AddOn\PlusPackage\Object\Taxonomy,
20
+ AAM\AddOn\PlusPackage\Hooks\ContentHooks;
21
+
22
+ /**
23
+ * Test cases for the Plus Package content access management
24
+ *
25
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
26
+ * @version 6.0.0
27
+ */
28
+ class ContentAccessTest extends TestCase
29
+ {
30
+ use ResetTrait,
31
+ AuthUserTrait;
32
+
33
+ /**
34
+ * Test that access settings are inherited from the parent term
35
+ *
36
+ * @return void
37
+ *
38
+ * @access public
39
+ * @version 6.0.0
40
+ */
41
+ public function testInheritPostAccessFromParentTerm()
42
+ {
43
+ $user = AAM::getUser();
44
+ $object = $user->getObject(
45
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
46
+ );
47
+
48
+ // Check if save returns positive result
49
+ $this->assertTrue($object->updateOptionItem('post/hidden', true)->save());
50
+
51
+ // Reset all internal cache
52
+ $this->_resetSubjects();
53
+ ContentHooks::bootstrap()->resetCache();
54
+
55
+ $post = $user->getObject(
56
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
57
+ );
58
+
59
+ $this->assertTrue($post->is('hidden'));
60
+ }
61
+
62
+ /**
63
+ * Test that access settings are inherited from the parent post type
64
+ *
65
+ * @return void
66
+ *
67
+ * @access public
68
+ * @version 6.0.0
69
+ */
70
+ public function testInheritPostAccessFromParentType()
71
+ {
72
+ $user = AAM::getUser();
73
+ $object = $user->getObject(Type::OBJECT_TYPE, 'post');
74
+
75
+ // Check if save returns positive result
76
+ $this->assertTrue($object->updateOptionItem('post/hidden', true)->save());
77
+
78
+ // Reset all internal cache
79
+ $this->_resetSubjects();
80
+ ContentHooks::bootstrap()->resetCache();
81
+
82
+ $post = $user->getObject(
83
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
84
+ );
85
+
86
+ $this->assertTrue($post->is('hidden'));
87
+ }
88
+
89
+ /**
90
+ * Test that access settings are inherited from the parent post
91
+ *
92
+ * @return void
93
+ *
94
+ * @access public
95
+ * @version 6.0.0
96
+ */
97
+ public function testInheritFromParentPost()
98
+ {
99
+ $user = AAM::getUser();
100
+ $object = $user->getObject(
101
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_PAGE_LEVEL_1_ID
102
+ );
103
+
104
+ // Check if save returns positive result
105
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
106
+
107
+ // Reset all internal cache
108
+ $this->_resetSubjects();
109
+
110
+ $post = $user->getObject(
111
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_PAGE_LEVEL_2_ID
112
+ );
113
+
114
+ $this->assertTrue($post->is('hidden'));
115
+ }
116
+
117
+ /**
118
+ * Test access settings adjusting based on [ACTION]_OTHERS access option
119
+ *
120
+ * @return void
121
+ *
122
+ * @access public
123
+ * @version 6.0.0
124
+ */
125
+ public function testAdjustedPostAccessSettings()
126
+ {
127
+ // Make other user as the owner of the post
128
+ wp_update_post(array(
129
+ 'ID' => AAM_UNITTEST_POST_ID,
130
+ 'post_author' => AAM_UNITTEST_JOHN_ID
131
+ ));
132
+
133
+ $user = AAM::getUser();
134
+ $object = $user->getObject(Type::OBJECT_TYPE, 'post');
135
+
136
+ foreach(array('edit', 'hidden', 'delete', 'publish', 'restricted') as $act) {
137
+ $object->updateOptionItem("post/{$act}_others", true);
138
+ }
139
+
140
+ // Check if save returns positive result
141
+ $this->assertTrue($object->save());
142
+
143
+ // Reset all internal cache
144
+ $this->_resetSubjects();
145
+ ContentHooks::bootstrap()->resetCache();
146
+
147
+ $post = $user->getObject(
148
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
149
+ );
150
+
151
+ $this->assertTrue($post->is('hidden'));
152
+ $this->assertTrue($post->is('restricted'));
153
+ $this->assertFalse($post->isAllowedTo('edit'));
154
+ $this->assertFalse($post->isAllowedTo('delete'));
155
+ $this->assertFalse($post->isAllowedTo('publish'));
156
+
157
+ // Reset back to the original author
158
+ wp_update_post(array(
159
+ 'ID' => AAM_UNITTEST_POST_ID,
160
+ 'post_author' => AAM_UNITTEST_AUTH_USER_ID
161
+ ));
162
+ }
163
+
164
+ /**
165
+ * Test that access is denied to create a new post of a specific post type
166
+ *
167
+ * @return void
168
+ *
169
+ * @access public
170
+ * @version 6.0.0
171
+ */
172
+ public function testDenyCreateNewPost()
173
+ {
174
+ $user = AAM::getUser();
175
+ $object = $user->getObject(Type::OBJECT_TYPE, 'aam_test');
176
+
177
+ // Check if save returns positive result
178
+ $this->assertTrue($object->updateOptionItem('post/create', true)->save());
179
+
180
+ // Reset all internal cache
181
+ $this->_resetSubjects();
182
+ ContentHooks::bootstrap()->resetCache();
183
+
184
+ register_post_type('aam_test', array(
185
+ 'label' => __('AAM Test', AAM_KEY),
186
+ 'description' => __('Just for testing purposes', AAM_KEY)
187
+ ));
188
+
189
+ $this->assertEquals(
190
+ get_post_type_object('aam_test')->cap->create_posts, 'do_not_allow'
191
+ );
192
+ }
193
+
194
+ /**
195
+ * Test that access is denied to edit or create a new term of a specific taxonomy
196
+ *
197
+ * @return void
198
+ *
199
+ * @access public
200
+ * @version 6.0.0
201
+ */
202
+ public function testDenyCreateOrEditTaxonomy()
203
+ {
204
+ $user = AAM::getUser();
205
+ $object = $user->getObject(Taxonomy::OBJECT_TYPE, 'aam_test');
206
+
207
+ // Check if save returns positive result
208
+ $this->assertTrue($object->updateOptionItem('term/edit', true)->save());
209
+
210
+ // Reset all internal cache
211
+ $this->_resetSubjects();
212
+ ContentHooks::bootstrap()->resetCache();
213
+
214
+ register_taxonomy('aam_test', 'post', array('hierarchical' => true));
215
+
216
+ $this->assertEquals(
217
+ get_taxonomy('aam_test')->cap->edit_terms, 'do_not_allow'
218
+ );
219
+ }
220
+
221
+ /**
222
+ * Test the ability to edit term
223
+ *
224
+ * @return void
225
+ *
226
+ * @access public
227
+ * @version 6.0.0
228
+ */
229
+ public function testEditTermAccessOption()
230
+ {
231
+ $user = AAM::getUser();
232
+ $role = $user->getParent(); // Administrator role
233
+
234
+ $object = $role->getObject(
235
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
236
+ );
237
+
238
+ $this->assertTrue($object->updateOptionItem('term/edit', true)->save());
239
+
240
+ // Reset all internal cache
241
+ $this->_resetSubjects();
242
+ ContentHooks::bootstrap()->resetCache();
243
+
244
+ $this->assertFalse(current_user_can('edit_term', AAM_UNITTEST_CATEGORY_ID));
245
+ }
246
+
247
+ /**
248
+ * Test the ability to delete term
249
+ *
250
+ * @return void
251
+ *
252
+ * @access public
253
+ * @version 6.0.0
254
+ */
255
+ public function testDeleteTermAccessOption()
256
+ {
257
+ $user = AAM::getUser();
258
+ $role = $user->getParent(); // Administrator role
259
+
260
+ $object = $role->getObject(
261
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
262
+ );
263
+
264
+ $this->assertTrue($object->updateOptionItem('term/delete', true)->save());
265
+
266
+ // Reset all internal cache
267
+ $this->_resetSubjects();
268
+ ContentHooks::bootstrap()->resetCache();
269
+
270
+ $this->assertFalse(current_user_can('delete_term', AAM_UNITTEST_CATEGORY_ID));
271
+ }
272
+
273
+ /**
274
+ * Test the ability to assign term
275
+ *
276
+ * @return void
277
+ *
278
+ * @access public
279
+ * @version 6.0.0
280
+ */
281
+ public function testAssignTermAccessOption()
282
+ {
283
+ $user = AAM::getUser();
284
+ $role = $user->getParent(); // Administrator role
285
+
286
+ $object = $role->getObject(
287
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
288
+ );
289
+
290
+ $this->assertTrue($object->updateOptionItem('term/assign', true)->save());
291
+
292
+ // Reset all internal cache
293
+ $this->_resetSubjects();
294
+ ContentHooks::bootstrap()->resetCache();
295
+
296
+ $this->assertFalse(current_user_can('assign_term', AAM_UNITTEST_CATEGORY_ID));
297
+ }
298
+
299
+ /**
300
+ * Test that term filter is working as expected
301
+ *
302
+ * There are multiple different ways to fetch the list of terms and this is
303
+ * defined by the $fields argument WP_Term_Query::__construct.
304
+ *
305
+ * @return void
306
+ *
307
+ * @access public
308
+ * @version 6.0.0
309
+ */
310
+ public function testFilterTerms()
311
+ {
312
+ $user = AAM::getUser();
313
+ $role = $user->getParent(); // Administrator role
314
+
315
+ $object = $role->getObject(
316
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
317
+ );
318
+
319
+ $this->assertTrue($object->updateOptionItem('term/hidden', true)->save());
320
+
321
+ // Reset all internal cache
322
+ $this->_resetSubjects();
323
+ ContentHooks::bootstrap()->resetCache();
324
+
325
+ $terms = get_terms(array(
326
+ 'number' => 0,
327
+ 'fields' => 'ids',
328
+ 'taxonomy' => 'category',
329
+ 'hide_empty' => false
330
+ ));
331
+
332
+ $this->assertFalse(in_array(AAM_UNITTEST_CATEGORY_ID, $terms));
333
+
334
+ $terms = get_terms(array(
335
+ 'number' => 0,
336
+ 'fields' => 'id=>slug',
337
+ 'taxonomy' => 'category',
338
+ 'hide_empty' => false
339
+ ));
340
+
341
+ $this->assertFalse(array_key_exists(AAM_UNITTEST_CATEGORY_ID, $terms));
342
+
343
+ $terms = get_terms(array(
344
+ 'number' => 0,
345
+ 'fields' => 'id=>name',
346
+ 'taxonomy' => 'category',
347
+ 'hide_empty' => false
348
+ ));
349
+
350
+ $this->assertFalse(array_key_exists(AAM_UNITTEST_CATEGORY_ID, $terms));
351
+
352
+ $terms = get_terms(array(
353
+ 'number' => 0,
354
+ 'fields' => 'id=>parent',
355
+ 'taxonomy' => 'category',
356
+ 'hide_empty' => false
357
+ ));
358
+
359
+ $this->assertFalse(array_key_exists(AAM_UNITTEST_CATEGORY_ID, $terms));
360
+
361
+ $terms = get_terms(array(
362
+ 'number' => 0,
363
+ 'fields' => 'all',
364
+ 'taxonomy' => 'category',
365
+ 'hide_empty' => false
366
+ ));
367
+
368
+ $this->assertCount(0, array_filter($terms, function($term) {
369
+ return $term->term_id === AAM_UNITTEST_CATEGORY_ID;
370
+ }));
371
+ }
372
+
373
+ /**
374
+ * Test that navigation menu is filtered as expected
375
+ *
376
+ * @return void
377
+ *
378
+ * @access public
379
+ * @version 5.0.0
380
+ */
381
+ public function testFilterNavMenu()
382
+ {
383
+ $user = AAM::getUser();
384
+ $role = $user->getParent(); // Administrator role
385
+
386
+ $object = $role->getObject(
387
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
388
+ );
389
+
390
+ $this->assertTrue($object->updateOptionItem('term/hidden', true)->save());
391
+
392
+ // Reset all internal cache
393
+ $this->_resetSubjects();
394
+ ContentHooks::bootstrap()->resetCache();
395
+
396
+ $menu = wp_get_nav_menu_items(AAM_UNITTEST_NAV_MENU_NAME);
397
+
398
+ $this->assertCount(0, array_filter($menu, function($item) {
399
+ return $item->object_id === AAM_UNITTEST_CATEGORY_ID && $item->object === 'category';
400
+ }));
401
+ }
402
+
403
+ /**
404
+ * Test that access is denied to browse the category
405
+ *
406
+ * @return void
407
+ *
408
+ * @access public
409
+ * @version 6.0.0
410
+ */
411
+ public function testTermBrowseAccessOption()
412
+ {
413
+ global $wp_query;
414
+
415
+ $user = AAM::getUser();
416
+ $role = $user->getParent(); // Administrator role
417
+
418
+ $object = $role->getObject(
419
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
420
+ );
421
+
422
+ $this->assertTrue($object->updateOptionItem('term/browse', true)->save());
423
+
424
+ $wp_query->is_category = true;
425
+ $wp_query->queried_object = get_term(AAM_UNITTEST_CATEGORY_ID, 'category');
426
+
427
+ // Override the default handlers so we can suppress die exit
428
+ add_filter('wp_die_handler', function() {
429
+ return function($message, $title) {
430
+ _default_wp_die_handler($message, $title, array('exit' => false));
431
+ };
432
+ }, PHP_INT_MAX);
433
+
434
+ // Capture the WP Die message
435
+ ob_start();
436
+ do_action('wp');
437
+ $content = ob_get_contents();
438
+ ob_end_clean();
439
+
440
+ $this->assertStringContainsString(
441
+ 'Access denied to browse this category', $content
442
+ );
443
+
444
+ // Reset WP Query
445
+ remove_all_filters('wp_die_handler', PHP_INT_MAX);
446
+
447
+ unset($wp_query->is_category);
448
+ unset($wp_query->queried_object);
449
+ }
450
+
451
+ }
tests/Addon/PlusPackage/ContentVisibilityTest.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Addon\PlusPackage;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Post,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait,
17
+ AAM\AddOn\PlusPackage\Object\Term,
18
+ AAM\AddOn\PlusPackage\Object\Type,
19
+ AAM\AddOn\PlusPackage\Hooks\ContentHooks;
20
+
21
+ /**
22
+ * Test cases for the Plus Package content visibility management
23
+ *
24
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
25
+ * @version 6.0.0
26
+ */
27
+ class ContentVisibilityTest extends TestCase
28
+ {
29
+ use ResetTrait,
30
+ AuthUserTrait;
31
+
32
+ /**
33
+ * Test that page is hidden when parent page is hidden to
34
+ *
35
+ * @return void
36
+ *
37
+ * @access public
38
+ * @version 6.0.0
39
+ */
40
+ public function testInheritanceFromParentPost()
41
+ {
42
+ $user = AAM::getUser();
43
+ $object = $user->getObject(
44
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_PAGE_LEVEL_1_ID
45
+ );
46
+
47
+ // Check if save returns positive result
48
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
49
+
50
+ // Reset all internal cache
51
+ $this->_resetSubjects();
52
+ ContentHooks::bootstrap()->resetCache();
53
+
54
+ $posts = get_posts(array(
55
+ 'post_type' => 'page',
56
+ 'fields' => 'ids',
57
+ 'numberposts' => -1,
58
+ 'suppress_filters' => false
59
+ ));
60
+
61
+ $this->assertFalse(in_array(AAM_UNITTEST_PAGE_LEVEL_2_ID, $posts));
62
+ }
63
+
64
+ /**
65
+ * Test that post is hidden when parent term states so
66
+ *
67
+ * @return void
68
+ *
69
+ * @access public
70
+ * @version 6.0.0
71
+ */
72
+ public function testInheritanceFromParentTerm()
73
+ {
74
+ $user = AAM::getUser();
75
+ $object = $user->getObject(
76
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
77
+ );
78
+
79
+ // Check if save returns positive result
80
+ $this->assertTrue($object->updateOptionItem('post/hidden', true)->save());
81
+
82
+ // Reset all internal cache
83
+ $this->_resetSubjects();
84
+ ContentHooks::bootstrap()->resetCache();
85
+
86
+ $posts = get_posts(array(
87
+ 'post_type' => 'post',
88
+ 'fields' => 'ids',
89
+ 'numberposts' => -1,
90
+ 'suppress_filters' => false
91
+ ));
92
+
93
+ $this->assertFalse(in_array(AAM_UNITTEST_POST_ID, $posts));
94
+ }
95
+
96
+ /**
97
+ * Test that posts are hidden when the entire post type states so
98
+ *
99
+ * @return void
100
+ *
101
+ * @access public
102
+ * @version 6.0.0
103
+ */
104
+ public function testInheritanceFromParentType()
105
+ {
106
+ $user = AAM::getUser();
107
+ $object = $user->getObject(
108
+ Type::OBJECT_TYPE, 'post'
109
+ );
110
+
111
+ // Check if save returns positive result
112
+ $this->assertTrue($object->updateOptionItem('post/hidden', true)->save());
113
+
114
+ // Reset all internal cache
115
+ $this->_resetSubjects();
116
+ ContentHooks::bootstrap()->resetCache();
117
+
118
+ $posts = get_posts(array(
119
+ 'post_type' => 'post',
120
+ 'fields' => 'ids',
121
+ 'numberposts' => -1,
122
+ 'suppress_filters' => false
123
+ ));
124
+
125
+ $this->assertCount(0, $posts);
126
+ }
127
+
128
+ /**
129
+ * Test that post if visible if explicitly defined so
130
+ *
131
+ * @return void
132
+ *
133
+ * @access public
134
+ * @version 6.0.0
135
+ */
136
+ public function testInheritanceFromParentTermButOverwritten()
137
+ {
138
+ $user = AAM::getUser();
139
+ $object = $user->getObject(
140
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
141
+ );
142
+
143
+ // Check if save returns positive result
144
+ $this->assertTrue($object->updateOptionItem('post/hidden', true)->save());
145
+
146
+ $post = $user->getObject(
147
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
148
+ );
149
+
150
+ // Check if save returns positive result
151
+ $this->assertTrue($post->updateOptionItem('hidden', false)->save());
152
+
153
+ // Reset all internal cache
154
+ $this->_resetSubjects();
155
+ ContentHooks::bootstrap()->resetCache();
156
+
157
+ $posts = get_posts(array(
158
+ 'post_type' => 'post',
159
+ 'fields' => 'ids',
160
+ 'numberposts' => -1,
161
+ 'suppress_filters' => false
162
+ ));
163
+
164
+ $this->assertContains(AAM_UNITTEST_POST_ID, $posts);
165
+ }
166
+
167
+ /**
168
+ * Test that post if visible if explicitly defined so
169
+ *
170
+ * @return void
171
+ *
172
+ * @access public
173
+ * @version 6.0.0
174
+ */
175
+ public function testInheritanceFromParentTypeButOverwritten()
176
+ {
177
+ $user = AAM::getUser();
178
+ $type = $user->getObject(Type::OBJECT_TYPE, 'post');
179
+
180
+ // Check if save returns positive result
181
+ $this->assertTrue($type->updateOptionItem('post/hidden', true)->save());
182
+
183
+ $term = $user->getObject(
184
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
185
+ );
186
+
187
+ // Check if save returns positive result
188
+ $this->assertTrue($term->updateOptionItem('post/hidden', false)->save());
189
+
190
+ // Reset all internal cache
191
+ $this->_resetSubjects();
192
+ ContentHooks::bootstrap()->resetCache();
193
+
194
+ $posts = get_posts(array(
195
+ 'post_type' => 'post',
196
+ 'fields' => 'ids',
197
+ 'numberposts' => -1,
198
+ 'suppress_filters' => false
199
+ ));
200
+
201
+ $this->assertContains(AAM_UNITTEST_POST_ID, $posts);
202
+ }
203
+
204
+ }
tests/Addon/PlusPackage/DefaultCategoryTest.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Addon\PlusPackage;
11
+
12
+ use AAM,
13
+ AAM_Core_Config,
14
+ AAM\AddOn\PlusPackage\Main,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait,
17
+ AAM\UnitTest\Libs\AuthUserTrait,
18
+ AAM\AddOn\PlusPackage\Object\System;
19
+
20
+ /**
21
+ * Test default category assignment to a post
22
+ *
23
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
24
+ * @version 6.0.0
25
+ */
26
+ class DefaultCategoryTest extends TestCase
27
+ {
28
+ use ResetTrait,
29
+ AuthUserTrait;
30
+
31
+ /**
32
+ * Test the new default category is assigned to post that has no categories
33
+ * attached
34
+ *
35
+ * @return void
36
+ *
37
+ * @access public
38
+ * @version 6.0.0
39
+ */
40
+ public function testPostSaveCategoryAssignment()
41
+ {
42
+ // Get original post terms
43
+ $terms = wp_get_object_terms(AAM_UNITTEST_POST_ID, 'category', array(
44
+ 'fields' => 'ids'
45
+ ));
46
+
47
+ // Remove all the terms from the post(
48
+ wp_remove_object_terms(AAM_UNITTEST_POST_ID, $terms, 'category');
49
+
50
+ // Set the default category
51
+ $system = AAM::getUser()->getObject(System::OBJECT_TYPE);
52
+ $this->assertTrue(
53
+ $system->updateOptionItem(
54
+ 'defaultTerm.post.category', AAM_UNITTEST_CATEGORY_LEVEL_1_ID
55
+ )->save()
56
+ );
57
+
58
+ // Reset all internal cache
59
+ $this->_resetSubjects();
60
+
61
+ wp_update_post(array(
62
+ 'ID' => AAM_UNITTEST_POST_ID
63
+ ));
64
+
65
+ $new_terms = wp_get_object_terms(AAM_UNITTEST_POST_ID, 'category', array(
66
+ 'fields' => 'ids'
67
+ ));
68
+
69
+ $this->assertContains(AAM_UNITTEST_CATEGORY_LEVEL_1_ID, $new_terms);
70
+
71
+ // Restore original categories
72
+ wp_set_object_terms(AAM_UNITTEST_POST_ID, $terms, 'category');
73
+ }
74
+
75
+ /**
76
+ * Test the new default category is not assigned to post that has already
77
+ * category(s) attached
78
+ *
79
+ * @return void
80
+ *
81
+ * @access public
82
+ * @version 6.0.0
83
+ */
84
+ public function testPostSaveCategoryPreserved()
85
+ {
86
+ // Get original post terms
87
+ $terms = wp_get_object_terms(AAM_UNITTEST_POST_ID, 'category', array(
88
+ 'fields' => 'ids'
89
+ ));
90
+
91
+ // Make sure that we have at least one category attached
92
+ $this->assertGreaterThanOrEqual(1, count($terms));
93
+
94
+ // Set the default category
95
+ $system = AAM::getUser()->getObject(System::OBJECT_TYPE);
96
+ $this->assertTrue(
97
+ $system->updateOptionItem(
98
+ 'defaultTerm.post.category', AAM_UNITTEST_CATEGORY_LEVEL_1_ID
99
+ )->save()
100
+ );
101
+
102
+ // Reset all internal cache
103
+ $this->_resetSubjects();
104
+
105
+ wp_update_post(array(
106
+ 'ID' => AAM_UNITTEST_POST_ID
107
+ ));
108
+
109
+ $new_terms = wp_get_object_terms(AAM_UNITTEST_POST_ID, 'category', array(
110
+ 'fields' => 'ids'
111
+ ));
112
+
113
+ $this->assertEquals($terms, $new_terms);
114
+ }
115
+
116
+ /**
117
+ * Test assigning default category to attachment when none is specified
118
+ *
119
+ * @return void
120
+ *
121
+ * @access public
122
+ * @version 6.0.0
123
+ */
124
+ public function testAttachmentUpdateCategoryAssignment()
125
+ {
126
+ // Enable media category
127
+ $this->assertTrue(AAM_Core_Config::set('core.settings.mediaCategory', true));
128
+ Main::bootstrap()->registerTaxonomies();
129
+
130
+ // Get original post terms
131
+ $terms = wp_get_object_terms(AAM_UNITTEST_ATTACHMENT_ID, 'media_category', array(
132
+ 'fields' => 'ids'
133
+ ));
134
+
135
+ // Remove all the terms from the post(
136
+ wp_remove_object_terms(AAM_UNITTEST_ATTACHMENT_ID, $terms, 'media_category');
137
+
138
+ // Set the default category
139
+ $system = AAM::getUser()->getObject(System::OBJECT_TYPE);
140
+ $this->assertTrue(
141
+ $system->updateOptionItem(
142
+ 'defaultTerm.attachment.media_category', AAM_UNITTEST_MEDIA_CATEGORY_ID
143
+ )->save()
144
+ );
145
+
146
+ // Reset all internal cache
147
+ $this->_resetSubjects();
148
+
149
+ wp_update_post(array(
150
+ 'ID' => AAM_UNITTEST_ATTACHMENT_ID
151
+ ));
152
+
153
+ $new_terms = wp_get_object_terms(AAM_UNITTEST_ATTACHMENT_ID, 'media_category', array(
154
+ 'fields' => 'ids'
155
+ ));
156
+
157
+ $this->assertContains(AAM_UNITTEST_MEDIA_CATEGORY_ID, $new_terms);
158
+
159
+ // Restore original categories
160
+ wp_set_object_terms(AAM_UNITTEST_ATTACHMENT_ID, $terms, 'media_category');
161
+ }
162
+
163
+ /**
164
+ * Test assigning default category to new attachment
165
+ *
166
+ * @return void
167
+ *
168
+ * @access public
169
+ * @version 6.0.0
170
+ */
171
+ public function testAttachmentAddCategoryAssignment()
172
+ {
173
+ // Enable media category
174
+ $this->assertTrue(AAM_Core_Config::set('core.settings.mediaCategory', true));
175
+ Main::bootstrap()->registerTaxonomies();
176
+
177
+ // Set the default category
178
+ $system = AAM::getUser()->getObject(System::OBJECT_TYPE);
179
+ $this->assertTrue(
180
+ $system->updateOptionItem(
181
+ 'defaultTerm.attachment.media_category', AAM_UNITTEST_MEDIA_CATEGORY_ID
182
+ )->save()
183
+ );
184
+
185
+ // Reset all internal cache
186
+ $this->_resetSubjects();
187
+
188
+ $id = wp_insert_post(array(
189
+ 'post_type' => 'attachment',
190
+ 'post_title' => 'Dummy Attachment'
191
+ ));
192
+
193
+ $new_terms = wp_get_object_terms($id, 'media_category', array(
194
+ 'fields' => 'ids'
195
+ ));
196
+
197
+ $this->assertContains(AAM_UNITTEST_MEDIA_CATEGORY_ID, $new_terms);
198
+
199
+ // Restore original categories
200
+ wp_delete_post($id, true);
201
+ }
202
+
203
+ /**
204
+ * Test that default_category option is adjusted to a new value
205
+ *
206
+ * @return void
207
+ *
208
+ * @access public
209
+ * @version 6.0.0
210
+ */
211
+ public function testGetDefaultCategoryOption()
212
+ {
213
+ // Set the default category
214
+ $system = AAM::getUser()->getObject(System::OBJECT_TYPE);
215
+ $this->assertTrue(
216
+ $system->updateOptionItem(
217
+ 'defaultTerm.post.category', AAM_UNITTEST_CATEGORY_LEVEL_2_ID
218
+ )->save()
219
+ );
220
+
221
+ $this->assertEquals(
222
+ AAM_UNITTEST_CATEGORY_LEVEL_2_ID, get_option('default_category')
223
+ );
224
+ }
225
+
226
+ }
tests/Addon/PlusPackage/TermRESTfulAccessTest.php ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Addon\PlusPackage;
11
+
12
+ use AAM,
13
+ WP_REST_Request,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait,
17
+ AAM\AddOn\PlusPackage\Object\Term,
18
+ AAM\AddOn\PlusPackage\Object\Taxonomy;
19
+
20
+ /**
21
+ * Test cases for the Plus Package term access management
22
+ *
23
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
24
+ * @version 6.0.0
25
+ */
26
+ class TermRESTfulAccessTest extends TestCase
27
+ {
28
+ use ResetTrait,
29
+ AuthUserTrait;
30
+
31
+ /**
32
+ * Test that term is hidden while going through RESTful API endpoint
33
+ *
34
+ * @return void
35
+ *
36
+ * @access public
37
+ * @version 6.0.0
38
+ */
39
+ public function testVisibilityTermDirectly()
40
+ {
41
+ $user = AAM::getUser();
42
+ $object = $user->getObject(
43
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
44
+ );
45
+
46
+ // Check if save returns positive result
47
+ $this->assertTrue($object->updateOptionItem('term/hidden', true)->save());
48
+
49
+ $server = rest_get_server();
50
+
51
+ // Verify that term is no longer in the list of terms
52
+ $request = new WP_REST_Request('GET', '/wp/v2/categories');
53
+ $request->set_param('context', 'view');
54
+
55
+ $data = $server->dispatch($request)->get_data();
56
+
57
+ // First, confirm that post is in the array of posts
58
+ $this->assertCount(0, array_filter($data, function($term) {
59
+ return $term['id'] === AAM_UNITTEST_CATEGORY_ID;
60
+ }));
61
+ }
62
+
63
+ /**
64
+ * Test that term is restricted while going through RESTful API endpoint
65
+ *
66
+ * @return void
67
+ *
68
+ * @access public
69
+ * @version 6.0.0
70
+ */
71
+ public function testRestrictedTermDirectly()
72
+ {
73
+ $user = AAM::getUser();
74
+ $object = $user->getObject(
75
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
76
+ );
77
+
78
+ // Check if save returns positive result
79
+ $this->assertTrue($object->updateOptionItem('term/restricted', true)->save());
80
+
81
+ $server = rest_get_server();
82
+
83
+ // Verify that term is no longer in the list of terms
84
+ $request = new WP_REST_Request('GET', '/wp/v2/categories/' . AAM_UNITTEST_CATEGORY_ID);
85
+ $request->set_param('context', 'view');
86
+
87
+ $response = $server->dispatch($request);
88
+
89
+ $this->assertEquals(403, $response->get_status());
90
+ $this->assertEquals('term_access_restricted', $response->get_data()['code']);
91
+ }
92
+
93
+ /**
94
+ * Test that term is not editable while going through RESTful API endpoint
95
+ *
96
+ * @return void
97
+ *
98
+ * @access public
99
+ * @version 6.0.0
100
+ */
101
+ public function testEditableTermDirectly()
102
+ {
103
+ $user = AAM::getUser();
104
+ $object = $user->getObject(
105
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
106
+ );
107
+
108
+ // Check if save returns positive result
109
+ $this->assertTrue($object->updateOptionItem('term/edit', true)->save());
110
+
111
+ $server = rest_get_server();
112
+
113
+ // Verify that term is no longer in the list of terms
114
+ $request = new WP_REST_Request('POST', '/wp/v2/categories/' . AAM_UNITTEST_CATEGORY_ID);
115
+ $request->set_param('description', 'Test');
116
+
117
+ $response = $server->dispatch($request);
118
+
119
+ $this->assertEquals(403, $response->get_status());
120
+ $this->assertEquals('rest_cannot_update', $response->get_data()['code']);
121
+ }
122
+
123
+ /**
124
+ * Test that access is restricted to create a new ter
125
+ *
126
+ * @return void
127
+ *
128
+ * @access public
129
+ * @version 6.0.0
130
+ */
131
+ public function testCreationTermDirectly()
132
+ {
133
+ global $wp_taxonomies;
134
+
135
+ $user = AAM::getUser();
136
+ $object = $user->getObject(Taxonomy::OBJECT_TYPE, 'category');
137
+
138
+ // Check if save returns positive result
139
+ $this->assertTrue($object->updateOptionItem('term/edit', true)->save());
140
+
141
+ // Emulate new taxonomy registration
142
+ do_action('registered_taxonomy', 'category', 'post');
143
+
144
+ $server = rest_get_server();
145
+
146
+ // Verify that term is no longer in the list of terms
147
+ $request = new WP_REST_Request('POST', '/wp/v2/categories');
148
+ $request->set_param('name', 'Test');
149
+ $request->set_param('description', 'Test');
150
+
151
+ $response = $server->dispatch($request);
152
+
153
+ $this->assertEquals(403, $response->get_status());
154
+ $this->assertEquals('rest_cannot_create', $response->get_data()['code']);
155
+
156
+ // Restore original
157
+ $wp_taxonomies['category']->cap->edit_terms = 'edit_categories';
158
+ }
159
+
160
+ /**
161
+ * Test that term cannot be deleted while going through RESTful API endpoint
162
+ *
163
+ * @return void
164
+ *
165
+ * @access public
166
+ * @version 6.0.0
167
+ */
168
+ public function testDeleteTermDirectly()
169
+ {
170
+ $user = AAM::getUser();
171
+ $object = $user->getObject(
172
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
173
+ );
174
+
175
+ // Check if save returns positive result
176
+ $this->assertTrue($object->updateOptionItem('term/delete', true)->save());
177
+
178
+ $server = rest_get_server();
179
+
180
+ // Verify that term is no longer in the list of terms
181
+ $request = new WP_REST_Request('DELETE', '/wp/v2/categories/' . AAM_UNITTEST_CATEGORY_ID);
182
+
183
+ $response = $server->dispatch($request);
184
+
185
+ $this->assertEquals(403, $response->get_status());
186
+ $this->assertEquals('rest_cannot_delete', $response->get_data()['code']);
187
+ }
188
+
189
+ /**
190
+ * Test that term cannot be assigned to a post while going through RESTful
191
+ * API endpoint
192
+ *
193
+ * @return void
194
+ *
195
+ * @access public
196
+ * @version 6.0.0
197
+ */
198
+ public function testAssignTermDirectly()
199
+ {
200
+ $user = AAM::getUser();
201
+ $object = $user->getObject(
202
+ Term::OBJECT_TYPE, AAM_UNITTEST_CATEGORY_ID . '|category'
203
+ );
204
+
205
+ // Check if save returns positive result
206
+ $this->assertTrue($object->updateOptionItem('term/assign', true)->save());
207
+
208
+ $server = rest_get_server();
209
+
210
+ // Verify that term is no longer in the list of terms
211
+ $request = new WP_REST_Request('POST', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
212
+ $request->set_param('context', 'edit');
213
+ $request->set_param('categories', array(AAM_UNITTEST_CATEGORY_ID));
214
+
215
+ $response = $server->dispatch($request);
216
+
217
+ $this->assertEquals(403, $response->get_status());
218
+ $this->assertEquals('rest_cannot_assign_term', $response->get_data()['code']);
219
+ }
220
+
221
+ }
tests/Addon/PlusPackage/UriAccessTest.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Addon\PlusPackage;
11
+
12
+ use AAM,
13
+ AAM_Service_Uri,
14
+ AAM_Core_Object_Uri,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait;
17
+
18
+ /**
19
+ * Test URI access enhancement
20
+ *
21
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
22
+ * @version 6.0.0
23
+ */
24
+ class UriAccessTest extends TestCase
25
+ {
26
+ use ResetTrait;
27
+
28
+ /**
29
+ * Test the wild card URI access rule
30
+ *
31
+ * @return void
32
+ *
33
+ * @access public
34
+ * @version 6.0.0
35
+ */
36
+ public function testWildCardMatch()
37
+ {
38
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
39
+ $result = $object->updateOptionItem('*', array(
40
+ 'type' => 'default',
41
+ 'action' => null
42
+ ))->save();
43
+
44
+ $this->assertTrue($result);
45
+
46
+ // Override the default handlers so we can suppress die exit
47
+ add_filter('wp_die_handler', function() {
48
+ return function($message, $title) {
49
+ _default_wp_die_handler($message, $title, array('exit' => false));
50
+ };
51
+ }, PHP_INT_MAX);
52
+ $_SERVER['REQUEST_URI'] = '/';
53
+
54
+ // Reset all internal cache
55
+ $this->_resetSubjects();
56
+
57
+ ob_start();
58
+ AAM_Service_Uri::getInstance()->authorizeUri();
59
+ $content = ob_get_contents();
60
+ ob_end_clean();
61
+
62
+ $this->assertStringContainsString('Access Denied', $content);
63
+ }
64
+
65
+ /**
66
+ * Test the wild card override rule
67
+ *
68
+ * The entire website is denied but only one specific URI is allowed
69
+ *
70
+ * @return void
71
+ *
72
+ * @access public
73
+ * @version 6.0.0
74
+ */
75
+ public function testWildCardOverride()
76
+ {
77
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
78
+
79
+ // Deny access ot the entire site
80
+ $this->assertTrue($object->updateOptionItem('*', array(
81
+ 'type' => 'default',
82
+ 'action' => null
83
+ ))->save());
84
+
85
+ // Allow to only one specific URI
86
+ $this->assertTrue($object->updateOptionItem('/hello-world', array(
87
+ 'type' => 'allow',
88
+ 'action' => null
89
+ ))->save());
90
+
91
+ // Reset all internal cache
92
+ $this->_resetSubjects();
93
+
94
+ $match = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE)->findMatch(
95
+ '/hello-world'
96
+ );
97
+
98
+ $this->assertEquals($match['type'], 'allow');
99
+ }
100
+
101
+ }
tests/Addon/RoleHierarchy/RoleHierarchyTest.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Addon\RoleHierarchy;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Menu,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Test cases for the Role Hierarchy addon
20
+ *
21
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
22
+ * @version 6.0.0
23
+ */
24
+ class RoleHierarchyTest extends TestCase
25
+ {
26
+ use ResetTrait,
27
+ AuthUserTrait;
28
+
29
+ /**
30
+ * Test that role can have a parent role and settings are propagated properly
31
+ *
32
+ * @return void
33
+ *
34
+ * @access public
35
+ * @version 6.0.0
36
+ */
37
+ public function testRoleInheritance()
38
+ {
39
+ $contributor = AAM::api()->getRole('contributor');
40
+ $object = $contributor->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
41
+
42
+ // Set fake settings for the Contributor
43
+ $this->assertTrue($object->updateOptionItem('index.php', true)->save());
44
+
45
+ // Fake the fact that Subscriber has a parent role Contributor
46
+ AAM::api()->updateConfig('system.role.subscriber.parent', 'contributor');
47
+
48
+ // Reset all internal cache
49
+ $this->_resetSubjects();
50
+
51
+ $subscriber = AAM::api()->getRole('subscriber');
52
+ $object = $subscriber->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
53
+
54
+ $this->assertEquals('contributor', $subscriber->getParent()->getId());
55
+ $this->assertTrue($object->isRestricted('index.php'));
56
+ }
57
+
58
+ }
tests/Core/GatewayTest.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Core;
11
+
12
+ use AAM_Core_Gateway,
13
+ PHPUnit\Framework\TestCase;
14
+
15
+ /**
16
+ * Test AAM core Gateway
17
+ *
18
+ * @package AAM\UnitTest
19
+ * @version 6.0.0
20
+ */
21
+ class GatewayTest extends TestCase
22
+ {
23
+ /**
24
+ * Test all possible merging permutations with preference
25
+ *
26
+ * @return void
27
+ *
28
+ * @access public
29
+ * @dataProvider mergingPreferenceData
30
+ * @version 6.0.0
31
+ */
32
+ public function testAccessOptionsMerging($set1, $set2, $preference, $expected)
33
+ {
34
+ $gateway = AAM_Core_Gateway::getInstance();
35
+
36
+ $this->assertSame(
37
+ $gateway->mergeSettings($set1, $set2, null, $preference), $expected
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Return the array of possible access option combinations
43
+ *
44
+ * @return array
45
+ *
46
+ * @access public
47
+ * @version 6.0.0
48
+ */
49
+ public function mergingPreferenceData()
50
+ {
51
+ return array(
52
+ array(array('hidden' => true), array('hidden' => true), 'deny', array('hidden' => true)),
53
+ array(array('hidden' => true), array('hidden' => false), 'deny', array('hidden' => true)),
54
+ array(array('hidden' => false), array('hidden' => true), 'deny', array('hidden' => true)),
55
+ array(array('hidden' => false), array('hidden' => false), 'deny', array('hidden' => false)),
56
+ array(array('hidden' => true), array('hidden' => true), 'allow', array('hidden' => true)),
57
+ array(array('hidden' => true), array('hidden' => false), 'allow', array('hidden' => false)),
58
+ array(array('hidden' => false), array('hidden' => true), 'allow', array('hidden' => false)),
59
+ array(array('hidden' => false), array('hidden' => false), 'allow', array('hidden' => false)),
60
+ // One of the options is not defined
61
+ array(array('hidden' => true), array(), 'deny', array('hidden' => true)),
62
+ array(array('hidden' => false), array(), 'deny', array('hidden' => false)),
63
+ array(array(), array('hidden' => true), 'deny', array('hidden' => true)),
64
+ array(array(), array('hidden' => false), 'deny', array('hidden' => false)),
65
+ array(array('hidden' => true), array(), 'allow', array('hidden' => false)),
66
+ array(array('hidden' => false), array(), 'allow', array('hidden' => false)),
67
+ array(array(), array('hidden' => true), 'allow', array('hidden' => false)),
68
+ array(array(), array('hidden' => false), 'allow', array('hidden' => false)),
69
+ // Complex access options that are defined as array
70
+ array(array('limited' => array('enabled' => true, 'threshold' => 1)), array('limited' => array('enabled' => true, 'threshold' => 2)), 'deny', array('limited' => array('enabled' => true, 'threshold' => 2))),
71
+ array(array('limited' => array('enabled' => true, 'threshold' => 1)), array('limited' => array('enabled' => false, 'threshold' => 2)), 'deny', array('limited' => array('enabled' => true, 'threshold' => 1))),
72
+ array(array('limited' => array('enabled' => false, 'threshold' => 1)), array('limited' => array('enabled' => true, 'threshold' => 2)), 'deny', array('limited' => array('enabled' => true, 'threshold' => 2))),
73
+ array(array('limited' => array('enabled' => false, 'threshold' => 1)), array('limited' => array('enabled' => false, 'threshold' => 2)), 'deny', array('limited' => array('enabled' => false, 'threshold' => 2))),
74
+ array(array('limited' => array('enabled' => true, 'threshold' => 1)), array('limited' => array('enabled' => true, 'threshold' => 2)), 'allow', array('limited' => array('enabled' => true, 'threshold' => 2))),
75
+ array(array('limited' => array('enabled' => true, 'threshold' => 1)), array('limited' => array('enabled' => false, 'threshold' => 2)), 'allow', array('limited' => array('enabled' => false, 'threshold' => 2))),
76
+ array(array('limited' => array('enabled' => false, 'threshold' => 1)), array('limited' => array('enabled' => true, 'threshold' => 2)), 'allow', array('limited' => array('enabled' => false, 'threshold' => 1))),
77
+ array(array('limited' => array('enabled' => false, 'threshold' => 1)), array('limited' => array('enabled' => false, 'threshold' => 2)), 'allow', array('limited' => array('enabled' => false, 'threshold' => 2))),
78
+ // One of the options is not defined
79
+ array(array('limited' => array('enabled' => true, 'threshold' => 1)), array(), 'deny', array('limited' => array('enabled' => true, 'threshold' => 1))),
80
+ array(array(), array('limited' => array('enabled' => true, 'threshold' => 2)), 'deny', array('limited' => array('enabled' => true, 'threshold' => 2))),
81
+ array(array('limited' => array('enabled' => false, 'threshold' => 1)), array(), 'deny', array('limited' => array('enabled' => false, 'threshold' => 1))),
82
+ array(array(), array('limited' => array('enabled' => false, 'threshold' => 2)), 'deny', array('limited' => array('enabled' => false, 'threshold' => 2))),
83
+ array(array('limited' => array('enabled' => true, 'threshold' => 1)), array(), 'allow', array('limited' => array('enabled' => false, 'threshold' => 1))),
84
+ array(array('limited' => array('enabled' => false, 'threshold' => 1)), array(), 'allow', array('limited' => array('enabled' => false, 'threshold' => 1))),
85
+ array(array(), array('limited' => array('enabled' => true, 'threshold' => 2)), 'allow', array('limited' => array('enabled' => false, 'threshold' => 2))),
86
+ array(array(), array('limited' => array('enabled' => false, 'threshold' => 2)), 'allow', array('limited' => array('enabled' => false, 'threshold' => 2))),
87
+ );
88
+ }
89
+
90
+ }
tests/Core/SubjectLoadTest.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 PHPUnit\Framework\TestCase;
11
+
12
+ /**
13
+ * Test if proper subject is picked correctly
14
+ *
15
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
16
+ * @version 6.0.0
17
+ */
18
+ class SubjectLoadTest extends TestCase
19
+ {
20
+
21
+ /**
22
+ * Test that AAM loaded Visitor subject
23
+ *
24
+ * AAM has to load Visitor subject when there is no indicators or authentication
25
+ */
26
+ public function testLoadedVisitorType()
27
+ {
28
+ $subject = AAM::getUser();
29
+
30
+ $this->assertSame('AAM_Core_Subject_Visitor', get_class($subject));
31
+ }
32
+ }
tests/Libs/AuthManagerUserTrait.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Libs;
11
+
12
+ /**
13
+ *
14
+ * @version 6.0.0
15
+ */
16
+ trait AuthManagerUserTrait
17
+ {
18
+ /**
19
+ * @inheritdoc
20
+ */
21
+ public static function setUpBeforeClass()
22
+ {
23
+ // Set current User. Emulate that this is admin login
24
+ wp_set_current_user(AAM_UNITTEST_AUTH_SUBADMIN_USER_ID);
25
+ }
26
+
27
+ /**
28
+ * @inheritdoc
29
+ */
30
+ public static function tearDownAfterClass()
31
+ {
32
+ // Unset the forced user
33
+ wp_set_current_user(0);
34
+ }
35
+
36
+ }
tests/Libs/AuthMultiRoleUserTrait.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Libs;
11
+
12
+ use AAM,
13
+ AAM_Core_Config,
14
+ AAM_Core_Subject_User;
15
+
16
+ /**
17
+ * Trait that setup multi-role support
18
+ *
19
+ * The `AAM_UNITTEST_AUTH_MULTIROLE_USER_ID` constant that is defined in the main
20
+ * phpunit.xml.dist config, has to point to the existing WP user that has more than
21
+ * one role assigned
22
+ *
23
+ * @package AAM\UnitTest
24
+ * @version 6.0.0
25
+ */
26
+ trait AuthMultiRoleUserTrait
27
+ {
28
+
29
+ /**
30
+ * @inheritdoc
31
+ */
32
+ public static function setUpBeforeClass()
33
+ {
34
+ if (is_subclass_of(self::class, 'AAM\UnitTest\Libs\MultiRoleOptionInterface')) {
35
+ // Enable Multiple Role Support
36
+ AAM_Core_Config::set('core.settings.multiSubject', true);
37
+ }
38
+
39
+ // Set current User. Emulate that this is admin login
40
+ wp_set_current_user(AAM_UNITTEST_AUTH_MULTIROLE_USER_ID);
41
+
42
+ // Override AAM current user
43
+ AAM::getInstance()->setUser(
44
+ new AAM_Core_Subject_User(AAM_UNITTEST_AUTH_MULTIROLE_USER_ID)
45
+ );
46
+ }
47
+
48
+ /**
49
+ * @inheritdoc
50
+ */
51
+ public static function tearDownAfterClass()
52
+ {
53
+ // Unset the forced user
54
+ wp_set_current_user(0);
55
+ }
56
+
57
+ }
tests/Libs/AuthUserTrait.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Libs;
11
+
12
+ /**
13
+ * Test access policy integration with core AAM objects
14
+ *
15
+ * @version 6.0.0
16
+ */
17
+ trait AuthUserTrait
18
+ {
19
+ /**
20
+ * @inheritdoc
21
+ */
22
+ public static function setUpBeforeClass()
23
+ {
24
+ // Set current User. Emulate that this is admin login
25
+ wp_set_current_user(AAM_UNITTEST_AUTH_USER_ID);
26
+ }
27
+
28
+ /**
29
+ * @inheritdoc
30
+ */
31
+ public static function tearDownAfterClass()
32
+ {
33
+ // Unset the forced user
34
+ wp_set_current_user(0);
35
+ }
36
+
37
+ }
tests/Libs/MultiRoleOptionInterface.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Libs;
11
+
12
+ /**
13
+ *
14
+ * @version 6.0.0
15
+ */
16
+ interface MultiRoleOptionInterface
17
+ { }
tests/Libs/ResetTrait.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Libs;
11
+
12
+ use AAM,
13
+ AAM_Core_API,
14
+ AAM_Core_Config,
15
+ AAM_Core_AccessSettings,
16
+ AAM_Core_Policy_Factory;
17
+
18
+ /**
19
+ * Reset access settings after each test
20
+ *
21
+ * @package AAM\UnitTest
22
+ * @version 6.0.0
23
+ */
24
+ trait ResetTrait
25
+ {
26
+
27
+ /**
28
+ * Reset all AAM settings to the default
29
+ *
30
+ * @return void
31
+ *
32
+ * @access protected
33
+ * @version 6.0.0
34
+ */
35
+ protected function tearDown()
36
+ {
37
+ // Clear all AAM settings
38
+ AAM_Core_API::clearSettings();
39
+
40
+ // Reset Access Settings repository
41
+ AAM_Core_AccessSettings::getInstance()->reset();
42
+
43
+ // Also clear all the internal caching
44
+ $this->_resetSubjects();
45
+
46
+ if (is_subclass_of(self::class, 'AAM\UnitTest\Libs\MultiRoleOptionInterface')) {
47
+ // Enable Multiple Role Support
48
+ AAM_Core_Config::set('core.settings.multiSubject', true);
49
+ }
50
+
51
+ // Clear WP core cache
52
+ wp_cache_flush();
53
+
54
+ // Reset internal AAM config cache
55
+ AAM_Core_Config::bootstrap();
56
+
57
+ // Reset Access Policy Factory cache
58
+ AAM_Core_Policy_Factory::reset();
59
+ }
60
+
61
+ /**
62
+ * Reset all subjects
63
+ *
64
+ * AAM Subject has internal cache that stored already initiated objects for
65
+ * performance reasons. Reset the cache to allow inheritance mechanism to go
66
+ * through.
67
+ *
68
+ * @return void
69
+ *
70
+ * @access private
71
+ * @see AAM_Core_Subject::getObject
72
+ * @link https://aamplugin.com/reference/plugin#multiple-roles-support
73
+ * @version 6.0.0
74
+ */
75
+ private function _resetSubjects()
76
+ {
77
+ $subject = AAM::getUser();
78
+
79
+ do {
80
+ // Take in consideration that a subject can have multiple parent subjects
81
+ // when "Multiple Roles Support" is enabled
82
+ $subject->flushCache();
83
+ if ($subject->hasSiblings()) {
84
+ $siblings = $subject->getSiblings();
85
+ array_walk($siblings, function($sibling) {
86
+ $sibling->flushCache();
87
+ });
88
+ }
89
+ } while ($subject = $subject->getParent());
90
+ }
91
+
92
+ }
tests/Service/AccessPolicy/PolicyConditionTest.php ADDED
@@ -0,0 +1,543 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AccessPolicy;
11
+
12
+ use AAM_Core_Policy_Condition,
13
+ PHPUnit\Framework\TestCase;
14
+
15
+ /**
16
+ * Test policy condition evaluator
17
+ *
18
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
19
+ * @version 6.0.0
20
+ */
21
+ class PolicyConditionTest extends TestCase
22
+ {
23
+ /**
24
+ * Validate Between condition evaluator
25
+ *
26
+ * @param array $condition
27
+ * @param boolean $expectedResult
28
+ *
29
+ * @return void
30
+ *
31
+ * @access public
32
+ * @dataProvider betweenDataProvider
33
+ * @version 6.0.0
34
+ */
35
+ public function testBetweenCondition($condition, $expectedResult)
36
+ {
37
+ $manager = AAM_Core_Policy_Condition::getInstance();
38
+
39
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
40
+ }
41
+
42
+ /**
43
+ * Between condition data provider
44
+ *
45
+ * @return void
46
+ *
47
+ * @access public
48
+ * @version 6.0.0
49
+ */
50
+ public function betweenDataProvider()
51
+ {
52
+ return array(
53
+ array(array('Between' => array(10 => array(5, 15))), true),
54
+ array(array('Between' => array(10 => array(array(1, 3), array(5, 12)))), true),
55
+ array(array('Between' => array(21 => array(array(1, 3), array(5, 12), array(20, 21)))), true),
56
+ array(array('Between' => array(1 => array(5, 15))), false)
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Validate Equals condition evaluator
62
+ *
63
+ * @param array $condition
64
+ * @param boolean $expectedResult
65
+ *
66
+ * @return void
67
+ *
68
+ * @access public
69
+ * @dataProvider equalsDataProvider
70
+ * @version 6.0.0
71
+ */
72
+ public function testEqualsCondition($condition, $expectedResult)
73
+ {
74
+ $manager = AAM_Core_Policy_Condition::getInstance();
75
+
76
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
77
+ }
78
+
79
+ /**
80
+ * Equals condition data provider
81
+ *
82
+ * @return void
83
+ *
84
+ * @access public
85
+ * @version 6.0.0
86
+ */
87
+ public function equalsDataProvider()
88
+ {
89
+ // Note! Left side of the condition should never be boolean
90
+ return array(
91
+ array(array('Equals' => array(0 => null)), false),
92
+ array(array('Equals' => array(5 => 4)), false),
93
+ array(array('Equals' => array(1 => 1)), true),
94
+ array(array('Equals' => array(1 => '1')), false),
95
+ array(array('Equals' => array('hello' => 'hello')), true),
96
+ array(array('Equals' => array('hello' => 'hello1')), false),
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Validate NotEquals condition evaluator
102
+ *
103
+ * @param array $condition
104
+ * @param boolean $expectedResult
105
+ *
106
+ * @return void
107
+ *
108
+ * @access public
109
+ * @dataProvider notEqualsDataProvider
110
+ * @version 6.0.0
111
+ */
112
+ public function testNotEqualsCondition($condition, $expectedResult)
113
+ {
114
+ $manager = AAM_Core_Policy_Condition::getInstance();
115
+
116
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
117
+ }
118
+
119
+ /**
120
+ * NotEquals condition data provider
121
+ *
122
+ * @return void
123
+ *
124
+ * @access public
125
+ * @version 6.0.0
126
+ */
127
+ public function notEqualsDataProvider()
128
+ {
129
+ // Note! Left side of the condition should never be boolean
130
+ return array(
131
+ array(array('NotEquals' => array(0 => null)), true),
132
+ array(array('NotEquals' => array(5 => 4)), true),
133
+ array(array('NotEquals' => array(1 => 1)), false),
134
+ array(array('NotEquals' => array(1 => '1')), true),
135
+ array(array('NotEquals' => array('2a' => 2)), true),
136
+ array(array('NotEquals' => array('hello' => 'hello')), false),
137
+ array(array('NotEquals' => array('hello' => 'hello1')), true),
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Validate Greater condition evaluator
143
+ *
144
+ * @param array $condition
145
+ * @param boolean $expectedResult
146
+ *
147
+ * @return void
148
+ *
149
+ * @access public
150
+ * @dataProvider greaterDataProvider
151
+ * @version 6.0.0
152
+ */
153
+ public function testGreaterCondition($condition, $expectedResult)
154
+ {
155
+ $manager = AAM_Core_Policy_Condition::getInstance();
156
+
157
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
158
+ }
159
+
160
+ /**
161
+ * Greater condition data provider
162
+ *
163
+ * @return void
164
+ *
165
+ * @access public
166
+ * @version 6.0.0
167
+ */
168
+ public function greaterDataProvider()
169
+ {
170
+ return array(
171
+ array(array('Greater' => array(5 => 1)), true),
172
+ array(array('Greater' => array(15 => 15)), false),
173
+ array(array('Greater' => array(3 => 5)), false)
174
+ );
175
+ }
176
+
177
+ /**
178
+ * Validate Less condition evaluator
179
+ *
180
+ * @param array $condition
181
+ * @param boolean $expectedResult
182
+ *
183
+ * @return void
184
+ *
185
+ * @access public
186
+ * @dataProvider lessDataProvider
187
+ * @version 6.0.0
188
+ */
189
+ public function testLessCondition($condition, $expectedResult)
190
+ {
191
+ $manager = AAM_Core_Policy_Condition::getInstance();
192
+
193
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
194
+ }
195
+
196
+ /**
197
+ * Less condition data provider
198
+ *
199
+ * @return void
200
+ *
201
+ * @access public
202
+ * @version 6.0.0
203
+ */
204
+ public function lessDataProvider()
205
+ {
206
+ return array(
207
+ array(array('Less' => array(5 => 10)), true),
208
+ array(array('Less' => array(15 => 15)), false),
209
+ array(array('Less' => array(13 => 5)), false)
210
+ );
211
+ }
212
+
213
+ /**
214
+ * Validate greater or equals condition evaluator
215
+ *
216
+ * @param array $condition
217
+ * @param boolean $expectedResult
218
+ *
219
+ * @return void
220
+ *
221
+ * @access public
222
+ * @dataProvider greaterOrEqualsDataProvider
223
+ * @version 6.0.0
224
+ */
225
+ public function testGreaterOrEqualsCondition($condition, $expectedResult)
226
+ {
227
+ $manager = AAM_Core_Policy_Condition::getInstance();
228
+
229
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
230
+ }
231
+
232
+ /**
233
+ * Greater or equals condition data provider
234
+ *
235
+ * @return void
236
+ *
237
+ * @access public
238
+ * @version 6.0.0
239
+ */
240
+ public function greaterOrEqualsDataProvider()
241
+ {
242
+ return array(
243
+ array(array('GreaterOrEquals' => array(5 => 1)), true),
244
+ array(array('GreaterOrEquals' => array(15 => 15)), true),
245
+ array(array('GreaterOrEquals' => array(3 => 5)), false)
246
+ );
247
+ }
248
+
249
+ /**
250
+ * Validate Less or equals condition evaluator
251
+ *
252
+ * @param array $condition
253
+ * @param boolean $expectedResult
254
+ *
255
+ * @return void
256
+ *
257
+ * @access public
258
+ * @dataProvider lessOrEqualsDataProvider
259
+ * @version 6.0.0
260
+ */
261
+ public function testLessOrEqualsCondition($condition, $expectedResult)
262
+ {
263
+ $manager = AAM_Core_Policy_Condition::getInstance();
264
+
265
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
266
+ }
267
+
268
+ /**
269
+ * Less or equals condition data provider
270
+ *
271
+ * @return void
272
+ *
273
+ * @access public
274
+ * @version 6.0.0
275
+ */
276
+ public function lessOrEqualsDataProvider()
277
+ {
278
+ return array(
279
+ array(array('LessOrEquals' => array(5 => 10)), true),
280
+ array(array('LessOrEquals' => array(15 => 15)), true),
281
+ array(array('LessOrEquals' => array(13 => 5)), false)
282
+ );
283
+ }
284
+
285
+ /**
286
+ * Validate In condition evaluator
287
+ *
288
+ * @param array $condition
289
+ * @param boolean $expectedResult
290
+ *
291
+ * @return void
292
+ *
293
+ * @access public
294
+ * @dataProvider inDataProvider
295
+ * @version 6.0.0
296
+ */
297
+ public function testInCondition($condition, $expectedResult)
298
+ {
299
+ $manager = AAM_Core_Policy_Condition::getInstance();
300
+
301
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
302
+ }
303
+
304
+ /**
305
+ * In condition data provider
306
+ *
307
+ * @return void
308
+ *
309
+ * @access public
310
+ * @version 6.0.0
311
+ */
312
+ public function inDataProvider()
313
+ {
314
+ return array(
315
+ array(array('In' => array('test' => array('test', 'test1'))), true),
316
+ array(array('In' => array(2 => array(2, 5, 7))), true),
317
+ array(array('In' => array('no' => array('yes', 'maybe'))), false)
318
+ );
319
+ }
320
+
321
+ /**
322
+ * Validate NotIn condition evaluator
323
+ *
324
+ * @param array $condition
325
+ * @param boolean $expectedResult
326
+ *
327
+ * @return void
328
+ *
329
+ * @access public
330
+ * @dataProvider notInDataProvider
331
+ * @version 6.0.0
332
+ */
333
+ public function testNotInCondition($condition, $expectedResult)
334
+ {
335
+ $manager = AAM_Core_Policy_Condition::getInstance();
336
+
337
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
338
+ }
339
+
340
+ /**
341
+ * NotIn condition data provider
342
+ *
343
+ * @return void
344
+ *
345
+ * @access public
346
+ * @version 6.0.0
347
+ */
348
+ public function notInDataProvider()
349
+ {
350
+ return array(
351
+ array(array('NotIn' => array('test' => array('test', 'test1'))), false),
352
+ array(array('NotIn' => array(2 => array(2, 5, 7))), false),
353
+ array(array('NotIn' => array('no' => array('yes', 'maybe'))), true)
354
+ );
355
+ }
356
+
357
+ /**
358
+ * Validate Like condition evaluator
359
+ *
360
+ * @param array $condition
361
+ * @param boolean $expectedResult
362
+ *
363
+ * @return void
364
+ *
365
+ * @access public
366
+ * @dataProvider likeDataProvider
367
+ * @version 6.0.0
368
+ */
369
+ public function testLikeCondition($condition, $expectedResult)
370
+ {
371
+ $manager = AAM_Core_Policy_Condition::getInstance();
372
+
373
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
374
+ }
375
+
376
+ /**
377
+ * Like condition data provider
378
+ *
379
+ * @return void
380
+ *
381
+ * @access public
382
+ * @version 6.0.0
383
+ */
384
+ public function likeDataProvider()
385
+ {
386
+ return array(
387
+ array(array('Like' => array('Lucy van Pelt' => 'Lucy*')), true),
388
+ array(array('Like' => array('Lucy van Pelt' => '*Pelt')), true),
389
+ array(array('Like' => array('Lucy van Pelt' => 'Lucy*Pelt')), true),
390
+ array(array('Like' => array('Lucy van Pelt' => 'Johny*Pelt')), false)
391
+ );
392
+ }
393
+
394
+ /**
395
+ * Validate NotLike condition evaluator
396
+ *
397
+ * @param array $condition
398
+ * @param boolean $expectedResult
399
+ *
400
+ * @return void
401
+ *
402
+ * @access public
403
+ * @dataProvider notLikeDataProvider
404
+ * @version 6.0.0
405
+ */
406
+ public function testNotLikeCondition($condition, $expectedResult)
407
+ {
408
+ $manager = AAM_Core_Policy_Condition::getInstance();
409
+
410
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
411
+ }
412
+
413
+ /**
414
+ * NotLike condition data provider
415
+ *
416
+ * @return void
417
+ *
418
+ * @access public
419
+ * @version 6.0.0
420
+ */
421
+ public function notLikeDataProvider()
422
+ {
423
+ return array(
424
+ array(array('NotLike' => array('Lucy van Pelt' => 'Lucy*')), false),
425
+ array(array('NotLike' => array('Lucy van Pelt' => '*Pelt')), false),
426
+ array(array('NotLike' => array('Lucy van Pelt' => 'Lucy*Pelt')), false),
427
+ array(array('NotLike' => array('Lucy van Pelt' => 'Johny*Pelt')), true)
428
+ );
429
+ }
430
+
431
+ /**
432
+ * Validate RegEx condition evaluator
433
+ *
434
+ * @param array $condition
435
+ * @param boolean $expectedResult
436
+ *
437
+ * @return void
438
+ *
439
+ * @access public
440
+ * @dataProvider regExDataProvider
441
+ * @version 6.0.0
442
+ */
443
+ public function testRegExCondition($condition, $expectedResult)
444
+ {
445
+ $manager = AAM_Core_Policy_Condition::getInstance();
446
+
447
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
448
+ }
449
+
450
+ /**
451
+ * RegEx condition data provider
452
+ *
453
+ * @return void
454
+ *
455
+ * @access public
456
+ * @version 6.0.0
457
+ */
458
+ public function regExDataProvider()
459
+ {
460
+ return array(
461
+ array(array('RegEx' => array('Hello World' => '/^[\w\s]+$/i')), true),
462
+ array(array('RegEx' => array('Hello World!' => '/^[\w]+$/')), false)
463
+ );
464
+ }
465
+
466
+ /**
467
+ * Validate condition type casting
468
+ *
469
+ * @param array $condition
470
+ * @param boolean $expectedResult
471
+ *
472
+ * @return void
473
+ *
474
+ * @access public
475
+ * @dataProvider typeCastingDataProvider
476
+ * @version 6.0.0
477
+ */
478
+ public function testTypeCasting($condition, $expectedResult)
479
+ {
480
+ $manager = AAM_Core_Policy_Condition::getInstance();
481
+
482
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
483
+ }
484
+
485
+ /**
486
+ * Type casting data provider
487
+ *
488
+ * @return void
489
+ *
490
+ * @access public
491
+ * @version 6.0.0
492
+ */
493
+ public function typeCastingDataProvider()
494
+ {
495
+ return array(
496
+ array(array('Equals' => array('(*int)1' => 1)), true),
497
+ array(array('Equals' => array('(*bool)false' => false)), true),
498
+ array(array('Equals' => array('(*boolean)true' => true)), true),
499
+ array(array('Equals' => array('(*string)1' => '1')), true),
500
+ array(array('Equals' => array('(*null)' => null)), true),
501
+ array(array('Equals' => array('(*array)[2,3]' => array(2,3))), true),
502
+ array(array('Equals' => array('(*ip)192.168.1.1' => inet_pton('192.168.1.1'))), true)
503
+ );
504
+ }
505
+
506
+ /**
507
+ * Validate complex condition
508
+ *
509
+ * @param array $condition
510
+ * @param boolean $expectedResult
511
+ *
512
+ * @return void
513
+ *
514
+ * @access public
515
+ * @dataProvider complexDataProvider
516
+ * @version 6.0.0
517
+ */
518
+ public function testComplexCondition($condition, $expectedResult)
519
+ {
520
+ $manager = AAM_Core_Policy_Condition::getInstance();
521
+
522
+ $this->assertEquals($expectedResult, $manager->evaluate($condition));
523
+ }
524
+
525
+ /**
526
+ * Complex condition data provider
527
+ *
528
+ * @return void
529
+ *
530
+ * @access public
531
+ * @version 6.0.0
532
+ */
533
+ public function complexDataProvider()
534
+ {
535
+ return array(
536
+ array(array(
537
+ 'Equals' => array('(*int)1' => 1),
538
+ 'NotEquals' => array('2a' => 2)
539
+ ), true)
540
+ );
541
+ }
542
+
543
+ }
tests/Service/AccessPolicy/PolicyManagerTest.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AccessPolicy;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Policy,
14
+ AAM_Core_Policy_Manager,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait,
17
+ AAM\UnitTest\Libs\AuthUserTrait;
18
+
19
+ /**
20
+ * Test policy manager
21
+ *
22
+ * Make sure that access policies are parsed properly
23
+ *
24
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
25
+ * @version 6.0.0
26
+ */
27
+ class PolicyManagerTest extends TestCase
28
+ {
29
+ use ResetTrait,
30
+ AuthUserTrait;
31
+
32
+ /**
33
+ * Test simple policy load
34
+ *
35
+ * @return void
36
+ *
37
+ * @access public
38
+ * @version 6.0.0
39
+ */
40
+ public function testSimplePolicy()
41
+ {
42
+ $stub = $this->prepareManagerStub('simple-policy');
43
+
44
+ $this->assertEquals($stub->getTree(), array(
45
+ 'Statement' => array(
46
+ 'backendmenu:edit.php' => array(
47
+ 'Effect' => 'deny'
48
+ )
49
+ ),
50
+ 'Param' => array()
51
+ ));
52
+ }
53
+
54
+ /**
55
+ * Test simple policy load
56
+ *
57
+ * @return void
58
+ *
59
+ * @access public
60
+ * @version 6.0.0
61
+ */
62
+ public function testSimplePolicyWithAction()
63
+ {
64
+ $stub = $this->prepareManagerStub('simple-policy-with-action');
65
+
66
+ $this->assertEquals($stub->getTree(), array(
67
+ 'Statement' => array(
68
+ 'capability:switch_themes:aam:toggle' => array(
69
+ 'Effect' => 'deny'
70
+ )
71
+ ),
72
+ 'Param' => array()
73
+ ));
74
+ }
75
+
76
+ /**
77
+ * Test that site options are overwritten by policy
78
+ *
79
+ * @return void
80
+ *
81
+ * @access public
82
+ * @version 6.0.0
83
+ */
84
+ public function testOptionOverridePolicy()
85
+ {
86
+ $stub = $this->prepareManagerStub('option-override-policy');
87
+
88
+ $this->assertEquals($stub->getTree(), array(
89
+ 'Statement' => array(),
90
+ 'Param' => array(
91
+ 'option:unittest' => array(
92
+ 'Key' => 'option:unittest',
93
+ 'Value' => 'unititest.me'
94
+ )
95
+ )
96
+ ));
97
+
98
+ $this->assertEquals('unititest.me', get_option('unittest'));
99
+ $this->assertEquals('unititest.me', get_site_option('unittest'));
100
+ }
101
+
102
+ /**
103
+ * Test that dynamic markers are replaced with actual value
104
+ *
105
+ * @return void
106
+ *
107
+ * @access public
108
+ * @version 6.0.0
109
+ */
110
+ public function testDynamicResourcePolicy()
111
+ {
112
+ $stub = $this->prepareManagerStub('dynamic-resource');
113
+
114
+ $this->assertArrayHasKey('post:post:1:read', $stub->getTree()['Statement']);
115
+ }
116
+
117
+ /**
118
+ * Test that dynamic markers are replaced with actual value
119
+ *
120
+ * @return void
121
+ *
122
+ * @access public
123
+ * @version 6.0.0
124
+ */
125
+ public function testDynamicParamPolicy()
126
+ {
127
+ $stub = $this->prepareManagerStub('dynamic-param');
128
+
129
+ $this->assertArrayHasKey('hello-world-admin', $stub->getTree()['Param']);
130
+ }
131
+
132
+ /**
133
+ * Prepare proper policy manager stub
134
+ *
135
+ * @param string $policy_file
136
+ *
137
+ * @return object
138
+ *
139
+ * @access protected
140
+ * @version 6.0.0
141
+ */
142
+ protected function prepareManagerStub($policy_file)
143
+ {
144
+ // Fake the assigned policy to the user
145
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Policy::OBJECT_TYPE);
146
+ $object->updateOptionItem(1, true)->save();
147
+
148
+ // Create a stub for the SomeClass class.
149
+ $stub = $this->getMockBuilder(AAM_Core_Policy_Manager::class)
150
+ ->setConstructorArgs(array(AAM::getUser()))
151
+ ->setMethods(array('fetchPolicies'))
152
+ ->getMock();
153
+
154
+ // Configure the stub
155
+ $stub->method('fetchPolicies')->willReturn(array(
156
+ (object) array(
157
+ 'ID' => 1,
158
+ 'post_content' => file_get_contents(
159
+ __DIR__ . '/policies/' . $policy_file . '.json'
160
+ )
161
+ )
162
+ ));
163
+
164
+ // Initialize the policy tree
165
+ $stub->initialize();
166
+
167
+ return $stub;
168
+ }
169
+
170
+ }
tests/Service/AccessPolicy/PolicyServiceIntegrationTest.php ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AccessPolicy;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Uri,
14
+ AAM_Core_Object_Post,
15
+ AAM_Core_Object_Menu,
16
+ AAM_Core_Object_Policy,
17
+ AAM_Core_Policy_Factory,
18
+ AAM_Core_Object_Toolbar,
19
+ AAM_Core_Object_Metabox,
20
+ PHPUnit\Framework\TestCase,
21
+ AAM\UnitTest\Libs\ResetTrait,
22
+ AAM\UnitTest\Libs\AuthUserTrait;
23
+
24
+ /**
25
+ * Test access policy integration with core AAM services
26
+ *
27
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
28
+ * @version 6.0.0
29
+ */
30
+ class PolicyServiceIntegrationTest extends TestCase
31
+ {
32
+ use ResetTrait,
33
+ AuthUserTrait;
34
+
35
+ /**
36
+ * Test that Access Policy integrates with Admin Menu service
37
+ *
38
+ * @return void
39
+ *
40
+ * @access public
41
+ * @version 6.0.0
42
+ */
43
+ public function testAdminMenuIntegration()
44
+ {
45
+ $this->preparePlayground('admin-menu');
46
+
47
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
48
+
49
+ $this->assertTrue($object->isRestricted('edit.php'));
50
+ }
51
+
52
+ /**
53
+ * Test that Access Policy integrates with Toolbar service
54
+ *
55
+ * @return void
56
+ *
57
+ * @access public
58
+ * @version 6.0.0
59
+ */
60
+ public function testToolbarIntegration()
61
+ {
62
+ $this->preparePlayground('toolbar');
63
+
64
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
65
+
66
+ $this->assertTrue($object->isHidden('about'));
67
+ }
68
+
69
+ /**
70
+ * Test that Access Policy integrates with Metaboxes & Widgets service
71
+ *
72
+ * @return void
73
+ *
74
+ * @access public
75
+ * @version 6.0.0
76
+ */
77
+ public function testMetaboxIntegration()
78
+ {
79
+ $this->preparePlayground('metabox');
80
+
81
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
82
+
83
+ $this->assertTrue($object->isHidden('widgets', 'WP_Widget_Pages'));
84
+ $this->assertTrue($object->isHidden('aam_policy', 'revisionsdiv'));
85
+ }
86
+
87
+ /**
88
+ * Test that Access Policy integrates with Content service for simple actions
89
+ *
90
+ * @return void
91
+ *
92
+ * @access public
93
+ * @version 6.0.0
94
+ */
95
+ public function testContentSimpleActionsIntegration()
96
+ {
97
+ $this->preparePlayground('post-simple-actions');
98
+
99
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
100
+
101
+ $this->assertFalse($object->isAllowedTo('edit'));
102
+ $this->assertFalse($object->isAllowedTo('delete'));
103
+ $this->assertFalse($object->isAllowedTo('publish'));
104
+ $this->assertFalse($object->isAllowedTo('comment'));
105
+ }
106
+
107
+ /**
108
+ * Test that Access Policy integrates with Content service for Restricted action
109
+ *
110
+ * @return void
111
+ *
112
+ * @access public
113
+ * @version 6.0.0
114
+ */
115
+ public function testContentRestrictedIntegration()
116
+ {
117
+ $this->preparePlayground('post-restricted');
118
+
119
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
120
+
121
+ $this->assertTrue($object->is('restricted'));
122
+ }
123
+
124
+ /**
125
+ * Test that Access Policy integrates with Content service for Hidden action
126
+ *
127
+ * @return void
128
+ *
129
+ * @access public
130
+ * @version 6.0.0
131
+ */
132
+ public function testContentHiddenIntegration()
133
+ {
134
+ $this->preparePlayground('post-hidden');
135
+
136
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
137
+
138
+ $this->assertTrue($object->is('hidden'));
139
+
140
+ // Verify that post is no longer in the list of posts
141
+ $posts = get_posts(array(
142
+ 'post_type' => 'post',
143
+ 'fields' => 'ids',
144
+ 'suppress_filters' => false
145
+ ));
146
+
147
+ // First, confirm that post is in the array of posts
148
+ $this->assertFalse(in_array(1, $posts));
149
+ }
150
+
151
+ /**
152
+ * Test that Access Policy integrates with Content service for Password protected
153
+ * action
154
+ *
155
+ * @return void
156
+ *
157
+ * @access public
158
+ * @version 6.0.0
159
+ */
160
+ public function testContentComplexActionsIntegration()
161
+ {
162
+ $this->preparePlayground('post-complex-actions');
163
+
164
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
165
+
166
+ $this->assertTrue($object->is('protected'));
167
+ $this->assertEquals(array(
168
+ 'enabled' => true,
169
+ 'password' => '123456'
170
+ ), $object->get('protected'));
171
+
172
+ $this->assertTrue($object->has('teaser'));
173
+ $this->assertEquals(array(
174
+ 'enabled' => true,
175
+ 'message' => 'This is just a teaser message'
176
+ ), $object->get('teaser'));
177
+ }
178
+
179
+ /**
180
+ * Test that Access Policy integrates with Content service for Redirected action
181
+ * where page ID is specified
182
+ *
183
+ * @return void
184
+ *
185
+ * @access public
186
+ * @version 6.0.0
187
+ */
188
+ public function testContentRedirectPageIdIntegration()
189
+ {
190
+ $this->preparePlayground('post-redirect-page-id');
191
+
192
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
193
+
194
+ $this->assertTrue($object->is('redirected'));
195
+ $this->assertEquals(array(
196
+ 'enabled' => true,
197
+ 'type' => 'page',
198
+ 'httpCode' => 301,
199
+ 'destination' => 2
200
+ ), $object->get('redirected'));
201
+ }
202
+
203
+ /**
204
+ * Test that Access Policy integrates with Content service for Redirected action
205
+ * where page slug is specified
206
+ *
207
+ * @return void
208
+ *
209
+ * @access public
210
+ * @version 6.0.0
211
+ */
212
+ public function testContentRedirectPageSlugIntegration()
213
+ {
214
+ $this->preparePlayground('post-redirect-page-slug');
215
+
216
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
217
+
218
+ $this->assertTrue($object->is('redirected'));
219
+ $this->assertEquals(array(
220
+ 'enabled' => true,
221
+ 'type' => 'page',
222
+ 'httpCode' => 301,
223
+ 'destination' => get_page_by_path('sample-page', OBJECT)->ID
224
+ ), $object->get('redirected'));
225
+ }
226
+
227
+ /**
228
+ * Test that Access Policy integrates with Content service for Redirected action
229
+ * where URL is specified
230
+ *
231
+ * @return void
232
+ *
233
+ * @access public
234
+ * @version 6.0.0
235
+ */
236
+ public function testContentRedirectUrlIntegration()
237
+ {
238
+ $this->preparePlayground('post-redirect-url');
239
+
240
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
241
+
242
+ $this->assertTrue($object->is('redirected'));
243
+ $this->assertEquals(array(
244
+ 'enabled' => true,
245
+ 'type' => 'url',
246
+ 'httpCode' => 307,
247
+ 'destination' => 'https://aamplugin.com'
248
+ ), $object->get('redirected'));
249
+ }
250
+
251
+ /**
252
+ * Test that Access Policy integrates with Content service for Redirected action
253
+ * where callback is specified
254
+ *
255
+ * @return void
256
+ *
257
+ * @access public
258
+ * @version 6.0.0
259
+ */
260
+ public function testContentRedirectCallbackIntegration()
261
+ {
262
+ $this->preparePlayground('post-redirect-callback');
263
+
264
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Post::OBJECT_TYPE, 1);
265
+
266
+ $this->assertTrue($object->is('redirected'));
267
+ $this->assertEquals(array(
268
+ 'enabled' => true,
269
+ 'type' => 'callback',
270
+ 'httpCode' => 307,
271
+ 'destination' => 'AAM\Callback\Main::helloWorld'
272
+ ), $object->get('redirected'));
273
+ }
274
+
275
+ /**
276
+ * Test that Access Policy integrates with URI service for all possible permutation
277
+ * of actions
278
+ *
279
+ * @return void
280
+ *
281
+ * @access public
282
+ * @version 6.0.0
283
+ */
284
+ public function testUriIntegration()
285
+ {
286
+ $this->preparePlayground('uri');
287
+
288
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
289
+
290
+ $this->assertEquals(array(
291
+ 'type' => 'default',
292
+ ), $object->findMatch('/hello-world-1/'));
293
+
294
+ $this->assertEquals(array(
295
+ 'type' => 'message',
296
+ 'action' => 'Access Is Denied',
297
+ 'code' => 307
298
+ ), $object->findMatch('/hello-world-2/'));
299
+
300
+ $this->assertEquals(array(
301
+ 'type' => 'page',
302
+ 'action' => 2,
303
+ 'code' => 307
304
+ ), $object->findMatch('/hello-world-3/'));
305
+
306
+ $this->assertEquals(array(
307
+ 'type' => 'page',
308
+ 'action' => get_page_by_path('sample-page', OBJECT, 'page')->ID,
309
+ 'code' => 307
310
+ ), $object->findMatch('/hello-world-4/'));
311
+
312
+ $this->assertEquals(array(
313
+ 'type' => 'url',
314
+ 'action' => '/another-location',
315
+ 'code' => 303
316
+ ), $object->findMatch('/hello-world-5/'));
317
+
318
+ $this->assertEquals(array(
319
+ 'type' => 'callback',
320
+ 'action' => 'AAM\\Callback\\Main::helloWorld',
321
+ 'code' => 307
322
+ ), $object->findMatch('/hello-world-6/'));
323
+
324
+ $this->assertEquals(array(
325
+ 'type' => 'login',
326
+ 'action' => null,
327
+ 'code' => 401
328
+ ), $object->findMatch('/hello-world-7/'));
329
+ }
330
+
331
+ /**
332
+ * Test ability to toggle the ability activate/deactivate individual plugin with
333
+ * Access Policy
334
+ *
335
+ * @return void
336
+ *
337
+ * @access public
338
+ * @version 6.0.0
339
+ */
340
+ public function testSinglePluginIntegration()
341
+ {
342
+ // Making sure that current user can activate/deactivate plugin
343
+ $this->assertTrue(current_user_can('activate_plugin', 'advanced-access-manager'));
344
+ $this->assertTrue(current_user_can('deactivate_plugin', 'advanced-access-manager'));
345
+
346
+ $this->preparePlayground('single-plugin');
347
+
348
+ // Making sure that current user no longer has these privileges
349
+ $this->assertFalse(current_user_can('activate_plugin', 'advanced-access-manager'));
350
+ $this->assertFalse(current_user_can('deactivate_plugin', 'advanced-access-manager'));
351
+ }
352
+
353
+ /**
354
+ * Test ability to toggle the ability activate/deactivate individual plugin with
355
+ * Access Policy
356
+ *
357
+ * @return void
358
+ *
359
+ * @access public
360
+ * @version 6.0.0
361
+ */
362
+ public function testAllPluginsIntegration()
363
+ {
364
+ // Making sure that current user can perform all 4 basic actions
365
+ $this->assertTrue(current_user_can('install_plugins'));
366
+ $this->assertTrue(current_user_can('update_plugins'));
367
+ $this->assertTrue(current_user_can('edit_plugins'));
368
+ $this->assertTrue(current_user_can('delete_plugins'));
369
+
370
+ $this->preparePlayground('plugins');
371
+
372
+ // Making sure that current user no longer has these privileges
373
+ $this->assertFalse(current_user_can('install_plugins'));
374
+ $this->assertFalse(current_user_can('update_plugins'));
375
+ $this->assertFalse(current_user_can('edit_plugins'));
376
+ $this->assertFalse(current_user_can('delete_plugins'));
377
+ }
378
+
379
+ /**
380
+ * Prepare the environment
381
+ *
382
+ * Update Unit Test access policy with proper policy
383
+ *
384
+ * @param string $policy_file
385
+ *
386
+ * @return void
387
+ *
388
+ * @access protected
389
+ * @version 6.0.0
390
+ */
391
+ protected function preparePlayground($policy_file)
392
+ {
393
+ // Update existing Access Policy with new policy
394
+ wp_update_post(array(
395
+ 'ID' => AAM_UNITTEST_ACCESS_POLICY_ID,
396
+ 'post_content' => file_get_contents(
397
+ __DIR__ . '/policies/' . $policy_file . '.json'
398
+ )
399
+ ));
400
+
401
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Policy::OBJECT_TYPE);
402
+ $this->assertTrue(
403
+ $object->updateOptionItem(AAM_UNITTEST_ACCESS_POLICY_ID, true)->save()
404
+ );
405
+
406
+ // Reset all internal cache
407
+ $this->_resetSubjects();
408
+
409
+ // Reset Access Policy Factory cache
410
+ AAM_Core_Policy_Factory::reset();
411
+ }
412
+
413
+ }
tests/Service/AccessPolicy/PolicyTokenTest.php ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AccessPolicy;
11
+
12
+ use AAM,
13
+ AAM_Core_Jwt_Issuer,
14
+ AAM_Core_Policy_Token,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait;
17
+
18
+ /**
19
+ * Test policy token evaluator
20
+ *
21
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
22
+ * @version 6.0.0
23
+ */
24
+ class PolicyTokenTest extends TestCase
25
+ {
26
+
27
+ use ResetTrait;
28
+
29
+ /**
30
+ * Validate correct USER token evaluation
31
+ *
32
+ * @return void
33
+ *
34
+ * @access public
35
+ * @version 6.0.0
36
+ */
37
+ public function testUserTokenEvaluation()
38
+ {
39
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
40
+
41
+ // Set current User. Emulate that this is admin login
42
+ wp_set_current_user(AAM_UNITTEST_AUTH_USER_ID);
43
+
44
+ $caps = array();
45
+ foreach ((array) AAM::getUser()->allcaps as $cap => $effect) {
46
+ if (!empty($effect)) {
47
+ $caps[] = $cap;
48
+ }
49
+ }
50
+
51
+ $cases = array(
52
+ array('${USER.ID}', 1),
53
+ array('${USER.ip}', '127.0.0.1'),
54
+ array('${USER.ipAddress}', '127.0.0.1'),
55
+ array('${USER.authenticated}', true),
56
+ array('${USER.isAuthenticated}', true),
57
+ array('${USER.capabilities}', json_encode($caps)),
58
+ array('${USER.caps}', json_encode($caps)),
59
+ );
60
+
61
+ foreach($cases as $case) {
62
+ $this->assertEquals(
63
+ $case[1], AAM_Core_Policy_Token::evaluate($case[0], array($case[0]))
64
+ );
65
+ }
66
+
67
+ // Reset user
68
+ wp_set_current_user(0);
69
+ unset($_SERVER['REMOTE_ADDR']);
70
+ }
71
+
72
+ /**
73
+ * Validate correct USER_META token evaluation
74
+ *
75
+ * @return void
76
+ *
77
+ * @access public
78
+ * @version 6.0.0
79
+ */
80
+ public function testUserMetaTokenEvaluation()
81
+ {
82
+ // Set current User. Emulate that this is admin login
83
+ wp_set_current_user(AAM_UNITTEST_AUTH_USER_ID);
84
+
85
+ add_user_meta(AAM_UNITTEST_AUTH_USER_ID, 'aam_unittest', 'hello');
86
+
87
+ $this->assertEquals(
88
+ 'hello',
89
+ AAM_Core_Policy_Token::evaluate(
90
+ '${USER_META.aam_unittest}', array('${USER_META.aam_unittest}')
91
+ )
92
+ );
93
+
94
+ // Reset user
95
+ wp_set_current_user(0);
96
+ unset($_SERVER['REMOTE_ADDR']);
97
+ delete_user_meta(AAM_UNITTEST_AUTH_USER_ID, 'aam_unittest');
98
+ }
99
+
100
+ /**
101
+ * Test DATETIME token evaluation
102
+ *
103
+ * @return void
104
+ *
105
+ * @access public
106
+ * @version 6.0.0
107
+ */
108
+ public function testDateTimeTokenEvaluation()
109
+ {
110
+ $this->assertEquals(
111
+ date('Y-m-d'),
112
+ AAM_Core_Policy_Token::evaluate(
113
+ '${DATETIME.Y-m-d}', array('${DATETIME.Y-m-d}')
114
+ )
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Test HTTP_* and PHP_* tokens evaluation
120
+ *
121
+ * @return void
122
+ *
123
+ * @access public
124
+ * @version 6.0.0
125
+ */
126
+ public function testHttpTokensEvaluation()
127
+ {
128
+ // Fake data
129
+ $_GET['aam_test'] = "1a";
130
+ $_POST['aam_test'] = "1b";
131
+ $_COOKIE['aam_test'] = "1c";
132
+ $_SERVER['aam_test'] = "1d";
133
+
134
+ $this->assertEquals(
135
+ '1a', AAM_Core_Policy_Token::evaluate('${HTTP_GET.aam_test}', array('${HTTP_GET.aam_test}'))
136
+ );
137
+
138
+ $this->assertEquals(
139
+ '1a', AAM_Core_Policy_Token::evaluate('${HTTP_QUERY.aam_test}', array('${HTTP_QUERY.aam_test}'))
140
+ );
141
+
142
+ $this->assertEquals(
143
+ '1b', AAM_Core_Policy_Token::evaluate('${HTTP_POST.aam_test}', array('${HTTP_POST.aam_test}'))
144
+ );
145
+
146
+ $this->assertEquals(
147
+ '1c', AAM_Core_Policy_Token::evaluate('${HTTP_COOKIE.aam_test}', array('${HTTP_COOKIE.aam_test}'))
148
+ );
149
+
150
+ $this->assertEquals(
151
+ '1d', AAM_Core_Policy_Token::evaluate('${PHP_SERVER.aam_test}', array('${PHP_SERVER.aam_test}'))
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Test ARGS token evaluation
157
+ *
158
+ * @return void
159
+ *
160
+ * @access public
161
+ * @version 6.0.0
162
+ */
163
+ public function testArgTokenEvaluation()
164
+ {
165
+ $this->assertEquals(
166
+ '1a',
167
+ AAM_Core_Policy_Token::evaluate(
168
+ '${ARGS.test}', array('${ARGS.test}'), array('test' => '1a')
169
+ )
170
+ );
171
+ }
172
+
173
+ /**
174
+ * Test CONST token evaluation
175
+ *
176
+ * @return void
177
+ *
178
+ * @access public
179
+ * @version 6.0.0
180
+ */
181
+ public function testConstTokenEvaluation()
182
+ {
183
+ $this->assertEquals(
184
+ AAM_VERSION,
185
+ AAM_Core_Policy_Token::evaluate(
186
+ '${CONST.AAM_VERSION}', array('${CONST.AAM_VERSION}')
187
+ )
188
+ );
189
+ }
190
+
191
+ /**
192
+ * Test WP_OPTION token evaluation
193
+ *
194
+ * @return void
195
+ *
196
+ * @access public
197
+ * @version 6.0.0
198
+ */
199
+ public function testWpOptionTokenEvaluation()
200
+ {
201
+ $this->assertEquals(
202
+ get_option('siteurl'),
203
+ AAM_Core_Policy_Token::evaluate(
204
+ '${WP_OPTION.siteurl}', array('${WP_OPTION.siteurl}')
205
+ )
206
+ );
207
+ }
208
+
209
+ /**
210
+ * Test JWT token evaluation
211
+ *
212
+ * @return void
213
+ *
214
+ * @access public
215
+ * @version 6.0.0
216
+ */
217
+ public function testJwtTokenEvaluation()
218
+ {
219
+ // generate token
220
+ $result = AAM_Core_Jwt_Issuer::getInstance()->issueToken(
221
+ array('testProp' => 'helloWorld')
222
+ );
223
+
224
+ $_SERVER['HTTP_AUTHENTICATION'] = $result->token;
225
+
226
+ $this->assertEquals(
227
+ 'helloWorld',
228
+ AAM_Core_Policy_Token::evaluate(
229
+ '${JWT.testProp}', array('${JWT.testProp}')
230
+ )
231
+ );
232
+
233
+ unset($_SERVER['HTTP_AUTHENTICATION']);
234
+ }
235
+
236
+ }
tests/Service/AccessPolicy/PolicyUserRoleIntegrationTest.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AccessPolicy;
11
+
12
+ use AAM,
13
+ AAM_Core_API,
14
+ AAM_Core_Config,
15
+ AAM_Core_Policy_Factory,
16
+ AAM_Core_AccessSettings,
17
+ PHPUnit\Framework\TestCase;
18
+
19
+
20
+ /**
21
+ * Test access policy integration with core user roles system
22
+ *
23
+ * @version 6.0.0
24
+ */
25
+ class PolicyUserRoleIntegrationTest extends TestCase
26
+ {
27
+
28
+ /**
29
+ * Test that policy allows to assign or deprive specific capabilities
30
+ *
31
+ * @return void
32
+ *
33
+ * @access public
34
+ * @version 6.0.0
35
+ */
36
+ public function testCapabilityAdded()
37
+ {
38
+ $this->preparePlayground('capability-changes');
39
+
40
+ // Reset current user to trigger policy changes
41
+ wp_set_current_user(AAM_UNITTEST_AUTH_USER_ID);
42
+
43
+ $this->assertFalse(current_user_can('switch_themes'));
44
+ $this->assertTrue(current_user_can('hello_world'));
45
+ }
46
+
47
+ /**
48
+ * Test that policy allows to add new role to user
49
+ *
50
+ * @return void
51
+ *
52
+ * @access public
53
+ * @version 6.0.0
54
+ */
55
+ public function testAddedRole()
56
+ {
57
+ $this->preparePlayground('role-add');
58
+
59
+ // Reset current user to trigger policy changes
60
+ wp_set_current_user(AAM_UNITTEST_AUTH_USER_ID);
61
+
62
+ $this->assertContains('administrator', AAM::getUser()->roles);
63
+ $this->assertContains('contributor', AAM::getUser()->roles);
64
+ }
65
+
66
+ /**
67
+ * Test that policy allows to add new role to user
68
+ *
69
+ * @return void
70
+ *
71
+ * @access public
72
+ * @version 6.0.0
73
+ */
74
+ public function testRemovedRole()
75
+ {
76
+ $this->preparePlayground('role-remove', AAM_UNITTEST_AUTH_MULTIROLE_USER_ID);
77
+
78
+ // Reset current user to trigger policy changes
79
+ wp_set_current_user(AAM_UNITTEST_AUTH_MULTIROLE_USER_ID);
80
+
81
+ $this->assertFalse(in_array('editor', AAM::getUser()->roles, true));
82
+ $this->assertContains('subscriber', AAM::getUser()->roles);
83
+ }
84
+
85
+ /**
86
+ * Prepare the environment
87
+ *
88
+ * Update Unit Test access policy with proper policy
89
+ *
90
+ * @param string $policy_file
91
+ * @param int $user
92
+ *
93
+ * @return void
94
+ *
95
+ * @access protected
96
+ * @version 6.0.0
97
+ */
98
+ protected function preparePlayground($policy_file, $user = AAM_UNITTEST_AUTH_USER_ID)
99
+ {
100
+ // Update existing Access Policy with new policy
101
+ wp_update_post(array(
102
+ 'ID' => AAM_UNITTEST_ACCESS_POLICY_ID,
103
+ 'post_content' => file_get_contents(
104
+ __DIR__ . '/policies/' . $policy_file . '.json'
105
+ )
106
+ ));
107
+
108
+ $settings = AAM_Core_AccessSettings::getInstance();
109
+ $settings->set(sprintf(
110
+ 'user.%d.policy.%d', $user, AAM_UNITTEST_ACCESS_POLICY_ID
111
+ ), true);
112
+ }
113
+
114
+ /**
115
+ * Reset all AAM settings to the default
116
+ *
117
+ * @return void
118
+ *
119
+ * @access protected
120
+ * @version 6.0.0
121
+ */
122
+ protected function tearDown()
123
+ {
124
+ // Clear all AAM settings
125
+ AAM_Core_API::clearSettings();
126
+
127
+ // Reset Access Settings repository
128
+ AAM_Core_AccessSettings::getInstance()->reset();
129
+
130
+ // Unset the forced user
131
+ wp_set_current_user(0);
132
+
133
+ // Clear WP core cache
134
+ wp_cache_flush();
135
+
136
+ // Reset internal AAM config cache
137
+ AAM_Core_Config::bootstrap();
138
+
139
+ // Reset Access Policy Factory cache
140
+ AAM_Core_Policy_Factory::reset();
141
+ }
142
+
143
+ }
tests/Service/AccessPolicy/PolicyValidationTest.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AccessPolicy;
11
+
12
+ use AAM_Backend_View_Helper,
13
+ AAM_Core_Policy_Validator,
14
+ PHPUnit\Framework\TestCase;
15
+
16
+ /**
17
+ * Test policy validator
18
+ *
19
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
20
+ * @version 6.0.0
21
+ */
22
+ class PolicyValidationTest extends TestCase
23
+ {
24
+ /**
25
+ * Test that error is triggered when policy is empty
26
+ *
27
+ * @return void
28
+ *
29
+ * @access public
30
+ * @version 6.0.0
31
+ */
32
+ public function testEmptyPolicy()
33
+ {
34
+ $validator = new AAM_Core_Policy_Validator('[]');
35
+
36
+ $this->assertEquals(array(
37
+ __('The policy document is empty', AAM_KEY)
38
+ ), $validator->validate());
39
+ }
40
+
41
+ /**
42
+ * Test that error is triggered when policy contains invalid JSON
43
+ *
44
+ * @return void
45
+ *
46
+ * @access public
47
+ * @version 6.0.0
48
+ */
49
+ public function testInvalidJsonPolicy()
50
+ {
51
+ $validator = new AAM_Core_Policy_Validator('--');
52
+
53
+ $this->assertEquals(array(
54
+ __('The policy is not valid JSON object', AAM_KEY)
55
+ ), $validator->validate());
56
+ }
57
+
58
+ /**
59
+ * Test that error is triggered when missing dependency
60
+ *
61
+ * @return void
62
+ *
63
+ * @access public
64
+ * @version 6.0.0
65
+ */
66
+ public function testMissingDependencyPolicy()
67
+ {
68
+ $validator = new AAM_Core_Policy_Validator('{
69
+ "Dependency": {
70
+ "advanced-access-manager-x": "^1.0.0"
71
+ }
72
+ }');
73
+
74
+ $this->assertEquals(array(
75
+ AAM_Backend_View_Helper::preparePhrase(
76
+ "The plugin [advanced-access-manager-x] is required by the policy",
77
+ 'b'
78
+ )
79
+ ), $validator->validate());
80
+ }
81
+
82
+ /**
83
+ * Test that error is triggered when dependency version is not satisfied
84
+ *
85
+ * @return void
86
+ *
87
+ * @access public
88
+ * @version 6.0.0
89
+ */
90
+ public function testLowDependencyPolicy()
91
+ {
92
+ $validator = new AAM_Core_Policy_Validator('{
93
+ "Dependency": {
94
+ "advanced-access-manager": "<6.0.0"
95
+ }
96
+ }');
97
+
98
+ $this->assertEquals(array(
99
+ AAM_Backend_View_Helper::preparePhrase(
100
+ "The dependency [advanced-access-manager] does not satisfy version requirement by the policy",
101
+ 'b'
102
+ )
103
+ ), $validator->validate());
104
+ }
105
+
106
+ /**
107
+ * Test that there is no error when everything is ok
108
+ *
109
+ * @return void
110
+ *
111
+ * @access public
112
+ * @version 6.0.0
113
+ */
114
+ public function testValidDependencyPolicy()
115
+ {
116
+ $validator = new AAM_Core_Policy_Validator('{
117
+ "Dependency": {
118
+ "advanced-access-manager": ">=' . AAM_VERSION . '"
119
+ }
120
+ }');
121
+
122
+ $this->assertEquals(0, count($validator->validate()));
123
+ }
124
+
125
+ }
tests/Service/AccessPolicy/policies/admin-menu.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": [
7
+ "BackendMenu:edit.php"
8
+ ]
9
+ }
10
+ ]
11
+ }
tests/Service/AccessPolicy/policies/capability-changes.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": [
7
+ "Capability:switch_themes"
8
+ ]
9
+ },
10
+ {
11
+ "Effect": "allow",
12
+ "Resource": [
13
+ "Capability:hello_world"
14
+ ]
15
+ }
16
+ ]
17
+ }
tests/Service/AccessPolicy/policies/dynamic-param.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Param": [
4
+ {
5
+ "Key": "hello-world-${USER.user_nicename}",
6
+ "Value": "hello"
7
+ }
8
+ ]
9
+ }
tests/Service/AccessPolicy/policies/dynamic-resource.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": [
7
+ "Post:post:${USER.ID}"
8
+ ],
9
+ "Action": ["Read"]
10
+ }
11
+ ]
12
+ }
tests/Service/AccessPolicy/policies/metabox.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": [
7
+ "Widget:widgets|wp_widget_pages",
8
+ "Metabox:aam_policy|revisionsdiv"
9
+ ]
10
+ }
11
+ ]
12
+ }
tests/Service/AccessPolicy/policies/option-override-policy.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Param": [
4
+ {
5
+ "Key": "option:unittest",
6
+ "Value": "unititest.me"
7
+ }
8
+ ]
9
+ }
tests/Service/AccessPolicy/policies/plugins.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": {
4
+ "Effect": "deny",
5
+ "Resource": "Plugin",
6
+ "Action": ["WP:install", "WP:edit", "WP:update", "WP:delete"]
7
+ }
8
+ }
tests/Service/AccessPolicy/policies/post-complex-actions.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "Post:post:1",
7
+ "Action": "Read",
8
+ "Metadata": {
9
+ "Password": {
10
+ "Value": "123456"
11
+ },
12
+ "Teaser": {
13
+ "Value": "This is just a teaser message"
14
+ }
15
+ }
16
+ }
17
+ ]
18
+ }
tests/Service/AccessPolicy/policies/post-hidden.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "Post:post:1",
7
+ "Action": ["List"]
8
+ }
9
+ ]
10
+ }
tests/Service/AccessPolicy/policies/post-redirect-callback.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "Post:post:1",
7
+ "Action": "Read",
8
+ "Metadata": {
9
+ "Redirect": {
10
+ "Type": "callback",
11
+ "Callback": "AAM\\\\Callback\\\\Main::helloWorld"
12
+ }
13
+ }
14
+ }
15
+ ]
16
+ }
tests/Service/AccessPolicy/policies/post-redirect-page-id.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "Post:post:1",
7
+ "Action": "Read",
8
+ "Metadata": {
9
+ "Redirect": {
10
+ "Type": "page",
11
+ "Id": 2,
12
+ "Code": 301
13
+ }
14
+ }
15
+ }
16
+ ]
17
+ }
tests/Service/AccessPolicy/policies/post-redirect-page-slug.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "Post:post:1",
7
+ "Action": "Read",
8
+ "Metadata": {
9
+ "Redirect": {
10
+ "Type": "page",
11
+ "Slug": "sample-page",
12
+ "Code": 301
13
+ }
14
+ }
15
+ }
16
+ ]
17
+ }
tests/Service/AccessPolicy/policies/post-redirect-url.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": {
4
+ "Effect": "deny",
5
+ "Resource": "Post:post:1",
6
+ "Action": "Read",
7
+ "Metadata": {
8
+ "Redirect": {
9
+ "Type": "url",
10
+ "URL": "https://aamplugin.com"
11
+ }
12
+ }
13
+ }
14
+ }
tests/Service/AccessPolicy/policies/post-restricted.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "Post:post:1",
7
+ "Action": ["Read"]
8
+ }
9
+ ]
10
+ }
tests/Service/AccessPolicy/policies/post-simple-actions.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "Post:post:1",
7
+ "Action": ["Edit", "Delete", "Publish", "Comment"]
8
+ }
9
+ ]
10
+ }
tests/Service/AccessPolicy/policies/role-add.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "allow",
6
+ "Resource": [
7
+ "Role:contributor"
8
+ ]
9
+ }
10
+ ]
11
+ }
tests/Service/AccessPolicy/policies/role-remove.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": [
7
+ "Role:editor"
8
+ ]
9
+ }
10
+ ]
11
+ }
tests/Service/AccessPolicy/policies/simple-policy-with-action.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": [
7
+ "Capability:switch_themes"
8
+ ],
9
+ "Action": "AAM:toggle"
10
+ }
11
+ ]
12
+ }
tests/Service/AccessPolicy/policies/simple-policy.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": {
4
+ "Effect": "deny",
5
+ "Resource": [
6
+ "BackendMenu:edit.php"
7
+ ]
8
+ }
9
+ }
tests/Service/AccessPolicy/policies/single-plugin.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": {
4
+ "Effect": "deny",
5
+ "Resource": [
6
+ "Plugin:advanced-access-manager"
7
+ ],
8
+ "Action": ["WP:deactivate", "WP:activate"]
9
+ }
10
+ }
tests/Service/AccessPolicy/policies/toolbar.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": [
7
+ "Toolbar:about"
8
+ ]
9
+ }
10
+ ]
11
+ }
tests/Service/AccessPolicy/policies/uri.json ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": "1.0.0",
3
+ "Statement": [
4
+ {
5
+ "Effect": "deny",
6
+ "Resource": "URI:/hello-world-1"
7
+ },
8
+ {
9
+ "Effect": "deny",
10
+ "Resource": "URI:/hello-world-2",
11
+ "Metadata": {
12
+ "Type": "message",
13
+ "Message": "Access Is Denied"
14
+ }
15
+ },
16
+ {
17
+ "Effect": "deny",
18
+ "Resource": "URI:/hello-world-3",
19
+ "Metadata": {
20
+ "Type": "page",
21
+ "Id": 2
22
+ }
23
+ },
24
+ {
25
+ "Effect": "deny",
26
+ "Resource": "URI:/hello-world-4",
27
+ "Metadata": {
28
+ "Type": "page",
29
+ "Slug": "sample-page"
30
+ }
31
+ },
32
+ {
33
+ "Effect": "deny",
34
+ "Resource": "URI:/hello-world-5",
35
+ "Metadata": {
36
+ "Type": "url",
37
+ "URL": "/another-location",
38
+ "Code": 303
39
+ }
40
+ },
41
+ {
42
+ "Effect": "deny",
43
+ "Resource": "URI:/hello-world-6",
44
+ "Metadata": {
45
+ "Type": "callback",
46
+ "Callback": "AAM\\\\Callback\\\\Main::helloWorld"
47
+ }
48
+ },
49
+ {
50
+ "Effect": "deny",
51
+ "Resource": "URI:/hello-world-7",
52
+ "Metadata": {
53
+ "Type": "login"
54
+ }
55
+ }
56
+ ]
57
+ }
tests/Service/AdminMenu/MultipleRoleInheritanceTest.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AdminMenu;
11
+
12
+ use AAM,
13
+ AAM_Core_Config,
14
+ AAM_Core_Object_Menu,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait,
17
+ AAM\UnitTest\Libs\AuthMultiRoleUserTrait,
18
+ AAM\UnitTest\Libs\MultiRoleOptionInterface;
19
+
20
+ /**
21
+ * Test AAM access settings inheritance mechanism for multiple roles per user for
22
+ * the Admin Menu service
23
+ *
24
+ * Admin Menu is available only for authenticated users so no Visitors are tested
25
+ *
26
+ * @package AAM\UnitTest
27
+ * @version 6.0.0
28
+ */
29
+ class MultipleRoleInheritanceTest extends TestCase implements MultiRoleOptionInterface
30
+ {
31
+ use ResetTrait,
32
+ AuthMultiRoleUserTrait;
33
+
34
+ /**
35
+ * Test that access settings are inherited from multiple parent roles
36
+ *
37
+ * This test is designed to verify that access settings are propagated property
38
+ * when there access settings defined for multiple parent roles.
39
+ *
40
+ * A. Test that settings can be stored for the parent roles;
41
+ * B. Test that access settings are propagated property to the User level
42
+ *
43
+ * @return void
44
+ *
45
+ * @access public
46
+ * @version 6.0.0
47
+ */
48
+ public function testInheritanceMergeFromMultipleRoles()
49
+ {
50
+ $user = AAM::getUser();
51
+ $role = $user->getParent();
52
+
53
+ // Make sure that we have parent roles defined properly
54
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
55
+
56
+ // Save access settings for the base role and iterate over each sibling and
57
+ // add additional settings
58
+ $object = $role->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true);
59
+ $this->assertTrue($object->updateOptionItem('index.php?id=0', true)->save());
60
+
61
+ foreach($role->getSiblings() as $i => $sibling) {
62
+ // Save access settings for each role and make sure they are saved property
63
+ // Check if save returns positive result
64
+ $this->assertTrue(
65
+ $sibling->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true)->updateOptionItem(
66
+ 'index.php?id=' . ($i + 1), ($i % 2 ? true : false)
67
+ )->save()
68
+ );
69
+ }
70
+
71
+ // Reset internal AAM cache
72
+ $this->_resetSubjects();
73
+
74
+ // Assert that we have both roles merged result is as following
75
+ // Array (
76
+ // index.php?id=0 => true,
77
+ // index.php?id=1 => false
78
+ // )
79
+ $option = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE)->getOption();
80
+ $this->assertSame(
81
+ array('index.php?id=0' => true, 'index.php?id=1' => false), $option
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Test that access settings are merged with default "deny" precedence correctly
87
+ *
88
+ * @return void
89
+ *
90
+ * @access public
91
+ * @version 6.0.0
92
+ */
93
+ public function testInheritanceDenyPrecedenceFromMultipleRoles()
94
+ {
95
+ $user = AAM::getUser();
96
+ $role = $user->getParent();
97
+
98
+ // Make sure that we have parent roles defined properly
99
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
100
+
101
+ // Save access settings for the base role and iterate over each sibling and
102
+ // add additional settings
103
+ $this->assertTrue(
104
+ $role->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true)->updateOptionItem(
105
+ 'index.php', true
106
+ )->save()
107
+ );
108
+
109
+ foreach($role->getSiblings() as $sibling) {
110
+ // Save access settings for each role and make sure they are saved property
111
+ // Check if save returns positive result
112
+ $this->assertTrue(
113
+ $sibling->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true)->updateOptionItem(
114
+ 'index.php', false
115
+ )->save()
116
+ );
117
+ }
118
+
119
+ // Reset internal AAM cache
120
+ $this->_resetSubjects();
121
+
122
+ // Assert that we have both roles merged result is as following
123
+ // Array (
124
+ // index.php => true
125
+ // )
126
+ $option = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE)->getOption();
127
+ $this->assertSame(
128
+ array('index.php' => true), $option
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Test that access settings are merged correctly with "allowed" precedence
134
+ * correctly
135
+ *
136
+ * @return void
137
+ * @version 6.0.0
138
+ */
139
+ public function testInheritanceAllowPrecedenceFromMultipleRoles()
140
+ {
141
+ $user = AAM::getUser();
142
+ $role = $user->getParent();
143
+
144
+ // Make sure that we have parent roles defined properly
145
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
146
+
147
+ // Save access settings for the base role and iterate over each sibling and
148
+ // add additional settings
149
+ $this->assertTrue(
150
+ $role->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true)->updateOptionItem(
151
+ 'index.php', true
152
+ )->save()
153
+ );
154
+
155
+ foreach($role->getSiblings() as $sibling) {
156
+ // Save access settings for each role and make sure they are saved property
157
+ // Check if save returns positive result
158
+ $this->assertTrue(
159
+ $sibling->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true)->updateOptionItem(
160
+ 'index.php', false
161
+ )->save()
162
+ );
163
+ }
164
+
165
+ // Override the default "deny" precedence
166
+ AAM_Core_Config::set(
167
+ sprintf('core.settings.%s.merge.preference', AAM_Core_Object_Menu::OBJECT_TYPE),
168
+ 'allow'
169
+ );
170
+
171
+ // Reset internal AAM cache
172
+ $this->_resetSubjects();
173
+
174
+ // Assert that we have both roles merged result is as following
175
+ // Array (
176
+ // index.php => false
177
+ // )
178
+ $option = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE)->getOption();
179
+ $this->assertSame(array('index.php' => false), $option);
180
+ }
181
+
182
+ }
tests/Service/AdminMenu/SingleRoleInheritanceTest.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AdminMenu;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Menu,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Test AAM access settings inheritance mechanism for the Admin Menu service
20
+ *
21
+ * Admin Menu is available only for authenticated users so no Visitors are tested
22
+ *
23
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
24
+ * @version 6.0.0
25
+ */
26
+ class SingleRoleInheritanceTest extends TestCase
27
+ {
28
+ use ResetTrait,
29
+ AuthUserTrait;
30
+
31
+ /**
32
+ * Test to insure that access settings are stored property on the User level
33
+ *
34
+ * A. Test that "index.php" is stored to the database with "true" flag and true
35
+ * is returned by AAM_Core_Subject_User::updateOption method;
36
+ * B. Test that information is actually stored property in the database and can
37
+ * be retrieved successfully.
38
+ *
39
+ * @return void
40
+ *
41
+ * @access public
42
+ * @see AAM_Core_Subject_User::updateOption
43
+ * @version 6.0.0
44
+ */
45
+ public function testSaveAdminMenuOption()
46
+ {
47
+ $user = AAM::getUser();
48
+ $object = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
49
+
50
+ // Check if save returns positive result
51
+ $this->assertTrue($object->updateOptionItem('index.php', true)->save());
52
+
53
+ // Read from the database saved values and assert that we have
54
+ // Array (
55
+ // index.php => true
56
+ // )
57
+ $option = $user->readOption('menu');
58
+ $this->assertSame(array('index.php' => true), $option);
59
+ }
60
+
61
+ /**
62
+ * Test that access settings are inherited from the parent role property
63
+ *
64
+ * This test is designed to verify that access settings are propagated property
65
+ * when there is only one role assigned to a user.
66
+ *
67
+ * A. Test that settings can be stored for the parent role;
68
+ * B. Test that access settings are propagated property to the User level
69
+ *
70
+ * @return void
71
+ *
72
+ * @access public
73
+ * @version 6.0.0
74
+ */
75
+ public function testInheritanceFromSingleRole()
76
+ {
77
+ $user = AAM::getUser();
78
+ $parent = $user->getParent();
79
+ $object = $parent->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
80
+
81
+ // Make sure that we have parent role defined
82
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($parent));
83
+
84
+ // Save access settings for the role and make sure they are saved property
85
+ // Check if save returns positive result
86
+ $this->assertTrue($object->updateOptionItem('index.php', true)->save());
87
+
88
+ // Read from the database saved values and assert that we have
89
+ // Array (
90
+ // index.php => true
91
+ // )
92
+ $option = $parent->readOption('menu');
93
+ $this->assertSame(array('index.php' => true), $option);
94
+
95
+ // Finally verify that access settings are propagated property to the User
96
+ // Level
97
+ $menu = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
98
+ $this->assertSame(array('index.php' => true), $menu->getOption());
99
+ }
100
+
101
+ /**
102
+ * Test that access settings are propagated and merged properly
103
+ *
104
+ * The test is designed to verify that access settings are propagated properly
105
+ * from the parent role and merged well with explicitly defined access settings on
106
+ * the User level.
107
+ *
108
+ * The expected result is to have combined array of access settings from the parent
109
+ * role and specific user.
110
+ *
111
+ * A. Test that access settings are stored for the parent role;
112
+ * B. Test that access settings are stored for the user;
113
+ * C. Test that access settings are propagated and merged properly;
114
+ *
115
+ * @return void
116
+ *
117
+ * @access public
118
+ * @version 6.0.0
119
+ */
120
+ public function testInheritanceMergeFromSingleRole()
121
+ {
122
+ $user = AAM::getUser();
123
+ $parent = $user->getParent();
124
+
125
+ $object = $parent->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
126
+
127
+ // Save access settings for the role and make sure they are saved property
128
+ // Check if save returns positive result
129
+ $this->assertTrue($object->updateOptionItem('update.php', true)->save());
130
+
131
+ // Save access setting for the user and make sure they are saved property
132
+ $menu = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true);
133
+ $this->assertTrue($menu->updateOptionItem('post.php?post_type=page', false)->save());
134
+
135
+ // Reset cache and try to kick-in the inheritance mechanism
136
+ $this->_resetSubjects();
137
+
138
+ $menu = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
139
+ $this->assertSame(
140
+ array('update.php' => true, 'post.php?post_type=page' => false),
141
+ $menu->getOption()
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Test that the full inheritance mechanism is working as expected
147
+ *
148
+ * Make sure that access settings are propagated and merged properly from the top
149
+ * (Default Level)to the bottom (User Level).
150
+ *
151
+ * A. Assert that access settings are stored properly for each Access Level;
152
+ * B. Assert that access settings are merged properly and assigned to User Level;
153
+ *
154
+ * @return void
155
+ *
156
+ * @access public
157
+ * @version 6.0.0
158
+ */
159
+ public function testFullInheritanceChainSingeRole()
160
+ {
161
+ $user = AAM::getUser();
162
+ $role = $user->getParent();
163
+ $default = $role->getParent();
164
+
165
+ $userMenu = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true);
166
+ $roleMenu = $role->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true);
167
+ $defaultMenu = $default->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true);
168
+
169
+ // Save access settings for all subjects
170
+ $this->assertTrue($userMenu->updateOptionItem('update.php', true)->save());
171
+ $this->assertTrue($roleMenu->updateOptionItem('post.php?post_type=page', true)->save());
172
+ $this->assertTrue($defaultMenu->updateOptionItem('customize.php', true)->save());
173
+
174
+ // Reset cache and try to kick-in the inheritance mechanism
175
+ $this->_resetSubjects();
176
+
177
+ // All settings has to be merged into one array
178
+ $menu = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
179
+ $this->assertSame(
180
+ array(
181
+ 'customize.php' => true,
182
+ 'post.php?post_type=page' => true,
183
+ 'update.php' => true
184
+ ),
185
+ $menu->getOption()
186
+ );
187
+ }
188
+
189
+ /**
190
+ * Test that access settings overwrite works as expected
191
+ *
192
+ * The expected result is lower Access Level overwrite access settings from the
193
+ * higher Access Level.
194
+ *
195
+ * A. Assert that access settings are stored properly for the parent role;
196
+ * B. Assert that access settings are stored properly for the specific user;
197
+ * C. Assert that access settings are overwritten properly on the User Level;
198
+ *
199
+ * @return void
200
+ *
201
+ * @access public
202
+ * @version 6.0.0
203
+ */
204
+ public function testInheritanceOverrideForSingleRole()
205
+ {
206
+ $user = AAM::getUser();
207
+ $parent = $user->getParent();
208
+
209
+ $object = $parent->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
210
+
211
+ // Save access settings for the role and make sure they are saved property
212
+ // Check if save returns positive result
213
+ $this->assertTrue($object->updateOptionItem('update.php', true)->save());
214
+
215
+ // Save access setting for the user and make sure they are saved property
216
+ $menu = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE, null, true);
217
+ $this->assertTrue($menu->updateOptionItem('update.php', false)->save());
218
+
219
+ // Reset cache and try to kick-in the inheritance mechanism
220
+ $this->_resetSubjects();
221
+
222
+ $menu = $user->getObject(AAM_Core_Object_Menu::OBJECT_TYPE);
223
+ $this->assertSame(array('update.php' => false), $menu->getOption());
224
+ }
225
+
226
+ }
tests/Service/Capabilities/CapabilityManagerTest.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Capability;
11
+
12
+ use AAM,
13
+ AAM_Core_Subject_Role,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\AuthUserTrait,
16
+ AAM_Backend_Feature_Main_Capability;
17
+
18
+ /**
19
+ * Test Capability manager features
20
+ *
21
+ * @version 6.0.0
22
+ */
23
+ class CapabilityManagerTest extends TestCase
24
+ {
25
+
26
+ use AuthUserTrait;
27
+
28
+ /**
29
+ * Test if capabilities can be added properly for defined role
30
+ *
31
+ * @return void
32
+ *
33
+ * @access public
34
+ * @version 6.0.0
35
+ */
36
+ public function testAssignCapabilityToRole()
37
+ {
38
+ global $wpdb;
39
+
40
+ $stub = $this->prepareRoleStub(
41
+ // Create a map of arguments to return values
42
+ array(
43
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_a'),
44
+ array('effect', FILTER_VALIDATE_BOOLEAN, null, true),
45
+ ),
46
+ // Subject callback
47
+ function() {
48
+ return new AAM_Core_Subject_Role('subscriber');
49
+ }
50
+ );
51
+
52
+ // Check if save returns positive result
53
+ $this->assertEquals(
54
+ $stub->save(), wp_json_encode(array('status' => 'success'))
55
+ );
56
+
57
+ // Verify that created capability actually is inside the user_roles option
58
+ $option = get_option(sprintf('%suser_roles', $wpdb->prefix));
59
+
60
+ $this->assertTrue(
61
+ array_key_exists('aam_test_cap_a', $option['subscriber']['capabilities'])
62
+ );
63
+
64
+ $this->assertTrue($option['subscriber']['capabilities']['aam_test_cap_a']);
65
+ }
66
+
67
+ /**
68
+ * Test if capabilities can be added properly for the defined role and also
69
+ * current user
70
+ *
71
+ * @return void
72
+ *
73
+ * @access public
74
+ * @version 6.0.0
75
+ */
76
+ public function testAssignCapabilityToRoleAndCurrentUser()
77
+ {
78
+ global $wpdb;
79
+
80
+ $stub = $this->prepareRoleStub(
81
+ // Create a map of arguments to return values
82
+ array(
83
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_c'),
84
+ array('effect', FILTER_VALIDATE_BOOLEAN, null, true),
85
+ array('assignToMe', FILTER_VALIDATE_BOOLEAN, null, true)
86
+ ),
87
+ // Subject callback
88
+ function() {
89
+ return new AAM_Core_Subject_Role('subscriber');
90
+ }
91
+ );
92
+
93
+ // Check if save returns positive result
94
+ $this->assertEquals(
95
+ $stub->save(), wp_json_encode(array('status' => 'success'))
96
+ );
97
+
98
+ // Verify that created capability actually is inside the user_roles option
99
+ $option = get_option(sprintf('%suser_roles', $wpdb->prefix));
100
+
101
+ $this->assertTrue(
102
+ array_key_exists('aam_test_cap_c', $option['subscriber']['capabilities'])
103
+ );
104
+
105
+ $this->assertTrue($option['subscriber']['capabilities']['aam_test_cap_c']);
106
+
107
+ $this->assertTrue(AAM::getUser()->hasCapability('aam_test_cap_c'));
108
+
109
+ // Clean-up after execution
110
+ AAM::getUser()->removeCapability('aam_test_cap_c');
111
+ $stub->delete();
112
+ }
113
+
114
+ /**
115
+ * Test if capabilities can be deprived properly for defined role
116
+ *
117
+ * @return void
118
+ *
119
+ * @access public
120
+ * @version 6.0.0
121
+ */
122
+ public function testDepriveCapabilityToRole()
123
+ {
124
+ global $wpdb;
125
+
126
+ $stub = $this->prepareRoleStub(
127
+ // Create a map of arguments to return values
128
+ array(
129
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_a'),
130
+ array('effect', FILTER_VALIDATE_BOOLEAN, null, false),
131
+ ),
132
+ // Subject callback
133
+ function() {
134
+ return new AAM_Core_Subject_Role('subscriber');
135
+ }
136
+ );
137
+
138
+ // Check if save returns positive result
139
+ $this->assertEquals(
140
+ $stub->save(), wp_json_encode(array('status' => 'success'))
141
+ );
142
+
143
+ // Verify that created capability actually is inside the user_roles option
144
+ $option = get_option(sprintf('%suser_roles', $wpdb->prefix));
145
+
146
+ $this->assertTrue(
147
+ array_key_exists('aam_test_cap_a', $option['subscriber']['capabilities'])
148
+ );
149
+
150
+ $this->assertFalse($option['subscriber']['capabilities']['aam_test_cap_a']);
151
+ }
152
+
153
+ /**
154
+ * Test if capabilities can be deleted from the very specific role
155
+ *
156
+ * @return void
157
+ *
158
+ * @access public
159
+ * @version 6.0.0
160
+ */
161
+ public function testCapabilityDeletionFromRole()
162
+ {
163
+ global $wpdb;
164
+
165
+ $stub = $this->prepareRoleStub(
166
+ // Create a map of arguments to return values
167
+ array(
168
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_a'),
169
+ array('effect', FILTER_VALIDATE_BOOLEAN, null, true),
170
+ array('subjectOnly', FILTER_VALIDATE_BOOLEAN, null, true)
171
+ ),
172
+ // Subject callback
173
+ function() {
174
+ return new AAM_Core_Subject_Role('subscriber');
175
+ }
176
+ );
177
+
178
+ // Insert the test capability before it'll be deleted
179
+ $stub->save();
180
+
181
+ // Delete the test capability from the subject
182
+ $this->assertEquals(
183
+ $stub->delete(), wp_json_encode(array('status' => 'success'))
184
+ );
185
+
186
+ // Confirm that deleted capability is no longer in the subscriber role
187
+ $option = get_option(sprintf('%suser_roles', $wpdb->prefix));
188
+
189
+ $this->assertFalse(
190
+ array_key_exists('aam_test_cap_a', $option['subscriber']['capabilities'])
191
+ );
192
+ }
193
+
194
+ /**
195
+ * Test if capabilities can be deleted from all roles
196
+ *
197
+ * @return void
198
+ *
199
+ * @access public
200
+ * @version 6.0.0
201
+ */
202
+ public function testCapabilityDeletionFromAllRoles()
203
+ {
204
+ global $wpdb;
205
+
206
+ // Prepare and insert test capability for the "subscriber" editor
207
+ $stubA = $this->prepareRoleStub(
208
+ // Create a map of arguments to return values
209
+ array(
210
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_a'),
211
+ array('effect', FILTER_VALIDATE_BOOLEAN, null, true),
212
+ array('subjectOnly', FILTER_VALIDATE_BOOLEAN, null, false)
213
+ ),
214
+ // Subject callback
215
+ function() {
216
+ return new AAM_Core_Subject_Role('subscriber');
217
+ }
218
+ );
219
+ // Insert the test capability before it'll be deleted
220
+ $this->assertEquals(
221
+ $stubA->save(), wp_json_encode(array('status' => 'success'))
222
+ );
223
+
224
+ // Prepare and insert test capability for the "editor" role
225
+ $stubB = $this->prepareRoleStub(
226
+ // Create a map of arguments to return values
227
+ array(
228
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_a'),
229
+ array('effect', FILTER_VALIDATE_BOOLEAN, null, true)
230
+ ),
231
+ // Subject callback
232
+ function() {
233
+ return new AAM_Core_Subject_Role('editor');
234
+ }
235
+ );
236
+ // Insert the test capability before it'll be deleted
237
+ $this->assertEquals(
238
+ $stubB->save(), wp_json_encode(array('status' => 'success'))
239
+ );
240
+
241
+ // Delete the test capability from all roles
242
+ $this->assertEquals(
243
+ $stubA->delete(), wp_json_encode(array('status' => 'success'))
244
+ );
245
+
246
+ // Confirm that deleted capability is no longer in the subscriber & editor
247
+ // roles
248
+ $option = get_option(sprintf('%suser_roles', $wpdb->prefix));
249
+
250
+ $this->assertFalse(
251
+ array_key_exists('aam_test_cap_a', $option['subscriber']['capabilities'])
252
+ );
253
+
254
+ $this->assertFalse(
255
+ array_key_exists('aam_test_cap_a', $option['editor']['capabilities'])
256
+ );
257
+ }
258
+
259
+ /**
260
+ * Test if capabilities can be updated properly for the defined subject
261
+ *
262
+ * @return void
263
+ *
264
+ * @access public
265
+ * @version 6.0.0
266
+ */
267
+ public function testUpdateCapability()
268
+ {
269
+ global $wpdb;
270
+
271
+ $stubA = $this->prepareRoleStub(
272
+ // Create a map of arguments to return values
273
+ array(
274
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_a'),
275
+ array('effect', FILTER_VALIDATE_BOOLEAN, null, false),
276
+ ),
277
+ // Subject callback
278
+ function() {
279
+ return new AAM_Core_Subject_Role('subscriber');
280
+ }
281
+ );
282
+
283
+ // Check if save returns positive result
284
+ $this->assertEquals(
285
+ $stubA->save(), wp_json_encode(array('status' => 'success'))
286
+ );
287
+
288
+ // Create a new stub that will update the test capability
289
+ $stubB = $this->prepareRoleStub(
290
+ // Create a map of arguments to return values
291
+ array(
292
+ array('capability', FILTER_DEFAULT, null, 'aam_test_cap_a'),
293
+ array('updated', FILTER_DEFAULT, null, 'aam_test_cap_b')
294
+ ),
295
+ // Subject callback
296
+ function() {
297
+ return new AAM_Core_Subject_Role('subscriber');
298
+ }
299
+ );
300
+
301
+ // Check if save returns positive result
302
+ $this->assertEquals(
303
+ $stubB->update(), wp_json_encode(array('status' => 'success'))
304
+ );
305
+
306
+ // Verify that capability actually is updated the user_roles option
307
+ $option = get_option(sprintf('%suser_roles', $wpdb->prefix));
308
+
309
+ $this->assertFalse(
310
+ array_key_exists('aam_test_cap_a', $option['subscriber']['capabilities'])
311
+ );
312
+
313
+ $this->assertTrue(
314
+ array_key_exists('aam_test_cap_b', $option['subscriber']['capabilities'])
315
+ );
316
+
317
+ $this->assertFalse($option['subscriber']['capabilities']['aam_test_cap_b']);
318
+ }
319
+
320
+ /**
321
+ * Prepare proper subject stub
322
+ *
323
+ * @param array $paramMap
324
+ * @param callback $callback
325
+ *
326
+ * @return object
327
+ *
328
+ * @access protected
329
+ * @version 6.0.0
330
+ */
331
+ protected function prepareRoleStub($paramMap, $callback)
332
+ {
333
+ // Create a stub for the SomeClass class.
334
+ $stub = $this->getMockBuilder(AAM_Backend_Feature_Main_Capability::class)
335
+ ->setMethods(array('getFromPost', 'getSubject'))
336
+ ->getMock();
337
+
338
+ // Configure the stub
339
+ $stub->method('getFromPost')->will($this->returnValueMap($paramMap));
340
+ $stub->method('getSubject')->will($this->returnCallback($callback));
341
+
342
+ return $stub;
343
+ }
344
+
345
+ }
tests/Service/Content/Callback.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace AAM\UnitTest\Service\Content;
4
+
5
+ class Callback
6
+ {
7
+ const REDIRECT_URL = 'https://aamplugin.com/redirect';
8
+
9
+ public static function redirectCallback()
10
+ {
11
+ return self::REDIRECT_URL;
12
+ }
13
+ }
tests/Service/Content/MultipleRoleInheritanceTest.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Content;
11
+
12
+ use AAM,
13
+ AAM_Core_Config,
14
+ AAM_Core_Object_Post,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait,
17
+ AAM\UnitTest\Libs\AuthMultiRoleUserTrait,
18
+ AAM\UnitTest\Libs\MultiRoleOptionInterface;
19
+
20
+ /**
21
+ * Test AAM access settings inheritance mechanism for multiple roles per user for
22
+ * the Content service
23
+ *
24
+ * @package AAM\UnitTest
25
+ * @version 6.0.0
26
+ */
27
+ class MultipleRoleInheritanceTest extends TestCase implements MultiRoleOptionInterface
28
+ {
29
+ use ResetTrait,
30
+ AuthMultiRoleUserTrait;
31
+
32
+ /**
33
+ * Test that access settings are inherited from multiple parent roles
34
+ *
35
+ * This test is designed to verify that access settings are propagated property
36
+ * when there access settings defined for multiple parent roles.
37
+ *
38
+ * A. Test that settings can be stored for the parent roles;
39
+ * B. Test that access settings are propagated property to the User level
40
+ *
41
+ * @return void
42
+ *
43
+ * @access public
44
+ * @version 6.0.0
45
+ */
46
+ public function testInheritanceMergeFromMultipleRoles()
47
+ {
48
+ $user = AAM::getUser();
49
+ $role = $user->getParent();
50
+
51
+ // Make sure that we have parent roles defined properly
52
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
53
+
54
+ // Save access settings for the base role and iterate over each sibling and
55
+ // add additional settings
56
+ $this->assertTrue(
57
+ $role->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true)->updateOptionItem(
58
+ 'limited',
59
+ array(
60
+ 'enabled' => true,
61
+ 'threshold' => 1
62
+ )
63
+ )->save()
64
+ );
65
+
66
+ // Set the access settings for the next Sibling
67
+ $sibling = $role->getSiblings()[0];
68
+
69
+ $sibling->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true)->updateOptionItem(
70
+ 'hidden',
71
+ false
72
+ )->save();
73
+
74
+ // Reset internal AAM cache
75
+ $this->_resetSubjects();
76
+
77
+ // Assert that we have both roles merged result is as following
78
+ // Array (
79
+ // limited => Array (
80
+ // enabled => true,
81
+ // threshold => 1
82
+ // ),
83
+ // hidden => false
84
+ // )
85
+ $object = $user->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID);
86
+
87
+ $this->assertSame(
88
+ array(
89
+ 'limited' => array(
90
+ 'enabled' => true,
91
+ 'threshold' => 1
92
+ ),
93
+ 'hidden' => false
94
+ ),
95
+ $object->getOption()
96
+ );
97
+ }
98
+
99
+ /**
100
+ * Test that access settings are merged with default "deny" preference correctly
101
+ *
102
+ * @return void
103
+ *
104
+ * @access public
105
+ * @version 6.0.0
106
+ */
107
+ public function testInheritanceDenyPreferenceFromMultipleRoles()
108
+ {
109
+ $user = AAM::getUser();
110
+ $role = $user->getParent();
111
+
112
+ // Make sure that we have parent roles defined properly
113
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
114
+
115
+ // Save access settings for the base role and iterate over each sibling and
116
+ // add additional settings
117
+ $this->assertTrue(
118
+ $role->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true)->updateOptionItem(
119
+ 'hidden', true
120
+ )->save()
121
+ );
122
+
123
+ // Set the access settings for the next Sibling
124
+ $sibling = $role->getSiblings()[0];
125
+
126
+ $sibling->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true)->updateOptionItem(
127
+ 'hidden',
128
+ false
129
+ )->save();
130
+
131
+ // Reset internal AAM cache
132
+ $this->_resetSubjects();
133
+
134
+ // Assert that we have both roles merged result is as following
135
+ // Array (
136
+ // hidden => true
137
+ // )
138
+ $option = $user->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID)->getOption();
139
+ $this->assertSame(array('hidden' => true), $option);
140
+ }
141
+
142
+ /**
143
+ * Test that access settings are merged with default "deny" preference correctly
144
+ *
145
+ * In this test, the first role will have explicitly defined access settings that
146
+ * deny access, while the second role has no settings defined. This way the
147
+ * expected outcome should be access allowed.
148
+ *
149
+ * @return void
150
+ *
151
+ * @access public
152
+ * @version 6.0.0
153
+ */
154
+ public function testInheritanceAllowPreferenceFromMultipleRoles()
155
+ {
156
+ $user = AAM::getUser();
157
+ $role = $user->getParent();
158
+
159
+ // Make sure that we have parent roles defined properly
160
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
161
+
162
+ // Save access settings for the base role and iterate over each sibling and
163
+ // add additional settings
164
+ $this->assertTrue(
165
+ $role->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true)->updateOptionItem(
166
+ 'limited', array('enabled' => true, 'threshold' => 10)
167
+ )->save()
168
+ );
169
+
170
+ // Override the default "deny" precedence
171
+ AAM_Core_Config::set(
172
+ sprintf('core.settings.%s.merge.preference', AAM_Core_Object_Post::OBJECT_TYPE),
173
+ 'allow'
174
+ );
175
+
176
+ // Reset internal AAM cache
177
+ $this->_resetSubjects();
178
+
179
+ // Assert that we have both roles merged result is as following
180
+ // Array (
181
+ // limited => Array (
182
+ // enabled => false,
183
+ // threshold => 10
184
+ // )
185
+ // )
186
+ $option = $user->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID)->getOption();
187
+ $this->assertSame(array('limited' => array('enabled' => false, 'threshold' => 10)), $option);
188
+ }
189
+
190
+ }
tests/Service/Content/RESTfulSingleRoleAccessControlTest.php ADDED
@@ -0,0 +1,579 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Content;
11
+
12
+ use AAM,
13
+ WP_REST_Request,
14
+ AAM_Service_Content,
15
+ AAM_Core_Object_Post,
16
+ PHPUnit\Framework\TestCase,
17
+ AAM\UnitTest\Libs\ResetTrait,
18
+ AAM\UnitTest\Libs\AuthUserTrait;
19
+
20
+ /**
21
+ * Test that content access settings through the WP RESTful API
22
+ *
23
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
24
+ * @version 6.0.0
25
+ */
26
+ class RESTfulSingleRoleAccessControlTest extends TestCase
27
+ {
28
+ use ResetTrait,
29
+ AuthUserTrait;
30
+
31
+ /**
32
+ * Test that user is not allowed to access the post when access settings are set
33
+ * so on the User Level
34
+ *
35
+ * @return void
36
+ *
37
+ * @access public
38
+ * @version 6.0.0
39
+ */
40
+ public function testRestrictedOption()
41
+ {
42
+ $user = AAM::getUser();
43
+ $object = $user->getObject(
44
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
45
+ );
46
+
47
+ // Check if save returns positive result
48
+ $this->assertTrue($object->updateOptionItem('restricted', true)->save());
49
+
50
+ // Reset all internal cache
51
+ $this->_resetSubjects();
52
+
53
+ $server = rest_get_server();
54
+
55
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
56
+ $request->set_param('context', 'view');
57
+
58
+ $data = $server->dispatch($request)->get_data();
59
+
60
+ $this->assertEquals('post_access_restricted', $data['code']);
61
+ }
62
+
63
+ /**
64
+ * Test that user does not have the ability to see hidden post
65
+ *
66
+ * @return void
67
+ *
68
+ * @access public
69
+ * @version 6.0.0
70
+ */
71
+ public function testHiddenOption()
72
+ {
73
+ $server = rest_get_server();
74
+
75
+ // Hide the post
76
+ $user = AAM::getUser();
77
+ $object = $user->getObject(
78
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
79
+ );
80
+
81
+ // Check if save returns positive result
82
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
83
+
84
+ // Reset all internal cache
85
+ $this->_resetSubjects();
86
+
87
+ // Verify that post is no longer in the list of posts
88
+ $request = new WP_REST_Request('GET', '/wp/v2/posts');
89
+ $request->set_param('context', 'view');
90
+
91
+ $data = $server->dispatch($request)->get_data();
92
+
93
+ // First, confirm that post is in the array of posts
94
+ $this->assertCount(0, array_filter($data, function($post) {
95
+ return $post['id'] === AAM_UNITTEST_POST_ID;
96
+ }));
97
+ }
98
+
99
+ /**
100
+ * Test that content is limited with the Teaser message and enabled excerpt
101
+ * shortcode
102
+ *
103
+ * @return void
104
+ *
105
+ * @access public
106
+ * @version 6.0.0
107
+ */
108
+ public function testTeaserMessageOption()
109
+ {
110
+ $user = AAM::getUser();
111
+ $object = $user->getObject(
112
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
113
+ );
114
+
115
+ // Check if save returns positive result
116
+ $this->assertTrue($object->updateOptionItem('teaser', array(
117
+ 'enabled' => true,
118
+ 'message' => 'Test teaser with [excerpt]'
119
+ ))->save());
120
+
121
+ // Reset all internal cache
122
+ $this->_resetSubjects();
123
+
124
+ // Confirm that teaser message is returned instead of actual content
125
+ $server = rest_get_server();
126
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
127
+ $request->set_param('context', 'view');
128
+
129
+ $data = $server->dispatch($request)->get_data();
130
+
131
+ $this->assertSame(
132
+ $data['content']['rendered'], 'Test teaser with ' . $object->post_excerpt
133
+ );
134
+ }
135
+
136
+ /**
137
+ * Test the LIMITED option
138
+ *
139
+ * @return void
140
+ *
141
+ * @access public
142
+ * @version 6.0.0
143
+ */
144
+ public function testLimitedOption()
145
+ {
146
+ // Limit the post
147
+ $user = AAM::getUser();
148
+ $object = $user->getObject(
149
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
150
+ );
151
+
152
+ // Check if save returns positive result
153
+ $this->assertTrue($object->updateOptionItem('limited', array(
154
+ 'enabled' => true,
155
+ 'threshold' => 1
156
+ ))->save());
157
+
158
+ // Faking the fact that user already seen this post once
159
+ update_user_meta(
160
+ AAM_UNITTEST_AUTH_USER_ID,
161
+ sprintf(AAM_Service_Content::POST_COUNTER_DB_OPTION, AAM_UNITTEST_POST_ID),
162
+ 1
163
+ );
164
+
165
+ // Reset all internal cache
166
+ $this->_resetSubjects();
167
+
168
+ $server = rest_get_server();
169
+
170
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
171
+ $request->set_param('context', 'view');
172
+
173
+ $data = $server->dispatch($request)->get_data();
174
+
175
+ $this->assertEquals('post_access_exceeded_limit', $data['code']);
176
+ }
177
+
178
+ /**
179
+ * Test that view counter is incremented after each view
180
+ *
181
+ * @return void
182
+ *
183
+ * @access public
184
+ * @version 6.0.0
185
+ */
186
+ public function testLimitedIncrementedCounterOption()
187
+ {
188
+ // Limit the post
189
+ $user = AAM::getUser();
190
+ $object = $user->getObject(
191
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
192
+ );
193
+
194
+ // Check if save returns positive result
195
+ $this->assertTrue($object->updateOptionItem('limited', array(
196
+ 'enabled' => true,
197
+ 'threshold' => 10
198
+ ))->save());
199
+
200
+ // Tracking key
201
+ $key = sprintf(AAM_Service_Content::POST_COUNTER_DB_OPTION, AAM_UNITTEST_POST_ID);
202
+
203
+ // Faking the fact that user already seen this post once
204
+ update_user_meta(AAM_UNITTEST_AUTH_USER_ID, $key, 1);
205
+
206
+ // Reset all internal cache
207
+ $this->_resetSubjects();
208
+
209
+ $server = rest_get_server();
210
+
211
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
212
+ $request->set_param('context', 'view');
213
+
214
+ $status = $server->dispatch($request)->get_status();
215
+
216
+ $this->assertEquals(200, $status);
217
+ $this->assertEquals(2, get_user_meta(AAM_UNITTEST_AUTH_USER_ID, $key, true));
218
+ }
219
+
220
+ /**
221
+ * Test that user does not have the ability to comment on a post
222
+ *
223
+ * @return void
224
+ *
225
+ * @access public
226
+ * @version 6.0.0
227
+ */
228
+ public function testCommentingOption()
229
+ {
230
+ $user = AAM::getUser();
231
+ $object = $user->getObject(
232
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
233
+ );
234
+
235
+ // Verify that commenting for this feature is set as open
236
+ $this->assertEquals($object->comment_status, 'open');
237
+
238
+ // Check if save returns positive result
239
+ $this->assertTrue($object->updateOptionItem('comment', true)->save());
240
+
241
+ // Reset all internal cache
242
+ $this->_resetSubjects();
243
+
244
+ $server = rest_get_server();
245
+
246
+ $request = new WP_REST_Request('POST', '/wp/v2/comments');
247
+ $request->set_param('post', AAM_UNITTEST_POST_ID);
248
+ $request->set_param('content', 'Test comment');
249
+
250
+ $data = $server->dispatch($request)->get_data();
251
+
252
+ $this->assertEquals('rest_comment_closed', $data['code']);
253
+ }
254
+
255
+ /**
256
+ * Test that REDIRECTED to Existing Page option is working as expected
257
+ *
258
+ * @return void
259
+ *
260
+ * @access public
261
+ * @version 6.0.0
262
+ */
263
+ public function testRedirectPageOption()
264
+ {
265
+ $user = AAM::getUser();
266
+ $object = $user->getObject(
267
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
268
+ );
269
+
270
+ // Check if save returns positive result
271
+ $this->assertTrue($object->updateOptionItem('redirected', array(
272
+ 'enabled' => true,
273
+ 'type' => 'page',
274
+ 'destination' => AAM_UNITTEST_PAGE_ID,
275
+ 'httpCode' => 301
276
+ ))->save());
277
+
278
+ // Reset all internal cache
279
+ $this->_resetSubjects();
280
+
281
+ $server = rest_get_server();
282
+
283
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
284
+ $request->set_param('context', 'view');
285
+
286
+ $data = $server->dispatch($request)->get_data();
287
+
288
+ $this->assertEquals('post_access_redirected', $data['code']);
289
+ $this->assertEquals(get_page_link(AAM_UNITTEST_PAGE_ID), $data['location']);
290
+ }
291
+
292
+ /**
293
+ * Test that REDIRECTED to URL option is working as expected
294
+ *
295
+ * @return void
296
+ *
297
+ * @access public
298
+ * @version 6.0.0
299
+ */
300
+ public function testRedirectURLOption()
301
+ {
302
+ $user = AAM::getUser();
303
+ $object = $user->getObject(
304
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
305
+ );
306
+
307
+ // Check if save returns positive result
308
+ $this->assertTrue($object->updateOptionItem('redirected', array(
309
+ 'enabled' => true,
310
+ 'type' => 'url',
311
+ 'destination' => 'https://aamplugin.com',
312
+ 'httpCode' => 307
313
+ ))->save());
314
+
315
+ // Reset all internal cache
316
+ $this->_resetSubjects();
317
+
318
+ $server = rest_get_server();
319
+
320
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
321
+ $request->set_param('context', 'view');
322
+
323
+ $data = $server->dispatch($request)->get_data();
324
+
325
+ $this->assertEquals('post_access_redirected', $data['code']);
326
+ $this->assertEquals('https://aamplugin.com', $data['location']);
327
+ }
328
+
329
+ /**
330
+ * Test that REDIRECTED to PHP Callback option is working as expected
331
+ *
332
+ * @return void
333
+ *
334
+ * @access public
335
+ * @version 6.0.0
336
+ */
337
+ public function testRedirectCallbackOption()
338
+ {
339
+ $user = AAM::getUser();
340
+ $object = $user->getObject(
341
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
342
+ );
343
+
344
+ // Check if save returns positive result
345
+ $this->assertTrue($object->updateOptionItem('redirected', array(
346
+ 'enabled' => true,
347
+ 'type' => 'callback',
348
+ // WordPress core strips slashes, so we have to double slash all this
349
+ 'destination' => 'AAM\\UnitTest\\Service\\Content\\Callback::redirectCallback',
350
+ 'httpCode' => 310
351
+ ))->save());
352
+
353
+ // Reset all internal cache
354
+ $this->_resetSubjects();
355
+
356
+ $server = rest_get_server();
357
+
358
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
359
+ $request->set_param('context', 'view');
360
+
361
+ $data = $server->dispatch($request)->get_data();
362
+
363
+ $this->assertEquals('post_access_redirected', $data['code']);
364
+ $this->assertEquals(Callback::REDIRECT_URL, $data['location']);
365
+ }
366
+
367
+ /**
368
+ * Test PASSWORD PROTECTED option when password is enforced by AAM and is valid
369
+ *
370
+ * @return void
371
+ *
372
+ * @access public
373
+ * @version 6.0.0
374
+ */
375
+ public function testAAMEnforcedPasswordValidOption()
376
+ {
377
+ $user = AAM::getUser();
378
+ $object = $user->getObject(
379
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
380
+ );
381
+
382
+ // Check if save returns positive result
383
+ $this->assertTrue($object->updateOptionItem('protected', array(
384
+ 'enabled' => true,
385
+ 'password' => '123456'
386
+ ))->save());
387
+
388
+ // Reset all internal cache
389
+ $this->_resetSubjects();
390
+
391
+ $server = rest_get_server();
392
+
393
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
394
+ $request->set_param('context', 'view');
395
+ $request->set_param('password', '123456');
396
+
397
+ $this->assertEquals(200, $server->dispatch($request)->get_status());
398
+ }
399
+
400
+ /**
401
+ * Test PASSWORD PROTECTED option when password is enforced by AAM and is invalid
402
+ *
403
+ * @return void
404
+ *
405
+ * @access public
406
+ * @version 6.0.0
407
+ */
408
+ public function testAAMEnforcedPasswordInvalidOption()
409
+ {
410
+ $user = AAM::getUser();
411
+ $object = $user->getObject(
412
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
413
+ );
414
+
415
+ // Check if save returns positive result
416
+ $this->assertTrue($object->updateOptionItem('protected', array(
417
+ 'enabled' => true,
418
+ 'password' => '123456'
419
+ ))->save());
420
+
421
+ // Reset all internal cache
422
+ $this->_resetSubjects();
423
+
424
+ $server = rest_get_server();
425
+
426
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
427
+ $request->set_param('context', 'view');
428
+ $request->set_param('password', 'abs');
429
+
430
+ $response = $server->dispatch($request);
431
+
432
+ $this->assertEquals(401, $response->get_status());
433
+ $this->assertEquals('post_access_protected', $response->get_data()['code']);
434
+ }
435
+
436
+ /**
437
+ * Test CEASED option
438
+ *
439
+ * @return void
440
+ *
441
+ * @access public
442
+ * @version 6.0.0
443
+ */
444
+ public function testCeasedOption()
445
+ {
446
+ // Hide the post
447
+ $user = AAM::getUser();
448
+ $object = $user->getObject(
449
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
450
+ );
451
+
452
+ // Check if save returns positive result
453
+ $this->assertTrue($object->updateOptionItem('ceased', array(
454
+ 'enabled' => true,
455
+ 'after' => '08/01/2019, 4:37 pm'
456
+ ))->save());
457
+
458
+ // Reset all internal cache
459
+ $this->_resetSubjects();
460
+
461
+ $server = rest_get_server();
462
+
463
+ $request = new WP_REST_Request('GET', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
464
+ $request->set_param('context', 'view');
465
+
466
+ $response = $server->dispatch($request);
467
+
468
+ $this->assertEquals(401, $response->get_status());
469
+ $this->assertEquals('post_access_expired', $response->get_data()['code']);
470
+ }
471
+
472
+ /**
473
+ * Test that user does not have the ability to edit a post
474
+ *
475
+ * @return void
476
+ *
477
+ * @access public
478
+ * @version 6.0.0
479
+ */
480
+ public function testEditOption()
481
+ {
482
+ $user = AAM::getUser();
483
+ $object = $user->getObject(
484
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
485
+ );
486
+
487
+ // Verify that editing is allowed for a specific post
488
+ $this->assertTrue(current_user_can('edit_post', AAM_UNITTEST_POST_ID));
489
+
490
+ // Check if save returns positive result
491
+ $this->assertTrue($object->updateOptionItem('edit', true)->save());
492
+
493
+ // Reset all internal cache
494
+ $this->_resetSubjects();
495
+
496
+ $server = rest_get_server();
497
+
498
+ $request = new WP_REST_Request('POST', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
499
+ $request->set_param('content', 'Test');
500
+
501
+ $response = $server->dispatch($request);
502
+
503
+ $this->assertEquals(403, $response->get_status());
504
+ $this->assertEquals('rest_cannot_edit', $response->get_data()['code']);
505
+ }
506
+
507
+ /**
508
+ * Test that user does not have the ability to delete a post
509
+ *
510
+ * @return void
511
+ *
512
+ * @access public
513
+ * @version 6.0.0
514
+ */
515
+ public function testDeleteOption()
516
+ {
517
+ $user = AAM::getUser();
518
+ $object = $user->getObject(
519
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
520
+ );
521
+
522
+ // Verify that deletion is allowed for a specific post
523
+ $this->assertTrue(current_user_can('delete_post', AAM_UNITTEST_POST_ID));
524
+
525
+ // Check if save returns positive result
526
+ $this->assertTrue($object->updateOptionItem('delete', true)->save());
527
+
528
+ // Reset all internal cache
529
+ $this->_resetSubjects();
530
+
531
+ $server = rest_get_server();
532
+
533
+ $request = new WP_REST_Request('DELETE', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
534
+ $response = $server->dispatch($request);
535
+
536
+ $this->assertEquals(403, $response->get_status());
537
+ $this->assertEquals('rest_cannot_delete', $response->get_data()['code']);
538
+ }
539
+
540
+ /**
541
+ * Test that user does not have the ability to publish a post
542
+ *
543
+ * @return void
544
+ *
545
+ * @access public
546
+ * @version 6.0.0
547
+ */
548
+ public function testPublishOption()
549
+ {
550
+ global $post;
551
+
552
+ $user = AAM::getUser();
553
+ $object = $user->getObject(
554
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
555
+ );
556
+
557
+ // Force global post
558
+ $post = get_post(AAM_UNITTEST_POST_ID);
559
+
560
+ // Verify that publishing is allowed for a specific post
561
+ $this->assertTrue(current_user_can('publish_post', AAM_UNITTEST_POST_ID));
562
+
563
+ // Check if save returns positive result
564
+ $this->assertTrue($object->updateOptionItem('publish', true)->save());
565
+
566
+ // Reset all internal cache
567
+ $this->_resetSubjects();
568
+
569
+ $server = rest_get_server();
570
+
571
+ $request = new WP_REST_Request('POST', '/wp/v2/posts/' . AAM_UNITTEST_POST_ID);
572
+ $request->set_param('status', 'publish');
573
+ $response = $server->dispatch($request);
574
+
575
+ $this->assertEquals(403, $response->get_status());
576
+ $this->assertEquals('rest_cannot_publish', $response->get_data()['code']);
577
+ }
578
+
579
+ }
tests/Service/Content/SingleRoleAccessControlTest.php ADDED
@@ -0,0 +1,602 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Content;
11
+
12
+ use AAM,
13
+ AAM_Core_API,
14
+ AAM_Service_Content,
15
+ AAM_Core_Object_Post,
16
+ PHPUnit\Framework\TestCase,
17
+ AAM\UnitTest\Libs\ResetTrait,
18
+ AAM\UnitTest\Libs\AuthUserTrait;
19
+
20
+ /**
21
+ * Test that content access settings are applied and used properly with WordPress core
22
+ *
23
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
24
+ * @version 6.0.0
25
+ */
26
+ class SingleRoleAccessControlTest extends TestCase
27
+ {
28
+ use ResetTrait,
29
+ AuthUserTrait;
30
+
31
+ /**
32
+ * Test that user is not allowed to access the post when access settings are set
33
+ * so on the User Level
34
+ *
35
+ * @return void
36
+ *
37
+ * @access public
38
+ * @version 6.0.0
39
+ */
40
+ public function testRestrictedOption()
41
+ {
42
+ $user = AAM::getUser();
43
+ $object = $user->getObject(
44
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
45
+ );
46
+
47
+ // Check if save returns positive result
48
+ $this->assertTrue($object->updateOptionItem('restricted', true)->save());
49
+
50
+ // Reset all internal cache
51
+ $this->_resetSubjects();
52
+
53
+ $post = $user->getObject(
54
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
55
+ );
56
+
57
+ // Make sure that AAM API returns correct result
58
+ $this->assertTrue($post->is('restricted'));
59
+
60
+ // Check that current user is not allowed to read_post
61
+ $this->assertFalse(current_user_can('read_post', AAM_UNITTEST_POST_ID));
62
+ }
63
+
64
+ /**
65
+ * Test that user does not have the ability to see hidden post
66
+ *
67
+ * @return void
68
+ *
69
+ * @access public
70
+ * @version 6.0.0
71
+ */
72
+ public function testHiddenOption()
73
+ {
74
+ $posts = get_posts(array(
75
+ 'post_type' => 'post',
76
+ 'fields' => 'ids',
77
+ 'suppress_filters' => false
78
+ ));
79
+
80
+ // First, confirm that post is in the array of posts
81
+ $this->assertTrue(in_array(AAM_UNITTEST_POST_ID, $posts));
82
+
83
+ // Hide the post
84
+ $user = AAM::getUser();
85
+ $object = $user->getObject(
86
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
87
+ );
88
+
89
+ // Check if save returns positive result
90
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
91
+
92
+ // Reset all internal cache
93
+ $this->_resetSubjects();
94
+
95
+ // Verify that post is no longer in the list of posts
96
+ $posts = get_posts(array(
97
+ 'post_type' => 'post',
98
+ 'fields' => 'ids',
99
+ 'suppress_filters' => false
100
+ ));
101
+
102
+ // First, confirm that post is in the array of posts
103
+ $this->assertFalse(in_array(AAM_UNITTEST_POST_ID, $posts));
104
+ }
105
+
106
+ /**
107
+ * Test that content is limited with the Teaser message and enabled excerpt
108
+ * shortcode
109
+ *
110
+ * @return void
111
+ *
112
+ * @access public
113
+ * @version 6.0.0
114
+ */
115
+ public function testTeaserMessageOption()
116
+ {
117
+ $user = AAM::getUser();
118
+ $object = $user->getObject(
119
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
120
+ );
121
+
122
+ // Check if save returns positive result
123
+ $this->assertTrue($object->updateOptionItem('teaser', array(
124
+ 'enabled' => true,
125
+ 'message' => 'Test teaser with [excerpt]'
126
+ ))->save());
127
+
128
+ // Reset all internal cache
129
+ $this->_resetSubjects();
130
+
131
+ // Confirm that teaser message is returned instead of actual content
132
+ $GLOBALS['post'] = AAM_UNITTEST_POST_ID;
133
+ ob_start();
134
+ the_content();
135
+ $this->assertSame(
136
+ ob_get_contents(), 'Test teaser with ' . $object->post_excerpt
137
+ );
138
+ ob_end_clean();
139
+ }
140
+
141
+ /**
142
+ * Test the LIMITED option
143
+ *
144
+ * Forcing $wp_query to trigger AAM_Service_Content::wp
145
+ *
146
+ * @return void
147
+ *
148
+ * @access public
149
+ * @see AAM_Service_Content::wp
150
+ * @version 6.0.0
151
+ */
152
+ public function testLimitedOption()
153
+ {
154
+ global $wp_query;
155
+
156
+ // Limit the post
157
+ $user = AAM::getUser();
158
+ $object = $user->getObject(
159
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
160
+ );
161
+
162
+ // Check if save returns positive result
163
+ $this->assertTrue($object->updateOptionItem('limited', array(
164
+ 'enabled' => true,
165
+ 'threshold' => 1
166
+ ))->save());
167
+
168
+ // Faking the fact that user already seen this post once
169
+ update_user_meta(
170
+ AAM_UNITTEST_AUTH_USER_ID,
171
+ sprintf(AAM_Service_Content::POST_COUNTER_DB_OPTION, AAM_UNITTEST_POST_ID),
172
+ 1
173
+ );
174
+
175
+ // Reset all internal cache
176
+ $this->_resetSubjects();
177
+
178
+ // Forcing WP_Query to the right path
179
+ $wp_query->is_single = true;
180
+ $GLOBALS['post'] = get_post(AAM_UNITTEST_POST_ID);
181
+
182
+ // Override the default handlers so we can suppress die exit
183
+ add_filter('wp_die_handler', function() {
184
+ return function($message, $title) {
185
+ _default_wp_die_handler($message, $title, array('exit' => false));
186
+ };
187
+ }, PHP_INT_MAX);
188
+
189
+ // Capture the WP Die message
190
+ ob_start();
191
+ do_action('wp');
192
+ $content = ob_get_contents();
193
+ ob_end_clean();
194
+
195
+ $this->assertStringContainsString(
196
+ 'User exceeded allowed access number. Access denied.', $content
197
+ );
198
+
199
+ // Reset WP Query
200
+ remove_all_filters('wp_die_handler', PHP_INT_MAX);
201
+
202
+ $wp_query->is_single = null;
203
+ unset($GLOBALS['post']);
204
+ }
205
+
206
+ /**
207
+ * Test that user does not have the ability to comment on a post
208
+ *
209
+ * @return void
210
+ *
211
+ * @access public
212
+ * @version 6.0.0
213
+ */
214
+ public function testCommentingOption()
215
+ {
216
+ $user = AAM::getUser();
217
+ $object = $user->getObject(
218
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
219
+ );
220
+
221
+ // Verify that commenting for this feature is set as open
222
+ $this->assertEquals($object->comment_status, 'open');
223
+
224
+ // Check if save returns positive result
225
+ $this->assertTrue($object->updateOptionItem('comment', true)->save());
226
+
227
+ // Reset all internal cache
228
+ $this->_resetSubjects();
229
+
230
+ // First, confirm that post is in the array of posts
231
+ $this->assertFalse(comments_open(AAM_UNITTEST_POST_ID));
232
+ }
233
+
234
+ /**
235
+ * Test that REDIRECTED to Existing Page option is working as expected
236
+ *
237
+ * @return void
238
+ *
239
+ * @access public
240
+ * @version 6.0.0
241
+ */
242
+ public function testRedirectPageOption()
243
+ {
244
+ $user = AAM::getUser();
245
+ $object = $user->getObject(
246
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
247
+ );
248
+
249
+ // Check if save returns positive result
250
+ $this->assertTrue($object->updateOptionItem('redirected', array(
251
+ 'enabled' => true,
252
+ 'type' => 'page',
253
+ 'destination' => AAM_UNITTEST_PAGE_ID,
254
+ 'httpCode' => 301
255
+ ))->save());
256
+
257
+ // Reset all internal cache
258
+ $this->_resetSubjects();
259
+
260
+ $service = AAM_Service_Content::getInstance();
261
+ $response = $service->isAuthorizedToReadPost($user->getObject(
262
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
263
+ ));
264
+
265
+ // Make sure that we have WP Error
266
+ $this->assertEquals(
267
+ $response->get_error_message(),
268
+ 'Direct access is not allowed. Follow the provided redirect rule.'
269
+ );
270
+
271
+ $this->assertEquals(array(
272
+ 'location' => get_page_link(AAM_UNITTEST_PAGE_ID),
273
+ 'status' => 301
274
+ ), $response->get_error_data());
275
+ }
276
+
277
+ /**
278
+ * Test that REDIRECTED to URL option is working as expected
279
+ *
280
+ * @return void
281
+ *
282
+ * @access public
283
+ * @version 6.0.0
284
+ */
285
+ public function testRedirectURLOption()
286
+ {
287
+ $user = AAM::getUser();
288
+ $object = $user->getObject(
289
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
290
+ );
291
+
292
+ // Check if save returns positive result
293
+ $this->assertTrue($object->updateOptionItem('redirected', array(
294
+ 'enabled' => true,
295
+ 'type' => 'url',
296
+ 'destination' => 'https://aamplugin.com',
297
+ 'httpCode' => 307
298
+ ))->save());
299
+
300
+ // Reset all internal cache
301
+ $this->_resetSubjects();
302
+
303
+ $service = AAM_Service_Content::getInstance();
304
+ $response = $service->isAuthorizedToReadPost($user->getObject(
305
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
306
+ ));
307
+
308
+ // Make sure that we have WP Error
309
+ $this->assertEquals(
310
+ $response->get_error_message(),
311
+ 'Direct access is not allowed. Follow the provided redirect rule.'
312
+ );
313
+
314
+ $this->assertEquals(array(
315
+ 'location' => 'https://aamplugin.com',
316
+ 'status' => 307
317
+ ), $response->get_error_data());
318
+ }
319
+
320
+ /**
321
+ * Test that REDIRECTED to PHP Callback option is working as expected
322
+ *
323
+ * @return void
324
+ *
325
+ * @access public
326
+ * @version 6.0.0
327
+ */
328
+ public function testRedirectCallbackOption()
329
+ {
330
+ $user = AAM::getUser();
331
+ $object = $user->getObject(
332
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
333
+ );
334
+
335
+ // Check if save returns positive result
336
+ $this->assertTrue($object->updateOptionItem('redirected', array(
337
+ 'enabled' => true,
338
+ 'type' => 'callback',
339
+ // WordPress core strips slashes, so we have to double slash all this
340
+ 'destination' => 'AAM\\UnitTest\\Service\\Content\\Callback::redirectCallback',
341
+ 'httpCode' => 310
342
+ ))->save());
343
+
344
+ // Reset all internal cache
345
+ $this->_resetSubjects();
346
+
347
+ $service = AAM_Service_Content::getInstance();
348
+ $response = $service->isAuthorizedToReadPost($user->getObject(
349
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
350
+ ));
351
+
352
+ // Make sure that we have WP Error
353
+ $this->assertEquals(
354
+ $response->get_error_message(),
355
+ 'Direct access is not allowed. Follow the provided redirect rule.'
356
+ );
357
+
358
+ $this->assertEquals(array(
359
+ 'location' => Callback::REDIRECT_URL,
360
+ 'status' => 310
361
+ ), $response->get_error_data());
362
+ }
363
+
364
+ /**
365
+ * Test PASSWORD PROTECTED option
366
+ *
367
+ * @return void
368
+ *
369
+ * @access public
370
+ * @version 6.0.0
371
+ */
372
+ public function testProtectedOption()
373
+ {
374
+ $user = AAM::getUser();
375
+ $object = $user->getObject(
376
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
377
+ );
378
+
379
+ // Check if save returns positive result
380
+ $this->assertTrue($object->updateOptionItem('protected', array(
381
+ 'enabled' => true,
382
+ 'password' => '123456'
383
+ ))->save());
384
+
385
+ // Reset all internal cache
386
+ $this->_resetSubjects();
387
+
388
+ // Get post
389
+ $post = $user->getObject(
390
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
391
+ );
392
+
393
+ // Verify that password is required
394
+ $this->assertTrue(
395
+ apply_filters('post_password_required', false, get_post(AAM_UNITTEST_POST_ID))
396
+ );
397
+
398
+ // Verify that password is not required when explicitly provided
399
+ $this->assertTrue(
400
+ AAM_Service_Content::getInstance()->checkPostPassword($post, '123456')
401
+ );
402
+
403
+ // Test that password is required when incorrect password is provided
404
+ $this->assertEquals(
405
+ 'WP_Error',
406
+ get_class(AAM_Service_Content::getInstance()->checkPostPassword($post, '654321'))
407
+ );
408
+ }
409
+
410
+ /**
411
+ * Test PASSWORD PROTECTED option with passed cookie
412
+ *
413
+ * @return void
414
+ *
415
+ * @access public
416
+ * @version 6.0.0
417
+ */
418
+ public function testProtectedWithCookieOption()
419
+ {
420
+ $user = AAM::getUser();
421
+ $object = $user->getObject(
422
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
423
+ );
424
+
425
+ // Check if save returns positive result
426
+ $this->assertTrue($object->updateOptionItem('protected', array(
427
+ 'enabled' => true,
428
+ 'password' => '123456'
429
+ ))->save());
430
+
431
+ // Reset all internal cache
432
+ $this->_resetSubjects();
433
+
434
+ // Get post
435
+ $post = $user->getObject(
436
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
437
+ );
438
+
439
+ // Verify that password is required
440
+ $this->assertTrue(
441
+ apply_filters('post_password_required', false, get_post(AAM_UNITTEST_POST_ID))
442
+ );
443
+
444
+ // Generate cookie
445
+ $hasher = AAM_Core_API::prepareHasher();
446
+ $_COOKIE['wp-postpass_' . COOKIEHASH] = $hasher->HashPassword('123456');
447
+
448
+ // Verify that password is not required when explicitly provided
449
+ $this->assertTrue(
450
+ AAM_Service_Content::getInstance()->checkPostPassword($post)
451
+ );
452
+
453
+ // Test that password is required when incorrect password is provided
454
+ $_COOKIE['wp-postpass_' . COOKIEHASH] = $hasher->HashPassword('654321');
455
+ $this->assertEquals(
456
+ 'WP_Error',
457
+ get_class(AAM_Service_Content::getInstance()->checkPostPassword($post))
458
+ );
459
+
460
+ // Reset
461
+ unset($_COOKIE['wp-postpass_' . COOKIEHASH]);
462
+ }
463
+
464
+ /**
465
+ * Test CEASED option
466
+ *
467
+ * @return void
468
+ *
469
+ * @access public
470
+ * @version 6.0.0
471
+ */
472
+ public function testCeasedOption()
473
+ {
474
+ // Hide the post
475
+ $user = AAM::getUser();
476
+ $object = $user->getObject(
477
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
478
+ );
479
+
480
+ // Check if save returns positive result
481
+ $this->assertTrue($object->updateOptionItem('ceased', array(
482
+ 'enabled' => true,
483
+ 'after' => '08/01/2019, 4:37 pm'
484
+ ))->save());
485
+
486
+ // Reset all internal cache
487
+ $this->_resetSubjects();
488
+
489
+ // Get post
490
+ $post = $user->getObject(
491
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
492
+ );
493
+
494
+ // Verify that access to the post is expired
495
+ $error = AAM_Service_Content::getInstance()->checkPostExpiration($post);
496
+
497
+ $this->assertEquals('WP_Error', get_class($error));
498
+ $this->assertEquals(
499
+ 'User is unauthorized to access this post. Access Expired.',
500
+ $error->get_error_message()
501
+ );
502
+
503
+ // Test that password is required when incorrect password is provided
504
+ $this->assertEquals(
505
+ 'WP_Error',
506
+ get_class(AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post))
507
+ );
508
+ }
509
+
510
+ /**
511
+ * Test that user does not have the ability to edit a post
512
+ *
513
+ * @return void
514
+ *
515
+ * @access public
516
+ * @version 6.0.0
517
+ */
518
+ public function testEditOption()
519
+ {
520
+ $user = AAM::getUser();
521
+ $object = $user->getObject(
522
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
523
+ );
524
+
525
+ // Verify that editing is allowed for a specific post
526
+ $this->assertTrue(current_user_can('edit_post', AAM_UNITTEST_POST_ID));
527
+
528
+ // Check if save returns positive result
529
+ $this->assertTrue($object->updateOptionItem('edit', true)->save());
530
+
531
+ // Reset all internal cache
532
+ $this->_resetSubjects();
533
+
534
+ // Verify that user is no longer allowed to edit a post
535
+ $this->assertFalse(current_user_can('edit_post', AAM_UNITTEST_POST_ID));
536
+ }
537
+
538
+ /**
539
+ * Test that user does not have the ability to delete a post
540
+ *
541
+ * @return void
542
+ *
543
+ * @access public
544
+ * @version 6.0.0
545
+ */
546
+ public function testDeleteOption()
547
+ {
548
+ $user = AAM::getUser();
549
+ $object = $user->getObject(
550
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
551
+ );
552
+
553
+ // Verify that deletion is allowed for a specific post
554
+ $this->assertTrue(current_user_can('delete_post', AAM_UNITTEST_POST_ID));
555
+
556
+ // Check if save returns positive result
557
+ $this->assertTrue($object->updateOptionItem('delete', true)->save());
558
+
559
+ // Reset all internal cache
560
+ $this->_resetSubjects();
561
+
562
+ // Verify that user is no longer allowed to delete a post
563
+ $this->assertFalse(current_user_can('delete_post', AAM_UNITTEST_POST_ID));
564
+ }
565
+
566
+ /**
567
+ * Test that user does not have the ability to publish a post
568
+ *
569
+ * @return void
570
+ *
571
+ * @access public
572
+ * @version 6.0.0
573
+ */
574
+ public function testPublishOption()
575
+ {
576
+ global $post;
577
+
578
+ $user = AAM::getUser();
579
+ $object = $user->getObject(
580
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
581
+ );
582
+
583
+ // Force global post
584
+ $post = get_post(AAM_UNITTEST_POST_ID);
585
+
586
+ // Verify that publishing is allowed for a specific post
587
+ $this->assertTrue(current_user_can('publish_post', AAM_UNITTEST_POST_ID));
588
+
589
+ // Check if save returns positive result
590
+ $this->assertTrue($object->updateOptionItem('publish', true)->save());
591
+
592
+ // Reset all internal cache
593
+ $this->_resetSubjects();
594
+
595
+ // Verify that user is no longer allowed to publish a post
596
+ $this->assertFalse(current_user_can('publish_post', AAM_UNITTEST_POST_ID));
597
+
598
+ // Reset to default the global state
599
+ unset($post);
600
+ }
601
+
602
+ }
tests/Service/Content/SingleRoleInheritanceTest.php ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Content;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Post,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Test AAM access settings inheritance mechanism for the Content (Posts & Terms)
20
+ * service
21
+ *
22
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
23
+ * @version 6.0.0
24
+ */
25
+ class SingleRoleInheritanceTest extends TestCase
26
+ {
27
+ use ResetTrait,
28
+ AuthUserTrait;
29
+
30
+ /**
31
+ * Test to insure that access settings are stored property on the User level
32
+ *
33
+ * @return void
34
+ *
35
+ * @access public
36
+ * @version 6.0.0
37
+ */
38
+ public function testSaveUserLevelOption()
39
+ {
40
+ $user = AAM::getUser();
41
+ $object = $user->getObject(
42
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
43
+ );
44
+
45
+ // Check if save returns positive result
46
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
47
+
48
+ // Read from the database saved values and assert that we have
49
+ // Array (
50
+ // hidden => true
51
+ // )
52
+ $option = $user->readOption(
53
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID . '|post'
54
+ );
55
+
56
+ $this->assertSame(array('hidden' => true), $option);
57
+ }
58
+
59
+ /**
60
+ * Test that access settings are inherited from the parent role property
61
+ *
62
+ * This test is designed to verify that access settings are propagated property
63
+ * when there is only one role assigned to a user.
64
+ *
65
+ * @return void
66
+ *
67
+ * @access public
68
+ * @version 6.0.0
69
+ */
70
+ public function testInheritanceFromSingleRole()
71
+ {
72
+ $user = AAM::getUser();
73
+ $parent = $user->getParent();
74
+ $object = $parent->getObject(
75
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
76
+ );
77
+
78
+ // Make sure that we have parent role defined
79
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($parent));
80
+
81
+ // Save access settings for the role and make sure they are saved property
82
+ // Check if save returns positive result
83
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
84
+
85
+ // Read from the database saved values and assert that we have
86
+ // Array (
87
+ // hidden => true
88
+ // )
89
+ $option = $parent->readOption(
90
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID . '|post'
91
+ );
92
+ $this->assertSame(array('hidden' => true), $option);
93
+
94
+ // Finally verify that access settings are propagated property to the User
95
+ // Level
96
+ $post = $user->getObject(
97
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
98
+ );
99
+ $this->assertSame(array('hidden' => true), $post->getOption());
100
+ }
101
+
102
+ /**
103
+ * Test that access settings are propagated and merged properly
104
+ *
105
+ * The test is designed to verify that access settings are propagated properly
106
+ * from the parent role and merged well with explicitly defined access settings on
107
+ * the User level.
108
+ *
109
+ * The expected result is to have combined array of access settings from the parent
110
+ * role and specific user.
111
+ *
112
+ * @return void
113
+ *
114
+ * @access public
115
+ * @version 6.0.0
116
+ */
117
+ public function testInheritanceMergeFromSingleRole()
118
+ {
119
+ $user = AAM::getUser();
120
+ $parent = $user->getParent();
121
+ $object = $parent->getObject(
122
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
123
+ );
124
+
125
+ // Save access settings for the role and make sure they are saved property
126
+ // Check if save returns positive result
127
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
128
+
129
+ // Save access setting for the user and make sure they are saved property
130
+ $post = $user->getObject(
131
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true
132
+ );
133
+ $this->assertTrue($post->updateOptionItem('comment', false)->save());
134
+
135
+ // Reset cache and try to kick-in the inheritance mechanism
136
+ $this->_resetSubjects();
137
+
138
+ $post = $user->getObject(
139
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
140
+ );
141
+ $this->assertSame(
142
+ array('hidden' => true, 'comment' => false),
143
+ $post->getOption()
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Test that the full inheritance mechanism is working as expected
149
+ *
150
+ * Make sure that access settings are propagated and merged properly from the top
151
+ * (Default Level) to the bottom (User Level).
152
+ *
153
+ * @return void
154
+ *
155
+ * @access public
156
+ * @version 6.0.0
157
+ */
158
+ public function testFullInheritanceChainSingeRole()
159
+ {
160
+ $user = AAM::getUser();
161
+ $role = $user->getParent();
162
+ $default = $role->getParent();
163
+
164
+ $userPost = $user->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true);
165
+ $rolePost = $role->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true);
166
+ $defaultPost = $default->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true);
167
+
168
+ // Save access settings for all subjects
169
+ $this->assertTrue($userPost->updateOptionItem('hidden', true)->save());
170
+ $this->assertTrue($rolePost->updateOptionItem('comment', true)->save());
171
+ $this->assertTrue($defaultPost->updateOptionItem('restricted', true)->save());
172
+
173
+ // Reset cache and try to kick-in the inheritance mechanism
174
+ $this->_resetSubjects();
175
+
176
+ // All settings has to be merged into one array
177
+ $post = $user->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID);
178
+ $this->assertSame(
179
+ array(
180
+ 'restricted' => true,
181
+ 'comment' => true,
182
+ 'hidden' => true
183
+ ),
184
+ $post->getOption()
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Test that access settings overwrite works as expected
190
+ *
191
+ * The expected result is lower Access Level overwrite access settings from the
192
+ * higher Access Level.
193
+ *
194
+ * @return void
195
+ *
196
+ * @access public
197
+ * @version 6.0.0
198
+ */
199
+ public function testInheritanceOverrideForSingleRole()
200
+ {
201
+ $user = AAM::getUser();
202
+ $parent = $user->getParent();
203
+
204
+ $object = $parent->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID);
205
+
206
+ // Save access settings for the role and make sure they are saved property
207
+ // Check if save returns positive result
208
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
209
+
210
+ // Save access setting for the user and make sure they are saved property
211
+ $post = $user->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID, true);
212
+ $this->assertTrue($post->updateOptionItem('hidden', false)->save());
213
+
214
+ // Reset cache and try to kick-in the inheritance mechanism
215
+ $this->_resetSubjects();
216
+
217
+ $post = $user->getObject(AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID);
218
+ $this->assertSame(array('hidden' => false), $post->getOption());
219
+ }
220
+
221
+ }
tests/Service/Content/VisitorAccessControlTest.php ADDED
@@ -0,0 +1,432 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Content;
11
+
12
+ use AAM,
13
+ AAM_Core_API,
14
+ AAM_Service_Content,
15
+ AAM_Core_Object_Post,
16
+ PHPUnit\Framework\TestCase,
17
+ AAM\UnitTest\Libs\ResetTrait;
18
+
19
+ /**
20
+ * Test that content access settings are applied and used properly with WordPress core
21
+ * for the unauthorized user
22
+ *
23
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
24
+ * @version 6.0.0
25
+ */
26
+ class VisitorAccessControlTest extends TestCase
27
+ {
28
+ use ResetTrait;
29
+
30
+ /**
31
+ * Test that visitor is not allowed to access the post when access settings
32
+ * are set so on the Visitor Level
33
+ *
34
+ * @return void
35
+ *
36
+ * @access public
37
+ * @version 6.0.0
38
+ */
39
+ public function testRestrictedOption()
40
+ {
41
+ $user = AAM::getUser();
42
+ $object = $user->getObject(
43
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
44
+ );
45
+
46
+ // Check if save returns positive result
47
+ $this->assertTrue($object->updateOptionItem('restricted', true)->save());
48
+
49
+ // Reset all internal cache
50
+ $this->_resetSubjects();
51
+
52
+ $post = $user->getObject(
53
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
54
+ );
55
+
56
+ // Make sure that AAM API returns correct result
57
+ $this->assertTrue($post->is('restricted'));
58
+
59
+ // Check that current user is not allowed to read_post
60
+ $this->assertFalse(current_user_can('read_post', AAM_UNITTEST_POST_ID));
61
+ }
62
+
63
+ /**
64
+ * Test that visitor does not have the ability to see hidden post
65
+ *
66
+ * @return void
67
+ *
68
+ * @access public
69
+ * @version 6.0.0
70
+ */
71
+ public function testHiddenOption()
72
+ {
73
+ $posts = get_posts(array(
74
+ 'post_type' => 'post',
75
+ 'fields' => 'ids',
76
+ 'suppress_filters' => false
77
+ ));
78
+
79
+ // First, confirm that post is in the array of posts
80
+ $this->assertTrue(in_array(AAM_UNITTEST_POST_ID, $posts));
81
+
82
+ // Hide the post
83
+ $user = AAM::getUser();
84
+ $object = $user->getObject(
85
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
86
+ );
87
+
88
+ // Check if save returns positive result
89
+ $this->assertTrue($object->updateOptionItem('hidden', true)->save());
90
+
91
+ // Reset all internal cache
92
+ $this->_resetSubjects();
93
+
94
+ // Verify that post is no longer in the list of posts
95
+ $posts = get_posts(array(
96
+ 'post_type' => 'post',
97
+ 'fields' => 'ids',
98
+ 'suppress_filters' => false
99
+ ));
100
+
101
+ // First, confirm that post is in the array of posts
102
+ $this->assertFalse(in_array(AAM_UNITTEST_POST_ID, $posts));
103
+ }
104
+
105
+ /**
106
+ * Test that content is limited with the Teaser message and enabled excerpt
107
+ * shortcode
108
+ *
109
+ * @return void
110
+ *
111
+ * @access public
112
+ * @version 6.0.0
113
+ */
114
+ public function testTeaserMessageOption()
115
+ {
116
+ $user = AAM::getUser();
117
+ $object = $user->getObject(
118
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
119
+ );
120
+
121
+ // Check if save returns positive result
122
+ $this->assertTrue($object->updateOptionItem('teaser', array(
123
+ 'enabled' => true,
124
+ 'message' => 'Test teaser with [excerpt]'
125
+ ))->save());
126
+
127
+ // Reset all internal cache
128
+ $this->_resetSubjects();
129
+
130
+ // Confirm that teaser message is returned instead of actual content
131
+ $GLOBALS['post'] = AAM_UNITTEST_POST_ID;
132
+ ob_start();
133
+ the_content();
134
+ $this->assertSame(
135
+ ob_get_contents(), 'Test teaser with ' . $object->post_excerpt
136
+ );
137
+ ob_end_clean();
138
+ }
139
+
140
+ /**
141
+ * Test that visitor does not have the ability to comment on a post
142
+ *
143
+ * @return void
144
+ *
145
+ * @access public
146
+ * @version 6.0.0
147
+ */
148
+ public function testCommentingOption()
149
+ {
150
+ $user = AAM::getUser();
151
+ $object = $user->getObject(
152
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
153
+ );
154
+
155
+ // Verify that commenting for this feature is set as open
156
+ $this->assertEquals($object->comment_status, 'open');
157
+
158
+ // Check if save returns positive result
159
+ $this->assertTrue($object->updateOptionItem('comment', true)->save());
160
+
161
+ // Reset all internal cache
162
+ $this->_resetSubjects();
163
+
164
+ // First, confirm that post is in the array of posts
165
+ $this->assertFalse(comments_open(AAM_UNITTEST_POST_ID));
166
+ }
167
+
168
+ /**
169
+ * Test that REDIRECTED to Existing Page option is working as expected
170
+ *
171
+ * @return void
172
+ *
173
+ * @access public
174
+ * @version 6.0.0
175
+ */
176
+ public function testRedirectPageOption()
177
+ {
178
+ $user = AAM::getUser();
179
+ $object = $user->getObject(
180
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
181
+ );
182
+
183
+ // Check if save returns positive result
184
+ $this->assertTrue($object->updateOptionItem('redirected', array(
185
+ 'enabled' => true,
186
+ 'type' => 'page',
187
+ 'destination' => AAM_UNITTEST_PAGE_ID,
188
+ 'httpCode' => 301
189
+ ))->save());
190
+
191
+ // Reset all internal cache
192
+ $this->_resetSubjects();
193
+
194
+ $service = AAM_Service_Content::getInstance();
195
+ $response = $service->isAuthorizedToReadPost($user->getObject(
196
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
197
+ ));
198
+
199
+ // Make sure that we have WP Error
200
+ $this->assertEquals(
201
+ $response->get_error_message(),
202
+ 'Direct access is not allowed. Follow the provided redirect rule.'
203
+ );
204
+
205
+ $this->assertEquals(array(
206
+ 'location' => get_page_link(AAM_UNITTEST_PAGE_ID),
207
+ 'status' => 301
208
+ ), $response->get_error_data());
209
+ }
210
+
211
+ /**
212
+ * Test that REDIRECTED to URL option is working as expected
213
+ *
214
+ * @return void
215
+ *
216
+ * @access public
217
+ * @version 6.0.0
218
+ */
219
+ public function testRedirectURLOption()
220
+ {
221
+ $user = AAM::getUser();
222
+ $object = $user->getObject(
223
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
224
+ );
225
+
226
+ // Check if save returns positive result
227
+ $this->assertTrue($object->updateOptionItem('redirected', array(
228
+ 'enabled' => true,
229
+ 'type' => 'url',
230
+ 'destination' => 'https://aamplugin.com',
231
+ 'httpCode' => 307
232
+ ))->save());
233
+
234
+ // Reset all internal cache
235
+ $this->_resetSubjects();
236
+
237
+ $service = AAM_Service_Content::getInstance();
238
+ $response = $service->isAuthorizedToReadPost($user->getObject(
239
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
240
+ ));
241
+
242
+ // Make sure that we have WP Error
243
+ $this->assertEquals(
244
+ $response->get_error_message(),
245
+ 'Direct access is not allowed. Follow the provided redirect rule.'
246
+ );
247
+
248
+ $this->assertEquals(array(
249
+ 'location' => 'https://aamplugin.com',
250
+ 'status' => 307
251
+ ), $response->get_error_data());
252
+ }
253
+
254
+ /**
255
+ * Test that REDIRECTED to PHP Callback option is working as expected
256
+ *
257
+ * @return void
258
+ *
259
+ * @access public
260
+ * @version 6.0.0
261
+ */
262
+ public function testRedirectCallbackOption()
263
+ {
264
+ $user = AAM::getUser();
265
+ $object = $user->getObject(
266
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
267
+ );
268
+
269
+ // Check if save returns positive result
270
+ $this->assertTrue($object->updateOptionItem('redirected', array(
271
+ 'enabled' => true,
272
+ 'type' => 'callback',
273
+ // WordPress core strips slashes, so we have to double slash all this
274
+ 'destination' => 'AAM\\UnitTest\\Service\\Content\\Callback::redirectCallback',
275
+ 'httpCode' => 310
276
+ ))->save());
277
+
278
+ // Reset all internal cache
279
+ $this->_resetSubjects();
280
+
281
+ $service = AAM_Service_Content::getInstance();
282
+ $response = $service->isAuthorizedToReadPost($user->getObject(
283
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
284
+ ));
285
+
286
+ // Make sure that we have WP Error
287
+ $this->assertEquals(
288
+ $response->get_error_message(),
289
+ 'Direct access is not allowed. Follow the provided redirect rule.'
290
+ );
291
+
292
+ $this->assertEquals(array(
293
+ 'location' => Callback::REDIRECT_URL,
294
+ 'status' => 310
295
+ ), $response->get_error_data());
296
+ }
297
+
298
+ /**
299
+ * Test that REDIRECTED to Login Page option is working as expected
300
+ *
301
+ * @return void
302
+ *
303
+ * @access public
304
+ * @version 6.0.0
305
+ */
306
+ public function testRedirectLoginOption()
307
+ {
308
+ $user = AAM::getUser();
309
+ $object = $user->getObject(
310
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
311
+ );
312
+
313
+ // Check if save returns positive result
314
+ $this->assertTrue($object->updateOptionItem('redirected', array(
315
+ 'enabled' => true,
316
+ 'type' => 'login',
317
+ 'httpCode' => 301
318
+ ))->save());
319
+
320
+ // Reset all internal cache
321
+ $this->_resetSubjects();
322
+
323
+ $service = AAM_Service_Content::getInstance();
324
+ $response = $service->isAuthorizedToReadPost($user->getObject(
325
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
326
+ ));
327
+
328
+ // Make sure that we have WP Error
329
+ $this->assertEquals(
330
+ $response->get_error_message(),
331
+ 'Direct access is not allowed. Follow the provided redirect rule.'
332
+ );
333
+
334
+ $this->assertEquals(array(
335
+ 'location' => wp_login_url() . '?reason=restricted',
336
+ 'status' => 301
337
+ ), $response->get_error_data());
338
+ }
339
+
340
+ /**
341
+ * Test PASSWORD PROTECTED option
342
+ *
343
+ * @return void
344
+ *
345
+ * @access public
346
+ * @version 6.0.0
347
+ */
348
+ public function testProtectedOption()
349
+ {
350
+ $user = AAM::getUser();
351
+ $object = $user->getObject(
352
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
353
+ );
354
+
355
+ // Check if save returns positive result
356
+ $this->assertTrue($object->updateOptionItem('protected', array(
357
+ 'enabled' => true,
358
+ 'password' => '123456'
359
+ ))->save());
360
+
361
+ // Reset all internal cache
362
+ $this->_resetSubjects();
363
+
364
+ // Get post
365
+ $post = $user->getObject(
366
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
367
+ );
368
+
369
+ // Verify that password is required
370
+ $this->assertTrue(
371
+ apply_filters('post_password_required', false, get_post(AAM_UNITTEST_POST_ID))
372
+ );
373
+
374
+ // Verify that password is not required when explicitly provided
375
+ $this->assertTrue(
376
+ AAM_Service_Content::getInstance()->checkPostPassword($post, '123456')
377
+ );
378
+
379
+ // Test that password is required when incorrect password is provided
380
+ $this->assertEquals(
381
+ 'WP_Error',
382
+ get_class(AAM_Service_Content::getInstance()->checkPostPassword($post, '654321'))
383
+ );
384
+ }
385
+
386
+ /**
387
+ * Test CEASED option
388
+ *
389
+ * @return void
390
+ *
391
+ * @access public
392
+ * @version 6.0.0
393
+ */
394
+ public function testCeasedOption()
395
+ {
396
+ // Hide the post
397
+ $user = AAM::getUser();
398
+ $object = $user->getObject(
399
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
400
+ );
401
+
402
+ // Check if save returns positive result
403
+ $this->assertTrue($object->updateOptionItem('ceased', array(
404
+ 'enabled' => true,
405
+ 'after' => '08/01/2019, 4:37 pm'
406
+ ))->save());
407
+
408
+ // Reset all internal cache
409
+ $this->_resetSubjects();
410
+
411
+ // Get post
412
+ $post = $user->getObject(
413
+ AAM_Core_Object_Post::OBJECT_TYPE, AAM_UNITTEST_POST_ID
414
+ );
415
+
416
+ // Verify that access to the post is expired
417
+ $error = AAM_Service_Content::getInstance()->checkPostExpiration($post);
418
+
419
+ $this->assertEquals('WP_Error', get_class($error));
420
+ $this->assertEquals(
421
+ 'User is unauthorized to access this post. Access Expired.',
422
+ $error->get_error_message()
423
+ );
424
+
425
+ // Test that password is required when incorrect password is provided
426
+ $this->assertEquals(
427
+ 'WP_Error',
428
+ get_class(AAM_Service_Content::getInstance()->isAuthorizedToReadPost($post))
429
+ );
430
+ }
431
+
432
+ }
tests/Service/Core/CoreServiceTest.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Core;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Menu,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Test AAM core service functionality
20
+ *
21
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
22
+ * @version 6.0.0
23
+ */
24
+ class CoreServiceTest extends TestCase
25
+ {
26
+ use ResetTrait,
27
+ AuthUserTrait;
28
+
29
+ /**
30
+ * Test that all AAM related labels are properly escaped to mitigate XSS
31
+ *
32
+ * @return void
33
+ *
34
+ * @access public
35
+ * @version 6.0.0
36
+ */
37
+ public function testEscapeTranslation()
38
+ {
39
+ $escaped = __('<script>alert(1);</script>', AAM_KEY);
40
+ $this->assertEquals($escaped, '&lt;script&gt;alert(1);&lt;/script&gt;');
41
+ }
42
+
43
+ }
tests/Service/DeniedRedirect/Callback.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace AAM\UnitTest\Service\DeniedRedirect;
4
+
5
+ class Callback
6
+ {
7
+ const OUTPUT = 'Redirect Callback Output';
8
+
9
+ public static function printOutput()
10
+ {
11
+ echo self::OUTPUT;
12
+ }
13
+
14
+ }
tests/Service/DeniedRedirect/DeniedRedirectTest.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\DeniedRedirect;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Redirect,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait;
16
+
17
+ /**
18
+ * Access Denied Redirect service
19
+ *
20
+ * @package AAM\UnitTest
21
+ * @version 6.0.0
22
+ */
23
+ class DeniedRedirectTest extends TestCase
24
+ {
25
+ use ResetTrait;
26
+
27
+ /**
28
+ * Test default redirect which is "Access Denied" message
29
+ *
30
+ * @return void
31
+ *
32
+ * @access public
33
+ * @version 6.0.0
34
+ */
35
+ public function testDefaultRedirect()
36
+ {
37
+ // Capture the WP Die message
38
+ ob_start();
39
+ wp_die('Restricted Access', 'aam_access_denied', array('exit' => false));
40
+ $content = ob_get_contents();
41
+ ob_end_clean();
42
+
43
+ $this->assertStringContainsString('Access Denied', $content);
44
+ }
45
+
46
+ /**
47
+ * Test custom WP Die message content
48
+ *
49
+ * @return void
50
+ *
51
+ * @access public
52
+ * @version 6.0.0
53
+ */
54
+ public function testCustomMessageRedirect()
55
+ {
56
+ // Define custom access denied message
57
+ $redirect = AAM::getUser()->getObject(AAM_Core_Object_Redirect::OBJECT_TYPE);
58
+ $redirect->updateOptionItem('frontend.redirect.type', 'message');
59
+ $redirect->updateOptionItem('frontend.redirect.message', 'Denied by test');
60
+
61
+ $this->assertTrue($redirect->save());
62
+
63
+ // Reset all internal cache
64
+ $this->_resetSubjects();
65
+
66
+ // Capture the WP Die message
67
+ ob_start();
68
+ wp_die('Test', 'aam_access_denied', array('exit' => false));
69
+ $content = ob_get_contents();
70
+ ob_end_clean();
71
+
72
+ $this->assertStringContainsString('Denied by test', $content);
73
+ }
74
+
75
+ /**
76
+ * Test redirect to the existing page
77
+ *
78
+ * @return void
79
+ *
80
+ * @access public
81
+ * @version 6.0.0
82
+ */
83
+ public function testExistingPageRedirect()
84
+ {
85
+ // Define custom access denied message
86
+ $redirect = AAM::getUser()->getObject(AAM_Core_Object_Redirect::OBJECT_TYPE);
87
+ $redirect->updateOptionItem('frontend.redirect.type', 'page');
88
+ $redirect->updateOptionItem('frontend.redirect.page', AAM_UNITTEST_PAGE_ID);
89
+
90
+ $this->assertTrue($redirect->save());
91
+
92
+ // Reset all internal cache
93
+ $this->_resetSubjects();
94
+
95
+ // Capture the WP Die message
96
+ ob_start();
97
+ wp_die('Access Denied', 'aam_access_denied', array('exit' => false));
98
+ ob_end_clean();
99
+
100
+ $this->assertContains('Location: ' . get_page_link(AAM_UNITTEST_PAGE_ID), xdebug_get_headers());
101
+ }
102
+
103
+ /**
104
+ * Test redirect to specified URI
105
+ *
106
+ * @return void
107
+ *
108
+ * @access public
109
+ * @version 6.0.0
110
+ */
111
+ public function testUrlRedirect()
112
+ {
113
+ // Define custom access denied message
114
+ $redirect = AAM::getUser()->getObject(AAM_Core_Object_Redirect::OBJECT_TYPE);
115
+ $redirect->updateOptionItem('frontend.redirect.type', 'url');
116
+ $redirect->updateOptionItem('frontend.redirect.url', '/hello-world');
117
+
118
+ $this->assertTrue($redirect->save());
119
+
120
+ // Reset all internal cache
121
+ $this->_resetSubjects();
122
+
123
+ // Capture the WP Die message
124
+ ob_start();
125
+ wp_die('Access Denied', 'aam_access_denied', array('exit' => false));
126
+ ob_end_clean();
127
+
128
+ $this->assertContains('Location: /hello-world', xdebug_get_headers());
129
+ }
130
+
131
+ /**
132
+ * Test redirect to the login screen
133
+ *
134
+ * @return void
135
+ *
136
+ * @access public
137
+ * @version 6.0.0
138
+ */
139
+ public function testLoginPageRedirect()
140
+ {
141
+ // Define custom access denied message
142
+ $redirect = AAM::getUser()->getObject(AAM_Core_Object_Redirect::OBJECT_TYPE);
143
+ $redirect->updateOptionItem('frontend.redirect.type', 'login');
144
+
145
+ $this->assertTrue($redirect->save());
146
+
147
+ // Reset all internal cache
148
+ $this->_resetSubjects();
149
+
150
+ // Capture the WP Die message
151
+ ob_start();
152
+ wp_die('Access Denied', 'aam_access_denied', array('exit' => false));
153
+ ob_end_clean();
154
+
155
+ $this->assertContains('Location: ' . add_query_arg(
156
+ array('reason' => 'restricted'), wp_login_url()
157
+ ), xdebug_get_headers());
158
+ }
159
+
160
+ /**
161
+ * Test redirect to the PHP callback function
162
+ *
163
+ * @return void
164
+ *
165
+ * @access public
166
+ * @version 6.0.0
167
+ */
168
+ public function testCallbackRedirect()
169
+ {
170
+ // Define custom access denied message
171
+ $redirect = AAM::getUser()->getObject(AAM_Core_Object_Redirect::OBJECT_TYPE);
172
+ $redirect->updateOptionItem('frontend.redirect.type', 'callback');
173
+ $redirect->updateOptionItem('frontend.redirect.callback', 'AAM\\UnitTest\\Service\\DeniedRedirect\\Callback::printOutput');
174
+
175
+ $this->assertTrue($redirect->save());
176
+
177
+ // Reset all internal cache
178
+ $this->_resetSubjects();
179
+
180
+ // Capture the WP Die message
181
+ ob_start();
182
+ wp_die('Access Denied', 'aam_access_denied', array('exit' => false));
183
+ $content = ob_get_contents();
184
+ ob_end_clean();
185
+
186
+ $this->assertStringContainsString(Callback::OUTPUT, $content);
187
+ }
188
+
189
+ }
tests/Service/Jwt/JwtTest.php ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Jwt;
11
+
12
+ use DateTime,
13
+ AAM_Service_Jwt,
14
+ WP_REST_Request,
15
+ AAM_Core_Config,
16
+ AAM_Core_Jwt_Issuer,
17
+ PHPUnit\Framework\TestCase,
18
+ AAM\UnitTest\Libs\ResetTrait;
19
+
20
+ /**
21
+ * Jwt service tests
22
+ *
23
+ * @package AAM\UnitTest
24
+ * @version 6.0.0
25
+ */
26
+ class JwtTest extends TestCase
27
+ {
28
+ use ResetTrait;
29
+
30
+ /**
31
+ * Assert that jwt token is generated for the authentication request
32
+ *
33
+ * @return void
34
+ *
35
+ * @access public
36
+ * @version 6.0.0
37
+ */
38
+ public function testAuthResponseContainsJwt()
39
+ {
40
+ $server = rest_get_server();
41
+
42
+ // No need to generate Auth cookies
43
+ add_filter('send_auth_cookies', '__return_false');
44
+
45
+ $request = new WP_REST_Request('POST', '/aam/v2/authenticate');
46
+ $request->set_param('username', AAM_UNITTEST_USERNAME);
47
+ $request->set_param('password', AAM_UNITTEST_PASSWORD);
48
+ $request->set_param('issueJWT', true);
49
+
50
+ $data = $server->dispatch($request)->get_data();
51
+
52
+ $this->assertArrayHasKey('jwt', $data);
53
+ }
54
+
55
+ /**
56
+ * Validate that issued JWT token is valid when it is marked as none-revokable
57
+ *
58
+ * @return void
59
+ *
60
+ * @access public
61
+ * @version 6.0.0
62
+ */
63
+ public function testValidateNotRevocableJwtToken()
64
+ {
65
+ $server = rest_get_server();
66
+
67
+ // Generate valid JWT token
68
+ $jwt = AAM_Core_Jwt_Issuer::getInstance()->issueToken(array(
69
+ 'userId' => AAM_UNITTEST_AUTH_USER_ID,
70
+ 'revocable' => false,
71
+ 'refreshable' => false
72
+ ));
73
+
74
+ $this->assertObjectHasAttribute('token', $jwt);
75
+ $this->assertObjectHasAttribute('claims', $jwt);
76
+
77
+ $request = new WP_REST_Request('POST', '/aam/v1/validate-jwt');
78
+ $request->set_param('jwt', $jwt->token);
79
+
80
+ $response = $server->dispatch($request);
81
+
82
+ $this->assertEquals(200, $response->get_status());
83
+ }
84
+
85
+ /**
86
+ * Validate that issued JWT is not valid when it is marked as revokable and is
87
+ * not stored in the JWT store
88
+ *
89
+ * @access public
90
+ * @version 6.0.0
91
+ */
92
+ public function testValidateRevocableJwtToken()
93
+ {
94
+ $server = rest_get_server();
95
+
96
+ // Generate valid JWT token
97
+ $jwt = AAM_Core_Jwt_Issuer::getInstance()->issueToken(array(
98
+ 'userId' => AAM_UNITTEST_AUTH_USER_ID,
99
+ 'revocable' => true,
100
+ 'refreshable' => false
101
+ ));
102
+
103
+ $this->assertObjectHasAttribute('token', $jwt);
104
+ $this->assertObjectHasAttribute('claims', $jwt);
105
+
106
+ $request = new WP_REST_Request('POST', '/aam/v1/validate-jwt');
107
+ $request->set_param('jwt', $jwt->token);
108
+
109
+ $response = $server->dispatch($request);
110
+
111
+ $this->assertEquals(400, $response->get_status());
112
+ $this->assertEquals('Token has been revoked', $response->get_data()->get_error_message());
113
+ }
114
+
115
+ /**
116
+ * Validate that JWT token is invalid when it is expired
117
+ *
118
+ * @access public
119
+ * @version 6.0.0
120
+ */
121
+ public function testValidateExpiredJwtToken()
122
+ {
123
+ $server = rest_get_server();
124
+
125
+ // Generate valid JWT token
126
+ $jwt = AAM_Core_Jwt_Issuer::getInstance()->issueToken(array(
127
+ 'userId' => AAM_UNITTEST_AUTH_USER_ID,
128
+ 'revocable' => true,
129
+ 'refreshable' => false
130
+ ), DateTime::createFromFormat('m/d/Y', '01/01/2018'));
131
+
132
+ $this->assertObjectHasAttribute('token', $jwt);
133
+ $this->assertObjectHasAttribute('claims', $jwt);
134
+
135
+ $request = new WP_REST_Request('POST', '/aam/v1/validate-jwt');
136
+ $request->set_param('jwt', $jwt->token);
137
+
138
+ $response = $server->dispatch($request);
139
+
140
+ $this->assertEquals(400, $response->get_status());
141
+ $this->assertEquals('Expired token', $response->get_data()->get_error_message());
142
+ }
143
+
144
+ /**
145
+ * Verify that user JWT token registry is populated correctly
146
+ *
147
+ * @return void
148
+ *
149
+ * @access public
150
+ * @version 6.0.0
151
+ */
152
+ public function testTokenRegistryPopulated()
153
+ {
154
+ $service = AAM_Service_Jwt::getInstance();
155
+ $tokens = $service->getTokenRegistry(AAM_UNITTEST_JOHN_ID);
156
+
157
+ // Assert that the registry is empty
158
+ $this->assertEquals(0, count($tokens));
159
+
160
+ // Issue new token and verify that registry increased by one
161
+ $res = $service->issueToken(AAM_UNITTEST_JOHN_ID);
162
+
163
+ // Reset cache
164
+ wp_cache_flush();
165
+
166
+ $tokens = $service->getTokenRegistry(AAM_UNITTEST_JOHN_ID);
167
+
168
+ // Assert that the new token is there
169
+ $this->assertEquals(1, count($tokens));
170
+ $this->assertTrue(in_array($res->token, $tokens, true));
171
+ }
172
+
173
+ /**
174
+ * Verify that registry implement ring-buffer approach and does not allow to
175
+ * overload the DB
176
+ *
177
+ * @return void
178
+ *
179
+ * @access public
180
+ * @version 6.0.0
181
+ */
182
+ public function testTokenRegistryOverflow()
183
+ {
184
+ AAM_Core_Config::set('authentication.jwt.registryLimit', 1);
185
+
186
+ // Reset cache
187
+ wp_cache_flush();
188
+
189
+ $service = AAM_Service_Jwt::getInstance();
190
+ $tokens = $service->getTokenRegistry(AAM_UNITTEST_JOHN_ID);
191
+
192
+ // Assert that the registry is empty
193
+ $this->assertEquals(0, count($tokens));
194
+
195
+ // Issue new token and verify that registry increased by one
196
+ $res1 = $service->issueToken(AAM_UNITTEST_JOHN_ID);
197
+
198
+ // Reset cache
199
+ wp_cache_flush();
200
+
201
+ $tokens = $service->getTokenRegistry(AAM_UNITTEST_JOHN_ID);
202
+
203
+ // Assert that token is in the registry
204
+ $this->assertEquals(1, count($tokens));
205
+
206
+ // Issue a new token and make sure that there is only one token in the
207
+ // registry
208
+ $res2 = $service->issueToken(AAM_UNITTEST_JOHN_ID);
209
+
210
+ // Reset cache
211
+ wp_cache_flush();
212
+
213
+ $tokens = $service->getTokenRegistry(AAM_UNITTEST_JOHN_ID);
214
+
215
+ // Assert that token is in the registry
216
+ $this->assertEquals(1, count($tokens));
217
+
218
+ $this->assertFalse(in_array($res1->token, $tokens, true));
219
+ $this->assertTrue(in_array($res2->token, $tokens, true));
220
+ }
221
+
222
+ /**
223
+ * Verify that token can be refreshed successfully
224
+ *
225
+ * @return void
226
+ *
227
+ * @access public
228
+ * @version 6.0.0
229
+ */
230
+ public function testTokenRefreshValid()
231
+ {
232
+ $server = rest_get_server();
233
+ $service = AAM_Service_Jwt::getInstance();
234
+
235
+ // Issue a token that later we'll refresh
236
+ $jwt = $service->issueToken(AAM_UNITTEST_JOHN_ID, null, null, true);
237
+
238
+ // Verify that token was issued
239
+ $this->assertObjectHasAttribute('token', $jwt);
240
+
241
+ // Refresh token
242
+ $request = new WP_REST_Request('POST', '/aam/v1/refresh-jwt');
243
+ $request->set_param('jwt', $jwt->token);
244
+
245
+ $response = $server->dispatch($request);
246
+
247
+ $this->assertEquals(200, $response->get_status());
248
+ }
249
+
250
+ /**
251
+ * Verify that token can't be refreshed if it is simply invalid JWT token
252
+ *
253
+ * @return void
254
+ *
255
+ * @access public
256
+ * @version 6.0.0
257
+ */
258
+ public function testTokenRefreshNotValid()
259
+ {
260
+ $server = rest_get_server();
261
+
262
+ // Refresh token
263
+ $request = new WP_REST_Request('POST', '/aam/v1/refresh-jwt');
264
+ $request->set_param('jwt', 'invalid-token');
265
+
266
+ $response = $server->dispatch($request);
267
+
268
+ $this->assertEquals(400, $response->get_status());
269
+ $this->assertStringContainsString('Invalid JWT token: Malformed UTF-8 characters', $response->get_data()->get_error_message());
270
+ }
271
+
272
+ /**
273
+ * Verify that new token is not issued for already expired token
274
+ *
275
+ * @return void
276
+ *
277
+ * @access public
278
+ * @version 6.0.0
279
+ */
280
+ public function testTokenRefreshExpired()
281
+ {
282
+ $server = rest_get_server();
283
+
284
+ // Generate valid JWT token
285
+ $jwt = AAM_Core_Jwt_Issuer::getInstance()->issueToken(array(
286
+ 'userId' => AAM_UNITTEST_AUTH_USER_ID,
287
+ 'revocable' => true,
288
+ 'refreshable' => true
289
+ ), DateTime::createFromFormat('m/d/Y', '01/01/2018'));
290
+
291
+ $this->assertObjectHasAttribute('token', $jwt);
292
+ $this->assertObjectHasAttribute('claims', $jwt);
293
+
294
+ $request = new WP_REST_Request('POST', '/aam/v1/refresh-jwt');
295
+ $request->set_param('jwt', $jwt->token);
296
+
297
+ $response = $server->dispatch($request);
298
+
299
+ $this->assertEquals(400, $response->get_status());
300
+ $this->assertEquals('Expired token', $response->get_data()->get_error_message());
301
+ }
302
+
303
+ /**
304
+ * Verify that new token is not issued for none-refreshable token
305
+ *
306
+ * @return void
307
+ *
308
+ * @access public
309
+ * @version 6.0.0
310
+ */
311
+ public function testTokenRefreshNotRefreshable()
312
+ {
313
+ $server = rest_get_server();
314
+
315
+ // Generate valid JWT token
316
+ $jwt = AAM_Core_Jwt_Issuer::getInstance()->issueToken(array(
317
+ 'userId' => AAM_UNITTEST_AUTH_USER_ID,
318
+ 'revocable' => false,
319
+ 'refreshable' => false
320
+ ));
321
+
322
+ $this->assertObjectHasAttribute('token', $jwt);
323
+ $this->assertObjectHasAttribute('claims', $jwt);
324
+
325
+ $request = new WP_REST_Request('POST', '/aam/v1/refresh-jwt');
326
+ $request->set_param('jwt', $jwt->token);
327
+
328
+ $response = $server->dispatch($request);
329
+
330
+ $this->assertEquals(400, $response->get_status());
331
+ $this->assertEquals('JWT token is not refreshable', $response->get_data()->get_error_message());
332
+ }
333
+
334
+ /**
335
+ * Verify that token is revoked properly
336
+ *
337
+ * @access public
338
+ * @version 6.0.0
339
+ */
340
+ public function testTokenRevoked()
341
+ {
342
+ $service = AAM_Service_Jwt::getInstance();
343
+
344
+ // Issue a token that later we'll refresh
345
+ $jwt = $service->issueToken(AAM_UNITTEST_JOHN_ID, null, null, true);
346
+
347
+ // Verify that token was issued
348
+ $this->assertObjectHasAttribute('token', $jwt);
349
+
350
+ $this->assertTrue($service->revokeToken(AAM_UNITTEST_JOHN_ID, $jwt->token));
351
+
352
+ // Reset cache
353
+ wp_cache_flush();
354
+
355
+ $tokens = $service->getTokenRegistry(AAM_UNITTEST_JOHN_ID);
356
+
357
+ $this->assertFalse(in_array($jwt->token, $tokens, true));
358
+ }
359
+
360
+ }
tests/Service/LoginRedirect/Callback.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace AAM\UnitTest\Service\LoginRedirect;
4
+
5
+ class Callback
6
+ {
7
+ const REDIRECT_URL = 'https://aamplugin.com/redirect';
8
+
9
+ public static function redirectCallback()
10
+ {
11
+ return self::REDIRECT_URL;
12
+ }
13
+
14
+ }
tests/Service/LoginRedirect/LoginRedirectTest.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\LoginRedirect;
11
+
12
+ use WP_REST_Request,
13
+ AAM_Core_Subject_User,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM_Core_Object_LoginRedirect;
17
+
18
+ /**
19
+ * Login Redirect feature
20
+ *
21
+ * @package AAM\UnitTest
22
+ * @version 6.0.0
23
+ */
24
+ class LoginRedirectTest extends TestCase
25
+ {
26
+ use ResetTrait;
27
+
28
+ /**
29
+ * Assert that correct URL login redirect is returns for RESTful auth call
30
+ *
31
+ * @return void
32
+ *
33
+ * @access public
34
+ * @version 6.0.0
35
+ */
36
+ public function testRESTfulLoginURLRedirect()
37
+ {
38
+ $server = rest_get_server();
39
+
40
+ // No need to generate Auth cookies
41
+ add_filter('send_auth_cookies', '__return_false');
42
+
43
+ // Set login redirect
44
+ $subject = new AAM_Core_Subject_User(AAM_UNITTEST_JOHN_ID);
45
+ $object = $subject->getObject(AAM_Core_Object_LoginRedirect::OBJECT_TYPE, null, true);
46
+ $object->setOption(array(
47
+ 'login.redirect.type' => 'url',
48
+ 'login.redirect.url' => 'https://aamplugin.com'
49
+ ));
50
+ $object->save();
51
+
52
+ $request = new WP_REST_Request('POST', '/aam/v2/authenticate');
53
+ $request->set_param('username', AAM_UNITTEST_USERNAME);
54
+ $request->set_param('password', AAM_UNITTEST_PASSWORD);
55
+
56
+ $data = $server->dispatch($request)->get_data();
57
+
58
+ $this->assertEquals('WP_User', get_class($data['user']));
59
+ $this->assertEquals('https://aamplugin.com', $data['redirect']);
60
+ }
61
+
62
+ /**
63
+ * Assert that correct Page login redirect is returns for RESTful auth call
64
+ *
65
+ * @return void
66
+ *
67
+ * @access public
68
+ * @version 6.0.0
69
+ */
70
+ public function testRESTfulLoginPageRedirect()
71
+ {
72
+ $server = rest_get_server();
73
+
74
+ // No need to generate Auth cookies
75
+ add_filter('send_auth_cookies', '__return_false');
76
+
77
+ // Set login redirect
78
+ $subject = new AAM_Core_Subject_User(AAM_UNITTEST_JOHN_ID);
79
+ $object = $subject->getObject(AAM_Core_Object_LoginRedirect::OBJECT_TYPE, null, true);
80
+ $object->setOption(array(
81
+ 'login.redirect.type' => 'page',
82
+ 'login.redirect.page' => AAM_UNITTEST_PAGE_ID
83
+ ));
84
+ $object->save();
85
+
86
+ $request = new WP_REST_Request('POST', '/aam/v2/authenticate');
87
+ $request->set_param('username', AAM_UNITTEST_USERNAME);
88
+ $request->set_param('password', AAM_UNITTEST_PASSWORD);
89
+
90
+ $data = $server->dispatch($request)->get_data();
91
+
92
+ $this->assertEquals('WP_User', get_class($data['user']));
93
+ $this->assertEquals(get_page_link(AAM_UNITTEST_PAGE_ID), $data['redirect']);
94
+ }
95
+
96
+ /**
97
+ * Assert that correct login redirect is returns for RESTful auth call for
98
+ * callback type of redirect
99
+ *
100
+ * @return void
101
+ *
102
+ * @access public
103
+ * @version 6.0.0
104
+ */
105
+ public function testRESTfulLoginCallbackRedirect()
106
+ {
107
+ $server = rest_get_server();
108
+
109
+ // No need to generate Auth cookies
110
+ add_filter('send_auth_cookies', '__return_false');
111
+
112
+ // Set login redirect
113
+ $subject = new AAM_Core_Subject_User(AAM_UNITTEST_JOHN_ID);
114
+ $object = $subject->getObject(AAM_Core_Object_LoginRedirect::OBJECT_TYPE, null, true);
115
+ $object->setOption(array(
116
+ 'login.redirect.type' => 'callback',
117
+ 'login.redirect.callback' => 'AAM\\UnitTest\\Service\\LoginRedirect\\Callback::redirectCallback'
118
+ ));
119
+ $object->save();
120
+
121
+ $request = new WP_REST_Request('POST', '/aam/v2/authenticate');
122
+ $request->set_param('username', AAM_UNITTEST_USERNAME);
123
+ $request->set_param('password', AAM_UNITTEST_PASSWORD);
124
+
125
+ $data = $server->dispatch($request)->get_data();
126
+
127
+ $this->assertEquals('WP_User', get_class($data['user']));
128
+ $this->assertEquals(Callback::REDIRECT_URL, $data['redirect']);
129
+ }
130
+
131
+ /**
132
+ * Assert that null login redirect is returns for RESTful auth call
133
+ *
134
+ * @return void
135
+ *
136
+ * @access public
137
+ * @version 6.0.0
138
+ */
139
+ public function testRESTfulLoginDefaultRedirect()
140
+ {
141
+ $server = rest_get_server();
142
+
143
+ // No need to generate Auth cookies
144
+ add_filter('send_auth_cookies', '__return_false');
145
+
146
+ $request = new WP_REST_Request('POST', '/aam/v2/authenticate');
147
+ $request->set_param('username', AAM_UNITTEST_USERNAME);
148
+ $request->set_param('password', AAM_UNITTEST_PASSWORD);
149
+
150
+ $data = $server->dispatch($request)->get_data();
151
+
152
+ $this->assertEquals('WP_User', get_class($data['user']));
153
+ $this->assertNull($data['redirect']);
154
+ }
155
+
156
+ /**
157
+ * Validate that `login_redirect` filter is triggered with AAM hook
158
+ *
159
+ * Make sure that user will be redirected to the existing page
160
+ *
161
+ * @return void
162
+ *
163
+ * @access public
164
+ * @version 6.0.0
165
+ */
166
+ public function testLoginRedirectHookTriggerChanges()
167
+ {
168
+ // Set login redirect
169
+ $subject = new AAM_Core_Subject_User(AAM_UNITTEST_JOHN_ID);
170
+ $object = $subject->getObject(AAM_Core_Object_LoginRedirect::OBJECT_TYPE, null, true);
171
+ $object->setOption(array(
172
+ 'login.redirect.type' => 'page',
173
+ 'login.redirect.page' => AAM_UNITTEST_PAGE_ID
174
+ ));
175
+ $object->save();
176
+
177
+ $redirect = apply_filters('login_redirect', admin_url(), admin_url(), $subject->getPrincipal());
178
+
179
+ $this->assertEquals(get_page_link(AAM_UNITTEST_PAGE_ID), $redirect);
180
+ }
181
+
182
+ /**
183
+ * Validate that `login_redirect` filter is triggered with AAM hook
184
+ *
185
+ * Make sure that user will be redirected to originally defined destination. By
186
+ * default AAM overwrites only destinations that are different than admin_url()
187
+ * return.
188
+ *
189
+ * @return void
190
+ *
191
+ * @access public
192
+ * @version 6.0.0
193
+ */
194
+ public function testLoginRedirectHookTriggerPersistOriginalRedirect()
195
+ {
196
+ // Set login redirect
197
+ $subject = new AAM_Core_Subject_User(AAM_UNITTEST_JOHN_ID);
198
+ $object = $subject->getObject(AAM_Core_Object_LoginRedirect::OBJECT_TYPE, null, true);
199
+ $object->setOption(array(
200
+ 'login.redirect.type' => 'url',
201
+ 'login.redirect.url' => 'https://aamplugin.com'
202
+ ));
203
+ $object->save();
204
+
205
+ $redirect = apply_filters(
206
+ 'login_redirect',
207
+ get_page_link(AAM_UNITTEST_PAGE_ID),
208
+ get_page_link(AAM_UNITTEST_PAGE_ID),
209
+ $subject->getPrincipal()
210
+ );
211
+
212
+ $this->assertEquals(get_page_link(AAM_UNITTEST_PAGE_ID), $redirect);
213
+ }
214
+
215
+ }
tests/Service/LogoutRedirect/Callback.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace AAM\UnitTest\Service\LogoutRedirect;
4
+
5
+ class Callback
6
+ {
7
+ const REDIRECT_URL = 'https://aamplugin.com/redirect';
8
+
9
+ public static function redirectCallback()
10
+ {
11
+ header('Location: ' . self::REDIRECT_URL);
12
+ }
13
+
14
+ }
tests/Service/LogoutRedirect/LogoutRedirectTest.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\LogoutRedirect;
11
+
12
+ use AAM,
13
+ PHPUnit\Framework\TestCase,
14
+ AAM\UnitTest\Libs\ResetTrait,
15
+ AAM_Core_Object_LogoutRedirect,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Logout Redirect feature
20
+ *
21
+ * @package AAM\UnitTest
22
+ * @version 6.0.0
23
+ */
24
+ class LogoutRedirectTest extends TestCase
25
+ {
26
+ use ResetTrait,
27
+ AuthUserTrait;
28
+
29
+ /**
30
+ * Test the default logout redirect
31
+ *
32
+ * AAM should not issue any redirect headers
33
+ *
34
+ * @return void
35
+ *
36
+ * @access public
37
+ * @version 6.0.0
38
+ */
39
+ public function testDefaultLogoutRedirect()
40
+ {
41
+ // Reset any already sent "Location" headers. This way insure that no other
42
+ // redirect headers are sent
43
+ header('Location: empty');
44
+ do_action('wp_logout');
45
+
46
+ $this->assertContains('Location: empty', xdebug_get_headers());
47
+ }
48
+
49
+ /**
50
+ * Test redirect to the existing page
51
+ *
52
+ * @return void
53
+ *
54
+ * @access public
55
+ * @version 6.0.0
56
+ */
57
+ public function testExistingPageLogoutRedirect()
58
+ {
59
+ $object = AAM::getUser()->getObject(AAM_Core_Object_LogoutRedirect::OBJECT_TYPE, null, true);
60
+ $object->setOption(array(
61
+ 'logout.redirect.type' => 'page',
62
+ 'logout.redirect.page' => AAM_UNITTEST_PAGE_ID
63
+ ));
64
+ $object->save();
65
+
66
+ do_action('wp_logout');
67
+
68
+ $this->assertContains('Location: ' . get_page_link(AAM_UNITTEST_PAGE_ID), xdebug_get_headers());
69
+ }
70
+
71
+ /**
72
+ * Test redirect to the defined URL
73
+ *
74
+ * @return void
75
+ *
76
+ * @access public
77
+ * @version 6.0.0
78
+ */
79
+ public function testUrlLogoutRedirect()
80
+ {
81
+ $object = AAM::getUser()->getObject(AAM_Core_Object_LogoutRedirect::OBJECT_TYPE, null, true);
82
+ $object->setOption(array(
83
+ 'logout.redirect.type' => 'url',
84
+ 'logout.redirect.url' => '/hello-world'
85
+ ));
86
+ $object->save();
87
+
88
+ do_action('wp_logout');
89
+
90
+ $this->assertContains('Location: /hello-world', xdebug_get_headers());
91
+ }
92
+
93
+ /**
94
+ * Test execution of the callback function as redirect
95
+ *
96
+ * @return void
97
+ *
98
+ * @access public
99
+ * @version 6.0.0
100
+ */
101
+ public function testCallbackLogoutRedirect()
102
+ {
103
+ $object = AAM::getUser()->getObject(AAM_Core_Object_LogoutRedirect::OBJECT_TYPE, null, true);
104
+ $object->setOption(array(
105
+ 'logout.redirect.type' => 'callback',
106
+ 'logout.redirect.callback' => 'AAM\\UnitTest\\Service\\LogoutRedirect\\Callback::redirectCallback'
107
+ ));
108
+ $object->save();
109
+
110
+ do_action('wp_logout');
111
+
112
+ $this->assertContains('Location: ' . Callback::REDIRECT_URL, xdebug_get_headers());
113
+ }
114
+
115
+ }
tests/Service/Metabox/MultipleRoleInheritanceTest.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Metabox;
11
+
12
+ use AAM,
13
+ AAM_Core_Config,
14
+ AAM_Core_Object_Metabox,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait,
17
+ AAM\UnitTest\Libs\AuthMultiRoleUserTrait,
18
+ AAM\UnitTest\Libs\MultiRoleOptionInterface;
19
+
20
+ /**
21
+ * Test AAM access settings inheritance mechanism for multiple roles per user for
22
+ * the Metaboxes & Widgets service
23
+ *
24
+ * @package AAM\UnitTest
25
+ * @version 6.0.0
26
+ */
27
+ class MultipleRoleInheritanceTest extends TestCase implements MultiRoleOptionInterface
28
+ {
29
+ use ResetTrait,
30
+ AuthMultiRoleUserTrait;
31
+
32
+ /**
33
+ * Test that access settings are inherited from multiple parent roles
34
+ *
35
+ * This test is designed to verify that access settings are propagated property
36
+ * when there access settings defined for multiple parent roles.
37
+ *
38
+ * A. Test that settings can be stored for the parent roles;
39
+ * B. Test that access settings are propagated property to the User level
40
+ *
41
+ * @return void
42
+ *
43
+ * @access public
44
+ * @version 6.0.0
45
+ */
46
+ public function testInheritanceMergeFromMultipleRoles()
47
+ {
48
+ $user = AAM::getUser();
49
+ $role = $user->getParent();
50
+
51
+ // Make sure that we have parent roles defined properly
52
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
53
+
54
+ // Save access settings for the base role and iterate over each sibling and
55
+ // add additional settings
56
+ $this->assertTrue(
57
+ $role->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true)->updateOptionItem(
58
+ 'dashboard|dashboard_quick_press_0', true
59
+ )->save()
60
+ );
61
+
62
+ foreach($role->getSiblings() as $i => $sibling) {
63
+ // Save access settings for each role and make sure they are saved property
64
+ // Check if save returns positive result
65
+ $this->assertTrue(
66
+ $sibling->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true)->updateOptionItem(
67
+ 'dashboard|dashboard_quick_press_' . ($i + 1), ($i % 2 ? true : false)
68
+ )->save()
69
+ );
70
+ }
71
+
72
+ // Reset internal AAM cache
73
+ $this->_resetSubjects();
74
+
75
+ // Assert that we have both roles merged result is as following
76
+ // Array (
77
+ // dashboard|dashboard_quick_press_0 => true,
78
+ // dashboard|dashboard_quick_press_1 => false
79
+ // )
80
+ $option = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE)->getOption();
81
+ $this->assertSame(
82
+ array(
83
+ 'dashboard|dashboard_quick_press_0' => true,
84
+ 'dashboard|dashboard_quick_press_1' => false
85
+ ),
86
+ $option
87
+ );
88
+ }
89
+
90
+ /**
91
+ * Check that access to resource is denied when two or more roles have the same
92
+ * resource defined
93
+ *
94
+ * @return void
95
+ *
96
+ * @access public
97
+ * @version 6.0.0
98
+ */
99
+ public function testInheritanceDenyPrecedenceFromMultipleRoles()
100
+ {
101
+ $user = AAM::getUser();
102
+ $role = $user->getParent();
103
+
104
+ // Make sure that we have parent roles defined properly
105
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
106
+
107
+ // Save access settings for the base role and iterate over each sibling and
108
+ // add additional settings
109
+ $this->assertTrue(
110
+ $role->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true)->updateOptionItem(
111
+ 'widgets|WP_Widget_Media_Video', true
112
+ )->save()
113
+ );
114
+
115
+ foreach($role->getSiblings() as $sibling) {
116
+ // Save access settings for each role and make sure they are saved property
117
+ // Check if save returns positive result
118
+ $this->assertTrue(
119
+ $sibling->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true)->updateOptionItem(
120
+ 'widgets|WP_Widget_Media_Video', false
121
+ )->save()
122
+ );
123
+ }
124
+
125
+ // Reset internal AAM cache
126
+ $this->_resetSubjects();
127
+
128
+ // Assert that we have both roles merged result is as following
129
+ // Array (
130
+ // widgets|WP_Widget_Media_Video => true
131
+ // )
132
+ $option = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE)->getOption();
133
+ $this->assertSame(
134
+ array('widgets|WP_Widget_Media_Video' => true), $option
135
+ );
136
+ }
137
+
138
+ /**
139
+ * Check that access is allowed to the resource when two or more roles have the
140
+ * same resource defined
141
+ *
142
+ * @return void
143
+ *
144
+ * @access public
145
+ * @version 6.0.0
146
+ */
147
+ public function testInheritanceAllowPrecedenceFromMultipleRoles()
148
+ {
149
+ $user = AAM::getUser();
150
+ $role = $user->getParent();
151
+
152
+ // Make sure that we have parent roles defined properly
153
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
154
+
155
+ // Save access settings for the base role and iterate over each sibling and
156
+ // add additional settings
157
+ $this->assertTrue(
158
+ $role->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true)->updateOptionItem(
159
+ 'widgets|WP_Widget_Media_Video', true
160
+ )->save()
161
+ );
162
+
163
+ foreach($role->getSiblings() as $sibling) {
164
+ // Save access settings for each role and make sure they are saved property
165
+ // Check if save returns positive result
166
+ $this->assertTrue(
167
+ $sibling->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true)->updateOptionItem(
168
+ 'widgets|WP_Widget_Media_Video', false
169
+ )->save()
170
+ );
171
+ }
172
+
173
+ // Override the default "deny" precedence
174
+ AAM_Core_Config::set(
175
+ sprintf('core.settings.%s.merge.preference', AAM_Core_Object_Metabox::OBJECT_TYPE),
176
+ 'allow'
177
+ );
178
+
179
+ // Reset internal AAM cache
180
+ $this->_resetSubjects();
181
+
182
+ // Assert that we have both roles merged result is as following
183
+ // Array (
184
+ // widgets|WP_Widget_Media_Video => false
185
+ // )
186
+ $option = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE)->getOption();
187
+ $this->assertSame(
188
+ array('widgets|WP_Widget_Media_Video' => false), $option
189
+ );
190
+ }
191
+
192
+ }
tests/Service/Metabox/SingleRoleInheritanceTest.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Metabox;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Metabox,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Test AAM access settings inheritance mechanism for the Metaboxes & Widgets service
20
+ *
21
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
22
+ * @version 6.0.0
23
+ */
24
+ class SingleRoleInheritanceTest extends TestCase
25
+ {
26
+ use ResetTrait,
27
+ AuthUserTrait;
28
+
29
+ /**
30
+ * Test to insure that access settings are stored property on the User level
31
+ *
32
+ * A. Test that metabox is stored to the database with "true" flag and true
33
+ * is returned by AAM_Core_Subject_User::updateOption method;
34
+ * B. Test that information is actually stored property in the database and can
35
+ * be retrieved successfully.
36
+ *
37
+ * @return void
38
+ *
39
+ * @access public
40
+ * @see AAM_Core_Subject_User::updateOption
41
+ * @version 6.0.0
42
+ */
43
+ public function testSaveMetaboxOption()
44
+ {
45
+ $user = AAM::getUser();
46
+ $object = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
47
+
48
+ // Check if save returns positive result
49
+ $this->assertTrue($object->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
50
+
51
+ // Read from the database saved values and assert that we have
52
+ // Array (
53
+ // widgets|WP_Widget_Media_Video => true
54
+ // )
55
+ $option = $user->readOption(AAM_Core_Object_Metabox::OBJECT_TYPE);
56
+ $this->assertSame(array('widgets|WP_Widget_Media_Video' => true), $option);
57
+ }
58
+
59
+ /**
60
+ * Test that access settings are inherited from the parent role property
61
+ *
62
+ * This test is designed to verify that access settings are propagated property
63
+ * when there is only one role assigned to a user.
64
+ *
65
+ * A. Test that settings can be stored for the parent role;
66
+ * B. Test that access settings are propagated property to the User level
67
+ *
68
+ * @return void
69
+ *
70
+ * @access public
71
+ * @version 6.0.0
72
+ */
73
+ public function testInheritanceFromSingleRole()
74
+ {
75
+ $user = AAM::getUser();
76
+ $parent = $user->getParent();
77
+ $object = $parent->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
78
+
79
+ // Make sure that we have parent role defined
80
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($parent));
81
+
82
+ // Save access settings for the role and make sure they are saved property
83
+ // Check if save returns positive result
84
+ $this->assertTrue($object->updateOptionItem('dashboard|dashboard_quick_press', true)->save());
85
+
86
+ // Read from the database saved values and assert that we have
87
+ // Array (
88
+ // dashboard|dashboard_quick_press => true
89
+ // )
90
+ $option = $parent->readOption(AAM_Core_Object_Metabox::OBJECT_TYPE);
91
+ $this->assertSame(array('dashboard|dashboard_quick_press' => true), $option);
92
+
93
+ // Finally verify that access settings are propagated property to the User
94
+ // Level
95
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
96
+ $this->assertSame(
97
+ array('dashboard|dashboard_quick_press' => true), $metabox->getOption()
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Test that access settings are propagated and merged properly
103
+ *
104
+ * The test is designed to verify that access settings are propagated properly
105
+ * from the parent role and merged well with explicitly defined access settings on
106
+ * the User level.
107
+ *
108
+ * The expected result is to have combined array of access settings from the parent
109
+ * role and specific user.
110
+ *
111
+ * A. Test that access settings are stored for the parent role;
112
+ * B. Test that access settings are stored for the user;
113
+ * C. Test that access settings are propagated and merged properly;
114
+ *
115
+ * @return void
116
+ *
117
+ * @access public
118
+ * @version 6.0.0
119
+ */
120
+ public function testInheritanceMergeFromSingleRole()
121
+ {
122
+ $user = AAM::getUser();
123
+ $parent = $user->getParent();
124
+
125
+ $object = $parent->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
126
+
127
+ // Save access settings for the role and make sure they are saved property
128
+ // Check if save returns positive result
129
+ $this->assertTrue($object->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
130
+
131
+ // Save access setting for the user and make sure they are saved property
132
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true);
133
+ $this->assertTrue($metabox->updateOptionItem('dashboard|dashboard_quick_press', false)->save());
134
+
135
+ // Reset cache and try to kick-in the inheritance mechanism
136
+ $this->_resetSubjects();
137
+
138
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
139
+ $this->assertSame(
140
+ array(
141
+ 'widgets|WP_Widget_Media_Video' => true,
142
+ 'dashboard|dashboard_quick_press' => false
143
+ ),
144
+ $metabox->getOption()
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Test that the full inheritance mechanism is working as expected
150
+ *
151
+ * Make sure that access settings are propagated and merged properly from the top
152
+ * (Default Level)to the bottom (User Level).
153
+ *
154
+ * A. Assert that access settings are stored properly for each Access Level;
155
+ * B. Assert that access settings are merged properly and assigned to User Level;
156
+ *
157
+ * @return void
158
+ *
159
+ * @access public
160
+ * @version 6.0.0
161
+ */
162
+ public function testFullInheritanceChainSingeRole()
163
+ {
164
+ $user = AAM::getUser();
165
+ $role = $user->getParent();
166
+ $default = $role->getParent();
167
+
168
+ $userMetabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true);
169
+ $roleMetabox = $role->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true);
170
+ $defaultMetabox = $default->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true);
171
+
172
+ // Save access settings for all subjects
173
+ $this->assertTrue($userMetabox->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
174
+ $this->assertTrue($roleMetabox->updateOptionItem('dashboard|dashboard_quick_press', true)->save());
175
+ $this->assertTrue($defaultMetabox->updateOptionItem('post|publish_post', true)->save());
176
+
177
+ // Reset cache and try to kick-in the inheritance mechanism
178
+ $this->_resetSubjects();
179
+
180
+ // All settings has to be merged into one array
181
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
182
+ $this->assertSame(
183
+ array(
184
+ 'post|publish_post' => true,
185
+ 'dashboard|dashboard_quick_press' => true,
186
+ 'widgets|WP_Widget_Media_Video' => true
187
+ ),
188
+ $metabox->getOption()
189
+ );
190
+ }
191
+
192
+ /**
193
+ * Test that access settings overwrite works as expected
194
+ *
195
+ * The expected result is lower Access Level overwrite access settings from the
196
+ * higher Access Level.
197
+ *
198
+ * A. Assert that access settings are stored properly for the parent role;
199
+ * B. Assert that access settings are stored properly for the specific user;
200
+ * C. Assert that access settings are overwritten properly on the User Level;
201
+ *
202
+ * @return void
203
+ *
204
+ * @access public
205
+ * @version 6.0.0
206
+ */
207
+ public function testInheritanceOverrideForSingleRole()
208
+ {
209
+ $user = AAM::getUser();
210
+ $parent = $user->getParent();
211
+
212
+ $object = $parent->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
213
+
214
+ // Save access settings for the role and make sure they are saved property
215
+ // Check if save returns positive result
216
+ $this->assertTrue($object->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
217
+
218
+ // Save access setting for the user and make sure they are saved property
219
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true);
220
+ $this->assertTrue($metabox->updateOptionItem('widgets|WP_Widget_Media_Video', false)->save());
221
+
222
+ // Reset cache and try to kick-in the inheritance mechanism
223
+ $this->_resetSubjects();
224
+
225
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
226
+ $this->assertSame(
227
+ array('widgets|WP_Widget_Media_Video' => false), $metabox->getOption()
228
+ );
229
+ }
230
+
231
+ }
tests/Service/Metabox/VisitorInheritanceTest.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Metabox;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Metabox,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Test AAM access settings inheritance mechanism for the Metaboxes & Widgets service
20
+ * for the visitor subject
21
+ *
22
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
23
+ * @version 6.0.0
24
+ */
25
+ class VisitorInheritanceTest extends TestCase
26
+ {
27
+ use ResetTrait;
28
+
29
+ /**
30
+ * Test to insure that access settings are stored property on the Visitor level
31
+ *
32
+ * A. Test that metabox is stored to the database with "true" flag and true
33
+ * is returned by AAM_Core_Subject_Visitor::updateOption method;
34
+ * B. Test that information is actually stored property in the database and can
35
+ * be retrieved successfully.
36
+ *
37
+ * @return void
38
+ *
39
+ * @access public
40
+ * @see AAM_Core_Subject_Visitor::updateOption
41
+ * @version 6.0.0
42
+ */
43
+ public function testSaveMetaboxOption()
44
+ {
45
+ $user = AAM::getUser();
46
+ $object = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
47
+
48
+ // Make sure that we actually are dealing with Visitor subject
49
+ $this->assertEquals('AAM_Core_Subject_Visitor', get_class($user));
50
+
51
+ // Check if save returns positive result
52
+ $this->assertTrue($object->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
53
+
54
+ // Read from the database saved values and assert that we have
55
+ // Array (
56
+ // widgets|WP_Widget_Media_Video => true
57
+ // )
58
+ $option = $user->readOption(AAM_Core_Object_Metabox::OBJECT_TYPE);
59
+ $this->assertSame(array('widgets|WP_Widget_Media_Video' => true), $option);
60
+ }
61
+
62
+ /**
63
+ * Test that access settings are inherited from the parent default subject
64
+ *
65
+ * This test is designed to verify that access settings are propagated property
66
+ * from the default settings
67
+ *
68
+ * A. Test that settings can be stored for the default subject
69
+ * B. Test that access settings are propagated property to the Visitor level
70
+ *
71
+ * @return void
72
+ *
73
+ * @access public
74
+ * @version 6.0.0
75
+ */
76
+ public function testInheritanceFromDefault()
77
+ {
78
+ $user = AAM::getUser();
79
+ $parent = $user->getParent();
80
+ $object = $parent->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
81
+
82
+ // Make sure that we work with Default subject
83
+ $this->assertEquals('AAM_Core_Subject_Default', get_class($parent));
84
+
85
+ // Save access settings for the Default and make sure they are saved property
86
+ // Check if save returns positive result
87
+ $this->assertTrue($object->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
88
+
89
+ // Read from the database saved values and assert that we have
90
+ // Array (
91
+ // widgets|WP_Widget_Media_Video => true
92
+ // )
93
+ $option = $parent->readOption(AAM_Core_Object_Metabox::OBJECT_TYPE);
94
+ $this->assertSame(array('widgets|WP_Widget_Media_Video' => true), $option);
95
+
96
+ // Finally verify that access settings are propagated property to the Visitor
97
+ // Level
98
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
99
+ $this->assertSame(
100
+ array('widgets|WP_Widget_Media_Video' => true), $metabox->getOption()
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Test that access settings are propagated and merged properly
106
+ *
107
+ * The test is designed to verify that access settings are propagated properly
108
+ * from the Default and merged well with explicitly defined access settings on
109
+ * the Visitor level.
110
+ *
111
+ * A. Test that access settings are stored for the Default subject;
112
+ * B. Test that access settings are stored for the Visitor;
113
+ * C. Test that access settings are propagated and merged properly;
114
+ *
115
+ * @return void
116
+ *
117
+ * @access public
118
+ * @version 6.0.0
119
+ */
120
+ public function testInheritanceMergeFromDefault()
121
+ {
122
+ $visitor = AAM::getUser();
123
+ $default = $visitor->getParent();
124
+
125
+ $object = $default->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
126
+
127
+ // Save access settings for the Default and make sure they are saved property
128
+ // Check if save returns positive result
129
+ $this->assertTrue($object->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
130
+
131
+ // Save access setting for the Visitor and make sure they are saved property
132
+ $metabox = $visitor->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true);
133
+ $this->assertTrue($metabox->updateOptionItem('widgets|WP_Widget_Media_Image', false)->save());
134
+
135
+ // Reset cache and try to kick-in the inheritance mechanism
136
+ $this->_resetSubjects();
137
+
138
+ $metabox = $visitor->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
139
+ $this->assertSame(
140
+ array(
141
+ 'widgets|WP_Widget_Media_Video' => true,
142
+ 'widgets|WP_Widget_Media_Image' => false
143
+ ),
144
+ $metabox->getOption()
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Test that access settings overwrite works as expected
150
+ *
151
+ * The expected result is lower Access Level overwrite access settings from the
152
+ * higher Access Level.
153
+ *
154
+ * A. Assert that access settings are stored properly for the parent subject;
155
+ * B. Assert that access settings are stored properly for the Visitor;
156
+ * C. Assert that access settings are overwritten properly on the Visitor Level;
157
+ *
158
+ * @return void
159
+ *
160
+ * @access public
161
+ * @version 6.0.0
162
+ */
163
+ public function testInheritanceOverride()
164
+ {
165
+ $user = AAM::getUser();
166
+ $parent = $user->getParent();
167
+
168
+ $object = $parent->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
169
+
170
+ // Save access settings for the Default and make sure they are saved property
171
+ // Check if save returns positive result
172
+ $this->assertTrue($object->updateOptionItem('widgets|WP_Widget_Media_Video', true)->save());
173
+
174
+ // Save access setting for the Visitor and make sure they are saved property
175
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE, null, true);
176
+ $this->assertTrue($metabox->updateOptionItem('widgets|WP_Widget_Media_Video', false)->save());
177
+
178
+ // Reset cache and try to kick-in the inheritance mechanism
179
+ $this->_resetSubjects();
180
+
181
+ $metabox = $user->getObject(AAM_Core_Object_Metabox::OBJECT_TYPE);
182
+ $this->assertSame(
183
+ array('widgets|WP_Widget_Media_Video' => false), $metabox->getOption()
184
+ );
185
+ }
186
+
187
+ }
tests/Service/NotFoundRedirect/Callback.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace AAM\UnitTest\Service\NotFoundRedirect;
4
+
5
+ class Callback
6
+ {
7
+ const REDIRECT_URL = 'https://aamplugin.com/redirect';
8
+
9
+ public static function redirectCallback()
10
+ {
11
+ header('Location: ' . self::REDIRECT_URL);
12
+ }
13
+
14
+ }
tests/Service/NotFoundRedirect/NotFoundRedirectTest.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\NotFoundRedirect;
11
+
12
+ use AAM_Core_Config,
13
+ PHPUnit\Framework\TestCase,
14
+ AAM_Service_NotFoundRedirect,
15
+ AAM\UnitTest\Libs\ResetTrait;
16
+
17
+ /**
18
+ * 404 Redirect service
19
+ *
20
+ * @package AAM\UnitTest
21
+ * @version 6.0.0
22
+ */
23
+ class NotFoundRedirectTest extends TestCase
24
+ {
25
+ use ResetTrait;
26
+
27
+ /**
28
+ * Test the default 404 redirect
29
+ *
30
+ * AAM should not issue any redirect headers
31
+ *
32
+ * @return void
33
+ *
34
+ * @access public
35
+ * @version 6.0.0
36
+ */
37
+ public function testDefault404Redirect()
38
+ {
39
+ global $wp_query;
40
+
41
+ // Force 404 path
42
+ $wp_query->is_404 = true;
43
+ $service = AAM_Service_NotFoundRedirect::getInstance();
44
+
45
+ // Reset any already sent "Location" headers. This way insure that no other
46
+ // redirect headers are sent
47
+ header('Location: empty');
48
+
49
+ $service->wp();
50
+
51
+ $this->assertContains('Location: empty', xdebug_get_headers());
52
+
53
+ // Reset to default
54
+ $wp_query->is_404 = null;
55
+ }
56
+
57
+ /**
58
+ * Test redirect to the existing page
59
+ *
60
+ * @return void
61
+ *
62
+ * @access public
63
+ * @version 6.0.0
64
+ */
65
+ public function testExistingPageLogoutRedirect()
66
+ {
67
+ global $wp_query;
68
+
69
+ // Set 404 config
70
+ AAM_Core_Config::set('frontend.404redirect.type', 'page');
71
+ AAM_Core_Config::set('frontend.404redirect.page', AAM_UNITTEST_PAGE_ID);
72
+
73
+ // Force 404 path
74
+ $wp_query->is_404 = true;
75
+ $service = AAM_Service_NotFoundRedirect::getInstance();
76
+
77
+ $service->wp();
78
+
79
+ $this->assertContains('Location: ' . get_page_link(AAM_UNITTEST_PAGE_ID), xdebug_get_headers());
80
+
81
+ // Reset to default
82
+ $wp_query->is_404 = null;
83
+ }
84
+
85
+ /**
86
+ * Test redirect to the defined URL
87
+ *
88
+ * @return void
89
+ *
90
+ * @access public
91
+ * @version 6.0.0
92
+ */
93
+ public function testUrlLogoutRedirect()
94
+ {
95
+ global $wp_query;
96
+
97
+ // Set 404 config
98
+ AAM_Core_Config::set('frontend.404redirect.type', 'url');
99
+ AAM_Core_Config::set('frontend.404redirect.url', '/hello-world');
100
+
101
+ // Force 404 path
102
+ $wp_query->is_404 = true;
103
+ $service = AAM_Service_NotFoundRedirect::getInstance();
104
+
105
+ $service->wp();
106
+
107
+ $this->assertContains('Location: /hello-world', xdebug_get_headers());
108
+
109
+ // Reset to default
110
+ $wp_query->is_404 = null;
111
+ }
112
+
113
+ /**
114
+ * Test execution of the callback function as redirect
115
+ *
116
+ * @return void
117
+ *
118
+ * @access public
119
+ * @version 6.0.0
120
+ */
121
+ public function testCallbackLogoutRedirect()
122
+ {
123
+ global $wp_query;
124
+
125
+ // Set 404 config
126
+ AAM_Core_Config::set('frontend.404redirect.type', 'callback');
127
+ AAM_Core_Config::set('frontend.404redirect.callback', 'AAM\\UnitTest\\Service\\NotFoundRedirect\\Callback::redirectCallback');
128
+
129
+ // Force 404 path
130
+ $wp_query->is_404 = true;
131
+ $service = AAM_Service_NotFoundRedirect::getInstance();
132
+
133
+ $service->wp();
134
+
135
+ $this->assertContains('Location: ' . Callback::REDIRECT_URL, xdebug_get_headers());
136
+
137
+ // Reset to default
138
+ $wp_query->is_404 = null;
139
+ }
140
+
141
+ }
tests/Service/Route/RouteTest.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Route;
11
+
12
+ use AAM,
13
+ WP_REST_Request,
14
+ AAM_Core_Config,
15
+ AAM_Service_Route,
16
+ AAM_Core_Object_Route,
17
+ PHPUnit\Framework\TestCase,
18
+ AAM\UnitTest\Libs\ResetTrait;
19
+
20
+ /**
21
+ * API Routes service tests
22
+ *
23
+ * @package AAM\UnitTest
24
+ * @version 6.0.0
25
+ */
26
+ class RouteTest extends TestCase
27
+ {
28
+ use ResetTrait;
29
+
30
+ /**
31
+ * Test that XML-PRC is disabled
32
+ *
33
+ * @return void
34
+ *
35
+ * @access public
36
+ * @version 6.0.0
37
+ */
38
+ public function testDisabledXMLRPC()
39
+ {
40
+ AAM_Core_Config::set('core.settings.xmlrpc', false);
41
+
42
+ $this->assertFalse(apply_filters('xmlrpc_enabled', true));
43
+ }
44
+
45
+ /**
46
+ * Test that RESTful API is disabled
47
+ *
48
+ * @return void
49
+ *
50
+ * @access public
51
+ * @version 6.0.0
52
+ */
53
+ public function testDisabledRESTfulAPI()
54
+ {
55
+ AAM_Core_Config::set('core.settings.restful', false);
56
+
57
+ $error = apply_filters('rest_authentication_errors', null);
58
+
59
+ $this->assertEquals('WP_Error', get_class($error));
60
+ $this->assertEquals('RESTful API is disabled', $error->get_error_message());
61
+ }
62
+
63
+ /**
64
+ * Assert that jwt token is generated for the authentication request
65
+ *
66
+ * @return void
67
+ *
68
+ * @access public
69
+ * @version 6.0.0
70
+ */
71
+ public function testRestrictedRESTfulEndpoint()
72
+ {
73
+ global $wp;
74
+
75
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Route::OBJECT_TYPE);
76
+
77
+ // Restrict AAM authentication endpoint
78
+ $this->assertTrue(
79
+ $object->updateOptionItem('restful|/aam/v2/authenticate|post', true)->save()
80
+ );
81
+
82
+ // Register all the necessary hooks
83
+
84
+ $wp->query_vars['rest_route'] = true;
85
+ AAM_Service_Route::getInstance()->registerRouteControllers();
86
+
87
+ $server = rest_get_server();
88
+
89
+ $request = new WP_REST_Request('POST', '/aam/v2/authenticate');
90
+ $request->set_param('username', AAM_UNITTEST_USERNAME);
91
+ $request->set_param('password', AAM_UNITTEST_PASSWORD);
92
+
93
+ $error = $server->dispatch($request);
94
+
95
+ $this->assertEquals('WP_Error', get_class($error));
96
+ $this->assertEquals('Access Denied', $error->get_error_message());
97
+ }
98
+
99
+ }
tests/Service/SecureLogin/SecureLoginTest.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\SecureLogin;
11
+
12
+ use AAM_Core_API,
13
+ AAM_Core_Config,
14
+ WP_Session_Tokens,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait;
17
+
18
+ /**
19
+ * Secure login features
20
+ *
21
+ * @package AAM\UnitTest
22
+ * @version 6.0.0
23
+ */
24
+ class SecureLoginTest extends TestCase
25
+ {
26
+ use ResetTrait;
27
+
28
+ /**
29
+ * Test that "One Session Per User" works as expected
30
+ *
31
+ * @return void
32
+ *
33
+ * @access public
34
+ * @version 6.0.0
35
+ */
36
+ public function testOneSessionPerUser()
37
+ {
38
+ // Enable "One Session Per User" feature
39
+ AAM_Core_Config::set('service.secureLogin.feature.singleSession', true);
40
+
41
+ // No need to generate Auth cookies
42
+ add_filter('send_auth_cookies', '__return_false');
43
+
44
+ // Define valid credentials
45
+ $creds = array(
46
+ 'user_login' => AAM_UNITTEST_USERNAME,
47
+ 'user_password' => AAM_UNITTEST_PASSWORD
48
+ );
49
+
50
+ // Sign-in user first time
51
+ $user = wp_signon($creds);
52
+ $this->assertEquals('WP_User', get_class($user));
53
+
54
+ // Now try to authenticate user again
55
+ $user = wp_signon($creds);
56
+ $this->assertEquals('WP_User', get_class($user));
57
+
58
+ // Finally verify that there is only one session persisted
59
+ $sessions = WP_Session_Tokens::get_instance($user->ID);
60
+ $this->assertCount(1, $sessions->get_all());
61
+
62
+ // Reset all sessions
63
+ $sessions->destroy_all();
64
+ }
65
+
66
+ /**
67
+ * Test the "Brute Force Lockout" feature
68
+ *
69
+ * Authentication process has to return WP_Error if number of allowed attempts
70
+ * exceeded its limit
71
+ *
72
+ * @return void
73
+ *
74
+ * @access public
75
+ * @version 6.0.0
76
+ */
77
+ public function testBruteForceLockout()
78
+ {
79
+ // Enable "Brute Force Lockout" feature
80
+ AAM_Core_Config::set('service.secureLogin.feature.bruteForceLockout', true);
81
+
82
+ // Force dummy user IP
83
+ $ip = '127.0.0.1';
84
+ $_SERVER['REMOTE_ADDR'] = $ip;
85
+
86
+ // Force to max out the number of attempts
87
+ set_transient('aam_failed_login_attempts_' . $ip, 50, time() + 10);
88
+
89
+ // No need to generate Auth cookies
90
+ add_filter('send_auth_cookies', '__return_false');
91
+
92
+ // Define valid credentials
93
+ $creds = array(
94
+ 'user_login' => AAM_UNITTEST_USERNAME,
95
+ 'user_password' => AAM_UNITTEST_PASSWORD
96
+ );
97
+
98
+ // Sign-in user first time
99
+ $user = wp_signon($creds);
100
+
101
+ $this->assertEquals('WP_Error', get_class($user));
102
+ $this->assertEquals('Exceeded maximum number for authentication attempts. Try again later.', $user->get_error_message());
103
+
104
+ // Also make sure that attempts counter was increased
105
+ $this->assertEquals(51, get_transient('aam_failed_login_attempts_' . $ip));
106
+
107
+ // Reset original state
108
+ delete_transient('aam_failed_login_attempts_' . $ip);
109
+ unset($_SERVER['REMOTE_ADDR']);
110
+ }
111
+
112
+ /**
113
+ * Test that it fails to authenticate locked user
114
+ *
115
+ * @return void
116
+ *
117
+ * @access public
118
+ * @version 6.0.0
119
+ */
120
+ public function testUserLockedStatus()
121
+ {
122
+ global $wpdb;
123
+
124
+ $result = $wpdb->update(
125
+ $wpdb->users, array('user_status' => 1), array('ID' => AAM_UNITTEST_JOHN_ID)
126
+ );
127
+
128
+ // Make sure that row is updated
129
+ $this->assertEquals(1, $result);
130
+
131
+ // No need to generate Auth cookies
132
+ add_filter('send_auth_cookies', '__return_false');
133
+
134
+ // Define valid credentials
135
+ $creds = array(
136
+ 'user_login' => AAM_UNITTEST_USERNAME,
137
+ 'user_password' => AAM_UNITTEST_PASSWORD
138
+ );
139
+
140
+ // Sign-in user first time
141
+ $user = wp_signon($creds);
142
+ $this->assertEquals('WP_Error', get_class($user));
143
+ $this->assertEquals('<strong>ERROR</strong>: User is locked. Contact website administrator.', $user->get_error_message());
144
+
145
+ // Restore user status
146
+ $result = $wpdb->update(
147
+ $wpdb->users, array('user_status' => 0), array('ID' => AAM_UNITTEST_JOHN_ID)
148
+ );
149
+ }
150
+
151
+ }
tests/Service/Toolbar/MultipleRoleInheritanceTest.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Toolbar;
11
+
12
+ use AAM,
13
+ AAM_Core_Config,
14
+ AAM_Core_Object_Toolbar,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait,
17
+ AAM\UnitTest\Libs\AuthMultiRoleUserTrait,
18
+ AAM\UnitTest\Libs\MultiRoleOptionInterface;
19
+
20
+ /**
21
+ * Test AAM access settings inheritance mechanism for multiple roles per user for
22
+ * the Admin Toolbar service
23
+ *
24
+ * Admin Toolbar is available only for authenticated users so no Visitors are tested
25
+ *
26
+ * @package AAM\UnitTest
27
+ * @version 6.0.0
28
+ */
29
+ class MultipleRoleInheritanceTest extends TestCase implements MultiRoleOptionInterface
30
+ {
31
+ use ResetTrait,
32
+ AuthMultiRoleUserTrait;
33
+
34
+ /**
35
+ * Test that access settings are inherited from multiple parent roles
36
+ *
37
+ * This test is designed to verify that access settings are propagated property
38
+ * when there access settings defined for multiple parent roles.
39
+ *
40
+ * @return void
41
+ *
42
+ * @access public
43
+ * @version 6.0.0
44
+ */
45
+ public function testInheritanceMergeFromMultipleRoles()
46
+ {
47
+ $user = AAM::getUser();
48
+ $role = $user->getParent();
49
+
50
+ // Make sure that we have parent roles defined properly
51
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
52
+
53
+ // Save access settings for the base role and iterate over each sibling and
54
+ // add additional settings
55
+ $object = $role->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true);
56
+ $this->assertTrue($object->updateOptionItem('new-page', true)->save());
57
+
58
+ foreach($role->getSiblings() as $i => $sibling) {
59
+ // Save access settings for each role and make sure they are saved property
60
+ // Check if save returns positive result
61
+ $this->assertTrue(
62
+ $sibling->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true)->updateOptionItem(
63
+ 'new-page-' . ($i + 1), ($i % 2 ? true : false)
64
+ )->save()
65
+ );
66
+ }
67
+
68
+ // Reset internal AAM cache
69
+ $this->_resetSubjects();
70
+
71
+ // Assert that we have both roles merged result is as following
72
+ // Array (
73
+ // new-page => true,
74
+ // new-page-1 => false
75
+ // )
76
+ $option = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE)->getOption();
77
+ $this->assertSame(
78
+ array('new-page' => true, 'new-page-1' => false), $option
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Test that access settings are merged with default "deny" precedence correctly
84
+ *
85
+ * @return void
86
+ *
87
+ * @access public
88
+ * @version 6.0.0
89
+ */
90
+ public function testInheritanceDenyPrecedenceFromMultipleRoles()
91
+ {
92
+ $user = AAM::getUser();
93
+ $role = $user->getParent();
94
+
95
+ // Make sure that we have parent roles defined properly
96
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
97
+
98
+ // Save access settings for the base role and iterate over each sibling and
99
+ // add additional settings
100
+ $this->assertTrue(
101
+ $role->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true)->updateOptionItem(
102
+ 'new-page', true
103
+ )->save()
104
+ );
105
+
106
+ foreach($role->getSiblings() as $sibling) {
107
+ // Save access settings for each role and make sure they are saved property
108
+ // Check if save returns positive result
109
+ $this->assertTrue(
110
+ $sibling->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true)->updateOptionItem(
111
+ 'new-page', false
112
+ )->save()
113
+ );
114
+ }
115
+
116
+ // Reset internal AAM cache
117
+ $this->_resetSubjects();
118
+
119
+ // Assert that we have both roles merged result is as following
120
+ // Array (
121
+ // new-page => true
122
+ // )
123
+ $option = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE)->getOption();
124
+ $this->assertSame(
125
+ array('new-page' => true), $option
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Test that access settings are merged correctly with "allowed" precedence
131
+ * correctly
132
+ *
133
+ * @return void
134
+ * @version 6.0.0
135
+ */
136
+ public function testInheritanceAllowPrecedenceFromMultipleRoles()
137
+ {
138
+ $user = AAM::getUser();
139
+ $role = $user->getParent();
140
+
141
+ // Make sure that we have parent roles defined properly
142
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($role));
143
+
144
+ // Save access settings for the base role and iterate over each sibling and
145
+ // add additional settings
146
+ $this->assertTrue(
147
+ $role->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true)->updateOptionItem(
148
+ 'new-page', true
149
+ )->save()
150
+ );
151
+
152
+ foreach($role->getSiblings() as $sibling) {
153
+ // Save access settings for each role and make sure they are saved property
154
+ // Check if save returns positive result
155
+ $this->assertTrue(
156
+ $sibling->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true)->updateOptionItem(
157
+ 'new-page', false
158
+ )->save()
159
+ );
160
+ }
161
+
162
+ // Override the default "deny" precedence
163
+ AAM_Core_Config::set(
164
+ sprintf('core.settings.%s.merge.preference', AAM_Core_Object_Toolbar::OBJECT_TYPE),
165
+ 'allow'
166
+ );
167
+
168
+ // Reset internal AAM cache
169
+ $this->_resetSubjects();
170
+
171
+ // Assert that we have both roles merged result is as following
172
+ // Array (
173
+ // new-page => false
174
+ // )
175
+ $option = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE)->getOption();
176
+ $this->assertSame(array('new-page' => false), $option);
177
+ }
178
+
179
+ }
tests/Service/Toolbar/SingleRoleInheritanceTest.php ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Toolbar;
11
+
12
+ use AAM,
13
+ AAM_Core_Object_Toolbar,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthUserTrait;
17
+
18
+ /**
19
+ * Test AAM access settings inheritance mechanism for the Toolbar service
20
+ *
21
+ * Toolbar is available only for authenticated users so no Visitors are tested
22
+ *
23
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
24
+ * @version 6.0.0
25
+ */
26
+ class SingleRoleInheritanceTest extends TestCase
27
+ {
28
+ use ResetTrait,
29
+ AuthUserTrait;
30
+
31
+ /**
32
+ * Test to insure that access settings are stored property on the User level
33
+ *
34
+ * @return void
35
+ *
36
+ * @access public
37
+ * @see AAM_Core_Subject_User::updateOption
38
+ * @version 6.0.0
39
+ */
40
+ public function testSaveToolbarOption()
41
+ {
42
+ $user = AAM::getUser();
43
+ $object = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
44
+
45
+ // Check if save returns positive result
46
+ $this->assertTrue($object->updateOptionItem('new-page', true)->save());
47
+
48
+ // Read from the database saved values and assert that we have
49
+ // Array (
50
+ // index.php => true
51
+ // )
52
+ $option = $user->readOption(AAM_Core_Object_Toolbar::OBJECT_TYPE);
53
+ $this->assertSame(array('new-page' => true), $option);
54
+ }
55
+
56
+ /**
57
+ * Test that access settings are inherited from the parent role property
58
+ *
59
+ * This test is designed to verify that access settings are propagated property
60
+ * when there is only one role assigned to a user.
61
+ *
62
+ * @return void
63
+ *
64
+ * @access public
65
+ * @version 6.0.0
66
+ */
67
+ public function testInheritanceFromSingleRole()
68
+ {
69
+ $user = AAM::getUser();
70
+ $parent = $user->getParent();
71
+ $object = $parent->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
72
+
73
+ // Make sure that we have parent role defined
74
+ $this->assertEquals('AAM_Core_Subject_Role', get_class($parent));
75
+
76
+ // Save access settings for the role and make sure they are saved property
77
+ // Check if save returns positive result
78
+ $this->assertTrue($object->updateOptionItem('new-page', true)->save());
79
+
80
+ // Read from the database saved values and assert that we have
81
+ // Array (
82
+ // index.php => true
83
+ // )
84
+ $option = $parent->readOption(AAM_Core_Object_Toolbar::OBJECT_TYPE);
85
+ $this->assertSame(array('new-page' => true), $option);
86
+
87
+ // Finally verify that access settings are propagated property to the User
88
+ // Level
89
+ $menu = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
90
+ $this->assertSame(array('new-page' => true), $menu->getOption());
91
+ }
92
+
93
+ /**
94
+ * Test that access settings are propagated and merged properly
95
+ *
96
+ * The test is designed to verify that access settings are propagated properly
97
+ * from the parent role and merged well with explicitly defined access settings on
98
+ * the User level.
99
+ *
100
+ * The expected result is to have combined array of access settings from the parent
101
+ * role and specific user.
102
+ *
103
+ * @return void
104
+ *
105
+ * @access public
106
+ * @version 6.0.0
107
+ */
108
+ public function testInheritanceMergeFromSingleRole()
109
+ {
110
+ $user = AAM::getUser();
111
+ $parent = $user->getParent();
112
+
113
+ $object = $parent->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
114
+
115
+ // Save access settings for the role and make sure they are saved property
116
+ // Check if save returns positive result
117
+ $this->assertTrue($object->updateOptionItem('new-page', true)->save());
118
+
119
+ // Save access setting for the user and make sure they are saved property
120
+ $menu = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true);
121
+ $this->assertTrue($menu->updateOptionItem('new-post', false)->save());
122
+
123
+ // Reset cache and try to kick-in the inheritance mechanism
124
+ $this->_resetSubjects();
125
+
126
+ $menu = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
127
+ $this->assertSame(
128
+ array('new-page' => true, 'new-post' => false),
129
+ $menu->getOption()
130
+ );
131
+ }
132
+
133
+ /**
134
+ * Test that the full inheritance mechanism is working as expected
135
+ *
136
+ * Make sure that access settings are propagated and merged properly from the top
137
+ * (Default Level) to the bottom (User Level).
138
+ *
139
+ * @return void
140
+ *
141
+ * @access public
142
+ * @version 6.0.0
143
+ */
144
+ public function testFullInheritanceChainSingeRole()
145
+ {
146
+ $user = AAM::getUser();
147
+ $role = $user->getParent();
148
+ $default = $role->getParent();
149
+
150
+ $userMenu = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true);
151
+ $roleMenu = $role->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true);
152
+ $defaultMenu = $default->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true);
153
+
154
+ // Save access settings for all subjects
155
+ $this->assertTrue($userMenu->updateOptionItem('new-post', true)->save());
156
+ $this->assertTrue($roleMenu->updateOptionItem('new-page', true)->save());
157
+ $this->assertTrue($defaultMenu->updateOptionItem('new-media', true)->save());
158
+
159
+ // Reset cache and try to kick-in the inheritance mechanism
160
+ $this->_resetSubjects();
161
+
162
+ // All settings has to be merged into one array
163
+ $menu = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
164
+ $this->assertSame(
165
+ array(
166
+ 'new-media' => true,
167
+ 'new-page' => true,
168
+ 'new-post' => true
169
+ ),
170
+ $menu->getOption()
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Test that access settings overwrite works as expected
176
+ *
177
+ * The expected result is lower Access Level overwrite access settings from the
178
+ * higher Access Level.
179
+ *
180
+ * @return void
181
+ *
182
+ * @access public
183
+ * @version 6.0.0
184
+ */
185
+ public function testInheritanceOverrideForSingleRole()
186
+ {
187
+ $user = AAM::getUser();
188
+ $parent = $user->getParent();
189
+
190
+ $object = $parent->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
191
+
192
+ // Save access settings for the role and make sure they are saved property
193
+ // Check if save returns positive result
194
+ $this->assertTrue($object->updateOptionItem('new-post', true)->save());
195
+
196
+ // Save access setting for the user and make sure they are saved property
197
+ $menu = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE, null, true);
198
+ $this->assertTrue($menu->updateOptionItem('new-post', false)->save());
199
+
200
+ // Reset cache and try to kick-in the inheritance mechanism
201
+ $this->_resetSubjects();
202
+
203
+ $menu = $user->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
204
+ $this->assertSame(array('new-post' => false), $menu->getOption());
205
+ }
206
+
207
+ public function testToolbarRendering()
208
+ {
209
+ $_SERVER['HTTP_HOST'] = 'aamplugin.com';
210
+ $_SERVER['REQUEST_URI'] = '/wp-admin';
211
+
212
+ // Restrict access to the Log Out menu and make sure it is not rendered
213
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Toolbar::OBJECT_TYPE);
214
+ $this->assertTrue($object->updateOptionItem('logout', true)->save());
215
+
216
+ ob_start();
217
+ _wp_admin_bar_init();
218
+ wp_admin_bar_render();
219
+ $content = ob_get_contents();
220
+ ob_end_clean();
221
+
222
+ $this->assertEquals(false, strpos($content, "id='wp-admin-bar-logout'"));
223
+ }
224
+
225
+ }
tests/Service/Uri/Callback.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace AAM\UnitTest\Service\Uri;
4
+
5
+ class Callback
6
+ {
7
+ const REDIRECT_URL = 'https://aamplugin.com/redirect';
8
+
9
+ public static function redirectCallback()
10
+ {
11
+ header('Location: ' . self::REDIRECT_URL);
12
+ }
13
+
14
+ }
tests/Service/Uri/UriTest.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\Uri;
11
+
12
+ use AAM,
13
+ AAM_Service_Uri,
14
+ AAM_Core_Object_Uri,
15
+ PHPUnit\Framework\TestCase,
16
+ AAM\UnitTest\Libs\ResetTrait;
17
+
18
+ /**
19
+ * URI Access service
20
+ *
21
+ * @package AAM\UnitTest
22
+ * @version 6.0.0
23
+ */
24
+ class UriTest extends TestCase
25
+ {
26
+ use ResetTrait;
27
+
28
+ /**
29
+ * Test default "Access Denied" message
30
+ *
31
+ * @return void
32
+ *
33
+ * @access public
34
+ * @version 6.0.0
35
+ */
36
+ public function testAccessDeniedMessage()
37
+ {
38
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
39
+ $result = $object->updateOptionItem('/hello-world', array(
40
+ 'type' => 'default',
41
+ 'action' => null
42
+ ))->save();
43
+
44
+ $this->assertTrue($result);
45
+
46
+ // Override the default handlers so we can suppress die exit
47
+ add_filter('wp_die_handler', function() {
48
+ return function($message, $title) {
49
+ _default_wp_die_handler($message, $title, array('exit' => false));
50
+ };
51
+ }, PHP_INT_MAX);
52
+ $_SERVER['REQUEST_URI'] = '/hello-world';
53
+
54
+ ob_start();
55
+ AAM_Service_Uri::getInstance()->authorizeUri();
56
+ $content = ob_get_contents();
57
+ ob_end_clean();
58
+
59
+ $this->assertStringContainsString('Access Denied', $content);
60
+ }
61
+
62
+ /**
63
+ * Test custom wp_die message
64
+ *
65
+ * @return void
66
+ *
67
+ * @access public
68
+ * @version 6.0.0
69
+ */
70
+ public function testCustomMessage()
71
+ {
72
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
73
+ $result = $object->updateOptionItem('/hello-world', array(
74
+ 'type' => 'message',
75
+ 'action' => 'This is not allowed'
76
+ ))->save();
77
+
78
+ $this->assertTrue($result);
79
+
80
+ // Override the default handlers so we can suppress die exit
81
+ add_filter('wp_die_handler', function() {
82
+ return function($message, $title) {
83
+ _default_wp_die_handler($message, $title, array('exit' => false));
84
+ };
85
+ }, PHP_INT_MAX);
86
+ $_SERVER['REQUEST_URI'] = '/hello-world';
87
+
88
+ ob_start();
89
+ AAM_Service_Uri::getInstance()->authorizeUri();
90
+ $content = ob_get_contents();
91
+ ob_end_clean();
92
+
93
+ $this->assertStringContainsString('This is not allowed', $content);
94
+ }
95
+
96
+ /**
97
+ * Test redirect to the custom page
98
+ *
99
+ * @return void
100
+ *
101
+ * @access public
102
+ * @version 6.0.0
103
+ */
104
+ public function testRedirectToExistingPage()
105
+ {
106
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
107
+ $result = $object->updateOptionItem('/hello-world', array(
108
+ 'type' => 'page',
109
+ 'action' => AAM_UNITTEST_PAGE_ID
110
+ ))->save();
111
+
112
+ $this->assertTrue($result);
113
+
114
+ $_SERVER['REQUEST_URI'] = '/hello-world';
115
+
116
+ AAM_Service_Uri::getInstance()->authorizeUri();
117
+
118
+ $this->assertContains(
119
+ 'Location: ' . get_page_link(AAM_UNITTEST_PAGE_ID), xdebug_get_headers()
120
+ );
121
+ }
122
+
123
+ /**
124
+ * Test redirect to the local URL
125
+ *
126
+ * @return void
127
+ *
128
+ * @access public
129
+ * @version 6.0.0
130
+ */
131
+ public function testRedirectToUrl()
132
+ {
133
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
134
+ $result = $object->updateOptionItem('/hello-world', array(
135
+ 'type' => 'url',
136
+ 'action' => '/another-page'
137
+ ))->save();
138
+
139
+ $this->assertTrue($result);
140
+
141
+ $_SERVER['REQUEST_URI'] = '/hello-world';
142
+
143
+ AAM_Service_Uri::getInstance()->authorizeUri();
144
+
145
+ $this->assertContains(
146
+ 'Location: /another-page', xdebug_get_headers()
147
+ );
148
+ }
149
+
150
+ /**
151
+ * Test trigger of the callback function
152
+ *
153
+ * @return void
154
+ *
155
+ * @access public
156
+ * @version 6.0.0
157
+ */
158
+ public function testTriggerCallback()
159
+ {
160
+ $object = AAM::getUser()->getObject(AAM_Core_Object_Uri::OBJECT_TYPE);
161
+ $result = $object->updateOptionItem('/hello-world', array(
162
+ 'type' => 'callback',
163
+ 'action' => 'AAM\\UnitTest\\Service\\Uri\\Callback::redirectCallback'
164
+ ))->save();
165
+
166
+ $this->assertTrue($result);
167
+
168
+ $_SERVER['REQUEST_URI'] = '/hello-world';
169
+
170
+ AAM_Service_Uri::getInstance()->authorizeUri();
171
+
172
+ $this->assertContains(
173
+ 'Location: ' . Callback::REDIRECT_URL, xdebug_get_headers()
174
+ );
175
+ }
176
+
177
+ }
tests/Service/UserLevelFilter/UserLevelFilterTest.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace AAM\UnitTest\Service\AdminMenu;
11
+
12
+ use AAM,
13
+ WP_User_Query,
14
+ PHPUnit\Framework\TestCase,
15
+ AAM\UnitTest\Libs\ResetTrait,
16
+ AAM\UnitTest\Libs\AuthManagerUserTrait;
17
+
18
+ /**
19
+ * Test User Level Filter service
20
+ *
21
+ * @package AAM\UnitTest
22
+ * @version 6.0.0
23
+ */
24
+ class UserLevelFilterTest extends TestCase
25
+ {
26
+ use ResetTrait,
27
+ AuthManagerUserTrait;
28
+
29
+ /**
30
+ * Test that only allowed roles are returned
31
+ *
32
+ * @return void
33
+ *
34
+ * @access public
35
+ * @version 6.0.0
36
+ */
37
+ public function testEditableRoles()
38
+ {
39
+ require_once ABSPATH . '/wp-admin/includes/user.php';
40
+
41
+ $roles = get_editable_roles();
42
+
43
+ $this->assertFalse(array_key_exists('administrator', $roles));
44
+ }
45
+
46
+ /**
47
+ * Test that restricted roles are added to the "excluded" list of roles during
48
+ * search
49
+ *
50
+ * @return void
51
+ *
52
+ * @access public
53
+ * @version 6.0.0
54
+ */
55
+ public function testPrepareUserQuery()
56
+ {
57
+ $query = new WP_User_Query(array(
58
+ 'search' => 'a'
59
+ ));
60
+
61
+ $this->assertEquals(array('administrator'), $query->query_vars['role__not_in']);
62
+ }
63
+
64
+ /**
65
+ * Test that top User List table view does not have restricted roles listed
66
+ *
67
+ * @return void
68
+ *
69
+ * @access public
70
+ * @version 6.0.0
71
+ */
72
+ public function testListTableViews()
73
+ {
74
+ if (!isset($GLOBALS['hook_suffix'])) {
75
+ $GLOBALS['hook_suffix'] = 'users';
76
+ }
77
+
78
+ require_once ABSPATH . 'wp-admin/includes/admin.php';
79
+
80
+ $table = _get_list_table( 'WP_Users_List_Table' , array('screen' => 'users'));
81
+
82
+ ob_start();
83
+ $table->views();
84
+ $content = ob_get_contents();
85
+ ob_end_clean();
86
+
87
+ $this->assertFalse(strpos($content, "class='administrator'"));
88
+ }
89
+
90
+ /**
91
+ * Test that subadmin is allowed to manage users with lower user level
92
+ *
93
+ * @return void
94
+ *
95
+ * @access public
96
+ * @version 6.0.0
97
+ */
98
+ public function testAllowedUserEdit()
99
+ {
100
+ $this->assertTrue(current_user_can('edit_user', AAM_UNITTEST_JOHN_ID));
101
+ }
102
+
103
+ /**
104
+ * Test that subadmin is not allowed to manage users with higher user level
105
+ *
106
+ * @return void
107
+ *
108
+ * @access public
109
+ * @version 6.0.0
110
+ */
111
+ public function testNotAllowedUserEdit()
112
+ {
113
+ $this->assertFalse(current_user_can('edit_user', AAM_UNITTEST_AUTH_USER_ID));
114
+ }
115
+
116
+ /**
117
+ * Test that subadmin is allowed to manage users with the same user level
118
+ *
119
+ * @return void
120
+ *
121
+ * @access public
122
+ * @version 6.0.0
123
+ */
124
+ public function testAllowedSameLevelUserEdit()
125
+ {
126
+ $this->assertTrue(
127
+ current_user_can('edit_user', AAM_UNITTEST_AUTH_SUBADMIN2_USER_ID)
128
+ );
129
+ }
130
+
131
+ /**
132
+ * Test that subadmin is not allowed to manage users with the same user level
133
+ *
134
+ * @return void
135
+ *
136
+ * @access public
137
+ * @version 6.0.0
138
+ */
139
+ public function testNotAllowedSameLevelUserEdit()
140
+ {
141
+ // Fake the un assigned `manage_same_user_level`
142
+ //wp_get_current_user()->caps['manage_same_user_level'] = false;
143
+ $user = AAM::getUser()->getPrincipal();
144
+ $user->caps['manage_same_user_level'] = false;
145
+
146
+ $this->assertFalse(
147
+ current_user_can('edit_user', AAM_UNITTEST_AUTH_SUBADMIN2_USER_ID)
148
+ );
149
+ }
150
+
151
+ }
tests/bootstrap.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Make sure that path to the PHPUnit is included in the PHP.ini include_path as well
5
+ * as PHPUnit is installed on your machine
6
+ */
7
+
8
+ // Autoloader for the PHPUnit Framework
9
+ spl_autoload_register(function ($classname) {
10
+ $filepath = null;
11
+
12
+ if (strpos($classname, 'PHPUnit') === 0) {
13
+ $filepath = __DIR__ . '\\' . $classname . '.php';
14
+ } elseif (strpos($classname, 'AAM\UnitTest') === 0) {
15
+ $filepath = __DIR__ . str_replace(array('AAM\UnitTest', '\\'), array('', '/'), $classname) . '.php';
16
+ }
17
+
18
+ if ($filepath && file_exists($filepath)) {
19
+ require $filepath;
20
+ }
21
+ });
22
+
23
+ // Load the WordPress library.
24
+ require_once dirname(__DIR__) . '/../../../wp-load.php';
25
+
26
+ // Very important to allow to test headers
27
+ ob_start();
vendor/composer/VersionParser.php CHANGED
@@ -159,6 +159,11 @@ class VersionParser
159
  try {
160
  return $this->normalizeBranch($match[1]);
161
  } catch (\Exception $e) {
 
 
 
 
 
162
  }
163
  }
164
 
@@ -474,6 +479,11 @@ class VersionParser
474
 
475
  return array(new Constraint($matches[1] ?: '=', $version));
476
  } catch (\Exception $e) {
 
 
 
 
 
477
  }
478
  }
479
 
159
  try {
160
  return $this->normalizeBranch($match[1]);
161
  } catch (\Exception $e) {
162
+ _doing_it_wrong(
163
+ __CLASS__ . '::' . __METHOD__,
164
+ $e->getMessage(),
165
+ AAM_VERSION
166
+ );
167
  }
168
  }
169
 
479
 
480
  return array(new Constraint($matches[1] ?: '=', $version));
481
  } catch (\Exception $e) {
482
+ _doing_it_wrong(
483
+ __CLASS__ . '::' . __METHOD__,
484
+ $e->getMessage(),
485
+ AAM_VERSION
486
+ );
487
  }
488
  }
489
 
vendor/firebase/JWT.php CHANGED
@@ -205,6 +205,8 @@ class JWT
205
  } else {
206
  return $signature;
207
  }
 
 
208
  }
209
  }
210
 
205
  } else {
206
  return $signature;
207
  }
208
+ default:
209
+ break;
210
  }
211
  }
212