Advanced Access Manager - Version 5.9.2

Version Description

  • Fixed the bug with Access Policy access control
  • Fixed the bug with Access Policy tab shows only 10 last Policies
  • Fixed the bug where AAM was not determining correct max user level
  • Fixed the bug where user was able to manage his roles on the profile page
  • Fixed the bug with Access Policy "Between" condition
  • Optimized AAM to support unusual access capabilities for custom post types https://forum.aamplugin.com/d/99-custom-post-type-does-not-honor-edit-delete-publish-overrides/5
  • Enhanced Access Policy with few new features. The complete reference is here https://aamplugin.com/reference/policy
  • Enabled 'JWT Authentication' by default
  • Significantly improved AAM UI page security
  • Added new JWT Tokens feature to the list of AAM features https://aamplugin.com/reference/plugin#jwt-tokens
  • Added new capability aam_manage_jwt
  • Added "Add New Policies" submenu to fix WordPress core bug with managing access to submenus
  • Removed "Role Expiration" feature - it was too confusing to work with
  • Removed allow_ajax_calls capability support - it was too confusing for end users
Download this release

Release Info

Developer vasyltech
Plugin Icon 128x128 Advanced Access Manager
Version 5.9.2
Comparing to
See all releases

Code changes from version 5.9.1.1 to 5.9.2

Files changed (50) hide show
  1. Application/Backend/Feature.php +6 -1
  2. Application/Backend/Feature/Main/404Redirect.php +2 -1
  3. Application/Backend/Feature/Main/Capability.php +4 -2
  4. Application/Backend/Feature/Main/Jwt.php +182 -0
  5. Application/Backend/Feature/Main/LoginRedirect.php +2 -1
  6. Application/Backend/Feature/Main/LogoutRedirect.php +2 -1
  7. Application/Backend/Feature/Main/Menu.php +2 -1
  8. Application/Backend/Feature/Main/Metabox.php +2 -1
  9. Application/Backend/Feature/Main/Policy.php +14 -21
  10. Application/Backend/Feature/Main/Post.php +2 -1
  11. Application/Backend/Feature/Main/Redirect.php +2 -1
  12. Application/Backend/Feature/Main/Route.php +2 -1
  13. Application/Backend/Feature/Main/Toolbar.php +2 -1
  14. Application/Backend/Feature/Main/Uri.php +3 -2
  15. Application/Backend/Feature/Settings/Core.php +1 -1
  16. Application/Backend/Feature/Subject/Role.php +1 -20
  17. Application/Backend/Feature/Subject/User.php +18 -21
  18. Application/Backend/Manager.php +24 -47
  19. Application/Backend/Subject.php +30 -7
  20. Application/Backend/View.php +1 -0
  21. Application/Backend/phtml/index.phtml +0 -27
  22. Application/Backend/phtml/main/jwt.phtml +126 -0
  23. Application/Backend/phtml/main/menu.phtml +2 -2
  24. Application/Backend/phtml/main/metabox.phtml +1 -1
  25. Application/Backend/phtml/main/toolbar.phtml +1 -1
  26. Application/Backend/phtml/user/multiple-roles.phtml +29 -27
  27. Application/Core/Compatibility.php +34 -1
  28. Application/Core/Gateway.php +9 -0
  29. Application/Core/Jwt/Auth.php +69 -0
  30. Application/Core/Jwt/Issuer.php +229 -0
  31. Application/Core/Jwt/Manager.php +356 -0
  32. Application/Core/JwtAuth.php +0 -347
  33. Application/Core/Login.php +20 -4
  34. Application/Core/Object/Post.php +8 -3
  35. Application/Core/Object/Route.php +5 -4
  36. Application/Core/Policy/Condition.php +21 -5
  37. Application/Core/Policy/Token.php +2 -0
  38. Application/Core/Subject/User.php +160 -81
  39. Application/Shared/Manager.php +117 -74
  40. aam.php +22 -14
  41. media/css/aam.css +15 -6
  42. media/font/fontello.eot +0 -0
  43. media/font/fontello.svg +4 -0
  44. media/font/fontello.ttf +0 -0
  45. media/font/fontello.woff +0 -0
  46. media/font/fontello.woff2 +0 -0
  47. media/js/{aam-5.9.1.js → aam-5.9.2.js} +311 -84
  48. media/js/vendor.js +9 -1
  49. readme.txt +18 -1
  50. vendor/firebase/JWT.php +4 -1
Application/Backend/Feature.php CHANGED
@@ -40,19 +40,24 @@ class AAM_Backend_Feature {
40
  public static function registerFeature(stdClass $feature) {
41
  $response = false;
42
 
 
43
  if (empty($feature->capability)){
44
  $cap = 'aam_manager';
45
  } else {
46
  $cap = $feature->capability;
47
  }
48
 
 
49
  if (isset($feature->option)) {
50
  $show = self::isVisible($feature->option);
51
  } else {
52
  $show = true;
53
  }
 
 
 
54
 
55
- if ($show && current_user_can($cap)) {
56
  self::$_features[] = $feature;
57
  $response = true;
58
  }
40
  public static function registerFeature(stdClass $feature) {
41
  $response = false;
42
 
43
+ // Determine correct AAM UI capability
44
  if (empty($feature->capability)){
45
  $cap = 'aam_manager';
46
  } else {
47
  $cap = $feature->capability;
48
  }
49
 
50
+ // Determine if minimum required options are enabled
51
  if (isset($feature->option)) {
52
  $show = self::isVisible($feature->option);
53
  } else {
54
  $show = true;
55
  }
56
+
57
+ // Determine that current user has enough level to manage requested subject
58
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
59
 
60
+ if ($show && $allowed && current_user_can($cap)) {
61
  self::$_features[] = $feature;
62
  $response = true;
63
  }
Application/Backend/Feature/Main/404Redirect.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_404Redirect extends AAM_Backend_Feature_Abstract
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_404_redirect')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_404_redirect'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_404_redirect')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_404_redirect'));
27
  }
28
  }
Application/Backend/Feature/Main/Capability.php CHANGED
@@ -56,7 +56,8 @@ class AAM_Backend_Feature_Main_Capability extends AAM_Backend_Feature_Abstract {
56
  'aam_edit_roles', 'aam_delete_roles', 'aam_toggle_users', 'aam_switch_users',
57
  'aam_manage_configpress', 'aam_manage_api_routes', 'aam_manage_uri', 'aam_manage_policy',
58
  'aam_view_help_btn', 'aam_edit_policy', 'aam_read_policy', 'aam_delete_policy',
59
- 'aam_delete_policies', 'aam_edit_policies', 'aam_edit_others_policies', 'aam_publish_policies'
 
60
  )
61
  );
62
 
@@ -66,7 +67,8 @@ class AAM_Backend_Feature_Main_Capability extends AAM_Backend_Feature_Abstract {
66
  public function __construct() {
67
  parent::__construct();
68
 
69
- if (!current_user_can('aam_manage_capabilities')) {
 
70
  AAM::api()->denyAccess(array('reason' => 'aam_manage_capabilities'));
71
  }
72
  }
56
  'aam_edit_roles', 'aam_delete_roles', 'aam_toggle_users', 'aam_switch_users',
57
  'aam_manage_configpress', 'aam_manage_api_routes', 'aam_manage_uri', 'aam_manage_policy',
58
  'aam_view_help_btn', 'aam_edit_policy', 'aam_read_policy', 'aam_delete_policy',
59
+ 'aam_delete_policies', 'aam_edit_policies', 'aam_edit_others_policies', 'aam_publish_policies',
60
+ 'aam_manage_jwt'
61
  )
62
  );
63
 
67
  public function __construct() {
68
  parent::__construct();
69
 
70
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
71
+ if (!$allowed || !current_user_can('aam_manage_capabilities')) {
72
  AAM::api()->denyAccess(array('reason' => 'aam_manage_capabilities'));
73
  }
74
  }
Application/Backend/Feature/Main/Jwt.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
+ /**
11
+ * JWT manager
12
+ *
13
+ * @package AAM
14
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
+ */
16
+ class AAM_Backend_Feature_Main_Jwt extends AAM_Backend_Feature_Abstract {
17
+
18
+ /**
19
+ * Construct
20
+ */
21
+ public function __construct() {
22
+ parent::__construct();
23
+
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_jwt')) {
26
+ AAM::api()->denyAccess(array('reason' => 'aam_manage_jwt'));
27
+ }
28
+ }
29
+
30
+ /**
31
+ *
32
+ * @return type
33
+ */
34
+ public function getTable() {
35
+ return wp_json_encode($this->retrieveList());
36
+ }
37
+
38
+ /**
39
+ *
40
+ * @return type
41
+ */
42
+ public function generate() {
43
+ $user = AAM_Backend_Subject::getInstance()->get();
44
+ $expires = filter_input(INPUT_POST, 'expires');
45
+
46
+ try {
47
+ $max = AAM::getUser()->getMaxLevel();
48
+ if ($max >= AAM_Core_API::maxLevel($user->allcaps)) {
49
+ $issuer = new AAM_Core_Jwt_Issuer();
50
+ $jwt = $issuer->issueToken(
51
+ array('userId' => $user->ID, 'revocable' => true),
52
+ $expires
53
+ );
54
+ $result = array(
55
+ 'status' => 'success',
56
+ 'jwt' => $jwt->token
57
+ );
58
+ } else {
59
+ throw new Exception('User ID has higher level than current user');
60
+ }
61
+ } catch (Exception $ex) {
62
+ $result = array('status' => 'failure', 'reason' => $ex->getMessage());
63
+ }
64
+
65
+ return wp_json_encode($result);
66
+ }
67
+
68
+ /**
69
+ *
70
+ * @return type
71
+ */
72
+ public function save() {
73
+ $user = AAM_Backend_Subject::getInstance()->get();
74
+ $token = filter_input(INPUT_POST, 'token');
75
+ $claims = AAM_Core_Jwt_Issuer::extractTokenClaims($token);
76
+
77
+ $result = AAM_Core_Jwt_Manager::getInstance()->registerToken(
78
+ $user->ID,
79
+ $token
80
+ );
81
+
82
+ if ($result) {
83
+ $response = array('status' => 'success');
84
+ } else {
85
+ $response = array(
86
+ 'status' => 'failure',
87
+ 'reason' => __('Failed to register JWT token', AAM_KEY)
88
+ );
89
+ }
90
+
91
+ return wp_json_encode($response);
92
+ }
93
+
94
+ /**
95
+ *
96
+ * @return type
97
+ */
98
+ public function delete() {
99
+ $user = AAM_Backend_Subject::getInstance()->get();
100
+ $token = filter_input(INPUT_POST, 'token');
101
+ $result = AAM_Core_Jwt_Manager::getInstance()->revokeToken($user->ID, $token);
102
+
103
+ if ($result) {
104
+ $response = array('status' => 'success');
105
+ } else {
106
+ $response = array(
107
+ 'status' => 'failure',
108
+ 'reason' => __('Failed to revoke JWT token', AAM_KEY)
109
+ );
110
+ }
111
+
112
+ return wp_json_encode($response);
113
+ }
114
+
115
+ /**
116
+ * @inheritdoc
117
+ */
118
+ public static function getTemplate() {
119
+ return 'main/jwt.phtml';
120
+ }
121
+
122
+ /**
123
+ *
124
+ * @return type
125
+ */
126
+ protected function retrieveList() {
127
+ $tokens = AAM_Core_Jwt_Manager::getInstance()->getTokenRegistry(
128
+ AAM_Backend_Subject::getInstance()->get()->ID
129
+ );
130
+
131
+ $response = array(
132
+ 'recordsTotal' => count($tokens),
133
+ 'recordsFiltered' => count($tokens),
134
+ 'draw' => AAM_Core_Request::request('draw'),
135
+ 'data' => array(),
136
+ );
137
+
138
+ $issuer = new AAM_Core_Jwt_Issuer();
139
+
140
+ foreach($tokens as $token) {
141
+ try {
142
+ $claims = $issuer->validateToken($token);
143
+ } catch(Exception $e) {
144
+ $claims = $issuer->extractTokenClaims($token);
145
+ $claims->status = 'invalid';
146
+ }
147
+
148
+ $response['data'][] = array(
149
+ $token,
150
+ add_query_arg('aam-jwt', $token, site_url()),
151
+ $claims->status,
152
+ $claims->exp,
153
+ 'view,delete'
154
+ );
155
+ }
156
+
157
+ return $response;
158
+ }
159
+
160
+ /**
161
+ * Register Menu feature
162
+ *
163
+ * @return void
164
+ *
165
+ * @access public
166
+ */
167
+ public static function register() {
168
+ AAM_Backend_Feature::registerFeature((object) array(
169
+ 'uid' => 'jwt',
170
+ 'position' => 65,
171
+ 'title' => __('JWT Tokens', AAM_KEY) . '<span class="badge">NEW</span>',
172
+ 'capability' => 'aam_manage_jwt',
173
+ 'type' => 'main',
174
+ 'subjects' => array(
175
+ AAM_Core_Subject_User::UID
176
+ ),
177
+ 'option' => 'core.settings.jwtAuthentication',
178
+ 'view' => __CLASS__
179
+ ));
180
+ }
181
+
182
+ }
Application/Backend/Feature/Main/LoginRedirect.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_LoginRedirect extends AAM_Backend_Feature_Abstrac
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_login_redirect')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_login_redirect'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_login_redirect')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_login_redirect'));
27
  }
28
  }
Application/Backend/Feature/Main/LogoutRedirect.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_LogoutRedirect extends AAM_Backend_Feature_Abstra
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_logout_redirect')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_logout_redirect'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_logout_redirect')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_logout_redirect'));
27
  }
28
  }
Application/Backend/Feature/Main/Menu.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_Menu extends AAM_Backend_Feature_Abstract {
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_admin_menu')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_admin_menu'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_admin_menu')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_admin_menu'));
27
  }
28
  }
Application/Backend/Feature/Main/Metabox.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_Metabox extends AAM_Backend_Feature_Abstract {
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_metaboxes')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_metaboxes'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_metaboxes')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_metaboxes'));
27
  }
28
  }
Application/Backend/Feature/Main/Policy.php CHANGED
@@ -15,6 +15,18 @@
15
  */
16
  class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract {
17
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  /**
19
  *
20
  * @return type
@@ -131,9 +143,8 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract {
131
 
132
  $list = get_posts(array(
133
  'post_type' => 'aam_policy',
134
- 'numberposts' => AAM_Core_Request::request('length'),
135
- 'offset' => AAM_Core_Request::request('start'),
136
- 's' => ($search ? $search . '*' : ''),
137
  ));
138
 
139
  $response = array(
@@ -159,24 +170,6 @@ class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract {
159
  return $response;
160
  }
161
 
162
- /**
163
- *
164
- * @global type $wpdb
165
- * @param type $type
166
- * @param type $search
167
- * @return type
168
- */
169
- protected function getPolicyCount($type, $search) {
170
- global $wpdb;
171
-
172
- $query = "SELECT COUNT(*) AS total FROM {$wpdb->posts} ";
173
- $query .= "WHERE (post_type = %s) AND (post_title LIKE %s) AND (post_status = %s)";
174
-
175
- $args = array($type, "{$search}%", 'publish');
176
-
177
- return $wpdb->get_var($wpdb->prepare($query, $args));
178
- }
179
-
180
  /**
181
  *
182
  * @param type $record
15
  */
16
  class AAM_Backend_Feature_Main_Policy extends AAM_Backend_Feature_Abstract {
17
 
18
+ /**
19
+ * Construct
20
+ */
21
+ public function __construct() {
22
+ parent::__construct();
23
+
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_policy')) {
26
+ AAM::api()->denyAccess(array('reason' => 'aam_manage_policy'));
27
+ }
28
+ }
29
+
30
  /**
31
  *
32
  * @return type
143
 
144
  $list = get_posts(array(
145
  'post_type' => 'aam_policy',
146
+ 'numberposts' => -1,
147
+ 'post_status' => 'publish'
 
148
  ));
149
 
150
  $response = array(
170
  return $response;
171
  }
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  /**
174
  *
175
  * @param type $record
Application/Backend/Feature/Main/Post.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_Post extends AAM_Backend_Feature_Abstract {
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_posts')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_posts'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_posts')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_posts'));
27
  }
28
  }
Application/Backend/Feature/Main/Redirect.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_Redirect extends AAM_Backend_Feature_Abstract {
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_access_denied_redirect')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_access_denied_redirect'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_access_denied_redirect')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_access_denied_redirect'));
27
  }
28
  }
Application/Backend/Feature/Main/Route.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_Route extends AAM_Backend_Feature_Abstract {
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_api_routes')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_api_routes'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_api_routes')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_api_routes'));
27
  }
28
  }
Application/Backend/Feature/Main/Toolbar.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_Toolbar extends AAM_Backend_Feature_Abstract {
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_admin_toolbar')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_admin_toolbar'));
26
  }
27
  }
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_admin_toolbar')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_admin_toolbar'));
27
  }
28
  }
Application/Backend/Feature/Main/Uri.php CHANGED
@@ -21,7 +21,8 @@ class AAM_Backend_Feature_Main_Uri extends AAM_Backend_Feature_Abstract {
21
  public function __construct() {
22
  parent::__construct();
23
 
24
- if (!current_user_can('aam_manage_uri')) {
 
25
  AAM::api()->denyAccess(array('reason' => 'aam_manage_uri'));
26
  }
27
  }
@@ -146,7 +147,7 @@ class AAM_Backend_Feature_Main_Uri extends AAM_Backend_Feature_Abstract {
146
  AAM_Backend_Feature::registerFeature((object) array(
147
  'uid' => 'uri',
148
  'position' => 55,
149
- 'title' => __('URI Access', AAM_KEY) . '<span class="badge">NEW</span>',
150
  'capability' => 'aam_manage_uri',
151
  'type' => 'main',
152
  'subjects' => array(
21
  public function __construct() {
22
  parent::__construct();
23
 
24
+ $allowed = AAM_Backend_Subject::getInstance()->isAllowedToManage();
25
+ if (!$allowed || !current_user_can('aam_manage_uri')) {
26
  AAM::api()->denyAccess(array('reason' => 'aam_manage_uri'));
27
  }
28
  }
147
  AAM_Backend_Feature::registerFeature((object) array(
148
  'uid' => 'uri',
149
  'position' => 55,
150
+ 'title' => __('URI Access', AAM_KEY),
151
  'capability' => 'aam_manage_uri',
152
  'type' => 'main',
153
  'subjects' => array(
Application/Backend/Feature/Settings/Core.php CHANGED
@@ -92,7 +92,7 @@ class AAM_Backend_Feature_Settings_Core extends AAM_Backend_Feature_Abstract {
92
  'core.settings.jwtAuthentication' => array(
93
  'title' => __('JWT Authentication', AAM_KEY),
94
  'descr' => sprintf(AAM_Backend_View_Helper::preparePhrase('[Note!] PHP 5.4 or higher is required for this feature. Enable the ability to authenticate user with WordPress RESTful API and JWT token. For more information, check %sHow to authenticate WordPress user with JWT token%s article', 'b'), '<a href="https://aamplugin.com/article/how-to-authenticate-wordpress-user-with-jwt-token">', '</a>'),
95
- 'value' => AAM_Core_Config::get('core.settings.jwtAuthentication', false)
96
  ),
97
  'core.settings.multiSubject' => array(
98
  'title' => __('Multiple Roles Support', AAM_KEY),
92
  'core.settings.jwtAuthentication' => array(
93
  'title' => __('JWT Authentication', AAM_KEY),
94
  'descr' => sprintf(AAM_Backend_View_Helper::preparePhrase('[Note!] PHP 5.4 or higher is required for this feature. Enable the ability to authenticate user with WordPress RESTful API and JWT token. For more information, check %sHow to authenticate WordPress user with JWT token%s article', 'b'), '<a href="https://aamplugin.com/article/how-to-authenticate-wordpress-user-with-jwt-token">', '</a>'),
95
+ 'value' => AAM_Core_Config::get('core.settings.jwtAuthentication', true)
96
  ),
97
  'core.settings.multiSubject' => array(
98
  'title' => __('Multiple Roles Support', AAM_KEY),
Application/Backend/Feature/Subject/Role.php CHANGED
@@ -59,8 +59,7 @@ class AAM_Backend_Feature_Subject_Role {
59
  implode(',', $this->prepareRowActions($uc, $id)),
60
  $data
61
  ),
62
- AAM_Core_API::maxLevel($data['capabilities']),
63
- AAM_Core_API::getOption("aam-role-{$id}-expiration", '')
64
  );
65
  }
66
 
@@ -163,7 +162,6 @@ class AAM_Backend_Feature_Subject_Role {
163
 
164
  if (current_user_can('aam_create_roles')) {
165
  $name = sanitize_text_field(filter_input(INPUT_POST, 'name'));
166
- $expire = filter_input(INPUT_POST, 'expire');
167
  $roles = AAM_Core_API::getRoles();
168
  $role_id = sanitize_key(strtolower($name));
169
 
@@ -185,13 +183,6 @@ class AAM_Backend_Feature_Subject_Role {
185
  $this->cloneSettings($role, $parent);
186
  }
187
 
188
- //save expiration rule if set
189
- if ($expire) {
190
- AAM_Core_API::updateOption("aam-role-{$role_id}-expiration", $expire);
191
- } else {
192
- AAM_Core_API::deleteOption("aam-role-{$role_id}-expiration");
193
- }
194
-
195
  do_action('aam-post-add-role-action', $role, $parent);
196
  } else {
197
  $response['reason'] = __("Role with slug [{$role_id}] already exists", AAM_KEY);
@@ -246,16 +237,6 @@ class AAM_Backend_Feature_Subject_Role {
246
  $role = AAM_Backend_Subject::getInstance();
247
  $role->update(trim(filter_input(INPUT_POST, 'name')));
248
 
249
- $expire = filter_input(INPUT_POST, 'expire');
250
- //save expiration rule if set
251
- if ($expire) {
252
- AAM_Core_API::updateOption(
253
- 'aam-role-' . $role->getId() .'-expiration', $expire
254
- );
255
- } else {
256
- AAM_Core_API::deleteOption('aam-role-' . $role->getId() .'-expiration');
257
- }
258
-
259
  do_action('aam-post-update-role-action', $role->get());
260
 
261
  $response = array('status' => 'success');
59
  implode(',', $this->prepareRowActions($uc, $id)),
60
  $data
61
  ),
62
+ AAM_Core_API::maxLevel($data['capabilities'])
 
63
  );
64
  }
65
 
162
 
163
  if (current_user_can('aam_create_roles')) {
164
  $name = sanitize_text_field(filter_input(INPUT_POST, 'name'));
 
165
  $roles = AAM_Core_API::getRoles();
166
  $role_id = sanitize_key(strtolower($name));
167
 
183
  $this->cloneSettings($role, $parent);
184
  }
185
 
 
 
 
 
 
 
 
186
  do_action('aam-post-add-role-action', $role, $parent);
187
  } else {
188
  $response['reason'] = __("Role with slug [{$role_id}] already exists", AAM_KEY);
237
  $role = AAM_Backend_Subject::getInstance();
238
  $role->update(trim(filter_input(INPUT_POST, 'name')));
239
 
 
 
 
 
 
 
 
 
 
 
240
  do_action('aam-post-update-role-action', $role->get());
241
 
242
  $response = array('status' => 'success');
Application/Backend/Feature/Subject/User.php CHANGED
@@ -98,10 +98,9 @@ class AAM_Backend_Feature_Subject_User {
98
  );
99
 
100
  if (current_user_can('aam_switch_users')) {
101
- $user = new WP_User(AAM_Core_Request::post('user'));
102
- $max = AAM::getUser()->getMaxLevel();
103
 
104
- if ($max >= AAM_Core_API::maxLevel($user->allcaps)) {
105
  AAM_Core_API::updateOption(
106
  'aam-user-switch-' . $user->ID, get_current_user_id()
107
  );
@@ -137,22 +136,6 @@ class AAM_Backend_Feature_Subject_User {
137
  return wp_json_encode($response);
138
  }
139
 
140
- /**
141
- *
142
- * @return type
143
- */
144
- public function generateJWT() {
145
- $userId = filter_input(INPUT_POST, 'user');
146
- $expires = filter_input(INPUT_POST, 'expires');
147
-
148
- $jwt = AAM_Core_JwtAuth::generateJWT($userId, $expires);
149
-
150
- return wp_json_encode(array(
151
- 'status' => 'success',
152
- 'jwt' => $jwt->token
153
- ));
154
- }
155
-
156
  /**
157
  * Query database for list of users
158
  *
@@ -361,9 +344,23 @@ class AAM_Backend_Feature_Subject_User {
361
  * @access protected
362
  */
363
  protected function isAllowed(AAM_Core_Subject_User $user) {
364
- $max = AAM::getUser()->getMaxLevel();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
- return $max >= AAM_Core_API::maxLevel($user->allcaps);
367
  }
368
 
369
  }
98
  );
99
 
100
  if (current_user_can('aam_switch_users')) {
101
+ $user = AAM_Backend_Subject::getInstance()->get();
 
102
 
103
+ if ($this->isAllowed($user)) {
104
  AAM_Core_API::updateOption(
105
  'aam-user-switch-' . $user->ID, get_current_user_id()
106
  );
136
  return wp_json_encode($response);
137
  }
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  /**
140
  * Query database for list of users
141
  *
344
  * @access protected
345
  */
346
  protected function isAllowed(AAM_Core_Subject_User $user) {
347
+ $sameLevel = false;
348
+ if (AAM_Core_API::capabilityExists('manage_same_user_level')) {
349
+ $sameLevel = current_user_can('manage_same_user_level');
350
+ } else {
351
+ $sameLevel = current_user_can('administrator');
352
+ }
353
+
354
+ $userMaxLevel = AAM::api()->getUser()->getMaxLevel();
355
+ $subjectMaxLevel = $user->getMaxLevel();
356
+
357
+ if ($sameLevel) {
358
+ $allowed = $userMaxLevel >= $subjectMaxLevel;
359
+ } else {
360
+ $allowed = $userMaxLevel > $subjectMaxLevel;
361
+ }
362
 
363
+ return $allowed;
364
  }
365
 
366
  }
Application/Backend/Manager.php CHANGED
@@ -46,9 +46,6 @@ class AAM_Backend_Manager {
46
  add_action('admin_print_footer_scripts', array($this, 'printFooterJavascript'));
47
  add_action('admin_print_styles', array($this, 'printStylesheet'));
48
 
49
- //map AAM UI specific capabilities
50
- add_filter('map_meta_cap', array($this, 'mapMetaCap'), 10, 4);
51
-
52
  //user profile update action
53
  add_action('profile_update', array($this, 'profileUpdate'), 10, 2);
54
 
@@ -74,9 +71,10 @@ class AAM_Backend_Manager {
74
  //manager Admin Menu
75
  if (is_multisite() && is_network_admin()) {
76
  //register AAM in the network admin panel
77
- add_action('network_admin_menu', array($this, 'adminMenu'));
78
  } else {
79
- add_action('admin_menu', array($this, 'adminMenu'));
 
80
  add_action('all_admin_notices', array($this, 'notification'));
81
  }
82
 
@@ -314,22 +312,6 @@ class AAM_Backend_Manager {
314
  $importer->dispatch();
315
  }
316
 
317
- /**
318
- *
319
- * @param type $caps
320
- * @param type $cap
321
- * @return type
322
- */
323
- public function mapMetaCap($caps, $cap) {
324
- if (in_array($cap, AAM_Backend_Feature_Main_Capability::$groups['aam'], true)) {
325
- if (!AAM_Core_API::capabilityExists($cap)) {
326
- $caps = array(AAM_Core_Config::get('page.capability', 'administrator'));
327
- }
328
- }
329
-
330
- return $caps;
331
- }
332
-
333
  /**
334
  *
335
  * @param string $html
@@ -368,7 +350,8 @@ class AAM_Backend_Manager {
368
 
369
  if (!empty($newRoles)) {
370
  //remove all current roles and then set new
371
- $user->set_role($role);
 
372
  foreach($newRoles as $role) {
373
  $user->add_role($role);
374
  }
@@ -378,19 +361,6 @@ class AAM_Backend_Manager {
378
  //role changed?
379
  if (implode('', $user->roles) !== implode('', $old->roles)) {
380
  AAM_Core_API::clearCache(new AAM_Core_Subject_User($id));
381
-
382
- // check if role has expiration data set
383
- // TODO: This supports only the first role and NOT the multi-roles
384
- if (is_array($user->roles)) {
385
- $roles = array_values($user->roles);
386
- $role = array_shift($roles);
387
- $expire = AAM_Core_API::getOption("aam-role-{$role}-expiration", '');
388
-
389
- if ($expire) {
390
- update_user_option($id, "aam-original-roles", $old->roles);
391
- update_user_option($id, "aam-role-expires", strtotime($expire));
392
- }
393
- }
394
  }
395
  }
396
 
@@ -514,17 +484,12 @@ class AAM_Backend_Manager {
514
 
515
  if ($uid && AAM_Core_API::capabilityExists('access_dashboard')) {
516
  $caps = AAM::getUser()->allcaps;
517
- if (empty($caps['access_dashboard'])) {
518
- //also additionally check for AJAX calls
519
- if (defined('DOING_AJAX') && empty($caps['allow_ajax_calls'])) {
520
- AAM_Core_API::reject(
521
- 'backend', array('hook' => 'access_dashboard')
522
- );
523
- } elseif (!defined('DOING_AJAX')) {
524
- AAM_Core_API::reject(
525
- 'backend', array('hook' => 'access_dashboard')
526
- );
527
- }
528
  }
529
  }
530
  }
@@ -833,7 +798,7 @@ class AAM_Backend_Manager {
833
  public function printJavascript() {
834
  if (AAM::isAAM()) {
835
  wp_enqueue_script('aam-vendor', AAM_MEDIA . '/js/vendor.js');
836
- wp_enqueue_script('aam-main', AAM_MEDIA . '/js/aam-5.9.1.js');
837
 
838
  //add plugin localization
839
  $this->printLocalization('aam-main');
@@ -956,6 +921,18 @@ class AAM_Backend_Manager {
956
  AAM_Core_Config::get('policy.capability', 'aam_manage_policy'),
957
  'edit.php?post_type=aam_policy'
958
  );
 
 
 
 
 
 
 
 
 
 
 
 
959
  }
960
 
961
  /**
46
  add_action('admin_print_footer_scripts', array($this, 'printFooterJavascript'));
47
  add_action('admin_print_styles', array($this, 'printStylesheet'));
48
 
 
 
 
49
  //user profile update action
50
  add_action('profile_update', array($this, 'profileUpdate'), 10, 2);
51
 
71
  //manager Admin Menu
72
  if (is_multisite() && is_network_admin()) {
73
  //register AAM in the network admin panel
74
+ add_action('_network_admin_menu', array($this, 'adminMenu'));
75
  } else {
76
+ add_action('_user_admin_menu', array($this, 'adminMenu'));
77
+ add_action('_admin_menu', array($this, 'adminMenu'));
78
  add_action('all_admin_notices', array($this, 'notification'));
79
  }
80
 
312
  $importer->dispatch();
313
  }
314
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  /**
316
  *
317
  * @param string $html
350
 
351
  if (!empty($newRoles)) {
352
  //remove all current roles and then set new
353
+ $user->set_role('');
354
+ // TODO: Fix the bug where multiple roles are not removed
355
  foreach($newRoles as $role) {
356
  $user->add_role($role);
357
  }
361
  //role changed?
362
  if (implode('', $user->roles) !== implode('', $old->roles)) {
363
  AAM_Core_API::clearCache(new AAM_Core_Subject_User($id));
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  }
365
  }
366
 
484
 
485
  if ($uid && AAM_Core_API::capabilityExists('access_dashboard')) {
486
  $caps = AAM::getUser()->allcaps;
487
+ // If this is the AJAX call, still allow it because it will break a lot
488
+ // of frontend stuff that depends on it
489
+ if (empty($caps['access_dashboard']) && !defined('DOING_AJAX')) {
490
+ AAM_Core_API::reject(
491
+ 'backend', array('hook' => 'access_dashboard')
492
+ );
 
 
 
 
 
493
  }
494
  }
495
  }
798
  public function printJavascript() {
799
  if (AAM::isAAM()) {
800
  wp_enqueue_script('aam-vendor', AAM_MEDIA . '/js/vendor.js');
801
+ wp_enqueue_script('aam-main', AAM_MEDIA . '/js/aam-5.9.2.js');
802
 
803
  //add plugin localization
804
  $this->printLocalization('aam-main');
921
  AAM_Core_Config::get('policy.capability', 'aam_manage_policy'),
922
  'edit.php?post_type=aam_policy'
923
  );
924
+
925
+ $type = get_post_type_object('aam_policy');
926
+ if (current_user_can($type->cap->create_posts)) {
927
+ add_submenu_page(
928
+ 'aam',
929
+ 'Add New Policies',
930
+ 'Add New Policies',
931
+ $type->cap->create_posts,
932
+ 'post-new.php?post_type=aam_policy'
933
+ );
934
+ }
935
+
936
  }
937
 
938
  /**
Application/Backend/Subject.php CHANGED
@@ -51,12 +51,6 @@ class AAM_Backend_Subject {
51
  $instance = $this->initRequestedSubject(
52
  $subject, AAM_Core_Request::request('subjectId')
53
  );
54
-
55
- $max = AAM::getUser()->getMaxLevel();
56
-
57
- if ($max < AAM_Core_API::maxLevel($instance->getMaxLevel())) {
58
- AAM::api()->denyAccess(array('reason' => 'User Level is too low'));
59
- }
60
  } else {
61
  $this->initDefaultSubject();
62
  }
@@ -99,7 +93,8 @@ class AAM_Backend_Subject {
99
  */
100
  protected function initDefaultSubject() {
101
  // This cover the scenario when we directly go to user e.g. ?page=aam&user=38
102
- $forceUser = AAM_Core_Request::get('user');
 
103
 
104
  // TODO: The aam_list_roles is legacy and can be removed in Oct 2021
105
  if (!$forceUser && (current_user_can('aam_manage_roles') || current_user_can('aam_list_roles'))) {
@@ -129,6 +124,34 @@ class AAM_Backend_Subject {
129
  protected function setSubject(AAM_Core_Subject $subject) {
130
  $this->subject = $subject;
131
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
  /**
134
  * Get subject property
51
  $instance = $this->initRequestedSubject(
52
  $subject, AAM_Core_Request::request('subjectId')
53
  );
 
 
 
 
 
 
54
  } else {
55
  $this->initDefaultSubject();
56
  }
93
  */
94
  protected function initDefaultSubject() {
95
  // This cover the scenario when we directly go to user e.g. ?page=aam&user=38
96
+ // or through AJAX post request with user ID
97
+ $forceUser = AAM_Core_Request::request('user');
98
 
99
  // TODO: The aam_list_roles is legacy and can be removed in Oct 2021
100
  if (!$forceUser && (current_user_can('aam_manage_roles') || current_user_can('aam_list_roles'))) {
124
  protected function setSubject(AAM_Core_Subject $subject) {
125
  $this->subject = $subject;
126
  }
127
+
128
+ /**
129
+ * Check if current subject is allowed to be managed
130
+ *
131
+ * @return boolean
132
+ *
133
+ * @access public
134
+ */
135
+ public function isAllowedToManage() {
136
+ // Determine that current user has enough level to manage requested subject
137
+ $sameLevel = false;
138
+ if (AAM_Core_API::capabilityExists('manage_same_user_level')) {
139
+ $sameLevel = current_user_can('manage_same_user_level');
140
+ } else {
141
+ $sameLevel = current_user_can('administrator');
142
+ }
143
+
144
+ $userMaxLevel = AAM::api()->getUser()->getMaxLevel();
145
+ $subjectMaxLevel = $this->subject->getMaxLevel();
146
+
147
+ if ($sameLevel) {
148
+ $allowed = $userMaxLevel >= $subjectMaxLevel;
149
+ } else {
150
+ $allowed = $userMaxLevel > $subjectMaxLevel;
151
+ }
152
+
153
+ return $allowed;
154
+ }
155
 
156
  /**
157
  * Get subject property
Application/Backend/View.php CHANGED
@@ -46,6 +46,7 @@ class AAM_Backend_View {
46
  AAM_Backend_Feature_Main_LogoutRedirect::register();
47
  AAM_Backend_Feature_Main_404Redirect::register();
48
  AAM_Backend_Feature_Main_Uri::register();
 
49
 
50
  AAM_Backend_Feature_Settings_Core::register();
51
  AAM_Backend_Feature_Settings_Content::register();
46
  AAM_Backend_Feature_Main_LogoutRedirect::register();
47
  AAM_Backend_Feature_Main_404Redirect::register();
48
  AAM_Backend_Feature_Main_Uri::register();
49
+ AAM_Backend_Feature_Main_Jwt::register();
50
 
51
  AAM_Backend_Feature_Settings_Core::register();
52
  AAM_Backend_Feature_Settings_Content::register();
Application/Backend/phtml/index.phtml CHANGED
@@ -248,7 +248,6 @@
248
  <th width="65%"><?php echo __('Role', AAM_KEY); ?></th>
249
  <th><?php echo __('Action', AAM_KEY); ?></th>
250
  <th>Level</th>
251
- <th>Expiration</th>
252
  </tr>
253
  </thead>
254
  <tbody></tbody>
@@ -266,10 +265,6 @@
266
  <label><?php echo __('Role Name', AAM_KEY); ?><span class="aam-asterix">*</span></label>
267
  <input type="text" class="form-control" name="name" placeholder="<?php echo __('Enter Role Name', AAM_KEY); ?>" />
268
  </div>
269
- <div class="form-group">
270
- <label><?php echo __('Role Expiration', AAM_KEY); ?> <a href="https://aamplugin.com/article/how-to-manage-wordpress-roles" target="_blank" data-toggle="tooltip" title="For how long user can have this role. Click to learn more."><i class="icon-help-circled"></i></a></label>
271
- <input type="text" class="form-control" name="expire" placeholder="<?php echo __('Enter Expiration Rule', AAM_KEY); ?>" />
272
- </div>
273
  <?php /* TODO: Rethink this filter */ do_action('aam-add-role-ui-action'); ?>
274
  <?php /* TODO: Rethink this filter */ echo apply_filters('aam-add-role-ui-filter', AAM_Backend_View::getInstance()->loadPartial('role-inheritance.phtml')); ?>
275
  </div>
@@ -293,10 +288,6 @@
293
  <label for="new-role-name"><?php echo __('Role Name', AAM_KEY); ?></label>
294
  <input type="text" class="form-control" id="edit-role-name" placeholder="<?php echo __('Enter Role Name', AAM_KEY); ?>" name="name" />
295
  </div>
296
- <div class="form-group">
297
- <label><?php echo __('Role Expiration', AAM_KEY); ?> <a href="https://aamplugin.com/article/how-to-manage-wordpress-roles" target="_blank" data-toggle="tooltip" title="For how long user can have this role. Click to learn more."><i class="icon-help-circled"></i></a></label>
298
- <input type="text" class="form-control" name="expire" id="edit-role-expiration" placeholder="<?php echo __('Enter Expiration Rule', AAM_KEY); ?>" />
299
- </div>
300
  <?php /* TODO: Rethink this filter */ do_action('aam-edit-role-ui-action'); ?>
301
  </div>
302
  <div class="modal-footer">
@@ -352,7 +343,6 @@
352
  <div class="modal-body">
353
  <ul class="nav nav-tabs" role="tablist">
354
  <li role="presentation" class="active"><a href="#edit-user-expiration" aria-controls="edit-user-expiration" role="tab" data-toggle="tab">Temporary Access</a></li>
355
- <li role="presentation"><a href="#edit-user-jwt" aria-controls="edit-user-jwt" role="tab" data-toggle="tab">JWT Auth Token</a></li>
356
  <li role="presentation"><a href="#edit-user-profile" aria-controls="edit-user-profile" role="tab" data-toggle="tab">Edit User</a></li>
357
  </ul>
358
 
@@ -380,23 +370,6 @@
380
  </select>
381
  </div>
382
  </div>
383
- <div role="tabpanel" class="tab-pane" id="edit-user-jwt">
384
- <div class="form-group">
385
- <p class="aam-info"><?php echo sprintf(__('User JWT token to authentication user without a need for entering username/password. To learn more about JWT authentication, please refer to %sHow to authenticate WordPress user with JWT token%s article.', AAM_KEY), '<a href="https://aamplugin.com/article/how-to-authenticate-wordpress-user-with-jwt-token" target="_blank">', '</a>'); ?></p>
386
- <div class="form-group">
387
- <label for="user-auth-url"><?php echo __('JWT Token (for any API calls)', AAM_KEY); ?></label>
388
- <textarea class="form-control" id="user-auth-jwt" readonly rows="5"></textarea>
389
- </div>
390
-
391
- <hr/>
392
-
393
- <div class="form-group">
394
- <label for="user-auth-url"><?php echo __('User Login URL (with JWT token)', AAM_KEY); ?></label>
395
- <textarea class="form-control" id="user-auth-url" data-url="<?php echo add_query_arg('aam-jwt', '%s', site_url()); ?>" readonly rows="5"></textarea>
396
- <small><?php echo __('With this URL user will be automatically logged in until the time defined on the "Temporary Access" tab.', AAM_KEY); ?></small>
397
- </div>
398
- </div>
399
- </div>
400
  <div role="tabpanel" class="tab-pane" id="edit-user-profile">
401
  <p class="aam-info"><?php echo __("To manage user profile, click on the button below.", AAM_KEY); ?></p>
402
  <p class="text-center">
248
  <th width="65%"><?php echo __('Role', AAM_KEY); ?></th>
249
  <th><?php echo __('Action', AAM_KEY); ?></th>
250
  <th>Level</th>
 
251
  </tr>
252
  </thead>
253
  <tbody></tbody>
265
  <label><?php echo __('Role Name', AAM_KEY); ?><span class="aam-asterix">*</span></label>
266
  <input type="text" class="form-control" name="name" placeholder="<?php echo __('Enter Role Name', AAM_KEY); ?>" />
267
  </div>
 
 
 
 
268
  <?php /* TODO: Rethink this filter */ do_action('aam-add-role-ui-action'); ?>
269
  <?php /* TODO: Rethink this filter */ echo apply_filters('aam-add-role-ui-filter', AAM_Backend_View::getInstance()->loadPartial('role-inheritance.phtml')); ?>
270
  </div>
288
  <label for="new-role-name"><?php echo __('Role Name', AAM_KEY); ?></label>
289
  <input type="text" class="form-control" id="edit-role-name" placeholder="<?php echo __('Enter Role Name', AAM_KEY); ?>" name="name" />
290
  </div>
 
 
 
 
291
  <?php /* TODO: Rethink this filter */ do_action('aam-edit-role-ui-action'); ?>
292
  </div>
293
  <div class="modal-footer">
343
  <div class="modal-body">
344
  <ul class="nav nav-tabs" role="tablist">
345
  <li role="presentation" class="active"><a href="#edit-user-expiration" aria-controls="edit-user-expiration" role="tab" data-toggle="tab">Temporary Access</a></li>
 
346
  <li role="presentation"><a href="#edit-user-profile" aria-controls="edit-user-profile" role="tab" data-toggle="tab">Edit User</a></li>
347
  </ul>
348
 
370
  </select>
371
  </div>
372
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  <div role="tabpanel" class="tab-pane" id="edit-user-profile">
374
  <p class="aam-info"><?php echo __("To manage user profile, click on the button below.", AAM_KEY); ?></p>
375
  <p class="text-center">
Application/Backend/phtml/main/jwt.phtml ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if (defined('AAM_KEY')) { ?>
2
+ <div class="aam-feature" id="jwt-content">
3
+ <?php $subject = AAM_Backend_Subject::getInstance(); ?>
4
+
5
+ <div class="row">
6
+ <div class="col-xs-12">
7
+ <p class="aam-info">
8
+ <?php echo sprintf(AAM_Backend_View_Helper::preparePhrase('Manage list of all valid JWT tokens to the website for [%s] account. For more information about JWT tokens please refer to the %sUltimate guide to WordPress JWT authentication%s article.', 'b'), AAM_Backend_Subject::getInstance()->getName(), '<a href="https://aamplugin.com/article/ultimate-guide-to-wordpress-jwt-authentication" target="_blank">', '</a>'); ?>
9
+ </p>
10
+ </div>
11
+ </div>
12
+
13
+ <div class="row">
14
+ <div class="col-xs-12">
15
+ <table id="jwt-list" class="table table-striped table-bordered">
16
+ <thead>
17
+ <tr>
18
+ <th>Token</th>
19
+ <th>URL</th>
20
+ <th width="8%">&nbsp;</th>
21
+ <th width="70%"><?php echo __('Expires', AAM_KEY); ?></th>
22
+ <th><?php echo __('Actions', AAM_KEY); ?></th>
23
+ </tr>
24
+ </thead>
25
+ <tbody></tbody>
26
+ </table>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="modal fade" id="create-jwt-modal" tabindex="-1" role="dialog">
31
+ <div class="modal-dialog" role="document">
32
+ <div class="modal-content">
33
+ <div class="modal-header">
34
+ <button type="button" class="close" data-dismiss="modal" aria-label="<?php echo __('Close', AAM_KEY); ?>"><span aria-hidden="true">&times;</span></button>
35
+ <h4 class="modal-title"><?php echo __('Create JWT Token', AAM_KEY); ?></h4>
36
+ </div>
37
+ <div class="modal-body">
38
+ <div class="form-group aam-bordered">
39
+ <label for="jwt-expiration-datapicker" class="aam-block">
40
+ <?php echo __('JWT Expires', AAM_KEY); ?>
41
+ </label>
42
+ <div id="jwt-expiration-datapicker"></div>
43
+ <input type="hidden" id="jwt-expires" />
44
+ </div>
45
+
46
+ <div class="form-group aam-outer-top-xs">
47
+ <label for="jwt-token-preview" class="aam-block">
48
+ <?php echo __('JWT Token (for any API calls)', AAM_KEY); ?>
49
+ <a href="#" class="aam-copy-clipboard" data-clipboard-target="#jwt-token-preview"><?php echo __('Copy to clipboard', AAM_KEY); ?></a>
50
+ </label>
51
+ <textarea class="form-control" id="jwt-token-preview" readonly rows="5"></textarea>
52
+ </div>
53
+
54
+ <hr/>
55
+
56
+ <div class="form-group">
57
+ <label for="jwt-url-preview" class="aam-block">
58
+ <?php echo __('Account Login URL (with JWT token)', AAM_KEY); ?>
59
+ <a href="#" class="aam-copy-clipboard" data-clipboard-target="#jwt-url-preview"><?php echo __('Copy to clipboard', AAM_KEY); ?></a>
60
+ </label>
61
+ <textarea class="form-control" id="jwt-url-preview" data-url="<?php echo add_query_arg('aam-jwt', '%s', site_url()); ?>" readonly rows="5"></textarea>
62
+ <small><?php echo __('With this URL account will be automatically logged in until the time defined on the "Temporary Access" tab.', AAM_KEY); ?></small>
63
+ </div>
64
+ </div>
65
+ <div class="modal-footer">
66
+ <button type="button" class="btn btn-success" id="create-jwt-btn"><?php echo __('Create', AAM_KEY); ?></button>
67
+ <button type="button" class="btn btn-default" data-dismiss="modal"><?php echo __('Close', AAM_KEY); ?></button>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="modal fade" id="view-jwt-modal" tabindex="-1" role="dialog">
74
+ <div class="modal-dialog" role="document">
75
+ <div class="modal-content">
76
+ <div class="modal-header">
77
+ <button type="button" class="close" data-dismiss="modal" aria-label="<?php echo __('Close', AAM_KEY); ?>"><span aria-hidden="true">&times;</span></button>
78
+ <h4 class="modal-title"><?php echo __('View JWT Token', AAM_KEY); ?></h4>
79
+ </div>
80
+ <div class="modal-body">
81
+ <div class="form-group">
82
+ <label for="view-jwt-token" class="aam-block">
83
+ <?php echo __('JWT Token (for any API calls)', AAM_KEY); ?>
84
+ <a href="#" class="aam-copy-clipboard" data-clipboard-target="#view-jwt-token"><?php echo __('Copy to clipboard', AAM_KEY); ?></a>
85
+ </label>
86
+ <textarea class="form-control" id="view-jwt-token" readonly rows="5"></textarea>
87
+ </div>
88
+
89
+ <hr/>
90
+
91
+ <div class="form-group">
92
+ <label for="view-jwt-url" class="aam-block">
93
+ <?php echo __('Account Login URL (with JWT token)', AAM_KEY); ?>
94
+ <a href="#" class="aam-copy-clipboard" data-clipboard-target="#view-jwt-url"><?php echo __('Copy to clipboard', AAM_KEY); ?></a>
95
+ </label>
96
+ <textarea class="form-control" id="view-jwt-url" readonly rows="5"></textarea>
97
+ <small><?php echo __('Use this URL to authenticate account without the need to enter username/password.', AAM_KEY); ?></small>
98
+ </div>
99
+ </div>
100
+ <div class="modal-footer">
101
+ <button type="button" class="btn btn-default" data-dismiss="modal"><?php echo __('Close', AAM_KEY); ?></button>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+
107
+ <div class="modal fade" id="delete-jwt-modal" tabindex="-1" role="dialog">
108
+ <div class="modal-dialog" role="document">
109
+ <div class="modal-content">
110
+ <div class="modal-header">
111
+ <button type="button" class="close" data-dismiss="modal" aria-label="<?php echo __('Close', AAM_KEY); ?>"><span aria-hidden="true">&times;</span></button>
112
+ <h4 class="modal-title"><?php echo __('Delete JWT Token', AAM_KEY); ?></h4>
113
+ </div>
114
+ <div class="modal-body">
115
+ <p class="alert alert-danger text-larger"><?php echo __('You are about to delete already issued JWT token. Any application or person that has this token, will no longer be able to use it. Please confirm.') ?></p>
116
+ </div>
117
+ <div class="modal-footer">
118
+ <button type="button" class="btn btn-danger" id="jwt-delete-btn"><?php echo __('Delete', AAM_KEY); ?></button>
119
+ <button type="button" class="btn btn-default" data-dismiss="modal"><?php echo __('Close', AAM_KEY); ?></button>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ </div>
126
+ <?php }
Application/Backend/phtml/main/menu.phtml CHANGED
@@ -56,13 +56,13 @@
56
  <?php if ($submenu['id'] == 'index.php') { ?>
57
  <div class="col-xs-12 col-md-6 aam-submenu-item">
58
  <label for="menu-item-<?php echo $i . $j; ?>">
59
- <u><?php echo $submenu['name']; ?></u><small class="aam-menu-capability"><?php echo __('Cap:', AAM_KEY), ' <b>', $submenu['capability']; ?></b></small></label>
60
  <a href="#dashboard-lockout-modal" data-toggle="modal"><i class="icon-help-circled"></i></a>
61
  </div>
62
  <?php } else { ?>
63
  <div class="col-xs-12 col-md-6 aam-submenu-item">
64
  <label for="menu-item-<?php echo $i . $j; ?>">
65
- <u><?php echo $submenu['name']; ?></u>
66
  <small class="aam-menu-capability"><?php echo __('Cap:', AAM_KEY), ' <b>', $submenu['capability']; ?></b></small>
67
  <small class="aam-menu-capability"><?php echo __('ID:', AAM_KEY), ' <b>', $submenu['crc32']; ?></b></small>
68
  </label>
56
  <?php if ($submenu['id'] == 'index.php') { ?>
57
  <div class="col-xs-12 col-md-6 aam-submenu-item">
58
  <label for="menu-item-<?php echo $i . $j; ?>">
59
+ <?php echo $submenu['name']; ?><small class="aam-menu-capability"><?php echo __('Cap:', AAM_KEY), ' <b>', $submenu['capability']; ?></b></small></label>
60
  <a href="#dashboard-lockout-modal" data-toggle="modal"><i class="icon-help-circled"></i></a>
61
  </div>
62
  <?php } else { ?>
63
  <div class="col-xs-12 col-md-6 aam-submenu-item">
64
  <label for="menu-item-<?php echo $i . $j; ?>">
65
+ <?php echo $submenu['name']; ?>
66
  <small class="aam-menu-capability"><?php echo __('Cap:', AAM_KEY), ' <b>', $submenu['capability']; ?></b></small>
67
  <small class="aam-menu-capability"><?php echo __('ID:', AAM_KEY), ' <b>', $submenu['crc32']; ?></b></small>
68
  </label>
Application/Backend/phtml/main/metabox.phtml CHANGED
@@ -61,7 +61,7 @@
61
  <?php foreach ($metaboxes as $metabox) { ?>
62
  <div class="col-xs-12 col-md-6 aam-submenu-item">
63
  <label for="metabox-<?php echo $screen; ?>-<?php echo $metabox['id']; ?>">
64
- <u><?php echo $metabox['title']; ?></u>
65
  <small class="aam-metabox-details"><?php echo __('ID:', AAM_KEY); ?> <b><?php echo crc32($screen . '|' . $metabox['id']); ?></b></small>
66
  </label>
67
  <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->has($screen, $metabox['id']) ? ' checked="checked"' : ''); ?> />
61
  <?php foreach ($metaboxes as $metabox) { ?>
62
  <div class="col-xs-12 col-md-6 aam-submenu-item">
63
  <label for="metabox-<?php echo $screen; ?>-<?php echo $metabox['id']; ?>">
64
+ <?php echo $metabox['title']; ?>
65
  <small class="aam-metabox-details"><?php echo __('ID:', AAM_KEY); ?> <b><?php echo crc32($screen . '|' . $metabox['id']); ?></b></small>
66
  </label>
67
  <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->has($screen, $metabox['id']) ? ' checked="checked"' : ''); ?> />
Application/Backend/phtml/main/toolbar.phtml CHANGED
@@ -51,7 +51,7 @@
51
  <?php foreach($this->getAllChildren($branch) as $child) { ?>
52
  <div class="col-xs-12 aam-submenu-item">
53
  <label for="toolbar-<?php echo $child->id; ?>">
54
- <u><?php echo $this->normalizeTitle($child); ?></u>
55
  <small class="aam-menu-capability"><?php echo __('URI:', AAM_KEY); ?> <b><?php echo str_replace(site_url(), '', $child->href); ?></b></small>
56
  <small class="aam-menu-capability"><?php echo __('ID:', AAM_KEY); ?> <b><?php echo esc_js($child->id); ?></b></small>
57
  </label>
51
  <?php foreach($this->getAllChildren($branch) as $child) { ?>
52
  <div class="col-xs-12 aam-submenu-item">
53
  <label for="toolbar-<?php echo $child->id; ?>">
54
+ <?php echo $this->normalizeTitle($child); ?>
55
  <small class="aam-menu-capability"><?php echo __('URI:', AAM_KEY); ?> <b><?php echo str_replace(site_url(), '', $child->href); ?></b></small>
56
  <small class="aam-menu-capability"><?php echo __('ID:', AAM_KEY); ?> <b><?php echo esc_js($child->id); ?></b></small>
57
  </label>
Application/Backend/phtml/user/multiple-roles.phtml CHANGED
@@ -1,30 +1,32 @@
1
  <?php if (defined('AAM_KEY')) { ?>
2
- <table class="form-table">
3
- <tr>
4
- <th><?php echo esc_html('User Roles', AAM_KEY); ?></th>
5
- <td>
6
- <div class="wp-tab-panel">
7
- <ul>
8
- <?php foreach (get_editable_roles() as $id => $role) { ?>
9
- <li>
10
- <label>
11
- <input type="checkbox" name="aam_user_roles[]" value="<?php echo esc_attr($id); ?>" <?php checked(in_array($id, $user->roles)); ?> />
12
- <?php echo esc_html(translate_user_role($role['name'])); ?>
13
- </label>
14
- </li>
15
- <?php } ?>
16
- </ul>
17
- </div>
18
- </td>
19
- </tr>
20
- </table>
 
21
 
22
- <!-- Remove standard WordPress roles selector-->
23
- <script>
24
- (function($) {
25
- $(document).ready(function(){
26
- $('.user-role-wrap').remove();
27
- });
28
- })(jQuery);
29
- </script>
 
30
  <?php } ?>
1
  <?php if (defined('AAM_KEY')) { ?>
2
+ <?php if ( !IS_PROFILE_PAGE && !is_network_admin() && current_user_can('promote_user', $user->ID)) { ?>
3
+ <table class="form-table">
4
+ <tr>
5
+ <th><?php echo esc_html('User Roles', AAM_KEY); ?></th>
6
+ <td>
7
+ <div class="wp-tab-panel">
8
+ <ul>
9
+ <?php foreach (get_editable_roles() as $id => $role) { ?>
10
+ <li>
11
+ <label>
12
+ <input type="checkbox" name="aam_user_roles[]" value="<?php echo esc_attr($id); ?>" <?php checked(in_array($id, $user->roles)); ?> />
13
+ <?php echo esc_html(translate_user_role($role['name'])); ?>
14
+ </label>
15
+ </li>
16
+ <?php } ?>
17
+ </ul>
18
+ </div>
19
+ </td>
20
+ </tr>
21
+ </table>
22
 
23
+ <!-- Remove standard WordPress roles selector-->
24
+ <script>
25
+ (function($) {
26
+ $(document).ready(function(){
27
+ $('.user-role-wrap').remove();
28
+ });
29
+ })(jQuery);
30
+ </script>
31
+ <?php } ?>
32
  <?php } ?>
Application/Core/Compatibility.php CHANGED
@@ -12,10 +12,31 @@
12
  *
13
  * @package AAM
14
  * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
- * @todo Remove Feb 2018
16
  */
17
  class AAM_Core_Compatibility {
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  /**
20
  * Convert config to the Policy Config
21
  *
@@ -271,4 +292,16 @@ class AAM_Core_Compatibility {
271
  }
272
  }
273
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  }
12
  *
13
  * @package AAM
14
  * @author Vasyl Martyniuk <vasyl@vasyltech.com>
 
15
  */
16
  class AAM_Core_Compatibility {
17
 
18
+ /**
19
+ * Undocumented variable
20
+ *
21
+ * @var [type]
22
+ */
23
+ protected static $instance = null;
24
+
25
+ /**
26
+ * Compatibility between post actions and policy actions
27
+ *
28
+ * @param [type] $action
29
+ * @param [type] $effect
30
+ * @return void
31
+ */
32
+ public static function convertPolicyAction($action, $effect) {
33
+ return array(
34
+ "frontend.{$action}" => $effect,
35
+ "backend.{$action}" => $effect,
36
+ "api.{$action}" => $effect
37
+ );
38
+ }
39
+
40
  /**
41
  * Convert config to the Policy Config
42
  *
292
  }
293
  }
294
 
295
+ /**
296
+ * Undocumented function
297
+ *
298
+ * @return void
299
+ */
300
+ public static function getInstance() {
301
+ if (is_null(self::$instance)) {
302
+ self::$instance = new self;
303
+ }
304
+
305
+ return self::$instance;
306
+ }
307
  }
Application/Core/Gateway.php CHANGED
@@ -123,6 +123,15 @@ final class AAM_Core_Gateway {
123
  );
124
  }
125
 
 
 
 
 
 
 
 
 
 
126
  /**
127
  * Redirect request
128
  *
123
  );
124
  }
125
 
126
+ /**
127
+ * Compatibility manager
128
+ *
129
+ * @return AAM_Core_Compatibility
130
+ */
131
+ public function getCompatibilityManager() {
132
+ return AAM_Core_Compatibility::getInstance();
133
+ }
134
+
135
  /**
136
  * Redirect request
137
  *
Application/Core/Jwt/Auth.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 JWT Authentication handler
12
+ *
13
+ * @package AAM
14
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
+ * @since v5.9.2
16
+ */
17
+ class AAM_Core_Jwt_Auth {
18
+
19
+ /**
20
+ * Authenticate user with username and password
21
+ *
22
+ * @param string $username
23
+ * @param string $password
24
+ *
25
+ * @return stdClass
26
+ *
27
+ * @access public
28
+ */
29
+ public function authenticateWithCredentials($username, $password) {
30
+ $response = array('error' => true);
31
+
32
+ // try to authenticate user with provided credentials
33
+ try {
34
+ $result = AAM_Core_Login::getInstance()->execute(
35
+ array(
36
+ 'user_login' => $username,
37
+ 'user_password' => $password
38
+ ),
39
+ false
40
+ );
41
+ } catch (Exception $ex) {
42
+ $result = array(
43
+ 'status' => 'failure',
44
+ 'reason' => $ex->getMessage(),
45
+ );
46
+ }
47
+
48
+ if ($result['status'] === 'success') { // generate token
49
+ try {
50
+ $issuer = new AAM_Core_Jwt_Issuer();
51
+ $token = $issuer->issueToken(
52
+ array('userId' => $result['user']->ID, 'revocable' => true)
53
+ );
54
+
55
+ $response = array(
56
+ 'jwt' => $token,
57
+ 'user' => $result['user']
58
+ );
59
+ } catch (Exception $ex) {
60
+ $response['reason'] = $ex->getMessage();
61
+ }
62
+ } else {
63
+ $response['reason'] = $result['reason'];
64
+ }
65
+
66
+ return (object) $response;
67
+ }
68
+
69
+ }
Application/Core/Jwt/Issuer.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 JWT Issuer
12
+ *
13
+ * @package AAM
14
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
+ * @since v5.9.2
16
+ */
17
+ class AAM_Core_Jwt_Issuer {
18
+
19
+ /**
20
+ * Just a local cache
21
+ *
22
+ * @var array
23
+ */
24
+ protected $cache = array();
25
+
26
+ /**
27
+ * Validate JWT token
28
+ *
29
+ * @param string $token
30
+ *
31
+ * @return stdClass
32
+ *
33
+ * @access public
34
+ */
35
+ public function validateToken($token) {
36
+ try {
37
+ $headers = $this->extractTokenHeaders($token);
38
+
39
+ if (strpos($headers->alg, 'RS') === 0) {
40
+ $filepath = AAM_Core_Config::get('authentication.jwt.publicKeyPath');
41
+ $key = (is_readable($filepath) ? file_get_contents($filepath) : null);
42
+ } else {
43
+ $key = AAM_Core_Config::get('authentication.jwt.secret', SECURE_AUTH_KEY);
44
+ }
45
+
46
+ // Step #1. Check if token is actually valid
47
+ $response = Firebase\JWT\JWT::decode(
48
+ $token, $key, array_keys(Firebase\JWT\JWT::$supported_algs)
49
+ );
50
+
51
+ // Step #2. If token is "revocable", make sure that claimed user still has
52
+ // the token in the meta
53
+ if (!empty($response->revocable)) {
54
+ $tokens = $this->getUsersTokens($response->userId);
55
+ if (!in_array($token, $tokens, true)) {
56
+ throw new Exception(__('Token has been revoked', AAM_KEY));
57
+ }
58
+ }
59
+
60
+ $response->status = 'valid';
61
+ } catch (Exception $ex) {
62
+ $response = array_merge(array(
63
+ 'status' => 'invalid',
64
+ 'reason' => $ex->getMessage()
65
+ ), (array) $this->extractTokenClaims($token));
66
+ }
67
+
68
+ return (object) $response;
69
+ }
70
+
71
+ /**
72
+ * Issue JWT token
73
+ *
74
+ * @param array $args
75
+ * @param string $expires
76
+ *
77
+ * @return stdClass
78
+ *
79
+ * @access public
80
+ * @throws Exception
81
+ */
82
+ public function issueToken($args = array(), $expires = null) {
83
+ if (!empty($expires)) {
84
+ $time = DateTime::createFromFormat('m/d/Y, H:i O', $expires);
85
+ } else {
86
+ $time = new DateTime(
87
+ AAM_Core_Config::get('authentication.jwt.expires', '+24 hours')
88
+ );
89
+ }
90
+
91
+ $claims = apply_filters(
92
+ 'aam-jwt-claims-filter',
93
+ array_merge(
94
+ array(
95
+ "iat" => time(),
96
+ 'iss' => get_site_url(),
97
+ 'exp' => $time->format('m/d/Y, H:i O'),
98
+ 'jti' => $this->generateUuid()
99
+ ),
100
+ $args
101
+ )
102
+ );
103
+
104
+ // Determine algorithm and key
105
+ $attr = $this->getJWTSigningAttributes();
106
+
107
+ return (object) array(
108
+ 'token' => Firebase\JWT\JWT::encode($claims, $attr->key, $attr->alg),
109
+ 'claims' => $claims
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Extract tokens headers
115
+ *
116
+ * @param string $token
117
+ *
118
+ * @return object
119
+ *
120
+ * @access public
121
+ */
122
+ public static function extractTokenHeaders($token) {
123
+ $parts = explode('.', $token);
124
+
125
+ try {
126
+ $headers = Firebase\JWT\JWT::jsonDecode(
127
+ Firebase\JWT\JWT::urlsafeB64Decode($parts[0])
128
+ );
129
+ } catch (Exception $ex) {
130
+ $headers = new stdClass();
131
+ }
132
+
133
+ return $headers;
134
+ }
135
+
136
+ /**
137
+ * Extract token claims
138
+ *
139
+ * @param string $token
140
+ *
141
+ * @return object
142
+ *
143
+ * @access public
144
+ */
145
+ public static function extractTokenClaims($token) {
146
+ $parts = explode('.', $token);
147
+
148
+ try {
149
+ $claims = Firebase\JWT\JWT::jsonDecode(
150
+ Firebase\JWT\JWT::urlsafeB64Decode($parts[1])
151
+ );
152
+ } catch (Exception $ex) {
153
+ $claims = new stdClass();
154
+ }
155
+
156
+ return $claims;
157
+ }
158
+
159
+ /**
160
+ * Get JWT attributes for signing
161
+ *
162
+ * @return object
163
+ *
164
+ * @access protected
165
+ */
166
+ protected function getJWTSigningAttributes() {
167
+ $alg = strtoupper(
168
+ AAM_Core_Config::get('authentication.jwt.algorithm', 'HS256')
169
+ );
170
+
171
+ if (strpos($alg, 'RS') === 0) {
172
+ $filepath = AAM_Core_Config::get('authentication.jwt.privateKeyPath');
173
+ $key = (is_readable($filepath) ? file_get_contents($filepath) : null);
174
+ } else {
175
+ $key = AAM_Core_Config::get('authentication.jwt.secret', SECURE_AUTH_KEY);
176
+ }
177
+
178
+ return (object) array(
179
+ 'alg' => $alg,
180
+ 'key' => $key
181
+ );
182
+ }
183
+
184
+ /**
185
+ * Get user's tokens
186
+ *
187
+ * @param int $userId
188
+ *
189
+ * @return array
190
+ *
191
+ * @access protected
192
+ */
193
+ protected function getUsersTokens($userId) {
194
+ if (!isset($this->cache[$userId])) {
195
+ $list = get_user_meta($userId, 'aam-jwt');
196
+ $this->cache[$userId] = is_array($list) ? $list : array();
197
+ }
198
+
199
+ return $this->cache[$userId];
200
+ }
201
+
202
+ /**
203
+ * Generate random uuid
204
+ *
205
+ * @return string
206
+ */
207
+ protected function generateUuid() {
208
+ return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
209
+ // 32 bits for "time_low"
210
+ mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
211
+
212
+ // 16 bits for "time_mid"
213
+ mt_rand( 0, 0xffff ),
214
+
215
+ // 16 bits for "time_hi_and_version",
216
+ // four most significant bits holds version number 4
217
+ mt_rand( 0, 0x0fff ) | 0x4000,
218
+
219
+ // 16 bits, 8 bits for "clk_seq_hi_res",
220
+ // 8 bits for "clk_seq_low",
221
+ // two most significant bits holds zero and one for variant DCE1.1
222
+ mt_rand( 0, 0x3fff ) | 0x8000,
223
+
224
+ // 48 bits for "node"
225
+ mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
226
+ );
227
+ }
228
+
229
+ }
Application/Core/Jwt/Manager.php ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 JWT Manager
12
+ *
13
+ * @package AAM
14
+ * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
+ * @since v5.9.2
16
+ */
17
+ class AAM_Core_Jwt_Manager {
18
+
19
+ /**
20
+ * Single instance of itself
21
+ *
22
+ * @var AAM_Core_Jwt_Manager
23
+ *
24
+ * @access protected
25
+ * @static
26
+ */
27
+ protected static $instance = null;
28
+
29
+ /**
30
+ * Constructor
31
+ *
32
+ * @return void
33
+ *
34
+ * @access protected
35
+ */
36
+ protected function __construct() {
37
+ //register API endpoint
38
+ add_action('rest_api_init', array($this, 'registerAPI'));
39
+
40
+ //register authentication hook
41
+ add_filter('determine_current_user', array($this, 'determineUser'), 999);
42
+ }
43
+
44
+ /**
45
+ * Register APIs
46
+ *
47
+ * @return void
48
+ *
49
+ * @access public
50
+ */
51
+ public function registerAPI() {
52
+ // Authenticate user
53
+ register_rest_route('aam/v1', '/authenticate', array(
54
+ 'methods' => 'POST',
55
+ 'callback' => array($this, 'issueToken'),
56
+ 'args' => array(
57
+ 'username' => array(
58
+ 'description' => __('Valid username.', AAM_KEY),
59
+ 'type' => 'string',
60
+ ),
61
+ 'password' => array(
62
+ 'description' => __('Valid password.', AAM_KEY),
63
+ 'type' => 'string',
64
+ )
65
+ ),
66
+ ));
67
+
68
+ // Validate JWT token
69
+ register_rest_route('aam/v1', '/validate-jwt', array(
70
+ 'methods' => 'POST',
71
+ 'callback' => array($this, 'validateToken'),
72
+ 'args' => array(
73
+ 'jwt' => array(
74
+ 'description' => __('JWT token.', AAM_KEY),
75
+ 'type' => 'string',
76
+ )
77
+ ),
78
+ ));
79
+ }
80
+
81
+ /**
82
+ * Authenticate user
83
+ *
84
+ * @param WP_REST_Request $request
85
+ *
86
+ * @return WP_REST_Response
87
+ *
88
+ * @access public
89
+ */
90
+ public function issueToken(WP_REST_Request $request) {
91
+ $username = $request->get_param('username');
92
+ $password = $request->get_param('password');
93
+ $response = new WP_REST_Response();
94
+
95
+ $auth = new AAM_Core_Jwt_Auth();
96
+ $result = $auth->authenticateWithCredentials($username, $password);
97
+
98
+ if (!empty($result->error)) {
99
+ $response->status = 403;
100
+ $response->data = new WP_Error(
101
+ 'rest_jwt_auth_failure',
102
+ strip_tags($result->reason)
103
+ );
104
+ } else {
105
+ $response->status = 200;
106
+ $response->data = array(
107
+ 'token' => $result->jwt->token,
108
+ 'token_expires' => $result->jwt->claims['exp'],
109
+ 'user' => $result->user
110
+ );
111
+
112
+ // Finally register token so it can be revoked
113
+ $this->registerToken($result->user->ID, $result->jwt->token);
114
+ }
115
+
116
+ return apply_filters('aam-jwt-response-filter', $response);
117
+ }
118
+
119
+ /**
120
+ * Validate JWT token
121
+ *
122
+ * @param WP_REST_Request $request
123
+ *
124
+ * @return WP_REST_Response
125
+ *
126
+ * @access public
127
+ */
128
+ public function validateToken(WP_REST_Request $request) {
129
+ $jwt = $request->get_param('jwt');
130
+ $issuer = new AAM_Core_Jwt_Issuer();
131
+ $response = new WP_REST_Response();
132
+
133
+ $result = $issuer->validateToken($jwt);
134
+
135
+ if ($result->status === 'valid') {
136
+ $response->status = 200;
137
+ $response->data = $result;
138
+ } else {
139
+ $response->status = 400;
140
+ $response->data = new WP_Error(
141
+ 'rest_jwt_validation_failure',
142
+ $result->reason
143
+ );
144
+ }
145
+
146
+ return $response;
147
+ }
148
+
149
+ /**
150
+ * Determine current user by JWT
151
+ *
152
+ * @param int $userId
153
+ *
154
+ * @return int
155
+ *
156
+ * @access public
157
+ */
158
+ public function determineUser($userId) {
159
+ if (empty($userId)) {
160
+ $token = $this->extractJwt();
161
+
162
+ if (!empty($token)) {
163
+ $issuer = new AAM_Core_Jwt_Issuer();
164
+ $result = $issuer->validateToken($token->jwt);
165
+
166
+ if ($result->status === 'valid') {
167
+ $userId = $result->userId;
168
+ $this->possiblyLoginUser($token->method, $result);
169
+ }
170
+ }
171
+ }
172
+
173
+ return $userId;
174
+ }
175
+
176
+ /**
177
+ * Register JWT token to user's registry
178
+ *
179
+ * @param int $userId
180
+ * @param string $token
181
+ * @param array $args
182
+ *
183
+ * @return bool
184
+ *
185
+ * @access public
186
+ */
187
+ public function registerToken($userId, $token) {
188
+ $registry = $this->getTokenRegistry($userId);
189
+ $limit = AAM_Core_Config::get('authentication.jwt.registryLimit', 10);
190
+
191
+ // Make sure that we do not overload the user meta
192
+ if (count($registry) >= $limit) {
193
+ $this->revokeToken($userId, array_shift($registry));
194
+ }
195
+
196
+ // Save token
197
+ return add_user_meta($userId, 'aam-jwt', $token);
198
+ }
199
+
200
+ /**
201
+ * Revoke JWT token
202
+ *
203
+ * @param int $userId
204
+ * @param string $token
205
+ *
206
+ * @return bool
207
+ *
208
+ * @access public
209
+ */
210
+ public function revokeToken($userId, $token) {
211
+ $result = false;
212
+ $registry = $this->getTokenRegistry($userId);
213
+
214
+ if (in_array($token, $registry, true)) {
215
+ $result = delete_user_meta($userId, 'aam-jwt', $token);
216
+ }
217
+
218
+ return $result;
219
+ }
220
+
221
+ /**
222
+ * Get JWT token registry
223
+ *
224
+ * @param int $userId
225
+ *
226
+ * @return array
227
+ *
228
+ * @access public
229
+ */
230
+ public function getTokenRegistry($userId) {
231
+ $registry = get_user_meta($userId, 'aam-jwt', false);
232
+
233
+ return (!empty($registry) ? $registry : array());
234
+ }
235
+
236
+ /**
237
+ * Extract JWT token from the request
238
+ *
239
+ * Based on the `authentication.jwt.container` setting, parse HTTP request and
240
+ * try to extract the JWT token
241
+ *
242
+ * @return object|null
243
+ *
244
+ * @access protected
245
+ */
246
+ protected function extractJwt() {
247
+ $container = explode(',', AAM_Core_Config::get(
248
+ 'authentication.jwt.container', 'header,post,query,cookie'
249
+ ));
250
+
251
+ $jwt = null;
252
+
253
+ foreach($container as $method) {
254
+ switch(strtolower(trim($method))) {
255
+ case 'header':
256
+ $jwt = AAM_Core_Request::server('HTTP_AUTHENTICATION');
257
+ break;
258
+
259
+ case 'cookie':
260
+ $jwt = AAM_Core_Request::cookie('aam-jwt');
261
+ break;
262
+
263
+ case 'query':
264
+ $jwt = AAM_Core_Request::get('aam-jwt');
265
+ break;
266
+
267
+ case 'post':
268
+ $jwt = AAM_Core_Request::post('aam-jwt');
269
+ break;
270
+
271
+ default:
272
+ $jwt = apply_filters('aam-get-jwt-filter', null, $method);
273
+ break;
274
+ }
275
+
276
+ if (!is_null($jwt)) {
277
+ break;
278
+ }
279
+ }
280
+
281
+ if (!empty($jwt)) {
282
+ $response = (object) array(
283
+ 'jwt' => preg_replace('/^Bearer /', '', $jwt),
284
+ 'method' => $method
285
+ );
286
+ } else {
287
+ $response = null;
288
+ }
289
+
290
+ return $response;
291
+ }
292
+
293
+ /**
294
+ * Also login user if HTTP request is get and JWT was extracted from query params
295
+ *
296
+ * @param string $container
297
+ * @param object $claims
298
+ *
299
+ * @return void
300
+ *
301
+ * @access protected
302
+ */
303
+ protected function possiblyLoginUser($container, $claims) {
304
+ // Also login user if REQUEST_METHOD is GET
305
+ $method = AAM_Core_Request::server('REQUEST_METHOD');
306
+
307
+ if ($container === 'query' && ($method === 'GET')) {
308
+ $exp = get_user_meta($claims->userId, 'aam_user_expiration', true);
309
+
310
+ // Do it only once
311
+ if (empty($exp)) {
312
+ wp_set_current_user($claims->userId);
313
+ wp_set_auth_cookie($claims->userId);
314
+
315
+ // TODO: Remove June 2020
316
+ $exp = (is_numeric($claims->exp) ? date('m/d/Y, H:i O', $claims->exp) : $claims->exp);
317
+
318
+ update_user_meta(
319
+ $claims->userId,
320
+ 'aam_user_expiration',
321
+ $exp . '|logout|'
322
+ );
323
+ do_action('wp_login', '', wp_get_current_user());
324
+ }
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Get single instance of itself
330
+ *
331
+ * @return AAM_Core_Jwt_Manager
332
+ *
333
+ * @access public
334
+ * @static
335
+ */
336
+ public static function getInstance() {
337
+ if (is_null(self::$instance)) {
338
+ self::$instance = new self;
339
+ }
340
+
341
+ return self::$instance;
342
+ }
343
+
344
+ /**
345
+ * Bootstrap AAM JWT Manager
346
+ *
347
+ * @return AAM_Core_Jwt_Manager
348
+ *
349
+ * @access public
350
+ * @static
351
+ */
352
+ public static function bootstrap() {
353
+ return self::getInstance();
354
+ }
355
+
356
+ }
Application/Core/JwtAuth.php DELETED
@@ -1,347 +0,0 @@
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 JWT Authentication
12
- *
13
- * @package AAM
14
- * @author Vasyl Martyniuk <vasyl@vasyltech.com>
15
- */
16
- class AAM_Core_JwtAuth {
17
-
18
- /**
19
- * Single instance of itself
20
- *
21
- * @var AAM_Core_JwtAuth
22
- *
23
- * @access protected
24
- * @static
25
- */
26
- protected static $instance = null;
27
-
28
- /**
29
- * Constructor
30
- *
31
- * @return void
32
- *
33
- * @access protected
34
- */
35
- protected function __construct() {
36
- //register API endpoint
37
- add_action('rest_api_init', array($this, 'registerAPI'));
38
-
39
- //register authentication hook
40
- add_filter('determine_current_user', array($this, 'determineCurrentUser'), 999);
41
- }
42
-
43
- /**
44
- * Register APIs
45
- *
46
- * @return void
47
- *
48
- * @access public
49
- */
50
- public function registerAPI() {
51
- // Authenticate user
52
- register_rest_route('aam/v1', '/authenticate', array(
53
- 'methods' => 'POST',
54
- 'callback' => array($this, 'authenticate'),
55
- 'args' => array(
56
- 'username' => array(
57
- 'description' => __('Valid username.', AAM_KEY),
58
- 'type' => 'string',
59
- ),
60
- 'password' => array(
61
- 'description' => __('Valid password.', AAM_KEY),
62
- 'type' => 'string',
63
- )
64
- ),
65
- ));
66
-
67
- // Validate JWT token
68
- register_rest_route('aam/v1', '/validate-jwt', array(
69
- 'methods' => 'POST',
70
- 'callback' => array($this, 'validateJWT'),
71
- 'args' => array(
72
- 'jwt' => array(
73
- 'description' => __('JWT token.', AAM_KEY),
74
- 'type' => 'string',
75
- )
76
- ),
77
- ));
78
- }
79
-
80
- /**
81
- * Authenticate user
82
- *
83
- * @param WP_REST_Request $request
84
- *
85
- * @return WP_REST_Response
86
- *
87
- * @access public
88
- */
89
- public function authenticate(WP_REST_Request $request) {
90
- $username = $request->get_param('username');
91
- $password = $request->get_param('password');
92
-
93
- // try to authenticate user
94
- $result = AAM_Core_Login::getInstance()->execute(array(
95
- 'user_login' => $username,
96
- 'user_password' => $password
97
- ), false);
98
-
99
- $response = new WP_REST_Response();
100
-
101
- if ($result['status'] === 'success') { // generate token
102
- try {
103
- $token = $this->issueJWT($result['user']->ID);
104
-
105
- $response->status = 200;
106
- $response->data = array(
107
- 'token' => $token->token,
108
- 'token_expires' => $token->claims['exp'],
109
- 'user' => $result['user']
110
- );
111
- } catch (Exception $ex) {
112
- $response->status = 400;
113
- $response->data = new WP_Error(
114
- 'rest_jwt_empty_secret_key',
115
- $ex->getMessage()
116
- );
117
- }
118
- } else {
119
- $response->data = $result['reason'];
120
- $response->status = 403;
121
- }
122
-
123
- return apply_filters('aam-jwt-response-filter', $response);
124
- }
125
-
126
- /**
127
- *
128
- * @param WP_REST_Request $request
129
- */
130
- public function validateJWT(WP_REST_Request $request) {
131
- $jwt = $request->get_param('jwt');
132
- $key = AAM_Core_Config::get('authentication.jwt.secret', SECURE_AUTH_KEY);
133
-
134
- $response = new WP_REST_Response(array(
135
- 'status' => 'invalid'
136
- ), 400);
137
-
138
- if (!empty($jwt)) {
139
- try {
140
- $claims = Firebase\JWT\JWT::decode(
141
- $jwt, $key, array_keys(Firebase\JWT\JWT::$supported_algs)
142
- );
143
-
144
- $response->status = 200;
145
- $response->data = array(
146
- 'status' => 'valid',
147
- 'token_expires' => $claims->exp
148
- );
149
- } catch (Exception $ex) {
150
- $response->data['reason'] = $ex->getMessage();
151
- }
152
- }
153
-
154
- return $response;
155
- }
156
-
157
- /**
158
- * Generate JWT token
159
- *
160
- * @param int $userId
161
- *
162
- * @return stdClass
163
- *
164
- * @access public
165
- * @throws Exception
166
- */
167
- public function issueJWT($userId, $container = 'header') {
168
- $container = explode(
169
- ',', AAM_Core_Config::get('authentication.jwt.container', $container)
170
- );
171
-
172
- $token = $this->generateJWT($userId);
173
-
174
- if (in_array('cookie', $container, true)) {
175
- setcookie(
176
- 'aam-jwt',
177
- $token->token,
178
- $token->claims['exp'],
179
- '/',
180
- parse_url(get_bloginfo('url'), PHP_URL_HOST),
181
- is_ssl(),
182
- AAM_Core_Config::get('authentication.jwt.cookie.httpOnly', false)
183
- );
184
- }
185
-
186
- return $token;
187
- }
188
-
189
- /**
190
- * Generate the token
191
- *
192
- * @param int $userId
193
- * @param int $expires
194
- *
195
- * @return stdObject
196
- *
197
- * @access public
198
- * @throws Exception
199
- */
200
- public static function generateJWT($userId, $expires = null) {
201
- $key = AAM_Core_Config::get('authentication.jwt.secret', SECURE_AUTH_KEY);
202
- $expire = AAM_Core_Config::get('authentication.jwt.expires', $expires);
203
- $alg = AAM_Core_Config::get('authentication.jwt.algorithm', 'HS256');
204
-
205
- if (!empty($expire)) {
206
- $time = DateTime::createFromFormat('m/d/Y, H:i O', $expires);
207
- } else {
208
- $time = new DateTime('+24 hours');
209
- }
210
-
211
- if ($key) {
212
- $claims = apply_filters('aam-jwt-claims-filter', array(
213
- "iat" => time(),
214
- 'exp' => $time->format('U'),
215
- 'userId' => $userId
216
- ));
217
-
218
- $token = Firebase\JWT\JWT::encode($claims, $key, $alg);
219
- } else {
220
- Throw new Exception(
221
- __('JWT Authentication is enabled but secret key is not defined', AAM_KEY)
222
- );
223
- }
224
-
225
- return (object) array(
226
- 'token' => $token,
227
- 'claims' => $claims
228
- );
229
- }
230
-
231
- /**
232
- *
233
- * @param type $result
234
- */
235
- public function determineCurrentUser($result) {
236
- $token = $this->extractJwt();
237
- $key = AAM_Core_Config::get('authentication.jwt.secret', SECURE_AUTH_KEY);
238
-
239
- if (!empty($token['jwt'])) {
240
- try {
241
- $claims = Firebase\JWT\JWT::decode(
242
- $token['jwt'], $key, array_keys(Firebase\JWT\JWT::$supported_algs)
243
- );
244
-
245
- if (isset($claims->userId)) {
246
- $result = $claims->userId;
247
-
248
- // Also login user if REQUEST_METHOD is GET
249
- if ($token['method'] === 'query'
250
- && AAM_Core_Request::server('REQUEST_METHOD') === 'GET') {
251
- wp_set_current_user($claims->userId);
252
- wp_set_auth_cookie($claims->userId);
253
-
254
- $exp = get_user_meta($claims->userId, 'aam_user_expiration', true);
255
- if (empty($exp)) {
256
- update_user_meta(
257
- $claims->userId,
258
- 'aam_user_expiration',
259
- date('m/d/Y, H:i O', $claims->exp) . '|logout|'
260
- );
261
- }
262
-
263
- do_action('wp_login', '', wp_get_current_user());
264
- }
265
- }
266
- } catch (Exception $ex) {
267
- // Do nothing
268
- }
269
- }
270
-
271
- return $result;
272
- }
273
-
274
- /**
275
- *
276
- * @return type
277
- */
278
- protected function extractJwt() {
279
- $container = explode(',', AAM_Core_Config::get(
280
- 'authentication.jwt.container', 'header,post,query,cookie'
281
- ));
282
-
283
- $jwt = null;
284
-
285
- foreach($container as $method) {
286
- switch(strtolower(trim($method))) {
287
- case 'header':
288
- $jwt = AAM_Core_Request::server('HTTP_AUTHENTICATION');
289
- break;
290
-
291
- case 'cookie':
292
- $jwt = AAM_Core_Request::cookie('aam-jwt');
293
- break;
294
-
295
- case 'query':
296
- $jwt = AAM_Core_Request::get('aam-jwt');
297
- break;
298
-
299
- case 'post':
300
- $jwt = AAM_Core_Request::post('aam-jwt');
301
- break;
302
-
303
- default:
304
- $jwt = apply_filters('aam-get-jwt-filter', null, $method);
305
- break;
306
- }
307
-
308
- if (!is_null($jwt)) {
309
- break;
310
- }
311
- }
312
-
313
- return array(
314
- 'jwt' => preg_replace('/^Bearer /', '', $jwt),
315
- 'method' => $method
316
- );
317
- }
318
-
319
- /**
320
- * Get single instance of itself
321
- *
322
- * @return AAM_Core_JwtAuth
323
- *
324
- * @access public
325
- * @static
326
- */
327
- public static function getInstance() {
328
- if (is_null(self::$instance)) {
329
- self::$instance = new self;
330
- }
331
-
332
- return self::$instance;
333
- }
334
-
335
- /**
336
- * Bootstrap AAM JWT Authentication feature
337
- *
338
- * @return AAM_Core_JwtAuth
339
- *
340
- * @access public
341
- * @static
342
- */
343
- public static function bootstrap() {
344
- return self::getInstance();
345
- }
346
-
347
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Application/Core/Login.php CHANGED
@@ -92,7 +92,17 @@ class AAM_Core_Login {
92
  }
93
 
94
  if (AAM::api()->getConfig('core.settings.setJwtCookieAfterLogin', false)) {
95
- AAM_Core_JwtAuth::getInstance()->issueJWT($user->ID, 'cookie');
 
 
 
 
 
 
 
 
 
 
96
  }
97
 
98
  if ($this->aamLogin === false) {
@@ -252,8 +262,14 @@ class AAM_Core_Login {
252
  }
253
 
254
  if ($attempts >= AAM_Core_Config::get('security.login.attempts', 20)) {
255
- wp_safe_redirect(site_url('index.php'));
256
- exit;
 
 
 
 
 
 
257
  } else {
258
  set_transient('aam_login_attempts', $attempts, $timeout);
259
  }
@@ -270,7 +286,7 @@ class AAM_Core_Login {
270
  $this->aamLogin = true;
271
 
272
  if ($set_cookie === false) {
273
- add_filter('send_auth_cookies', function() { return false; });
274
  }
275
 
276
  $response = array(
92
  }
93
 
94
  if (AAM::api()->getConfig('core.settings.setJwtCookieAfterLogin', false)) {
95
+ $issuer = new AAM_Core_Jwt_Issuer();
96
+ $token = $issuer->issueToken(array('userId' => $user->ID));
97
+ setcookie(
98
+ 'aam-jwt',
99
+ $token->token,
100
+ $token->claims['exp'],
101
+ '/',
102
+ parse_url(get_bloginfo('url'), PHP_URL_HOST),
103
+ is_ssl(),
104
+ AAM_Core_Config::get('authentication.jwt.cookie.httpOnly', false)
105
+ );
106
  }
107
 
108
  if ($this->aamLogin === false) {
262
  }
263
 
264
  if ($attempts >= AAM_Core_Config::get('security.login.attempts', 20)) {
265
+ if (AAM_Core_Api_Area::isAPI()) {
266
+ throw new Exception(
267
+ 'Exceeded maximum number for authentication attempts. Please try later again.'
268
+ );
269
+ } else {
270
+ wp_safe_redirect(site_url('index.php'));
271
+ exit;
272
+ }
273
  } else {
274
  set_transient('aam_login_attempts', $attempts, $timeout);
275
  }
286
  $this->aamLogin = true;
287
 
288
  if ($set_cookie === false) {
289
+ add_filter('send_auth_cookies', '__return_false');
290
  }
291
 
292
  $response = array(
Application/Core/Object/Post.php CHANGED
@@ -105,9 +105,14 @@ class AAM_Core_Object_Post extends AAM_Core_Object {
105
  // TODO: Prepare better conversion from policy Action to AAM
106
  // post & term action. For example listToOthers -> list_others
107
  $chunks = explode(':', $key);
108
- $option["frontend.{$chunks[3]}"] = $stm['Effect'] === 'deny';
109
- $option["backend.{$chunks[3]}"] = $stm['Effect'] === 'deny';
110
- $option["api.{$chunks[3]}"] = $stm['Effect'] === 'deny';
 
 
 
 
 
111
  }
112
  }
113
 
105
  // TODO: Prepare better conversion from policy Action to AAM
106
  // post & term action. For example listToOthers -> list_others
107
  $chunks = explode(':', $key);
108
+
109
+ $option = array_merge(
110
+ $option,
111
+ AAM_Core_Compatibility::convertPolicyAction(
112
+ (isset($chunks[3]) ? $chunks[3] : 'read'),
113
+ $stm['Effect'] === 'deny'
114
+ )
115
+ );
116
  }
117
  }
118
 
Application/Core/Object/Route.php CHANGED
@@ -34,14 +34,15 @@ class AAM_Core_Object_Route extends AAM_Core_Object {
34
  if (!empty($option)) {
35
  $this->setOverwritten(true);
36
  }
37
-
38
  // Load settings from Access & Security Policy
39
  if (empty($option)) {
40
  $stms = AAM_Core_Policy_Factory::get($subject)->find("/^Route:/i");
41
-
42
  foreach($stms as $key => $stm) {
43
  $chunks = explode(':', $key);
44
- $id = "{$chunks[1]}|{$chunks[2]}|{$chunks[3]}";
 
45
 
46
  $option[$id] = ($stm['Effect'] === 'deny' ? 1 : 0);
47
  }
@@ -68,7 +69,7 @@ class AAM_Core_Object_Route extends AAM_Core_Object {
68
  public function has($type, $route, $method = 'POST') {
69
  $options = $this->getOption();
70
  $id = strtolower("{$type}|{$route}|{$method}");
71
-
72
  return !empty($options[$id]);
73
  }
74
 
34
  if (!empty($option)) {
35
  $this->setOverwritten(true);
36
  }
37
+
38
  // Load settings from Access & Security Policy
39
  if (empty($option)) {
40
  $stms = AAM_Core_Policy_Factory::get($subject)->find("/^Route:/i");
41
+
42
  foreach($stms as $key => $stm) {
43
  $chunks = explode(':', $key);
44
+ $method = (isset($chunks[3]) ? $chunks[3] : 'post');
45
+ $id = "{$chunks[1]}|{$chunks[2]}|{$method}";
46
 
47
  $option[$id] = ($stm['Effect'] === 'deny' ? 1 : 0);
48
  }
69
  public function has($type, $route, $method = 'POST') {
70
  $options = $this->getOption();
71
  $id = strtolower("{$type}|{$route}|{$method}");
72
+
73
  return !empty($options[$id]);
74
  }
75
 
Application/Core/Policy/Condition.php CHANGED
@@ -70,18 +70,27 @@ final class AAM_Core_Policy_Condition {
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])) {
78
  $callback = array($this, $this->map[$type]);
79
- $result = $result && call_user_func($callback, $conditions, $args);
 
 
 
 
 
 
 
 
 
80
  } else {
81
  $result = false;
82
  }
83
  }
84
-
85
  return $result;
86
  }
87
 
@@ -99,10 +108,17 @@ final class AAM_Core_Policy_Condition {
99
  $result = false;
100
 
101
  foreach($this->prepareConditions($conditions, $args) as $condition) {
102
- foreach((array)$condition['right'] as $subset) {
 
 
 
 
 
 
 
103
  $min = (is_array($subset) ? array_shift($subset) : $subset);
104
  $max = (is_array($subset) ? end($subset) : $subset);
105
-
106
  $result = $result || ($condition['left'] >= $min && $condition['left'] <= $max);
107
  }
108
  }
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])) {
78
  $callback = array($this, $this->map[$type]);
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
 
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
  }
Application/Core/Policy/Token.php CHANGED
@@ -105,10 +105,12 @@ final class AAM_Core_Policy_Token {
105
  break;
106
 
107
  case 'authenticated':
 
108
  $value = $user->isVisitor() ? false : true;
109
  break;
110
 
111
  case 'capabilities':
 
112
  $value = array();
113
  foreach($user->allcaps as $cap => $effect) {
114
  if (!empty($effect)) {
105
  break;
106
 
107
  case 'authenticated':
108
+ case 'isauthenticated':
109
  $value = $user->isVisitor() ? false : true;
110
  break;
111
 
112
  case 'capabilities':
113
+ case 'caps':
114
  $value = array();
115
  foreach($user->allcaps as $cap => $effect) {
116
  if (!empty($effect)) {
Application/Core/Subject/User.php CHANGED
@@ -19,6 +19,11 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
19
  * Subject UID: USER
20
  */
21
  const UID = 'user';
 
 
 
 
 
22
 
23
  /**
24
  * AAM Capability Key
@@ -55,6 +60,15 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
55
  * @access protected
56
  */
57
  protected $maxLevel = null;
 
 
 
 
 
 
 
 
 
58
 
59
  /**
60
  * Constructor
@@ -176,20 +190,103 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
176
  }
177
 
178
  /**
 
 
 
179
  *
 
180
  */
181
- public function validateUserStatus() {
182
- //check if user is blocked
183
- if ($this->user_status === 1) {
184
- wp_logout();
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
- //check if user is expired
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  $expired = get_user_meta($this->ID, 'aam_user_expiration', true);
189
  if (!empty($expired)) {
190
  $parts = explode('|', $expired);
191
 
192
- // Set time
193
  // TODO: Remove in Jan 2020
194
  if (preg_match('/^[\d]{4}-/', $parts[0])) {
195
  $expires = DateTime::createFromFormat('Y-m-d H:i:s', $parts[0]);
@@ -197,27 +294,67 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
197
  $expires = DateTime::createFromFormat('m/d/Y, H:i O', $parts[0]);
198
  }
199
 
200
- $compare = new DateTime();
201
- //TODO - PHP Warning: DateTime::setTimezone(): Can only do this for zones with ID for now in
202
- @$compare->setTimezone($expires->getTimezone());
203
-
204
- if ($expires->getTimestamp() <= $compare->getTimestamp()) {
205
- $this->triggerExpiredUserAction($parts);
 
 
 
 
 
 
206
  }
207
  }
208
 
209
- //check if user's role expired
210
- $roleExpire = get_user_option('aam-role-expires', $this->ID);
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  if ($roleExpire && ($roleExpire <= time())) {
212
- $this->restoreRoles();
213
- }
 
 
 
 
 
214
 
215
- //finally check if session tracking is enabled and if so, check if used
216
- //has to be logged out
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  if (AAM::api()->getConfig('core.session.tracking', false)) {
218
  $ttl = AAM::api()->getConfig(
219
- "core.session.user.{$this->ID}.ttl",
220
- AAM::api()->getConfig("core.session.user.ttl", null)
221
  );
222
 
223
  if (!empty($ttl)) {
@@ -227,51 +364,14 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
227
 
228
  if ($timestamp && ($timestamp + intval($ttl) <= time())) {
229
  delete_user_meta($this->ID, 'aam-authenticated-timestamp');
230
- wp_logout();
231
  }
232
  }
233
  }
234
- }
235
-
236
- /**
237
- * Expire user
238
- *
239
- * @param array $config
240
- *
241
- * @return void
242
- *
243
- * @access
244
- */
245
- public function triggerExpiredUserAction($config) {
246
- switch($config[1]) {
247
- case 'lock':
248
- $this->block();
249
- break;
250
-
251
- case 'logout':
252
- wp_logout();
253
- break;
254
-
255
- case 'change-role':
256
- if (AAM_Core_API::getRoles()->is_role($config[2])) {
257
- $this->getSubject()->set_role($config[2]);
258
- delete_user_option($this->getSubject()->ID, 'aam_user_expiration');
259
- }
260
- break;
261
-
262
- case 'delete':
263
- require_once(ABSPATH . 'wp-admin/includes/user.php' );
264
- wp_delete_user(
265
- $this->getId(), AAM_Core_Config::get('core.reasign.ownership.user')
266
- );
267
- wp_logout();
268
- break;
269
 
270
- default:
271
- break;
272
- }
273
  }
274
-
275
  /**
276
  * Block User
277
  *
@@ -298,27 +398,6 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
298
  return $result;
299
  }
300
 
301
- /**
302
- *
303
- */
304
- public function restoreRoles() {
305
- $roles = get_user_option('aam-original-roles');
306
-
307
- //remove curren roles
308
- foreach((array) $this->roles as $role) {
309
- $this->remove_role($role);
310
- }
311
-
312
- //add original roles
313
- foreach(($roles ? $roles : array('subscriber')) as $role) {
314
- $this->add_role($role);
315
- }
316
-
317
- //delete options
318
- delete_user_option($this->getId(), 'aam-role-expires');
319
- delete_user_option($this->getId(), 'aam-original-roles');
320
- }
321
-
322
  /**
323
  * Retrieve User based on ID
324
  *
19
  * Subject UID: USER
20
  */
21
  const UID = 'user';
22
+
23
+ /**
24
+ * User status is BLOCKED
25
+ */
26
+ const STATUS_BLOCKED = 1;
27
 
28
  /**
29
  * AAM Capability Key
60
  * @access protected
61
  */
62
  protected $maxLevel = null;
63
+
64
+ /**
65
+ * Current user status
66
+ *
67
+ * @var array
68
+ *
69
+ * @access protected
70
+ */
71
+ protected $status = null;
72
 
73
  /**
74
  * Constructor
190
  }
191
 
192
  /**
193
+ * Get current user's status
194
+ *
195
+ * @return array
196
  *
197
+ * @access public
198
  */
199
+ public function getUserStatus() {
200
+ if (is_null($this->status)) {
201
+ $this->status = array('status' => 'active');
202
+ $steps = array(
203
+ 'UserRecordStatus', // Check if user's record states that it is blocked
204
+ 'UserExpiration', // Check if user's account is expired
205
+ 'RoleExpiration', // Legacy: Check if user's role is expired
206
+ 'UserTtl' // Check if user's session ttl is expired
207
+ );
208
+
209
+ foreach($steps as $step) {
210
+ $result = call_user_func(array($this, "check{$step}"));
211
+ if ($result !== true) {
212
+ $this->status = $result;
213
+ break;
214
+ }
215
+ }
216
  }
217
+
218
+ return $this->status;
219
+ }
220
+
221
+ /**
222
+ * Restrain user account based on status
223
+ *
224
+ * @param array $status
225
+ *
226
+ * @return void
227
+ * @see AAM_Core_Subject_User::getUserStatus
228
+ *
229
+ * @access public
230
+ */
231
+ public function restrainUserAccount(array $status) {
232
+ switch($status['action']) {
233
+ case 'lock':
234
+ $this->block();
235
+ break;
236
 
237
+ case 'change-role':
238
+ $this->getSubject()->set_role(''); // First reset all roles
239
+ foreach((array)$status['meta'] as $role) {
240
+ $this->getSubject()->add_role($role);
241
+ }
242
+ break;
243
+
244
+ case 'delete':
245
+ require_once(ABSPATH . 'wp-admin/includes/user.php' );
246
+ $reasign = AAM_Core_Config::get('core.reasign.ownership.user');
247
+ wp_delete_user($this->getId(), $reasign);
248
+ // Finally logout user
249
+
250
+ default:
251
+ wp_logout();
252
+ break;
253
+ }
254
+
255
+ // Delete `aam_user_expiration`
256
+ delete_user_meta($this->getId(), 'aam_user_expiration');
257
+ }
258
+
259
+ /**
260
+ * Check if user status is blocked
261
+ *
262
+ * @return array|bool
263
+ *
264
+ * @access protected
265
+ */
266
+ protected function checkUserRecordStatus() {
267
+ if (intval($this->user_status) === self::STATUS_BLOCKED) {
268
+ $status = array('status' => 'inactive', 'action' => 'logout');
269
+ } else {
270
+ $status = true;
271
+ }
272
+
273
+ return $status;
274
+ }
275
+
276
+ /**
277
+ * Check if user account is expired
278
+ *
279
+ * @return array|bool
280
+ *
281
+ * @access protected
282
+ */
283
+ protected function checkUserExpiration() {
284
+ $status = true;
285
+
286
  $expired = get_user_meta($this->ID, 'aam_user_expiration', true);
287
  if (!empty($expired)) {
288
  $parts = explode('|', $expired);
289
 
 
290
  // TODO: Remove in Jan 2020
291
  if (preg_match('/^[\d]{4}-/', $parts[0])) {
292
  $expires = DateTime::createFromFormat('Y-m-d H:i:s', $parts[0]);
294
  $expires = DateTime::createFromFormat('m/d/Y, H:i O', $parts[0]);
295
  }
296
 
297
+ if ($expires) {
298
+ $compare = new DateTime();
299
+ //TODO - PHP Warning: DateTime::setTimezone(): Can only do this for zones with ID for now in
300
+ @$compare->setTimezone($expires->getTimezone());
301
+
302
+ if ($expires->getTimestamp() <= $compare->getTimestamp()) {
303
+ $status = array(
304
+ 'status' => 'inactive',
305
+ 'action' => $parts[1],
306
+ 'meta' => (isset($parts[2]) ? $parts[2] : null)
307
+ );
308
+ }
309
  }
310
  }
311
 
312
+ return $status;
313
+ }
314
+
315
+ /**
316
+ * Check if role is expired
317
+ *
318
+ * @return array|bool
319
+ *
320
+ * @access protected
321
+ * @todo Remove in April 2020
322
+ */
323
+ protected function checkRoleExpiration() {
324
+ $status = true;
325
+
326
+ $roleExpire = get_user_option('aam-role-expires', $this->getId());
327
  if ($roleExpire && ($roleExpire <= time())) {
328
+ $roles = get_user_option('aam-original-roles');
329
+
330
+ $status = array(
331
+ 'status' => 'inactive',
332
+ 'action' => 'change-role',
333
+ 'meta' => ($roles ? $roles : 'subscriber')
334
+ );
335
 
336
+ //delete options
337
+ delete_user_option($this->getId(), 'aam-role-expires');
338
+ delete_user_option($this->getId(), 'aam-original-roles');
339
+ }
340
+
341
+ return $status;
342
+ }
343
+
344
+ /**
345
+ * Check user TTL
346
+ *
347
+ * @return array|bool
348
+ *
349
+ * @access protected
350
+ */
351
+ protected function checkUserTtl() {
352
+ $status = true;
353
+
354
  if (AAM::api()->getConfig('core.session.tracking', false)) {
355
  $ttl = AAM::api()->getConfig(
356
+ "core.session.user.{$this->ID}.ttl",
357
+ AAM::api()->getConfig("core.session.user.ttl", null)
358
  );
359
 
360
  if (!empty($ttl)) {
364
 
365
  if ($timestamp && ($timestamp + intval($ttl) <= time())) {
366
  delete_user_meta($this->ID, 'aam-authenticated-timestamp');
367
+ $status = array('status' => 'inactive', 'action' => 'logout');
368
  }
369
  }
370
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
 
372
+ return $status;
 
 
373
  }
374
+
375
  /**
376
  * Block User
377
  *
398
  return $result;
399
  }
400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  /**
402
  * Retrieve User based on ID
403
  *
Application/Shared/Manager.php CHANGED
@@ -89,6 +89,9 @@ class AAM_Shared_Manager {
89
  );
90
  }
91
  }
 
 
 
92
 
93
  // Check if user has ability to perform certain task based on provided
94
  // capability and meta data
@@ -110,6 +113,26 @@ class AAM_Shared_Manager {
110
 
111
  return self::$_instance;
112
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  /**
115
  *
@@ -180,13 +203,6 @@ class AAM_Shared_Manager {
180
  public function userRoleAdded($userId, $role) {
181
  $user = new AAM_Core_Subject_User($userId);
182
  AAM_Core_API::clearCache($user);
183
-
184
- $expire = AAM_Core_API::getOption("aam-role-{$role}-expiration", '');
185
-
186
- if ($expire) {
187
- update_user_option($userId, "aam-original-roles", $user->roles);
188
- update_user_option($userId, "aam-role-expires", strtotime($expire));
189
- }
190
  }
191
 
192
  /**
@@ -197,12 +213,6 @@ class AAM_Shared_Manager {
197
  public function userRoleRemoved($userId, $role) {
198
  $user = new AAM_Core_Subject_User($userId);
199
  AAM_Core_API::clearCache($user);
200
-
201
- $expire = AAM_Core_API::getOption("aam-role-{$role}-expiration", '');
202
-
203
- if ($expire) {
204
- delete_user_option($userId, "aam-role-expires");
205
- }
206
  }
207
 
208
  /**
@@ -252,7 +262,6 @@ class AAM_Shared_Manager {
252
  */
253
  public function authorizeXMLRPCRequest($method) {
254
  $object = AAM::api()->getUser(get_current_user_id())->getObject('route');
255
-
256
  if ($object->has('xmlrpc', $method)) {
257
  AAM_Core_API::getXMLRPCServer()->error(
258
  401,
@@ -443,103 +452,137 @@ class AAM_Shared_Manager {
443
  * @access public
444
  */
445
  public function mapMetaCaps($caps, $cap, $user_id, $args) {
446
- global $post;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
 
448
  switch($cap) {
449
  case 'edit_user':
450
  case 'delete_user':
451
- if (isset($args[0])) {
452
- $caps = $this->authorizeUserUpdate($caps, $args[0]);
 
 
 
453
  }
454
  break;
455
 
456
  case 'install_plugins':
457
- $caps = $this->checkPluginsAction('install', $caps, $cap);
458
- break;
459
-
460
  case 'delete_plugins':
461
- $caps = $this->checkPluginsAction('delete', $caps, $cap);
462
- break;
463
-
464
  case 'edit_plugins':
465
- $caps = $this->checkPluginsAction('edit', $caps, $cap);
466
- break;
467
-
468
  case 'update_plugins':
469
- $caps = $this->checkPluginsAction('update', $caps, $cap);
470
- break;
471
-
472
- case 'activate_plugin':
473
- $caps = $this->checkPluginAction(
474
- (isset($args[0]) ? $args[0] : ''), 'activate', $caps, $cap
475
- );
476
  break;
477
 
 
478
  case 'deactivate_plugin':
479
- $caps = $this->checkPluginAction(
480
- (isset($args[0]) ? $args[0] : ''), 'deactivate', $caps, $cap
481
- );
482
  break;
483
-
484
- default:
485
- //potentially post type cap
486
- $caps = $this->checkPostPermission(
487
- $caps, $cap, (isset($args[0]) ? $args[0] : null)
488
- );
489
- break;
490
- }
491
-
492
- return $caps;
493
- }
494
 
495
- /**
496
- * Check Post Permissions
497
- *
498
- * @param [type] $caps
499
- * @param [type] $cap
500
- * @param [type] $id
501
- *
502
- * @return array
503
- *
504
- * @access protected
505
- */
506
- protected function checkPostPermission($caps, $cap, $id = null) {
507
- global $post;
508
-
509
- $postId = (empty($id) && is_a($post, 'WP_Post') ? $post->ID : $id);
510
- switch($cap) {
511
  case 'edit_post':
512
- case 'aam_edit_policy':
513
- $caps = $this->authorizePostEdit($caps, $postId);
514
  break;
515
 
516
  case 'delete_post':
517
- case 'aam_delete_policy':
518
- $caps = $this->authorizePostDelete($caps, $postId);
519
  break;
520
 
521
  case 'read_post':
522
- case 'read':
523
- case 'aam_read_policy':
524
- $caps = $this->authorizePostRead($caps, $postId);
525
  break;
526
 
527
 
528
  case 'publish_post':
529
  case 'publish_posts':
530
  case 'publish_pages':
531
- case 'aam_publish_policies':
532
  // There is a bug in WP core that instead of checking if user has
533
  // ability to publish_post, it checks for edit_post. That is why
534
  // user has to be on the edit
535
  if (is_a($post, 'WP_Post')) {
536
- $caps = $this->authorizePublishPost($caps, $postId);
537
  }
538
  break;
539
-
540
  default:
 
 
 
 
 
541
  break;
542
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
543
 
544
  return $caps;
545
  }
@@ -655,7 +698,7 @@ class AAM_Shared_Manager {
655
  * @access protected
656
  */
657
  protected function authorizeUserUpdate($caps, $userId) {
658
- $user = new WP_User($userId);
659
 
660
  //current user max level
661
  $maxLevel = AAM::getUser()->getMaxLevel();
89
  );
90
  }
91
  }
92
+
93
+ // Working with post types
94
+ add_action('registered_post_type', array(self::$_instance, 'registerPostType'), 999, 2);
95
 
96
  // Check if user has ability to perform certain task based on provided
97
  // capability and meta data
113
 
114
  return self::$_instance;
115
  }
116
+
117
+ /**
118
+ * Hook into post type registration process
119
+ *
120
+ * @param string $type
121
+ * @param WP_Post_Type $object
122
+ *
123
+ * @return void
124
+ *
125
+ * @access public
126
+ */
127
+ public function registerPostType($type, $object) {
128
+ if (is_a($object, 'WP_Post_Type')) { // Work only with WP 4.6.0 or higher
129
+ foreach($object->cap as $type => $capability) {
130
+ if (in_array($type, array('edit_post', 'delete_post', 'read_post'), true)) {
131
+ $object->cap->{$type} = "aam-{$type}-{$capability}";
132
+ }
133
+ }
134
+ }
135
+ }
136
 
137
  /**
138
  *
203
  public function userRoleAdded($userId, $role) {
204
  $user = new AAM_Core_Subject_User($userId);
205
  AAM_Core_API::clearCache($user);
 
 
 
 
 
 
 
206
  }
207
 
208
  /**
213
  public function userRoleRemoved($userId, $role) {
214
  $user = new AAM_Core_Subject_User($userId);
215
  AAM_Core_API::clearCache($user);
 
 
 
 
 
 
216
  }
217
 
218
  /**
262
  */
263
  public function authorizeXMLRPCRequest($method) {
264
  $object = AAM::api()->getUser(get_current_user_id())->getObject('route');
 
265
  if ($object->has('xmlrpc', $method)) {
266
  AAM_Core_API::getXMLRPCServer()->error(
267
  401,
452
  * @access public
453
  */
454
  public function mapMetaCaps($caps, $cap, $user_id, $args) {
455
+ global $post;
456
+
457
+ $objectId = (isset($args[0]) ? $args[0] : null);
458
+
459
+ // First of all delete all artificial capability from the $caps
460
+ foreach($caps as $i => $capability) {
461
+ if (strpos($capability, 'aam-') === 0) {
462
+ $parts = explode('-', $capability);
463
+ $capability = $parts[2];
464
+ }
465
+
466
+ if (in_array($capability, AAM_Backend_Feature_Main_Capability::$groups['aam'], true)) {
467
+ if (!AAM_Core_API::capabilityExists($capability)) {
468
+ $capability = AAM_Core_Config::get(
469
+ 'page.capability', 'administrator'
470
+ );
471
+ }
472
+ }
473
+
474
+ $caps[$i] = $capability;
475
+ }
476
 
477
  switch($cap) {
478
  case 'edit_user':
479
  case 'delete_user':
480
+ // Some plugins or themes simply do not provide the the user ID for
481
+ // these capabilities. I did not find in WP core any place were they
482
+ // violate this rule
483
+ if (!empty($objectId)) {
484
+ $caps = $this->authorizeUserUpdate($caps, $objectId);
485
  }
486
  break;
487
 
488
  case 'install_plugins':
 
 
 
489
  case 'delete_plugins':
 
 
 
490
  case 'edit_plugins':
 
 
 
491
  case 'update_plugins':
492
+ $action = explode('_', $cap);
493
+ $caps = $this->checkPluginsAction($action[0], $caps, $cap);
 
 
 
 
 
494
  break;
495
 
496
+ case 'activate_plugin':
497
  case 'deactivate_plugin':
498
+ $action = explode('_', $cap);
499
+ $caps = $this->checkPluginAction($objectId, $action[0], $caps, $cap);
 
500
  break;
 
 
 
 
 
 
 
 
 
 
 
501
 
502
+ // This part needs to stay to cover scenarios where WP_Post_Type->cap->...
503
+ // is not used but rather the hardcoded capability
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
  case 'edit_post':
505
+ $caps = $this->authorizePostEdit($caps, $objectId);
 
506
  break;
507
 
508
  case 'delete_post':
509
+ $caps = $this->authorizePostDelete($caps, $objectId);
 
510
  break;
511
 
512
  case 'read_post':
513
+ $caps = $this->authorizePostRead($caps, $objectId);
 
 
514
  break;
515
 
516
 
517
  case 'publish_post':
518
  case 'publish_posts':
519
  case 'publish_pages':
 
520
  // There is a bug in WP core that instead of checking if user has
521
  // ability to publish_post, it checks for edit_post. That is why
522
  // user has to be on the edit
523
  if (is_a($post, 'WP_Post')) {
524
+ $caps = $this->authorizePublishPost($caps, $post->ID);
525
  }
526
  break;
527
+
528
  default:
529
+ if (strpos($cap, 'aam-') === 0) {
530
+ $caps = $this->checkPostTypePermission($caps, $cap, $objectId);
531
+ } else {
532
+ $caps = apply_filters('aam-map-meta-caps-filter', $caps, $cap, $args);
533
+ }
534
  break;
535
  }
536
+
537
+ return $caps;
538
+ }
539
+
540
+ /**
541
+ * Check Post Permissions
542
+ *
543
+ * @param [type] $caps
544
+ * @param [type] $cap
545
+ * @param [type] $id
546
+ *
547
+ * @return array
548
+ *
549
+ * @access protected
550
+ */
551
+ protected function checkPostTypePermission($caps, $cap, $id = null) {
552
+ // Expecting to have:
553
+ // [0] === aam
554
+ // [1] === WP_Post_Type->cap key
555
+ // [2] === The capability
556
+ $parts = explode('-', $cap);
557
+
558
+ // Build the argument array for the current_user_can
559
+ $args = array($parts[2]);
560
+ if (!is_null($id)) {
561
+ $args[] = $id;
562
+ }
563
+
564
+ // NOTE! DO NOT FORGET TO UPDATE REGISTERED_POST_TYPE if new capability is
565
+ // added
566
+ if (call_user_func_array('current_user_can', $args)) {
567
+ switch($parts[1]) {
568
+ case 'edit_post':
569
+ $caps = $this->authorizePostEdit($caps, $id);
570
+ break;
571
+
572
+ case 'read_post':
573
+ $caps = $this->authorizePostRead($caps, $id);
574
+ break;
575
+
576
+ case 'delete_post':
577
+ $caps = $this->authorizePostDelete($caps, $id);
578
+ break;
579
+
580
+ default:
581
+ break;
582
+ }
583
+ } else {
584
+ $caps[] = 'do_not_allow';
585
+ }
586
 
587
  return $caps;
588
  }
698
  * @access protected
699
  */
700
  protected function authorizeUserUpdate($caps, $userId) {
701
+ $user = AAM::api()->getUser($userId);
702
 
703
  //current user max level
704
  $maxLevel = AAM::getUser()->getMaxLevel();
aam.php CHANGED
@@ -1,17 +1,17 @@
1
  <?php
2
 
3
  /**
4
- Plugin Name: Advanced Access Manager
5
- Description: All you need to manage access to your WordPress website
6
- Version: 5.9.1.1
7
- Author: Vasyl Martyniuk <vasyl@vasyltech.com>
8
- Author URI: https://vasyltech.com
9
-
10
- -------
11
- LICENSE: This file is subject to the terms and conditions defined in
12
- file 'license.txt', which is part of Advanced Access Manager source package.
13
  *
14
- */
 
 
 
 
15
 
16
  /**
17
  * Main plugin's class
@@ -121,8 +121,8 @@ class AAM {
121
  }
122
 
123
  //JWT Authentication
124
- if (AAM_Core_Config::get('core.settings.jwtAuthentication', false)) {
125
- AAM_Core_JwtAuth::bootstrap();
126
  }
127
 
128
  // Load AAM
@@ -165,12 +165,20 @@ class AAM {
165
  public static function getInstance() {
166
  if (is_null(self::$_instance)) {
167
  self::$_instance = new self;
 
 
 
168
 
169
  // Load user capabilities
170
- self::$_instance->getUser()->initialize();
171
 
172
  // Logout user if he/she is blocked
173
- self::$_instance->getUser()->validateUserStatus();
 
 
 
 
 
174
 
175
  load_plugin_textdomain(AAM_KEY, false, 'advanced-access-manager/Lang');
176
  }
1
  <?php
2
 
3
  /**
4
+ * Plugin Name: Advanced Access Manager
5
+ * Description: All you need to manage access to your WordPress website
6
+ * Version: 5.9.2
7
+ * Author: Vasyl Martyniuk <vasyl@vasyltech.com>
8
+ * Author URI: https://vasyltech.com
 
 
 
 
9
  *
10
+ * -------
11
+ * LICENSE: This file is subject to the terms and conditions defined in
12
+ * file 'license.txt', which is part of Advanced Access Manager source package.
13
+ *
14
+ **/
15
 
16
  /**
17
  * Main plugin's class
121
  }
122
 
123
  //JWT Authentication
124
+ if (AAM_Core_Config::get('core.settings.jwtAuthentication', true)) {
125
+ AAM_Core_Jwt_Manager::bootstrap();
126
  }
127
 
128
  // Load AAM
165
  public static function getInstance() {
166
  if (is_null(self::$_instance)) {
167
  self::$_instance = new self;
168
+
169
+ // Get current user
170
+ $user = self::$_instance->getUser();
171
 
172
  // Load user capabilities
173
+ $user->initialize();
174
 
175
  // Logout user if he/she is blocked
176
+ $status = $user->getUserStatus();
177
+
178
+ // If user is not active, then perform rollback on user
179
+ if ($status['status'] !== 'active') {
180
+ $user->restrainUserAccount($status);
181
+ }
182
 
183
  load_plugin_textdomain(AAM_KEY, false, 'advanced-access-manager/Lang');
184
  }
media/css/aam.css CHANGED
@@ -103,6 +103,8 @@
103
  .icon-cubes:before { content: '\f1b3' !important; } /* '' */
104
  .icon-cog-alt:before { content: '\e80d' !important; } /* '' */
105
  .icon-wrench:before { content: '\e80e' !important; } /* '' */
 
 
106
 
107
  .animate-spin {
108
  -moz-animation: spin 2s infinite linear;
@@ -310,6 +312,18 @@ div.error {
310
  font-weight: bold;
311
  }
312
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  .panel-title {
314
  font-size: 13px;
315
  }
@@ -594,11 +608,6 @@ input[type=radio]:checked + label:before {
594
  margin-top: 30px;
595
  }
596
 
597
- .aam-block {
598
- display: block;
599
- letter-spacing: 1.5px;
600
- }
601
-
602
  .aam-highligh-row {
603
  border:3px solid #337ab7;
604
  }
@@ -754,7 +763,7 @@ input[type=radio]:checked + label:before {
754
  font-weight: 500;
755
  padding: 15px;
756
  text-align: center;
757
- z-index: 999;
758
  color: #FFFFFF;
759
  letter-spacing: 1px;
760
  }
103
  .icon-cubes:before { content: '\f1b3' !important; } /* '' */
104
  .icon-cog-alt:before { content: '\e80d' !important; } /* '' */
105
  .icon-wrench:before { content: '\e80e' !important; } /* '' */
106
+ .icon-ok-circled:before { content: '\e81c' !important; } /* '' */
107
+ .icon-cancel-circled:before { content: '\e81d' !important; } /* '' */
108
 
109
  .animate-spin {
110
  -moz-animation: spin 2s infinite linear;
312
  font-weight: bold;
313
  }
314
 
315
+ .aam-block {
316
+ display: block;
317
+ }
318
+
319
+ .aam-block .aam-copy-clipboard{
320
+ float: right;
321
+ }
322
+
323
+ .aam-block .aam-copy-clipboard:after {
324
+ clear:both;
325
+ }
326
+
327
  .panel-title {
328
  font-size: 13px;
329
  }
608
  margin-top: 30px;
609
  }
610
 
 
 
 
 
 
611
  .aam-highligh-row {
612
  border:3px solid #337ab7;
613
  }
763
  font-weight: 500;
764
  padding: 15px;
765
  text-align: center;
766
+ z-index: 9999;
767
  color: #FFFFFF;
768
  letter-spacing: 1px;
769
  }
media/font/fontello.eot CHANGED
Binary file
media/font/fontello.svg CHANGED
@@ -62,6 +62,10 @@
62
 
63
  <glyph glyph-name="users" unicode="&#xe81b;" d="M331 350q-90-3-148-71h-75q-45 0-77 22t-31 66q0 197 69 197 4 0 25-11t54-24 66-12q38 0 75 13-3-21-3-37 0-78 45-143z m598-356q0-66-41-105t-108-39h-488q-68 0-108 39t-41 105q0 30 2 58t8 61 14 61 24 54 35 45 48 30 62 11q6 0 24-12t41-26 59-27 76-12 75 12 60 27 41 26 23 12q35 0 63-11t47-30 35-45 24-54 15-61 8-61 2-58z m-572 713q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m393-214q0-89-63-152t-151-62-152 62-63 152 63 151 152 63 151-63 63-151z m321-126q0-43-31-66t-77-22h-75q-57 68-147 71 45 65 45 143 0 16-3 37 37-13 74-13 33 0 67 12t54 24 24 11q69 0 69-197z m-71 340q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z" horiz-adv-x="1071.4" />
64
 
 
 
 
 
65
  <glyph glyph-name="user-plus" unicode="&#xe820;" d="M393 350q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" />
66
 
67
  <glyph glyph-name="box" unicode="&#xe822;" d="M607 386q0 14-10 25t-26 10h-142q-15 0-26-10t-10-25 10-25 26-11h142q15 0 26 11t10 25z m322 107v-536q0-14-11-25t-25-11h-786q-14 0-25 11t-11 25v536q0 14 11 25t25 11h786q14 0 25-11t11-25z m35 250v-143q0-15-10-25t-25-11h-858q-14 0-25 11t-10 25v143q0 14 10 25t25 11h858q14 0 25-11t10-25z" horiz-adv-x="1000" />
62
 
63
  <glyph glyph-name="users" unicode="&#xe81b;" d="M331 350q-90-3-148-71h-75q-45 0-77 22t-31 66q0 197 69 197 4 0 25-11t54-24 66-12q38 0 75 13-3-21-3-37 0-78 45-143z m598-356q0-66-41-105t-108-39h-488q-68 0-108 39t-41 105q0 30 2 58t8 61 14 61 24 54 35 45 48 30 62 11q6 0 24-12t41-26 59-27 76-12 75 12 60 27 41 26 23 12q35 0 63-11t47-30 35-45 24-54 15-61 8-61 2-58z m-572 713q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m393-214q0-89-63-152t-151-62-152 62-63 152 63 151 152 63 151-63 63-151z m321-126q0-43-31-66t-77-22h-75q-57 68-147 71 45 65 45 143 0 16-3 37 37-13 74-13 33 0 67 12t54 24 24 11q69 0 69-197z m-71 340q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z" horiz-adv-x="1071.4" />
64
 
65
+ <glyph glyph-name="ok-circled" unicode="&#xe81c;" d="M717 440q0 16-10 26l-51 50q-11 11-25 11t-25-11l-228-227-126 126q-11 11-25 11t-25-11l-51-50q-10-10-10-26 0-15 10-25l202-202q10-10 25-10 15 0 26 10l303 303q10 10 10 25z m140-90q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
66
+
67
+ <glyph glyph-name="cancel-circled" unicode="&#xe81d;" d="M641 224q0 14-10 25l-101 101 101 101q10 11 10 25 0 15-10 26l-51 50q-10 11-25 11-15 0-25-11l-101-101-101 101q-11 11-25 11-16 0-26-11l-50-50q-11-11-11-26 0-14 11-25l101-101-101-101q-11-11-11-25 0-15 11-26l50-50q10-11 26-11 14 0 25 11l101 101 101-101q10-11 25-11 15 0 25 11l51 50q10 11 10 26z m216 126q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
68
+
69
  <glyph glyph-name="user-plus" unicode="&#xe820;" d="M393 350q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" />
70
 
71
  <glyph glyph-name="box" unicode="&#xe822;" d="M607 386q0 14-10 25t-26 10h-142q-15 0-26-10t-10-25 10-25 26-11h142q15 0 26 11t10 25z m322 107v-536q0-14-11-25t-25-11h-786q-14 0-25 11t-11 25v536q0 14 11 25t25 11h786q14 0 25-11t11-25z m35 250v-143q0-15-10-25t-25-11h-858q-14 0-25 11t-10 25v143q0 14 10 25t25 11h858q14 0 25-11t10-25z" horiz-adv-x="1000" />
media/font/fontello.ttf CHANGED
Binary file
media/font/fontello.woff CHANGED
Binary file
media/font/fontello.woff2 CHANGED
Binary file
media/js/{aam-5.9.1.js → aam-5.9.2.js} RENAMED
@@ -110,6 +110,41 @@
110
  });
111
  });
112
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  /**
115
  *
@@ -171,7 +206,7 @@
171
  getAAM().triggerHook('post-get-role-list', {
172
  list : response
173
  });
174
- //TODO - Rerwite JavaScript to support $.aam
175
  $.aamEditRole = null;
176
  }
177
  });
@@ -216,8 +251,8 @@
216
  }
217
  },
218
  columnDefs: [
219
- {visible: false, targets: [0, 1, 4, 5]},
220
- {orderable: false, targets: [0, 1, 3, 4, 5]}
221
  ],
222
  language: {
223
  search: '_INPUT_',
@@ -256,12 +291,11 @@
256
  $(row).attr('data-id', data[0]);
257
 
258
  //add subtitle
259
- var expire = (data[5] ? '; <i class="icon-clock"></i>' : '');
260
  $('td:eq(0)', row).append(
261
  $('<i/>', {'class': 'aam-row-subtitle'}).html(
262
  getAAM().applyFilters(
263
  'role-subtitle',
264
- getAAM().__('Users') + ': <b>' + parseInt(data[1]) + '</b>; ID: <b>' + data[0] + '</b>' + expire,
265
  data
266
  )
267
  )
@@ -319,11 +353,10 @@
319
  resetForm('#edit-role-modal .modal-body');
320
  $('#edit-role-btn').data('role', data[0]);
321
  $('#edit-role-name').val(data[2]);
322
- $('#edit-role-expiration').val(data[5]);
323
  $('#edit-role-modal').modal('show');
324
  fetchRoleList(data[0]);
325
 
326
- //TODO - Rerwite JavaScript to support $.aam
327
  $.aamEditRole = data;
328
 
329
  getAAM().triggerHook('edit-role-modal', data);
@@ -347,7 +380,7 @@
347
  $(container).append($('<i/>', {
348
  'class': 'aam-row-action icon-clone text-success'
349
  }).bind('click', function () {
350
- //TODO - Rerwite JavaScript to support $.aam
351
  $.aamEditRole = data;
352
  $('#clone-role').prop('checked', true);
353
  $('#add-role-modal').modal('show');
@@ -678,40 +711,6 @@
678
  return (subject.type === 'user' && parseInt(subject.id) === id);
679
  }
680
 
681
- /**
682
- *
683
- * @param {type} selected
684
- * @returns {undefined}
685
- */
686
- function loadRoleList(selected) {
687
- $.ajax(getLocal().ajaxurl, {
688
- type: 'POST',
689
- dataType: 'json',
690
- data: {
691
- action: 'aam',
692
- sub_action: 'Subject_Role.getList',
693
- _ajax_nonce: getLocal().nonce
694
- },
695
- beforeSend: function () {
696
- $('#expiration-change-role').html(
697
- '<option value="">' + getAAM().__('Loading...') + '</option>'
698
- );
699
- },
700
- success: function (response) {
701
- $('#expiration-change-role').html(
702
- '<option value="">' + getAAM().__('Select Role') + '</option>'
703
- );
704
- for (var i in response) {
705
- $('#expiration-change-role').append(
706
- '<option value="' + i + '">' + response[i].name + '</option>'
707
- );
708
- }
709
-
710
- $('#expiration-change-role').val(selected);
711
- }
712
- });
713
- }
714
-
715
  /**
716
  *
717
  * @param {type} id
@@ -759,44 +758,6 @@
759
  });
760
  }
761
 
762
- /**
763
- *
764
- * @param {type} id
765
- * @param {type} expires
766
- * @returns {undefined}
767
- */
768
- function generateJWT(id, expires) {
769
- $.ajax(getLocal().ajaxurl, {
770
- type: 'POST',
771
- dataType: 'json',
772
- data: {
773
- action: 'aam',
774
- sub_action: 'Subject_User.generateJWT',
775
- _ajax_nonce: getLocal().nonce,
776
- user: id,
777
- expires: expires
778
- },
779
- beforeSend: function () {
780
- $('#user-auth-jwt').val(getAAM().__('Generating token...'));
781
- },
782
- success: function (response) {
783
- if (response.status === 'success') {
784
- $('#user-auth-jwt').val(response.jwt);
785
- $('#user-auth-url').val(
786
- $('#user-auth-url').data('url').replace('%s', response.jwt)
787
- );
788
- } else {
789
- getAAM().notification(
790
- 'danger', getAAM().__('Failed to generate JWT token')
791
- );
792
- }
793
- },
794
- error: function () {
795
- getAAM().notification('danger', getAAM().__('Application error'));
796
- }
797
- });
798
- }
799
-
800
  //initialize the user list table
801
  $('#user-list').DataTable({
802
  autoWidth: false,
@@ -1125,10 +1086,6 @@
1125
  $('#user-expires').val(
1126
  res.date.format('MM/DD/YYYY, H:mm Z')
1127
  );
1128
- generateJWT(
1129
- $('#edit-user-expiration-btn').attr('data-user-id'),
1130
- $('#user-expires').val()
1131
- );
1132
  });
1133
 
1134
  //edit role button
@@ -3749,6 +3706,265 @@
3749
 
3750
  })(jQuery);
3751
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3752
  /**
3753
  * Extensions Interface
3754
  *
@@ -4557,6 +4773,17 @@
4557
  $('#aam-container').delegate('a[href="#"]', 'click', function(event) {
4558
  event.preventDefault();
4559
  });
 
 
 
 
 
 
 
 
 
 
 
4560
  };
4561
 
4562
  /**
110
  });
111
  });
112
  }
113
+
114
+ /**
115
+ *
116
+ * @param {type} selected
117
+ * @returns {undefined}
118
+ */
119
+ function loadRoleList(selected, target) {
120
+ target = (typeof target === 'undefined' ? '#expiration-change-role' : target);
121
+ $.ajax(getLocal().ajaxurl, {
122
+ type: 'POST',
123
+ dataType: 'json',
124
+ data: {
125
+ action: 'aam',
126
+ sub_action: 'Subject_Role.getList',
127
+ _ajax_nonce: getLocal().nonce
128
+ },
129
+ beforeSend: function () {
130
+ $(target).html(
131
+ '<option value="">' + getAAM().__('Loading...') + '</option>'
132
+ );
133
+ },
134
+ success: function (response) {
135
+ $(target).html(
136
+ '<option value="">' + getAAM().__('Select Role') + '</option>'
137
+ );
138
+ for (var i in response) {
139
+ $(target).append(
140
+ '<option value="' + i + '">' + response[i].name + '</option>'
141
+ );
142
+ }
143
+
144
+ $(target).val(selected);
145
+ }
146
+ });
147
+ }
148
 
149
  /**
150
  *
206
  getAAM().triggerHook('post-get-role-list', {
207
  list : response
208
  });
209
+ //TODO - Rewrite JavaScript to support $.aam
210
  $.aamEditRole = null;
211
  }
212
  });
251
  }
252
  },
253
  columnDefs: [
254
+ {visible: false, targets: [0, 1, 4]},
255
+ {orderable: false, targets: [0, 1, 3, 4]}
256
  ],
257
  language: {
258
  search: '_INPUT_',
291
  $(row).attr('data-id', data[0]);
292
 
293
  //add subtitle
 
294
  $('td:eq(0)', row).append(
295
  $('<i/>', {'class': 'aam-row-subtitle'}).html(
296
  getAAM().applyFilters(
297
  'role-subtitle',
298
+ getAAM().__('Users') + ': <b>' + parseInt(data[1]) + '</b>; ID: <b>' + data[0] + '</b>',
299
  data
300
  )
301
  )
353
  resetForm('#edit-role-modal .modal-body');
354
  $('#edit-role-btn').data('role', data[0]);
355
  $('#edit-role-name').val(data[2]);
 
356
  $('#edit-role-modal').modal('show');
357
  fetchRoleList(data[0]);
358
 
359
+ //TODO - Rewrite JavaScript to support $.aam
360
  $.aamEditRole = data;
361
 
362
  getAAM().triggerHook('edit-role-modal', data);
380
  $(container).append($('<i/>', {
381
  'class': 'aam-row-action icon-clone text-success'
382
  }).bind('click', function () {
383
+ //TODO - Rewrite JavaScript to support $.aam
384
  $.aamEditRole = data;
385
  $('#clone-role').prop('checked', true);
386
  $('#add-role-modal').modal('show');
711
  return (subject.type === 'user' && parseInt(subject.id) === id);
712
  }
713
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
714
  /**
715
  *
716
  * @param {type} id
758
  });
759
  }
760
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761
  //initialize the user list table
762
  $('#user-list').DataTable({
763
  autoWidth: false,
1086
  $('#user-expires').val(
1087
  res.date.format('MM/DD/YYYY, H:mm Z')
1088
  );
 
 
 
 
1089
  });
1090
 
1091
  //edit role button
3706
 
3707
  })(jQuery);
3708
 
3709
+ /**
3710
+ * JWT Interface
3711
+ *
3712
+ * @param {jQuery} $
3713
+ *
3714
+ * @returns {void}
3715
+ */
3716
+ (function ($) {
3717
+
3718
+ /**
3719
+ *
3720
+ * @param {type} expires
3721
+ * @returns {undefined}
3722
+ */
3723
+ function generateJWT(expires) {
3724
+ $.ajax(getLocal().ajaxurl, {
3725
+ type: 'POST',
3726
+ dataType: 'json',
3727
+ data: {
3728
+ action: 'aam',
3729
+ sub_action: 'Main_Jwt.generate',
3730
+ _ajax_nonce: getLocal().nonce,
3731
+ subject: getAAM().getSubject().type,
3732
+ subjectId: getAAM().getSubject().id,
3733
+ expires: expires
3734
+ },
3735
+ beforeSend: function () {
3736
+ $('#jwt-token-preview').val(getAAM().__('Generating token...'));
3737
+ $('#jwt-url-preview').val(getAAM().__('Generating URL...'));
3738
+ },
3739
+ success: function (response) {
3740
+ if (response.status === 'success') {
3741
+ $('#jwt-token-preview').val(response.jwt);
3742
+ $('#jwt-url-preview').val(
3743
+ $('#jwt-url-preview').data('url').replace('%s', response.jwt)
3744
+ );
3745
+ } else {
3746
+ getAAM().notification(
3747
+ 'danger', getAAM().__('Failed to generate JWT token')
3748
+ );
3749
+ }
3750
+ },
3751
+ error: function () {
3752
+ getAAM().notification('danger', getAAM().__('Application error'));
3753
+ }
3754
+ });
3755
+ }
3756
+
3757
+ /**
3758
+ *
3759
+ */
3760
+ function initialize() {
3761
+ var container = '#jwt-content';
3762
+
3763
+ if ($(container).length) {
3764
+ $('#jwt-expiration-datapicker').datetimepicker({
3765
+ icons: {
3766
+ time: "icon-clock",
3767
+ date: "icon-calendar",
3768
+ up: "icon-angle-up",
3769
+ down: "icon-angle-down",
3770
+ previous: "icon-angle-left",
3771
+ next: "icon-angle-right"
3772
+ },
3773
+ minDate: new Date(),
3774
+ inline: true,
3775
+ sideBySide: true
3776
+ });
3777
+
3778
+ $('#create-jwt-modal').on('show.bs.modal', function() {
3779
+ try{
3780
+ var tomorrow = new Date();
3781
+ tomorrow.setDate(tomorrow.getDate() + 1);
3782
+ $('#jwt-expiration-datapicker').data('DateTimePicker').defaultDate(
3783
+ tomorrow
3784
+ );
3785
+ $('#jwt-expires').val('');
3786
+ } catch(e) {
3787
+ // do nothing. Prevent from any kind of corrupted data
3788
+ }
3789
+ });
3790
+
3791
+ $('#jwt-expiration-datapicker').on('dp.change', function(res) {
3792
+ $('#jwt-expires').val(
3793
+ res.date.format('MM/DD/YYYY, H:mm Z')
3794
+ );
3795
+ generateJWT(
3796
+ $('#jwt-expires').val()
3797
+ );
3798
+ });
3799
+
3800
+ $('#jwt-list').DataTable({
3801
+ autoWidth: false,
3802
+ ordering: true,
3803
+ dom: 'ftrip',
3804
+ pagingType: 'simple',
3805
+ processing: true,
3806
+ stateSave: false,
3807
+ serverSide: false,
3808
+ ajax: {
3809
+ url: getLocal().ajaxurl,
3810
+ type: 'POST',
3811
+ dataType: 'json',
3812
+ data: {
3813
+ action: 'aam',
3814
+ sub_action: 'Main_Jwt.getTable',
3815
+ _ajax_nonce: getLocal().nonce,
3816
+ subject: getAAM().getSubject().type,
3817
+ subjectId: getAAM().getSubject().id
3818
+ }
3819
+ },
3820
+ language: {
3821
+ search: '_INPUT_',
3822
+ searchPlaceholder: getAAM().__('Search Tokens'),
3823
+ info: getAAM().__('_TOTAL_ token(s)'),
3824
+ infoFiltered: '',
3825
+ emptyTable: getAAM().__('No JWT tokens have been generated.'),
3826
+ infoEmpty: getAAM().__('Nothing to show'),
3827
+ lengthMenu: '_MENU_'
3828
+ },
3829
+ columnDefs: [
3830
+ {visible: false, targets: [0, 1]},
3831
+ {orderable: false, targets: [0, 1, 2, 4]}
3832
+ ],
3833
+ initComplete: function () {
3834
+ var create = $('<a/>', {
3835
+ 'href': '#',
3836
+ 'class': 'btn btn-primary'
3837
+ }).html('<i class="icon-plus"></i> ' + getAAM().__('Create'))
3838
+ .bind('click', function () {
3839
+ $('#create-jwt-modal').modal('show');
3840
+ });
3841
+
3842
+ $('.dataTables_filter', '#jwt-list_wrapper').append(create);
3843
+ },
3844
+ createdRow: function (row, data) {
3845
+ // Render status
3846
+ if (data[2] === 'valid') {
3847
+ $('td:eq(0)', row).html(
3848
+ '<i class="icon-ok-circled text-success"></i>'
3849
+ );
3850
+ } else {
3851
+ $('td:eq(0)', row).html(
3852
+ '<i class="icon-cancel-circled text-danger"></i>'
3853
+ );
3854
+ }
3855
+
3856
+ var actions = data[4].split(',');
3857
+
3858
+ var container = $('<div/>', {'class': 'aam-row-actions'});
3859
+ $.each(actions, function (i, action) {
3860
+ switch (action) {
3861
+ case 'delete':
3862
+ $(container).append($('<i/>', {
3863
+ 'class': 'aam-row-action icon-trash-empty text-danger'
3864
+ }).bind('click', function () {
3865
+ $('#jwt-delete-btn').attr('data-id', data[0]);
3866
+ $('#delete-jwt-modal').modal('show');
3867
+ }).attr({
3868
+ 'data-toggle': "tooltip",
3869
+ 'title': getAAM().__('Delete Token')
3870
+ }));
3871
+ break;
3872
+
3873
+ case 'view':
3874
+ $(container).append($('<i/>', {
3875
+ 'class': 'aam-row-action icon-eye text-success'
3876
+ }).bind('click', function () {
3877
+ $('#view-jwt-token').val(data[0]);
3878
+ $('#view-jwt-url').val(data[1]);
3879
+ $('#view-jwt-modal').modal('show');
3880
+ }).attr({
3881
+ 'data-toggle': "tooltip",
3882
+ 'title': getAAM().__('View Token')
3883
+ }));
3884
+ break;
3885
+
3886
+ default:
3887
+ break;
3888
+ }
3889
+ });
3890
+ $('td:eq(2)', row).html(container);
3891
+ }
3892
+ });
3893
+
3894
+ $('#create-jwt-btn').bind('click', function() {
3895
+ $.ajax(getLocal().ajaxurl, {
3896
+ type: 'POST',
3897
+ dataType: 'json',
3898
+ data: {
3899
+ action: 'aam',
3900
+ sub_action: 'Main_Jwt.save',
3901
+ _ajax_nonce: getLocal().nonce,
3902
+ subject: getAAM().getSubject().type,
3903
+ subjectId: getAAM().getSubject().id,
3904
+ token: $('#jwt-token-preview').val()
3905
+ },
3906
+ beforeSend: function () {
3907
+ $('#create-jwt-btn').html(getAAM().__('Creating...'));
3908
+ },
3909
+ success: function (response) {
3910
+ if (response.status === 'success') {
3911
+ $('#create-jwt-modal').modal('hide');
3912
+ $('#jwt-list').DataTable().ajax.reload();
3913
+ } else {
3914
+ getAAM().notification('danger', response.reason);
3915
+ }
3916
+ },
3917
+ error: function () {
3918
+ getAAM().notification(
3919
+ 'danger', getAAM().__('Application error')
3920
+ );
3921
+ },
3922
+ complete: function() {
3923
+ $('#create-jwt-btn').html(getAAM().__('Create'));
3924
+ }
3925
+ });
3926
+ });
3927
+
3928
+ $('#jwt-delete-btn').bind('click', function() {
3929
+ $.ajax(getLocal().ajaxurl, {
3930
+ type: 'POST',
3931
+ dataType: 'json',
3932
+ data: {
3933
+ action: 'aam',
3934
+ sub_action: 'Main_Jwt.delete',
3935
+ _ajax_nonce: getLocal().nonce,
3936
+ subject: getAAM().getSubject().type,
3937
+ subjectId: getAAM().getSubject().id,
3938
+ token: $('#jwt-delete-btn').attr('data-id')
3939
+ },
3940
+ beforeSend: function () {
3941
+ $('#jwt-delete-btn').html(getAAM().__('Deleting...'));
3942
+ },
3943
+ success: function (response) {
3944
+ if (response.status === 'success') {
3945
+ $('#delete-jwt-modal').modal('hide');
3946
+ $('#jwt-list').DataTable().ajax.reload();
3947
+ } else {
3948
+ getAAM().notification('danger', response.reason);
3949
+ }
3950
+ },
3951
+ error: function () {
3952
+ getAAM().notification(
3953
+ 'danger', getAAM().__('Application error')
3954
+ );
3955
+ },
3956
+ complete: function() {
3957
+ $('#jwt-delete-btn').html(getAAM().__('Delete'));
3958
+ }
3959
+ });
3960
+ });
3961
+ }
3962
+ }
3963
+
3964
+ getAAM().addHook('init', initialize);
3965
+
3966
+ })(jQuery);
3967
+
3968
  /**
3969
  * Extensions Interface
3970
  *
4773
  $('#aam-container').delegate('a[href="#"]', 'click', function(event) {
4774
  event.preventDefault();
4775
  });
4776
+
4777
+ // Initialize clipboard
4778
+ var clipboard = new ClipboardJS('.aam-copy-clipboard');
4779
+
4780
+ clipboard.on('success', function(e) {
4781
+ getAAM().notification('success', 'Data has been saved to clipboard');
4782
+ });
4783
+
4784
+ clipboard.on('error', function(e) {
4785
+ getAAM().notification('danger', 'Failed to save data to clipboard');
4786
+ });
4787
  };
4788
 
4789
  /**
media/js/vendor.js CHANGED
@@ -602,4 +602,12 @@ jsonldMode:U,jsonMode:G,expressionAllowed:na,skipExpression:function(a){var b=a.
602
  // Bootstrap datapicker
603
  // https://eonasdan.github.io/bootstrap-datetimepicker/
604
  !function(a){"use strict";if("function"==typeof define&&define.amd)define(["jquery","moment"],a);else if("object"==typeof exports)module.exports=a(require("jquery"),require("moment"));else{if("undefined"==typeof jQuery)throw"bootstrap-datetimepicker requires jQuery to be loaded first";if("undefined"==typeof moment)throw"bootstrap-datetimepicker requires Moment.js to be loaded first";a(jQuery,moment)}}(function(a,b){"use strict";if(!b)throw new Error("bootstrap-datetimepicker requires Moment.js to be loaded first");var c=function(c,d){var e,f,g,h,i,j,k,l={},m=!0,n=!1,o=!1,p=0,q=[{clsName:"days",navFnc:"M",navStep:1},{clsName:"months",navFnc:"y",navStep:1},{clsName:"years",navFnc:"y",navStep:10},{clsName:"decades",navFnc:"y",navStep:100}],r=["days","months","years","decades"],s=["top","bottom","auto"],t=["left","right","auto"],u=["default","top","bottom"],v={up:38,38:"up",down:40,40:"down",left:37,37:"left",right:39,39:"right",tab:9,9:"tab",escape:27,27:"escape",enter:13,13:"enter",pageUp:33,33:"pageUp",pageDown:34,34:"pageDown",shift:16,16:"shift",control:17,17:"control",space:32,32:"space",t:84,84:"t",delete:46,46:"delete"},w={},x=function(){return void 0!==b.tz&&void 0!==d.timeZone&&null!==d.timeZone&&""!==d.timeZone},y=function(a){var c;return c=void 0===a||null===a?b():b.isDate(a)||b.isMoment(a)?b(a):x()?b.tz(a,j,d.useStrict,d.timeZone):b(a,j,d.useStrict),x()&&c.tz(d.timeZone),c},z=function(a){if("string"!=typeof a||a.length>1)throw new TypeError("isEnabled expects a single character string parameter");switch(a){case"y":return i.indexOf("Y")!==-1;case"M":return i.indexOf("M")!==-1;case"d":return i.toLowerCase().indexOf("d")!==-1;case"h":case"H":return i.toLowerCase().indexOf("h")!==-1;case"m":return i.indexOf("m")!==-1;case"s":return i.indexOf("s")!==-1;default:return!1}},A=function(){return z("h")||z("m")||z("s")},B=function(){return z("y")||z("M")||z("d")},C=function(){var b=a("<thead>").append(a("<tr>").append(a("<th>").addClass("prev").attr("data-action","previous").append(a("<span>").addClass(d.icons.previous))).append(a("<th>").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",d.calendarWeeks?"6":"5")).append(a("<th>").addClass("next").attr("data-action","next").append(a("<span>").addClass(d.icons.next)))),c=a("<tbody>").append(a("<tr>").append(a("<td>").attr("colspan",d.calendarWeeks?"8":"7")));return[a("<div>").addClass("datepicker-days").append(a("<table>").addClass("table-condensed").append(b).append(a("<tbody>"))),a("<div>").addClass("datepicker-months").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone())),a("<div>").addClass("datepicker-years").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone())),a("<div>").addClass("datepicker-decades").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone()))]},D=function(){var b=a("<tr>"),c=a("<tr>"),e=a("<tr>");return z("h")&&(b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-hour").attr({"data-time-component":"hours",title:d.tooltips.pickHour}).attr("data-action","showHours"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(a("<span>").addClass(d.icons.down))))),z("m")&&(z("h")&&(b.append(a("<td>").addClass("separator")),c.append(a("<td>").addClass("separator").html(":")),e.append(a("<td>").addClass("separator"))),b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:d.tooltips.pickMinute}).attr("data-action","showMinutes"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(a("<span>").addClass(d.icons.down))))),z("s")&&(z("m")&&(b.append(a("<td>").addClass("separator")),c.append(a("<td>").addClass("separator").html(":")),e.append(a("<td>").addClass("separator"))),b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-second").attr({"data-time-component":"seconds",title:d.tooltips.pickSecond}).attr("data-action","showSeconds"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(a("<span>").addClass(d.icons.down))))),h||(b.append(a("<td>").addClass("separator")),c.append(a("<td>").append(a("<button>").addClass("btn btn-primary").attr({"data-action":"togglePeriod",tabindex:"-1",title:d.tooltips.togglePeriod}))),e.append(a("<td>").addClass("separator"))),a("<div>").addClass("timepicker-picker").append(a("<table>").addClass("table-condensed").append([b,c,e]))},E=function(){var b=a("<div>").addClass("timepicker-hours").append(a("<table>").addClass("table-condensed")),c=a("<div>").addClass("timepicker-minutes").append(a("<table>").addClass("table-condensed")),d=a("<div>").addClass("timepicker-seconds").append(a("<table>").addClass("table-condensed")),e=[D()];return z("h")&&e.push(b),z("m")&&e.push(c),z("s")&&e.push(d),e},F=function(){var b=[];return d.showTodayButton&&b.push(a("<td>").append(a("<a>").attr({"data-action":"today",title:d.tooltips.today}).append(a("<span>").addClass(d.icons.today)))),!d.sideBySide&&B()&&A()&&b.push(a("<td>").append(a("<a>").attr({"data-action":"togglePicker",title:d.tooltips.selectTime}).append(a("<span>").addClass(d.icons.time)))),d.showClear&&b.push(a("<td>").append(a("<a>").attr({"data-action":"clear",title:d.tooltips.clear}).append(a("<span>").addClass(d.icons.clear)))),d.showClose&&b.push(a("<td>").append(a("<a>").attr({"data-action":"close",title:d.tooltips.close}).append(a("<span>").addClass(d.icons.close)))),a("<table>").addClass("table-condensed").append(a("<tbody>").append(a("<tr>").append(b)))},G=function(){var b=a("<div>").addClass("bootstrap-datetimepicker-widget dropdown-menu"),c=a("<div>").addClass("datepicker").append(C()),e=a("<div>").addClass("timepicker").append(E()),f=a("<ul>").addClass("list-unstyled"),g=a("<li>").addClass("picker-switch"+(d.collapse?" accordion-toggle":"")).append(F());return d.inline&&b.removeClass("dropdown-menu"),h&&b.addClass("usetwentyfour"),z("s")&&!h&&b.addClass("wider"),d.sideBySide&&B()&&A()?(b.addClass("timepicker-sbs"),"top"===d.toolbarPlacement&&b.append(g),b.append(a("<div>").addClass("row").append(c.addClass("col-md-6")).append(e.addClass("col-md-6"))),"bottom"===d.toolbarPlacement&&b.append(g),b):("top"===d.toolbarPlacement&&f.append(g),B()&&f.append(a("<li>").addClass(d.collapse&&A()?"collapse in":"").append(c)),"default"===d.toolbarPlacement&&f.append(g),A()&&f.append(a("<li>").addClass(d.collapse&&B()?"collapse":"").append(e)),"bottom"===d.toolbarPlacement&&f.append(g),b.append(f))},H=function(){var b,e={};return b=c.is("input")||d.inline?c.data():c.find("input").data(),b.dateOptions&&b.dateOptions instanceof Object&&(e=a.extend(!0,e,b.dateOptions)),a.each(d,function(a){var c="date"+a.charAt(0).toUpperCase()+a.slice(1);void 0!==b[c]&&(e[a]=b[c])}),e},I=function(){var b,e=(n||c).position(),f=(n||c).offset(),g=d.widgetPositioning.vertical,h=d.widgetPositioning.horizontal;if(d.widgetParent)b=d.widgetParent.append(o);else if(c.is("input"))b=c.after(o).parent();else{if(d.inline)return void(b=c.append(o));b=c,c.children().first().after(o)}if("auto"===g&&(g=f.top+1.5*o.height()>=a(window).height()+a(window).scrollTop()&&o.height()+c.outerHeight()<f.top?"top":"bottom"),"auto"===h&&(h=b.width()<f.left+o.outerWidth()/2&&f.left+o.outerWidth()>a(window).width()?"right":"left"),"top"===g?o.addClass("top").removeClass("bottom"):o.addClass("bottom").removeClass("top"),"right"===h?o.addClass("pull-right"):o.removeClass("pull-right"),"static"===b.css("position")&&(b=b.parents().filter(function(){return"static"!==a(this).css("position")}).first()),0===b.length)throw new Error("datetimepicker component should be placed within a non-static positioned container");o.css({top:"top"===g?"auto":e.top+c.outerHeight(),bottom:"top"===g?b.outerHeight()-(b===c?0:e.top):"auto",left:"left"===h?b===c?0:e.left:"auto",right:"left"===h?"auto":b.outerWidth()-c.outerWidth()-(b===c?0:e.left)})},J=function(a){"dp.change"===a.type&&(a.date&&a.date.isSame(a.oldDate)||!a.date&&!a.oldDate)||c.trigger(a)},K=function(a){"y"===a&&(a="YYYY"),J({type:"dp.update",change:a,viewDate:f.clone()})},L=function(a){o&&(a&&(k=Math.max(p,Math.min(3,k+a))),o.find(".datepicker > div").hide().filter(".datepicker-"+q[k].clsName).show())},M=function(){var b=a("<tr>"),c=f.clone().startOf("w").startOf("d");for(d.calendarWeeks===!0&&b.append(a("<th>").addClass("cw").text("#"));c.isBefore(f.clone().endOf("w"));)b.append(a("<th>").addClass("dow").text(c.format("dd"))),c.add(1,"d");o.find(".datepicker-days thead").append(b)},N=function(a){return d.disabledDates[a.format("YYYY-MM-DD")]===!0},O=function(a){return d.enabledDates[a.format("YYYY-MM-DD")]===!0},P=function(a){return d.disabledHours[a.format("H")]===!0},Q=function(a){return d.enabledHours[a.format("H")]===!0},R=function(b,c){if(!b.isValid())return!1;if(d.disabledDates&&"d"===c&&N(b))return!1;if(d.enabledDates&&"d"===c&&!O(b))return!1;if(d.minDate&&b.isBefore(d.minDate,c))return!1;if(d.maxDate&&b.isAfter(d.maxDate,c))return!1;if(d.daysOfWeekDisabled&&"d"===c&&d.daysOfWeekDisabled.indexOf(b.day())!==-1)return!1;if(d.disabledHours&&("h"===c||"m"===c||"s"===c)&&P(b))return!1;if(d.enabledHours&&("h"===c||"m"===c||"s"===c)&&!Q(b))return!1;if(d.disabledTimeIntervals&&("h"===c||"m"===c||"s"===c)){var e=!1;if(a.each(d.disabledTimeIntervals,function(){if(b.isBetween(this[0],this[1]))return e=!0,!1}),e)return!1}return!0},S=function(){for(var b=[],c=f.clone().startOf("y").startOf("d");c.isSame(f,"y");)b.push(a("<span>").attr("data-action","selectMonth").addClass("month").text(c.format("MMM"))),c.add(1,"M");o.find(".datepicker-months td").empty().append(b)},T=function(){var b=o.find(".datepicker-months"),c=b.find("th"),g=b.find("tbody").find("span");c.eq(0).find("span").attr("title",d.tooltips.prevYear),c.eq(1).attr("title",d.tooltips.selectYear),c.eq(2).find("span").attr("title",d.tooltips.nextYear),b.find(".disabled").removeClass("disabled"),R(f.clone().subtract(1,"y"),"y")||c.eq(0).addClass("disabled"),c.eq(1).text(f.year()),R(f.clone().add(1,"y"),"y")||c.eq(2).addClass("disabled"),g.removeClass("active"),e.isSame(f,"y")&&!m&&g.eq(e.month()).addClass("active"),g.each(function(b){R(f.clone().month(b),"M")||a(this).addClass("disabled")})},U=function(){var a=o.find(".datepicker-years"),b=a.find("th"),c=f.clone().subtract(5,"y"),g=f.clone().add(6,"y"),h="";for(b.eq(0).find("span").attr("title",d.tooltips.prevDecade),b.eq(1).attr("title",d.tooltips.selectDecade),b.eq(2).find("span").attr("title",d.tooltips.nextDecade),a.find(".disabled").removeClass("disabled"),d.minDate&&d.minDate.isAfter(c,"y")&&b.eq(0).addClass("disabled"),b.eq(1).text(c.year()+"-"+g.year()),d.maxDate&&d.maxDate.isBefore(g,"y")&&b.eq(2).addClass("disabled");!c.isAfter(g,"y");)h+='<span data-action="selectYear" class="year'+(c.isSame(e,"y")&&!m?" active":"")+(R(c,"y")?"":" disabled")+'">'+c.year()+"</span>",c.add(1,"y");a.find("td").html(h)},V=function(){var a,c=o.find(".datepicker-decades"),g=c.find("th"),h=b({y:f.year()-f.year()%100-1}),i=h.clone().add(100,"y"),j=h.clone(),k=!1,l=!1,m="";for(g.eq(0).find("span").attr("title",d.tooltips.prevCentury),g.eq(2).find("span").attr("title",d.tooltips.nextCentury),c.find(".disabled").removeClass("disabled"),(h.isSame(b({y:1900}))||d.minDate&&d.minDate.isAfter(h,"y"))&&g.eq(0).addClass("disabled"),g.eq(1).text(h.year()+"-"+i.year()),(h.isSame(b({y:2e3}))||d.maxDate&&d.maxDate.isBefore(i,"y"))&&g.eq(2).addClass("disabled");!h.isAfter(i,"y");)a=h.year()+12,k=d.minDate&&d.minDate.isAfter(h,"y")&&d.minDate.year()<=a,l=d.maxDate&&d.maxDate.isAfter(h,"y")&&d.maxDate.year()<=a,m+='<span data-action="selectDecade" class="decade'+(e.isAfter(h)&&e.year()<=a?" active":"")+(R(h,"y")||k||l?"":" disabled")+'" data-selection="'+(h.year()+6)+'">'+(h.year()+1)+" - "+(h.year()+12)+"</span>",h.add(12,"y");m+="<span></span><span></span><span></span>",c.find("td").html(m),g.eq(1).text(j.year()+1+"-"+h.year())},W=function(){var b,c,g,h=o.find(".datepicker-days"),i=h.find("th"),j=[],k=[];if(B()){for(i.eq(0).find("span").attr("title",d.tooltips.prevMonth),i.eq(1).attr("title",d.tooltips.selectMonth),i.eq(2).find("span").attr("title",d.tooltips.nextMonth),h.find(".disabled").removeClass("disabled"),i.eq(1).text(f.format(d.dayViewHeaderFormat)),R(f.clone().subtract(1,"M"),"M")||i.eq(0).addClass("disabled"),R(f.clone().add(1,"M"),"M")||i.eq(2).addClass("disabled"),b=f.clone().startOf("M").startOf("w").startOf("d"),g=0;g<42;g++)0===b.weekday()&&(c=a("<tr>"),d.calendarWeeks&&c.append('<td class="cw">'+b.week()+"</td>"),j.push(c)),k=["day"],b.isBefore(f,"M")&&k.push("old"),b.isAfter(f,"M")&&k.push("new"),b.isSame(e,"d")&&!m&&k.push("active"),R(b,"d")||k.push("disabled"),b.isSame(y(),"d")&&k.push("today"),0!==b.day()&&6!==b.day()||k.push("weekend"),J({type:"dp.classify",date:b,classNames:k}),c.append('<td data-action="selectDay" data-day="'+b.format("L")+'" class="'+k.join(" ")+'">'+b.date()+"</td>"),b.add(1,"d");h.find("tbody").empty().append(j),T(),U(),V()}},X=function(){var b=o.find(".timepicker-hours table"),c=f.clone().startOf("d"),d=[],e=a("<tr>");for(f.hour()>11&&!h&&c.hour(12);c.isSame(f,"d")&&(h||f.hour()<12&&c.hour()<12||f.hour()>11);)c.hour()%4===0&&(e=a("<tr>"),d.push(e)),e.append('<td data-action="selectHour" class="hour'+(R(c,"h")?"":" disabled")+'">'+c.format(h?"HH":"hh")+"</td>"),c.add(1,"h");b.empty().append(d)},Y=function(){for(var b=o.find(".timepicker-minutes table"),c=f.clone().startOf("h"),e=[],g=a("<tr>"),h=1===d.stepping?5:d.stepping;f.isSame(c,"h");)c.minute()%(4*h)===0&&(g=a("<tr>"),e.push(g)),g.append('<td data-action="selectMinute" class="minute'+(R(c,"m")?"":" disabled")+'">'+c.format("mm")+"</td>"),c.add(h,"m");b.empty().append(e)},Z=function(){for(var b=o.find(".timepicker-seconds table"),c=f.clone().startOf("m"),d=[],e=a("<tr>");f.isSame(c,"m");)c.second()%20===0&&(e=a("<tr>"),d.push(e)),e.append('<td data-action="selectSecond" class="second'+(R(c,"s")?"":" disabled")+'">'+c.format("ss")+"</td>"),c.add(5,"s");b.empty().append(d)},$=function(){var a,b,c=o.find(".timepicker span[data-time-component]");h||(a=o.find(".timepicker [data-action=togglePeriod]"),b=e.clone().add(e.hours()>=12?-12:12,"h"),a.text(e.format("A")),R(b,"h")?a.removeClass("disabled"):a.addClass("disabled")),c.filter("[data-time-component=hours]").text(e.format(h?"HH":"hh")),c.filter("[data-time-component=minutes]").text(e.format("mm")),c.filter("[data-time-component=seconds]").text(e.format("ss")),X(),Y(),Z()},_=function(){o&&(W(),$())},aa=function(a){var b=m?null:e;if(!a)return m=!0,g.val(""),c.data("date",""),J({type:"dp.change",date:!1,oldDate:b}),void _();if(a=a.clone().locale(d.locale),x()&&a.tz(d.timeZone),1!==d.stepping)for(a.minutes(Math.round(a.minutes()/d.stepping)*d.stepping).seconds(0);d.minDate&&a.isBefore(d.minDate);)a.add(d.stepping,"minutes");R(a)?(e=a,f=e.clone(),g.val(e.format(i)),c.data("date",e.format(i)),m=!1,_(),J({type:"dp.change",date:e.clone(),oldDate:b})):(d.keepInvalid?J({type:"dp.change",date:a,oldDate:b}):g.val(m?"":e.format(i)),J({type:"dp.error",date:a,oldDate:b}))},ba=function(){var b=!1;return o?(o.find(".collapse").each(function(){var c=a(this).data("collapse");return!c||!c.transitioning||(b=!0,!1)}),b?l:(n&&n.hasClass("btn")&&n.toggleClass("active"),o.hide(),a(window).off("resize",I),o.off("click","[data-action]"),o.off("mousedown",!1),o.remove(),o=!1,J({type:"dp.hide",date:e.clone()}),g.blur(),f=e.clone(),l)):l},ca=function(){aa(null)},da=function(a){return void 0===d.parseInputDate?(!b.isMoment(a)||a instanceof Date)&&(a=y(a)):a=d.parseInputDate(a),a},ea={next:function(){var a=q[k].navFnc;f.add(q[k].navStep,a),W(),K(a)},previous:function(){var a=q[k].navFnc;f.subtract(q[k].navStep,a),W(),K(a)},pickerSwitch:function(){L(1)},selectMonth:function(b){var c=a(b.target).closest("tbody").find("span").index(a(b.target));f.month(c),k===p?(aa(e.clone().year(f.year()).month(f.month())),d.inline||ba()):(L(-1),W()),K("M")},selectYear:function(b){var c=parseInt(a(b.target).text(),10)||0;f.year(c),k===p?(aa(e.clone().year(f.year())),d.inline||ba()):(L(-1),W()),K("YYYY")},selectDecade:function(b){var c=parseInt(a(b.target).data("selection"),10)||0;f.year(c),k===p?(aa(e.clone().year(f.year())),d.inline||ba()):(L(-1),W()),K("YYYY")},selectDay:function(b){var c=f.clone();a(b.target).is(".old")&&c.subtract(1,"M"),a(b.target).is(".new")&&c.add(1,"M"),aa(c.date(parseInt(a(b.target).text(),10))),A()||d.keepOpen||d.inline||ba()},incrementHours:function(){var a=e.clone().add(1,"h");R(a,"h")&&aa(a)},incrementMinutes:function(){var a=e.clone().add(d.stepping,"m");R(a,"m")&&aa(a)},incrementSeconds:function(){var a=e.clone().add(1,"s");R(a,"s")&&aa(a)},decrementHours:function(){var a=e.clone().subtract(1,"h");R(a,"h")&&aa(a)},decrementMinutes:function(){var a=e.clone().subtract(d.stepping,"m");R(a,"m")&&aa(a)},decrementSeconds:function(){var a=e.clone().subtract(1,"s");R(a,"s")&&aa(a)},togglePeriod:function(){aa(e.clone().add(e.hours()>=12?-12:12,"h"))},togglePicker:function(b){var c,e=a(b.target),f=e.closest("ul"),g=f.find(".in"),h=f.find(".collapse:not(.in)");if(g&&g.length){if(c=g.data("collapse"),c&&c.transitioning)return;g.collapse?(g.collapse("hide"),h.collapse("show")):(g.removeClass("in"),h.addClass("in")),e.is("span")?e.toggleClass(d.icons.time+" "+d.icons.date):e.find("span").toggleClass(d.icons.time+" "+d.icons.date)}},showPicker:function(){o.find(".timepicker > div:not(.timepicker-picker)").hide(),o.find(".timepicker .timepicker-picker").show()},showHours:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-hours").show()},showMinutes:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-minutes").show()},showSeconds:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-seconds").show()},selectHour:function(b){var c=parseInt(a(b.target).text(),10);h||(e.hours()>=12?12!==c&&(c+=12):12===c&&(c=0)),aa(e.clone().hours(c)),ea.showPicker.call(l)},selectMinute:function(b){aa(e.clone().minutes(parseInt(a(b.target).text(),10))),ea.showPicker.call(l)},selectSecond:function(b){aa(e.clone().seconds(parseInt(a(b.target).text(),10))),ea.showPicker.call(l)},clear:ca,today:function(){var a=y();R(a,"d")&&aa(a)},close:ba},fa=function(b){return!a(b.currentTarget).is(".disabled")&&(ea[a(b.currentTarget).data("action")].apply(l,arguments),!1)},ga=function(){var b,c={year:function(a){return a.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(a){return a.date(1).hours(0).seconds(0).minutes(0)},day:function(a){return a.hours(0).seconds(0).minutes(0)},hour:function(a){return a.seconds(0).minutes(0)},minute:function(a){return a.seconds(0)}};return g.prop("disabled")||!d.ignoreReadonly&&g.prop("readonly")||o?l:(void 0!==g.val()&&0!==g.val().trim().length?aa(da(g.val().trim())):m&&d.useCurrent&&(d.inline||g.is("input")&&0===g.val().trim().length)&&(b=y(),"string"==typeof d.useCurrent&&(b=c[d.useCurrent](b)),aa(b)),o=G(),M(),S(),o.find(".timepicker-hours").hide(),o.find(".timepicker-minutes").hide(),o.find(".timepicker-seconds").hide(),_(),L(),a(window).on("resize",I),o.on("click","[data-action]",fa),o.on("mousedown",!1),n&&n.hasClass("btn")&&n.toggleClass("active"),I(),o.show(),d.focusOnShow&&!g.is(":focus")&&g.focus(),J({type:"dp.show"}),l)},ha=function(){return o?ba():ga()},ia=function(a){var b,c,e,f,g=null,h=[],i={},j=a.which,k="p";w[j]=k;for(b in w)w.hasOwnProperty(b)&&w[b]===k&&(h.push(b),parseInt(b,10)!==j&&(i[b]=!0));for(b in d.keyBinds)if(d.keyBinds.hasOwnProperty(b)&&"function"==typeof d.keyBinds[b]&&(e=b.split(" "),e.length===h.length&&v[j]===e[e.length-1])){for(f=!0,c=e.length-2;c>=0;c--)if(!(v[e[c]]in i)){f=!1;break}if(f){g=d.keyBinds[b];break}}g&&(g.call(l,o),a.stopPropagation(),a.preventDefault())},ja=function(a){w[a.which]="r",a.stopPropagation(),a.preventDefault()},ka=function(b){var c=a(b.target).val().trim(),d=c?da(c):null;return aa(d),b.stopImmediatePropagation(),!1},la=function(){g.on({change:ka,blur:d.debug?"":ba,keydown:ia,keyup:ja,focus:d.allowInputToggle?ga:""}),c.is("input")?g.on({focus:ga}):n&&(n.on("click",ha),n.on("mousedown",!1))},ma=function(){g.off({change:ka,blur:blur,keydown:ia,keyup:ja,focus:d.allowInputToggle?ba:""}),c.is("input")?g.off({focus:ga}):n&&(n.off("click",ha),n.off("mousedown",!1))},na=function(b){var c={};return a.each(b,function(){var a=da(this);a.isValid()&&(c[a.format("YYYY-MM-DD")]=!0)}),!!Object.keys(c).length&&c},oa=function(b){var c={};return a.each(b,function(){c[this]=!0}),!!Object.keys(c).length&&c},pa=function(){var a=d.format||"L LT";i=a.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){var b=e.localeData().longDateFormat(a)||a;return b.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){return e.localeData().longDateFormat(a)||a})}),j=d.extraFormats?d.extraFormats.slice():[],j.indexOf(a)<0&&j.indexOf(i)<0&&j.push(i),h=i.toLowerCase().indexOf("a")<1&&i.replace(/\[.*?\]/g,"").indexOf("h")<1,z("y")&&(p=2),z("M")&&(p=1),z("d")&&(p=0),k=Math.max(p,k),m||aa(e)};if(l.destroy=function(){ba(),ma(),c.removeData("DateTimePicker"),c.removeData("date")},l.toggle=ha,l.show=ga,l.hide=ba,l.disable=function(){return ba(),n&&n.hasClass("btn")&&n.addClass("disabled"),g.prop("disabled",!0),l},l.enable=function(){return n&&n.hasClass("btn")&&n.removeClass("disabled"),g.prop("disabled",!1),l},l.ignoreReadonly=function(a){if(0===arguments.length)return d.ignoreReadonly;if("boolean"!=typeof a)throw new TypeError("ignoreReadonly () expects a boolean parameter");return d.ignoreReadonly=a,l},l.options=function(b){if(0===arguments.length)return a.extend(!0,{},d);if(!(b instanceof Object))throw new TypeError("options() options parameter should be an object");return a.extend(!0,d,b),a.each(d,function(a,b){if(void 0===l[a])throw new TypeError("option "+a+" is not recognized!");l[a](b)}),l},l.date=function(a){if(0===arguments.length)return m?null:e.clone();if(!(null===a||"string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");return aa(null===a?null:da(a)),l},l.format=function(a){if(0===arguments.length)return d.format;if("string"!=typeof a&&("boolean"!=typeof a||a!==!1))throw new TypeError("format() expects a string or boolean:false parameter "+a);return d.format=a,i&&pa(),l},l.timeZone=function(a){if(0===arguments.length)return d.timeZone;if("string"!=typeof a)throw new TypeError("newZone() expects a string parameter");return d.timeZone=a,l},l.dayViewHeaderFormat=function(a){if(0===arguments.length)return d.dayViewHeaderFormat;if("string"!=typeof a)throw new TypeError("dayViewHeaderFormat() expects a string parameter");return d.dayViewHeaderFormat=a,l},l.extraFormats=function(a){if(0===arguments.length)return d.extraFormats;if(a!==!1&&!(a instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");return d.extraFormats=a,j&&pa(),l},l.disabledDates=function(b){if(0===arguments.length)return d.disabledDates?a.extend({},d.disabledDates):d.disabledDates;if(!b)return d.disabledDates=!1,_(),l;if(!(b instanceof Array))throw new TypeError("disabledDates() expects an array parameter");return d.disabledDates=na(b),d.enabledDates=!1,_(),l},l.enabledDates=function(b){if(0===arguments.length)return d.enabledDates?a.extend({},d.enabledDates):d.enabledDates;if(!b)return d.enabledDates=!1,_(),l;if(!(b instanceof Array))throw new TypeError("enabledDates() expects an array parameter");return d.enabledDates=na(b),d.disabledDates=!1,_(),l},l.daysOfWeekDisabled=function(a){if(0===arguments.length)return d.daysOfWeekDisabled.splice(0);if("boolean"==typeof a&&!a)return d.daysOfWeekDisabled=!1,_(),l;if(!(a instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(d.daysOfWeekDisabled=a.reduce(function(a,b){return b=parseInt(b,10),b>6||b<0||isNaN(b)?a:(a.indexOf(b)===-1&&a.push(b),a)},[]).sort(),d.useCurrent&&!d.keepInvalid){for(var b=0;!R(e,"d");){if(e.add(1,"d"),31===b)throw"Tried 31 times to find a valid date";b++}aa(e)}return _(),l},l.maxDate=function(a){if(0===arguments.length)return d.maxDate?d.maxDate.clone():d.maxDate;if("boolean"==typeof a&&a===!1)return d.maxDate=!1,_(),l;"string"==typeof a&&("now"!==a&&"moment"!==a||(a=y()));var b=da(a);if(!b.isValid())throw new TypeError("maxDate() Could not parse date parameter: "+a);if(d.minDate&&b.isBefore(d.minDate))throw new TypeError("maxDate() date parameter is before options.minDate: "+b.format(i));return d.maxDate=b,d.useCurrent&&!d.keepInvalid&&e.isAfter(a)&&aa(d.maxDate),f.isAfter(b)&&(f=b.clone().subtract(d.stepping,"m")),_(),l},l.minDate=function(a){if(0===arguments.length)return d.minDate?d.minDate.clone():d.minDate;if("boolean"==typeof a&&a===!1)return d.minDate=!1,_(),l;"string"==typeof a&&("now"!==a&&"moment"!==a||(a=y()));var b=da(a);if(!b.isValid())throw new TypeError("minDate() Could not parse date parameter: "+a);if(d.maxDate&&b.isAfter(d.maxDate))throw new TypeError("minDate() date parameter is after options.maxDate: "+b.format(i));return d.minDate=b,d.useCurrent&&!d.keepInvalid&&e.isBefore(a)&&aa(d.minDate),f.isBefore(b)&&(f=b.clone().add(d.stepping,"m")),_(),l},l.defaultDate=function(a){if(0===arguments.length)return d.defaultDate?d.defaultDate.clone():d.defaultDate;if(!a)return d.defaultDate=!1,l;"string"==typeof a&&(a="now"===a||"moment"===a?y():y(a));var b=da(a);if(!b.isValid())throw new TypeError("defaultDate() Could not parse date parameter: "+a);if(!R(b))throw new TypeError("defaultDate() date passed is invalid according to component setup validations");return d.defaultDate=b,(d.defaultDate&&d.inline||""===g.val().trim())&&aa(d.defaultDate),l},l.locale=function(a){if(0===arguments.length)return d.locale;if(!b.localeData(a))throw new TypeError("locale() locale "+a+" is not loaded from moment locales!");return d.locale=a,e.locale(d.locale),f.locale(d.locale),i&&pa(),o&&(ba(),ga()),l},l.stepping=function(a){return 0===arguments.length?d.stepping:(a=parseInt(a,10),(isNaN(a)||a<1)&&(a=1),d.stepping=a,l)},l.useCurrent=function(a){var b=["year","month","day","hour","minute"];if(0===arguments.length)return d.useCurrent;if("boolean"!=typeof a&&"string"!=typeof a)throw new TypeError("useCurrent() expects a boolean or string parameter");if("string"==typeof a&&b.indexOf(a.toLowerCase())===-1)throw new TypeError("useCurrent() expects a string parameter of "+b.join(", "));return d.useCurrent=a,l},l.collapse=function(a){if(0===arguments.length)return d.collapse;if("boolean"!=typeof a)throw new TypeError("collapse() expects a boolean parameter");return d.collapse===a?l:(d.collapse=a,o&&(ba(),ga()),l)},l.icons=function(b){if(0===arguments.length)return a.extend({},d.icons);if(!(b instanceof Object))throw new TypeError("icons() expects parameter to be an Object");return a.extend(d.icons,b),o&&(ba(),ga()),l},l.tooltips=function(b){if(0===arguments.length)return a.extend({},d.tooltips);if(!(b instanceof Object))throw new TypeError("tooltips() expects parameter to be an Object");return a.extend(d.tooltips,b),o&&(ba(),ga()),l},l.useStrict=function(a){if(0===arguments.length)return d.useStrict;if("boolean"!=typeof a)throw new TypeError("useStrict() expects a boolean parameter");return d.useStrict=a,l},l.sideBySide=function(a){if(0===arguments.length)return d.sideBySide;if("boolean"!=typeof a)throw new TypeError("sideBySide() expects a boolean parameter");return d.sideBySide=a,o&&(ba(),ga()),l},l.viewMode=function(a){if(0===arguments.length)return d.viewMode;if("string"!=typeof a)throw new TypeError("viewMode() expects a string parameter");if(r.indexOf(a)===-1)throw new TypeError("viewMode() parameter must be one of ("+r.join(", ")+") value");return d.viewMode=a,k=Math.max(r.indexOf(a),p),L(),l},l.toolbarPlacement=function(a){if(0===arguments.length)return d.toolbarPlacement;if("string"!=typeof a)throw new TypeError("toolbarPlacement() expects a string parameter");if(u.indexOf(a)===-1)throw new TypeError("toolbarPlacement() parameter must be one of ("+u.join(", ")+") value");return d.toolbarPlacement=a,o&&(ba(),ga()),l},l.widgetPositioning=function(b){if(0===arguments.length)return a.extend({},d.widgetPositioning);if("[object Object]"!=={}.toString.call(b))throw new TypeError("widgetPositioning() expects an object variable");if(b.horizontal){if("string"!=typeof b.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(b.horizontal=b.horizontal.toLowerCase(),t.indexOf(b.horizontal)===-1)throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+t.join(", ")+")");d.widgetPositioning.horizontal=b.horizontal}if(b.vertical){if("string"!=typeof b.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(b.vertical=b.vertical.toLowerCase(),s.indexOf(b.vertical)===-1)throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+s.join(", ")+")");d.widgetPositioning.vertical=b.vertical}return _(),l},l.calendarWeeks=function(a){if(0===arguments.length)return d.calendarWeeks;if("boolean"!=typeof a)throw new TypeError("calendarWeeks() expects parameter to be a boolean value");return d.calendarWeeks=a,_(),l},l.showTodayButton=function(a){if(0===arguments.length)return d.showTodayButton;if("boolean"!=typeof a)throw new TypeError("showTodayButton() expects a boolean parameter");return d.showTodayButton=a,o&&(ba(),ga()),l},l.showClear=function(a){if(0===arguments.length)return d.showClear;if("boolean"!=typeof a)throw new TypeError("showClear() expects a boolean parameter");return d.showClear=a,o&&(ba(),ga()),l},l.widgetParent=function(b){if(0===arguments.length)return d.widgetParent;if("string"==typeof b&&(b=a(b)),null!==b&&"string"!=typeof b&&!(b instanceof a))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");return d.widgetParent=b,o&&(ba(),ga()),l},l.keepOpen=function(a){if(0===arguments.length)return d.keepOpen;if("boolean"!=typeof a)throw new TypeError("keepOpen() expects a boolean parameter");return d.keepOpen=a,l},l.focusOnShow=function(a){if(0===arguments.length)return d.focusOnShow;if("boolean"!=typeof a)throw new TypeError("focusOnShow() expects a boolean parameter");return d.focusOnShow=a,l},l.inline=function(a){if(0===arguments.length)return d.inline;if("boolean"!=typeof a)throw new TypeError("inline() expects a boolean parameter");return d.inline=a,l},l.clear=function(){return ca(),l},l.keyBinds=function(a){return 0===arguments.length?d.keyBinds:(d.keyBinds=a,l)},l.getMoment=function(a){return y(a)},l.debug=function(a){if("boolean"!=typeof a)throw new TypeError("debug() expects a boolean parameter");return d.debug=a,l},l.allowInputToggle=function(a){if(0===arguments.length)return d.allowInputToggle;if("boolean"!=typeof a)throw new TypeError("allowInputToggle() expects a boolean parameter");return d.allowInputToggle=a,l},l.showClose=function(a){if(0===arguments.length)return d.showClose;if("boolean"!=typeof a)throw new TypeError("showClose() expects a boolean parameter");return d.showClose=a,l},l.keepInvalid=function(a){if(0===arguments.length)return d.keepInvalid;if("boolean"!=typeof a)throw new TypeError("keepInvalid() expects a boolean parameter");
605
- return d.keepInvalid=a,l},l.datepickerInput=function(a){if(0===arguments.length)return d.datepickerInput;if("string"!=typeof a)throw new TypeError("datepickerInput() expects a string parameter");return d.datepickerInput=a,l},l.parseInputDate=function(a){if(0===arguments.length)return d.parseInputDate;if("function"!=typeof a)throw new TypeError("parseInputDate() sholud be as function");return d.parseInputDate=a,l},l.disabledTimeIntervals=function(b){if(0===arguments.length)return d.disabledTimeIntervals?a.extend({},d.disabledTimeIntervals):d.disabledTimeIntervals;if(!b)return d.disabledTimeIntervals=!1,_(),l;if(!(b instanceof Array))throw new TypeError("disabledTimeIntervals() expects an array parameter");return d.disabledTimeIntervals=b,_(),l},l.disabledHours=function(b){if(0===arguments.length)return d.disabledHours?a.extend({},d.disabledHours):d.disabledHours;if(!b)return d.disabledHours=!1,_(),l;if(!(b instanceof Array))throw new TypeError("disabledHours() expects an array parameter");if(d.disabledHours=oa(b),d.enabledHours=!1,d.useCurrent&&!d.keepInvalid){for(var c=0;!R(e,"h");){if(e.add(1,"h"),24===c)throw"Tried 24 times to find a valid date";c++}aa(e)}return _(),l},l.enabledHours=function(b){if(0===arguments.length)return d.enabledHours?a.extend({},d.enabledHours):d.enabledHours;if(!b)return d.enabledHours=!1,_(),l;if(!(b instanceof Array))throw new TypeError("enabledHours() expects an array parameter");if(d.enabledHours=oa(b),d.disabledHours=!1,d.useCurrent&&!d.keepInvalid){for(var c=0;!R(e,"h");){if(e.add(1,"h"),24===c)throw"Tried 24 times to find a valid date";c++}aa(e)}return _(),l},l.viewDate=function(a){if(0===arguments.length)return f.clone();if(!a)return f=e.clone(),l;if(!("string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("viewDate() parameter must be one of [string, moment or Date]");return f=da(a),K(),l},c.is("input"))g=c;else if(g=c.find(d.datepickerInput),0===g.length)g=c.find("input");else if(!g.is("input"))throw new Error('CSS class "'+d.datepickerInput+'" cannot be applied to non input element');if(c.hasClass("input-group")&&(n=0===c.find(".datepickerbutton").length?c.find(".input-group-addon"):c.find(".datepickerbutton")),!d.inline&&!g.is("input"))throw new Error("Could not initialize DateTimePicker without an input element");return e=y(),f=e.clone(),a.extend(!0,d,H()),l.options(d),pa(),la(),g.prop("disabled")&&l.disable(),g.is("input")&&0!==g.val().trim().length?aa(da(g.val().trim())):d.defaultDate&&void 0===g.attr("placeholder")&&aa(d.defaultDate),d.inline&&ga(),l};return a.fn.datetimepicker=function(b){b=b||{};var d,e=Array.prototype.slice.call(arguments,1),f=!0,g=["destroy","hide","show","toggle"];if("object"==typeof b)return this.each(function(){var d,e=a(this);e.data("DateTimePicker")||(d=a.extend(!0,{},a.fn.datetimepicker.defaults,b),e.data("DateTimePicker",c(e,d)))});if("string"==typeof b)return this.each(function(){var c=a(this),g=c.data("DateTimePicker");if(!g)throw new Error('bootstrap-datetimepicker("'+b+'") method was called on an element that is not using DateTimePicker');d=g[b].apply(g,e),f=d===g}),f||a.inArray(b,g)>-1?this:d;throw new TypeError("Invalid arguments for DateTimePicker: "+b)},a.fn.datetimepicker.defaults={timeZone:"",format:!1,dayViewHeaderFormat:"MMMM YYYY",extraFormats:!1,stepping:1,minDate:!1,maxDate:!1,useCurrent:!0,collapse:!0,locale:b.locale(),defaultDate:!1,disabledDates:!1,enabledDates:!1,icons:{time:"glyphicon glyphicon-time",date:"glyphicon glyphicon-calendar",up:"glyphicon glyphicon-chevron-up",down:"glyphicon glyphicon-chevron-down",previous:"glyphicon glyphicon-chevron-left",next:"glyphicon glyphicon-chevron-right",today:"glyphicon glyphicon-screenshot",clear:"glyphicon glyphicon-trash",close:"glyphicon glyphicon-remove"},tooltips:{today:"Go to today",clear:"Clear selection",close:"Close the picker",selectMonth:"Select Month",prevMonth:"Previous Month",nextMonth:"Next Month",selectYear:"Select Year",prevYear:"Previous Year",nextYear:"Next Year",selectDecade:"Select Decade",prevDecade:"Previous Decade",nextDecade:"Next Decade",prevCentury:"Previous Century",nextCentury:"Next Century",pickHour:"Pick Hour",incrementHour:"Increment Hour",decrementHour:"Decrement Hour",pickMinute:"Pick Minute",incrementMinute:"Increment Minute",decrementMinute:"Decrement Minute",pickSecond:"Pick Second",incrementSecond:"Increment Second",decrementSecond:"Decrement Second",togglePeriod:"Toggle Period",selectTime:"Select Time"},useStrict:!1,sideBySide:!1,daysOfWeekDisabled:!1,calendarWeeks:!1,viewMode:"days",toolbarPlacement:"default",showTodayButton:!1,showClear:!1,showClose:!1,widgetPositioning:{horizontal:"auto",vertical:"auto"},widgetParent:null,ignoreReadonly:!1,keepOpen:!1,focusOnShow:!0,inline:!1,keepInvalid:!1,datepickerInput:".datepickerinput",keyBinds:{up:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().subtract(7,"d")):this.date(b.clone().add(this.stepping(),"m"))}},down:function(a){if(!a)return void this.show();var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().add(7,"d")):this.date(b.clone().subtract(this.stepping(),"m"))},"control up":function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().subtract(1,"y")):this.date(b.clone().add(1,"h"))}},"control down":function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().add(1,"y")):this.date(b.clone().subtract(1,"h"))}},left:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().subtract(1,"d"))}},right:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().add(1,"d"))}},pageUp:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().subtract(1,"M"))}},pageDown:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().add(1,"M"))}},enter:function(){this.hide()},escape:function(){this.hide()},"control space":function(a){a&&a.find(".timepicker").is(":visible")&&a.find('.btn[data-action="togglePeriod"]').click()},t:function(){this.date(this.getMoment())},delete:function(){this.clear()}},debug:!1,allowInputToggle:!1,disabledTimeIntervals:!1,disabledHours:!1,enabledHours:!1,viewDate:!1},a.fn.datetimepicker});
 
 
 
 
 
 
 
 
602
  // Bootstrap datapicker
603
  // https://eonasdan.github.io/bootstrap-datetimepicker/
604
  !function(a){"use strict";if("function"==typeof define&&define.amd)define(["jquery","moment"],a);else if("object"==typeof exports)module.exports=a(require("jquery"),require("moment"));else{if("undefined"==typeof jQuery)throw"bootstrap-datetimepicker requires jQuery to be loaded first";if("undefined"==typeof moment)throw"bootstrap-datetimepicker requires Moment.js to be loaded first";a(jQuery,moment)}}(function(a,b){"use strict";if(!b)throw new Error("bootstrap-datetimepicker requires Moment.js to be loaded first");var c=function(c,d){var e,f,g,h,i,j,k,l={},m=!0,n=!1,o=!1,p=0,q=[{clsName:"days",navFnc:"M",navStep:1},{clsName:"months",navFnc:"y",navStep:1},{clsName:"years",navFnc:"y",navStep:10},{clsName:"decades",navFnc:"y",navStep:100}],r=["days","months","years","decades"],s=["top","bottom","auto"],t=["left","right","auto"],u=["default","top","bottom"],v={up:38,38:"up",down:40,40:"down",left:37,37:"left",right:39,39:"right",tab:9,9:"tab",escape:27,27:"escape",enter:13,13:"enter",pageUp:33,33:"pageUp",pageDown:34,34:"pageDown",shift:16,16:"shift",control:17,17:"control",space:32,32:"space",t:84,84:"t",delete:46,46:"delete"},w={},x=function(){return void 0!==b.tz&&void 0!==d.timeZone&&null!==d.timeZone&&""!==d.timeZone},y=function(a){var c;return c=void 0===a||null===a?b():b.isDate(a)||b.isMoment(a)?b(a):x()?b.tz(a,j,d.useStrict,d.timeZone):b(a,j,d.useStrict),x()&&c.tz(d.timeZone),c},z=function(a){if("string"!=typeof a||a.length>1)throw new TypeError("isEnabled expects a single character string parameter");switch(a){case"y":return i.indexOf("Y")!==-1;case"M":return i.indexOf("M")!==-1;case"d":return i.toLowerCase().indexOf("d")!==-1;case"h":case"H":return i.toLowerCase().indexOf("h")!==-1;case"m":return i.indexOf("m")!==-1;case"s":return i.indexOf("s")!==-1;default:return!1}},A=function(){return z("h")||z("m")||z("s")},B=function(){return z("y")||z("M")||z("d")},C=function(){var b=a("<thead>").append(a("<tr>").append(a("<th>").addClass("prev").attr("data-action","previous").append(a("<span>").addClass(d.icons.previous))).append(a("<th>").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",d.calendarWeeks?"6":"5")).append(a("<th>").addClass("next").attr("data-action","next").append(a("<span>").addClass(d.icons.next)))),c=a("<tbody>").append(a("<tr>").append(a("<td>").attr("colspan",d.calendarWeeks?"8":"7")));return[a("<div>").addClass("datepicker-days").append(a("<table>").addClass("table-condensed").append(b).append(a("<tbody>"))),a("<div>").addClass("datepicker-months").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone())),a("<div>").addClass("datepicker-years").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone())),a("<div>").addClass("datepicker-decades").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone()))]},D=function(){var b=a("<tr>"),c=a("<tr>"),e=a("<tr>");return z("h")&&(b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-hour").attr({"data-time-component":"hours",title:d.tooltips.pickHour}).attr("data-action","showHours"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(a("<span>").addClass(d.icons.down))))),z("m")&&(z("h")&&(b.append(a("<td>").addClass("separator")),c.append(a("<td>").addClass("separator").html(":")),e.append(a("<td>").addClass("separator"))),b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:d.tooltips.pickMinute}).attr("data-action","showMinutes"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(a("<span>").addClass(d.icons.down))))),z("s")&&(z("m")&&(b.append(a("<td>").addClass("separator")),c.append(a("<td>").addClass("separator").html(":")),e.append(a("<td>").addClass("separator"))),b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-second").attr({"data-time-component":"seconds",title:d.tooltips.pickSecond}).attr("data-action","showSeconds"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(a("<span>").addClass(d.icons.down))))),h||(b.append(a("<td>").addClass("separator")),c.append(a("<td>").append(a("<button>").addClass("btn btn-primary").attr({"data-action":"togglePeriod",tabindex:"-1",title:d.tooltips.togglePeriod}))),e.append(a("<td>").addClass("separator"))),a("<div>").addClass("timepicker-picker").append(a("<table>").addClass("table-condensed").append([b,c,e]))},E=function(){var b=a("<div>").addClass("timepicker-hours").append(a("<table>").addClass("table-condensed")),c=a("<div>").addClass("timepicker-minutes").append(a("<table>").addClass("table-condensed")),d=a("<div>").addClass("timepicker-seconds").append(a("<table>").addClass("table-condensed")),e=[D()];return z("h")&&e.push(b),z("m")&&e.push(c),z("s")&&e.push(d),e},F=function(){var b=[];return d.showTodayButton&&b.push(a("<td>").append(a("<a>").attr({"data-action":"today",title:d.tooltips.today}).append(a("<span>").addClass(d.icons.today)))),!d.sideBySide&&B()&&A()&&b.push(a("<td>").append(a("<a>").attr({"data-action":"togglePicker",title:d.tooltips.selectTime}).append(a("<span>").addClass(d.icons.time)))),d.showClear&&b.push(a("<td>").append(a("<a>").attr({"data-action":"clear",title:d.tooltips.clear}).append(a("<span>").addClass(d.icons.clear)))),d.showClose&&b.push(a("<td>").append(a("<a>").attr({"data-action":"close",title:d.tooltips.close}).append(a("<span>").addClass(d.icons.close)))),a("<table>").addClass("table-condensed").append(a("<tbody>").append(a("<tr>").append(b)))},G=function(){var b=a("<div>").addClass("bootstrap-datetimepicker-widget dropdown-menu"),c=a("<div>").addClass("datepicker").append(C()),e=a("<div>").addClass("timepicker").append(E()),f=a("<ul>").addClass("list-unstyled"),g=a("<li>").addClass("picker-switch"+(d.collapse?" accordion-toggle":"")).append(F());return d.inline&&b.removeClass("dropdown-menu"),h&&b.addClass("usetwentyfour"),z("s")&&!h&&b.addClass("wider"),d.sideBySide&&B()&&A()?(b.addClass("timepicker-sbs"),"top"===d.toolbarPlacement&&b.append(g),b.append(a("<div>").addClass("row").append(c.addClass("col-md-6")).append(e.addClass("col-md-6"))),"bottom"===d.toolbarPlacement&&b.append(g),b):("top"===d.toolbarPlacement&&f.append(g),B()&&f.append(a("<li>").addClass(d.collapse&&A()?"collapse in":"").append(c)),"default"===d.toolbarPlacement&&f.append(g),A()&&f.append(a("<li>").addClass(d.collapse&&B()?"collapse":"").append(e)),"bottom"===d.toolbarPlacement&&f.append(g),b.append(f))},H=function(){var b,e={};return b=c.is("input")||d.inline?c.data():c.find("input").data(),b.dateOptions&&b.dateOptions instanceof Object&&(e=a.extend(!0,e,b.dateOptions)),a.each(d,function(a){var c="date"+a.charAt(0).toUpperCase()+a.slice(1);void 0!==b[c]&&(e[a]=b[c])}),e},I=function(){var b,e=(n||c).position(),f=(n||c).offset(),g=d.widgetPositioning.vertical,h=d.widgetPositioning.horizontal;if(d.widgetParent)b=d.widgetParent.append(o);else if(c.is("input"))b=c.after(o).parent();else{if(d.inline)return void(b=c.append(o));b=c,c.children().first().after(o)}if("auto"===g&&(g=f.top+1.5*o.height()>=a(window).height()+a(window).scrollTop()&&o.height()+c.outerHeight()<f.top?"top":"bottom"),"auto"===h&&(h=b.width()<f.left+o.outerWidth()/2&&f.left+o.outerWidth()>a(window).width()?"right":"left"),"top"===g?o.addClass("top").removeClass("bottom"):o.addClass("bottom").removeClass("top"),"right"===h?o.addClass("pull-right"):o.removeClass("pull-right"),"static"===b.css("position")&&(b=b.parents().filter(function(){return"static"!==a(this).css("position")}).first()),0===b.length)throw new Error("datetimepicker component should be placed within a non-static positioned container");o.css({top:"top"===g?"auto":e.top+c.outerHeight(),bottom:"top"===g?b.outerHeight()-(b===c?0:e.top):"auto",left:"left"===h?b===c?0:e.left:"auto",right:"left"===h?"auto":b.outerWidth()-c.outerWidth()-(b===c?0:e.left)})},J=function(a){"dp.change"===a.type&&(a.date&&a.date.isSame(a.oldDate)||!a.date&&!a.oldDate)||c.trigger(a)},K=function(a){"y"===a&&(a="YYYY"),J({type:"dp.update",change:a,viewDate:f.clone()})},L=function(a){o&&(a&&(k=Math.max(p,Math.min(3,k+a))),o.find(".datepicker > div").hide().filter(".datepicker-"+q[k].clsName).show())},M=function(){var b=a("<tr>"),c=f.clone().startOf("w").startOf("d");for(d.calendarWeeks===!0&&b.append(a("<th>").addClass("cw").text("#"));c.isBefore(f.clone().endOf("w"));)b.append(a("<th>").addClass("dow").text(c.format("dd"))),c.add(1,"d");o.find(".datepicker-days thead").append(b)},N=function(a){return d.disabledDates[a.format("YYYY-MM-DD")]===!0},O=function(a){return d.enabledDates[a.format("YYYY-MM-DD")]===!0},P=function(a){return d.disabledHours[a.format("H")]===!0},Q=function(a){return d.enabledHours[a.format("H")]===!0},R=function(b,c){if(!b.isValid())return!1;if(d.disabledDates&&"d"===c&&N(b))return!1;if(d.enabledDates&&"d"===c&&!O(b))return!1;if(d.minDate&&b.isBefore(d.minDate,c))return!1;if(d.maxDate&&b.isAfter(d.maxDate,c))return!1;if(d.daysOfWeekDisabled&&"d"===c&&d.daysOfWeekDisabled.indexOf(b.day())!==-1)return!1;if(d.disabledHours&&("h"===c||"m"===c||"s"===c)&&P(b))return!1;if(d.enabledHours&&("h"===c||"m"===c||"s"===c)&&!Q(b))return!1;if(d.disabledTimeIntervals&&("h"===c||"m"===c||"s"===c)){var e=!1;if(a.each(d.disabledTimeIntervals,function(){if(b.isBetween(this[0],this[1]))return e=!0,!1}),e)return!1}return!0},S=function(){for(var b=[],c=f.clone().startOf("y").startOf("d");c.isSame(f,"y");)b.push(a("<span>").attr("data-action","selectMonth").addClass("month").text(c.format("MMM"))),c.add(1,"M");o.find(".datepicker-months td").empty().append(b)},T=function(){var b=o.find(".datepicker-months"),c=b.find("th"),g=b.find("tbody").find("span");c.eq(0).find("span").attr("title",d.tooltips.prevYear),c.eq(1).attr("title",d.tooltips.selectYear),c.eq(2).find("span").attr("title",d.tooltips.nextYear),b.find(".disabled").removeClass("disabled"),R(f.clone().subtract(1,"y"),"y")||c.eq(0).addClass("disabled"),c.eq(1).text(f.year()),R(f.clone().add(1,"y"),"y")||c.eq(2).addClass("disabled"),g.removeClass("active"),e.isSame(f,"y")&&!m&&g.eq(e.month()).addClass("active"),g.each(function(b){R(f.clone().month(b),"M")||a(this).addClass("disabled")})},U=function(){var a=o.find(".datepicker-years"),b=a.find("th"),c=f.clone().subtract(5,"y"),g=f.clone().add(6,"y"),h="";for(b.eq(0).find("span").attr("title",d.tooltips.prevDecade),b.eq(1).attr("title",d.tooltips.selectDecade),b.eq(2).find("span").attr("title",d.tooltips.nextDecade),a.find(".disabled").removeClass("disabled"),d.minDate&&d.minDate.isAfter(c,"y")&&b.eq(0).addClass("disabled"),b.eq(1).text(c.year()+"-"+g.year()),d.maxDate&&d.maxDate.isBefore(g,"y")&&b.eq(2).addClass("disabled");!c.isAfter(g,"y");)h+='<span data-action="selectYear" class="year'+(c.isSame(e,"y")&&!m?" active":"")+(R(c,"y")?"":" disabled")+'">'+c.year()+"</span>",c.add(1,"y");a.find("td").html(h)},V=function(){var a,c=o.find(".datepicker-decades"),g=c.find("th"),h=b({y:f.year()-f.year()%100-1}),i=h.clone().add(100,"y"),j=h.clone(),k=!1,l=!1,m="";for(g.eq(0).find("span").attr("title",d.tooltips.prevCentury),g.eq(2).find("span").attr("title",d.tooltips.nextCentury),c.find(".disabled").removeClass("disabled"),(h.isSame(b({y:1900}))||d.minDate&&d.minDate.isAfter(h,"y"))&&g.eq(0).addClass("disabled"),g.eq(1).text(h.year()+"-"+i.year()),(h.isSame(b({y:2e3}))||d.maxDate&&d.maxDate.isBefore(i,"y"))&&g.eq(2).addClass("disabled");!h.isAfter(i,"y");)a=h.year()+12,k=d.minDate&&d.minDate.isAfter(h,"y")&&d.minDate.year()<=a,l=d.maxDate&&d.maxDate.isAfter(h,"y")&&d.maxDate.year()<=a,m+='<span data-action="selectDecade" class="decade'+(e.isAfter(h)&&e.year()<=a?" active":"")+(R(h,"y")||k||l?"":" disabled")+'" data-selection="'+(h.year()+6)+'">'+(h.year()+1)+" - "+(h.year()+12)+"</span>",h.add(12,"y");m+="<span></span><span></span><span></span>",c.find("td").html(m),g.eq(1).text(j.year()+1+"-"+h.year())},W=function(){var b,c,g,h=o.find(".datepicker-days"),i=h.find("th"),j=[],k=[];if(B()){for(i.eq(0).find("span").attr("title",d.tooltips.prevMonth),i.eq(1).attr("title",d.tooltips.selectMonth),i.eq(2).find("span").attr("title",d.tooltips.nextMonth),h.find(".disabled").removeClass("disabled"),i.eq(1).text(f.format(d.dayViewHeaderFormat)),R(f.clone().subtract(1,"M"),"M")||i.eq(0).addClass("disabled"),R(f.clone().add(1,"M"),"M")||i.eq(2).addClass("disabled"),b=f.clone().startOf("M").startOf("w").startOf("d"),g=0;g<42;g++)0===b.weekday()&&(c=a("<tr>"),d.calendarWeeks&&c.append('<td class="cw">'+b.week()+"</td>"),j.push(c)),k=["day"],b.isBefore(f,"M")&&k.push("old"),b.isAfter(f,"M")&&k.push("new"),b.isSame(e,"d")&&!m&&k.push("active"),R(b,"d")||k.push("disabled"),b.isSame(y(),"d")&&k.push("today"),0!==b.day()&&6!==b.day()||k.push("weekend"),J({type:"dp.classify",date:b,classNames:k}),c.append('<td data-action="selectDay" data-day="'+b.format("L")+'" class="'+k.join(" ")+'">'+b.date()+"</td>"),b.add(1,"d");h.find("tbody").empty().append(j),T(),U(),V()}},X=function(){var b=o.find(".timepicker-hours table"),c=f.clone().startOf("d"),d=[],e=a("<tr>");for(f.hour()>11&&!h&&c.hour(12);c.isSame(f,"d")&&(h||f.hour()<12&&c.hour()<12||f.hour()>11);)c.hour()%4===0&&(e=a("<tr>"),d.push(e)),e.append('<td data-action="selectHour" class="hour'+(R(c,"h")?"":" disabled")+'">'+c.format(h?"HH":"hh")+"</td>"),c.add(1,"h");b.empty().append(d)},Y=function(){for(var b=o.find(".timepicker-minutes table"),c=f.clone().startOf("h"),e=[],g=a("<tr>"),h=1===d.stepping?5:d.stepping;f.isSame(c,"h");)c.minute()%(4*h)===0&&(g=a("<tr>"),e.push(g)),g.append('<td data-action="selectMinute" class="minute'+(R(c,"m")?"":" disabled")+'">'+c.format("mm")+"</td>"),c.add(h,"m");b.empty().append(e)},Z=function(){for(var b=o.find(".timepicker-seconds table"),c=f.clone().startOf("m"),d=[],e=a("<tr>");f.isSame(c,"m");)c.second()%20===0&&(e=a("<tr>"),d.push(e)),e.append('<td data-action="selectSecond" class="second'+(R(c,"s")?"":" disabled")+'">'+c.format("ss")+"</td>"),c.add(5,"s");b.empty().append(d)},$=function(){var a,b,c=o.find(".timepicker span[data-time-component]");h||(a=o.find(".timepicker [data-action=togglePeriod]"),b=e.clone().add(e.hours()>=12?-12:12,"h"),a.text(e.format("A")),R(b,"h")?a.removeClass("disabled"):a.addClass("disabled")),c.filter("[data-time-component=hours]").text(e.format(h?"HH":"hh")),c.filter("[data-time-component=minutes]").text(e.format("mm")),c.filter("[data-time-component=seconds]").text(e.format("ss")),X(),Y(),Z()},_=function(){o&&(W(),$())},aa=function(a){var b=m?null:e;if(!a)return m=!0,g.val(""),c.data("date",""),J({type:"dp.change",date:!1,oldDate:b}),void _();if(a=a.clone().locale(d.locale),x()&&a.tz(d.timeZone),1!==d.stepping)for(a.minutes(Math.round(a.minutes()/d.stepping)*d.stepping).seconds(0);d.minDate&&a.isBefore(d.minDate);)a.add(d.stepping,"minutes");R(a)?(e=a,f=e.clone(),g.val(e.format(i)),c.data("date",e.format(i)),m=!1,_(),J({type:"dp.change",date:e.clone(),oldDate:b})):(d.keepInvalid?J({type:"dp.change",date:a,oldDate:b}):g.val(m?"":e.format(i)),J({type:"dp.error",date:a,oldDate:b}))},ba=function(){var b=!1;return o?(o.find(".collapse").each(function(){var c=a(this).data("collapse");return!c||!c.transitioning||(b=!0,!1)}),b?l:(n&&n.hasClass("btn")&&n.toggleClass("active"),o.hide(),a(window).off("resize",I),o.off("click","[data-action]"),o.off("mousedown",!1),o.remove(),o=!1,J({type:"dp.hide",date:e.clone()}),g.blur(),f=e.clone(),l)):l},ca=function(){aa(null)},da=function(a){return void 0===d.parseInputDate?(!b.isMoment(a)||a instanceof Date)&&(a=y(a)):a=d.parseInputDate(a),a},ea={next:function(){var a=q[k].navFnc;f.add(q[k].navStep,a),W(),K(a)},previous:function(){var a=q[k].navFnc;f.subtract(q[k].navStep,a),W(),K(a)},pickerSwitch:function(){L(1)},selectMonth:function(b){var c=a(b.target).closest("tbody").find("span").index(a(b.target));f.month(c),k===p?(aa(e.clone().year(f.year()).month(f.month())),d.inline||ba()):(L(-1),W()),K("M")},selectYear:function(b){var c=parseInt(a(b.target).text(),10)||0;f.year(c),k===p?(aa(e.clone().year(f.year())),d.inline||ba()):(L(-1),W()),K("YYYY")},selectDecade:function(b){var c=parseInt(a(b.target).data("selection"),10)||0;f.year(c),k===p?(aa(e.clone().year(f.year())),d.inline||ba()):(L(-1),W()),K("YYYY")},selectDay:function(b){var c=f.clone();a(b.target).is(".old")&&c.subtract(1,"M"),a(b.target).is(".new")&&c.add(1,"M"),aa(c.date(parseInt(a(b.target).text(),10))),A()||d.keepOpen||d.inline||ba()},incrementHours:function(){var a=e.clone().add(1,"h");R(a,"h")&&aa(a)},incrementMinutes:function(){var a=e.clone().add(d.stepping,"m");R(a,"m")&&aa(a)},incrementSeconds:function(){var a=e.clone().add(1,"s");R(a,"s")&&aa(a)},decrementHours:function(){var a=e.clone().subtract(1,"h");R(a,"h")&&aa(a)},decrementMinutes:function(){var a=e.clone().subtract(d.stepping,"m");R(a,"m")&&aa(a)},decrementSeconds:function(){var a=e.clone().subtract(1,"s");R(a,"s")&&aa(a)},togglePeriod:function(){aa(e.clone().add(e.hours()>=12?-12:12,"h"))},togglePicker:function(b){var c,e=a(b.target),f=e.closest("ul"),g=f.find(".in"),h=f.find(".collapse:not(.in)");if(g&&g.length){if(c=g.data("collapse"),c&&c.transitioning)return;g.collapse?(g.collapse("hide"),h.collapse("show")):(g.removeClass("in"),h.addClass("in")),e.is("span")?e.toggleClass(d.icons.time+" "+d.icons.date):e.find("span").toggleClass(d.icons.time+" "+d.icons.date)}},showPicker:function(){o.find(".timepicker > div:not(.timepicker-picker)").hide(),o.find(".timepicker .timepicker-picker").show()},showHours:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-hours").show()},showMinutes:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-minutes").show()},showSeconds:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-seconds").show()},selectHour:function(b){var c=parseInt(a(b.target).text(),10);h||(e.hours()>=12?12!==c&&(c+=12):12===c&&(c=0)),aa(e.clone().hours(c)),ea.showPicker.call(l)},selectMinute:function(b){aa(e.clone().minutes(parseInt(a(b.target).text(),10))),ea.showPicker.call(l)},selectSecond:function(b){aa(e.clone().seconds(parseInt(a(b.target).text(),10))),ea.showPicker.call(l)},clear:ca,today:function(){var a=y();R(a,"d")&&aa(a)},close:ba},fa=function(b){return!a(b.currentTarget).is(".disabled")&&(ea[a(b.currentTarget).data("action")].apply(l,arguments),!1)},ga=function(){var b,c={year:function(a){return a.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(a){return a.date(1).hours(0).seconds(0).minutes(0)},day:function(a){return a.hours(0).seconds(0).minutes(0)},hour:function(a){return a.seconds(0).minutes(0)},minute:function(a){return a.seconds(0)}};return g.prop("disabled")||!d.ignoreReadonly&&g.prop("readonly")||o?l:(void 0!==g.val()&&0!==g.val().trim().length?aa(da(g.val().trim())):m&&d.useCurrent&&(d.inline||g.is("input")&&0===g.val().trim().length)&&(b=y(),"string"==typeof d.useCurrent&&(b=c[d.useCurrent](b)),aa(b)),o=G(),M(),S(),o.find(".timepicker-hours").hide(),o.find(".timepicker-minutes").hide(),o.find(".timepicker-seconds").hide(),_(),L(),a(window).on("resize",I),o.on("click","[data-action]",fa),o.on("mousedown",!1),n&&n.hasClass("btn")&&n.toggleClass("active"),I(),o.show(),d.focusOnShow&&!g.is(":focus")&&g.focus(),J({type:"dp.show"}),l)},ha=function(){return o?ba():ga()},ia=function(a){var b,c,e,f,g=null,h=[],i={},j=a.which,k="p";w[j]=k;for(b in w)w.hasOwnProperty(b)&&w[b]===k&&(h.push(b),parseInt(b,10)!==j&&(i[b]=!0));for(b in d.keyBinds)if(d.keyBinds.hasOwnProperty(b)&&"function"==typeof d.keyBinds[b]&&(e=b.split(" "),e.length===h.length&&v[j]===e[e.length-1])){for(f=!0,c=e.length-2;c>=0;c--)if(!(v[e[c]]in i)){f=!1;break}if(f){g=d.keyBinds[b];break}}g&&(g.call(l,o),a.stopPropagation(),a.preventDefault())},ja=function(a){w[a.which]="r",a.stopPropagation(),a.preventDefault()},ka=function(b){var c=a(b.target).val().trim(),d=c?da(c):null;return aa(d),b.stopImmediatePropagation(),!1},la=function(){g.on({change:ka,blur:d.debug?"":ba,keydown:ia,keyup:ja,focus:d.allowInputToggle?ga:""}),c.is("input")?g.on({focus:ga}):n&&(n.on("click",ha),n.on("mousedown",!1))},ma=function(){g.off({change:ka,blur:blur,keydown:ia,keyup:ja,focus:d.allowInputToggle?ba:""}),c.is("input")?g.off({focus:ga}):n&&(n.off("click",ha),n.off("mousedown",!1))},na=function(b){var c={};return a.each(b,function(){var a=da(this);a.isValid()&&(c[a.format("YYYY-MM-DD")]=!0)}),!!Object.keys(c).length&&c},oa=function(b){var c={};return a.each(b,function(){c[this]=!0}),!!Object.keys(c).length&&c},pa=function(){var a=d.format||"L LT";i=a.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){var b=e.localeData().longDateFormat(a)||a;return b.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){return e.localeData().longDateFormat(a)||a})}),j=d.extraFormats?d.extraFormats.slice():[],j.indexOf(a)<0&&j.indexOf(i)<0&&j.push(i),h=i.toLowerCase().indexOf("a")<1&&i.replace(/\[.*?\]/g,"").indexOf("h")<1,z("y")&&(p=2),z("M")&&(p=1),z("d")&&(p=0),k=Math.max(p,k),m||aa(e)};if(l.destroy=function(){ba(),ma(),c.removeData("DateTimePicker"),c.removeData("date")},l.toggle=ha,l.show=ga,l.hide=ba,l.disable=function(){return ba(),n&&n.hasClass("btn")&&n.addClass("disabled"),g.prop("disabled",!0),l},l.enable=function(){return n&&n.hasClass("btn")&&n.removeClass("disabled"),g.prop("disabled",!1),l},l.ignoreReadonly=function(a){if(0===arguments.length)return d.ignoreReadonly;if("boolean"!=typeof a)throw new TypeError("ignoreReadonly () expects a boolean parameter");return d.ignoreReadonly=a,l},l.options=function(b){if(0===arguments.length)return a.extend(!0,{},d);if(!(b instanceof Object))throw new TypeError("options() options parameter should be an object");return a.extend(!0,d,b),a.each(d,function(a,b){if(void 0===l[a])throw new TypeError("option "+a+" is not recognized!");l[a](b)}),l},l.date=function(a){if(0===arguments.length)return m?null:e.clone();if(!(null===a||"string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");return aa(null===a?null:da(a)),l},l.format=function(a){if(0===arguments.length)return d.format;if("string"!=typeof a&&("boolean"!=typeof a||a!==!1))throw new TypeError("format() expects a string or boolean:false parameter "+a);return d.format=a,i&&pa(),l},l.timeZone=function(a){if(0===arguments.length)return d.timeZone;if("string"!=typeof a)throw new TypeError("newZone() expects a string parameter");return d.timeZone=a,l},l.dayViewHeaderFormat=function(a){if(0===arguments.length)return d.dayViewHeaderFormat;if("string"!=typeof a)throw new TypeError("dayViewHeaderFormat() expects a string parameter");return d.dayViewHeaderFormat=a,l},l.extraFormats=function(a){if(0===arguments.length)return d.extraFormats;if(a!==!1&&!(a instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");return d.extraFormats=a,j&&pa(),l},l.disabledDates=function(b){if(0===arguments.length)return d.disabledDates?a.extend({},d.disabledDates):d.disabledDates;if(!b)return d.disabledDates=!1,_(),l;if(!(b instanceof Array))throw new TypeError("disabledDates() expects an array parameter");return d.disabledDates=na(b),d.enabledDates=!1,_(),l},l.enabledDates=function(b){if(0===arguments.length)return d.enabledDates?a.extend({},d.enabledDates):d.enabledDates;if(!b)return d.enabledDates=!1,_(),l;if(!(b instanceof Array))throw new TypeError("enabledDates() expects an array parameter");return d.enabledDates=na(b),d.disabledDates=!1,_(),l},l.daysOfWeekDisabled=function(a){if(0===arguments.length)return d.daysOfWeekDisabled.splice(0);if("boolean"==typeof a&&!a)return d.daysOfWeekDisabled=!1,_(),l;if(!(a instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(d.daysOfWeekDisabled=a.reduce(function(a,b){return b=parseInt(b,10),b>6||b<0||isNaN(b)?a:(a.indexOf(b)===-1&&a.push(b),a)},[]).sort(),d.useCurrent&&!d.keepInvalid){for(var b=0;!R(e,"d");){if(e.add(1,"d"),31===b)throw"Tried 31 times to find a valid date";b++}aa(e)}return _(),l},l.maxDate=function(a){if(0===arguments.length)return d.maxDate?d.maxDate.clone():d.maxDate;if("boolean"==typeof a&&a===!1)return d.maxDate=!1,_(),l;"string"==typeof a&&("now"!==a&&"moment"!==a||(a=y()));var b=da(a);if(!b.isValid())throw new TypeError("maxDate() Could not parse date parameter: "+a);if(d.minDate&&b.isBefore(d.minDate))throw new TypeError("maxDate() date parameter is before options.minDate: "+b.format(i));return d.maxDate=b,d.useCurrent&&!d.keepInvalid&&e.isAfter(a)&&aa(d.maxDate),f.isAfter(b)&&(f=b.clone().subtract(d.stepping,"m")),_(),l},l.minDate=function(a){if(0===arguments.length)return d.minDate?d.minDate.clone():d.minDate;if("boolean"==typeof a&&a===!1)return d.minDate=!1,_(),l;"string"==typeof a&&("now"!==a&&"moment"!==a||(a=y()));var b=da(a);if(!b.isValid())throw new TypeError("minDate() Could not parse date parameter: "+a);if(d.maxDate&&b.isAfter(d.maxDate))throw new TypeError("minDate() date parameter is after options.maxDate: "+b.format(i));return d.minDate=b,d.useCurrent&&!d.keepInvalid&&e.isBefore(a)&&aa(d.minDate),f.isBefore(b)&&(f=b.clone().add(d.stepping,"m")),_(),l},l.defaultDate=function(a){if(0===arguments.length)return d.defaultDate?d.defaultDate.clone():d.defaultDate;if(!a)return d.defaultDate=!1,l;"string"==typeof a&&(a="now"===a||"moment"===a?y():y(a));var b=da(a);if(!b.isValid())throw new TypeError("defaultDate() Could not parse date parameter: "+a);if(!R(b))throw new TypeError("defaultDate() date passed is invalid according to component setup validations");return d.defaultDate=b,(d.defaultDate&&d.inline||""===g.val().trim())&&aa(d.defaultDate),l},l.locale=function(a){if(0===arguments.length)return d.locale;if(!b.localeData(a))throw new TypeError("locale() locale "+a+" is not loaded from moment locales!");return d.locale=a,e.locale(d.locale),f.locale(d.locale),i&&pa(),o&&(ba(),ga()),l},l.stepping=function(a){return 0===arguments.length?d.stepping:(a=parseInt(a,10),(isNaN(a)||a<1)&&(a=1),d.stepping=a,l)},l.useCurrent=function(a){var b=["year","month","day","hour","minute"];if(0===arguments.length)return d.useCurrent;if("boolean"!=typeof a&&"string"!=typeof a)throw new TypeError("useCurrent() expects a boolean or string parameter");if("string"==typeof a&&b.indexOf(a.toLowerCase())===-1)throw new TypeError("useCurrent() expects a string parameter of "+b.join(", "));return d.useCurrent=a,l},l.collapse=function(a){if(0===arguments.length)return d.collapse;if("boolean"!=typeof a)throw new TypeError("collapse() expects a boolean parameter");return d.collapse===a?l:(d.collapse=a,o&&(ba(),ga()),l)},l.icons=function(b){if(0===arguments.length)return a.extend({},d.icons);if(!(b instanceof Object))throw new TypeError("icons() expects parameter to be an Object");return a.extend(d.icons,b),o&&(ba(),ga()),l},l.tooltips=function(b){if(0===arguments.length)return a.extend({},d.tooltips);if(!(b instanceof Object))throw new TypeError("tooltips() expects parameter to be an Object");return a.extend(d.tooltips,b),o&&(ba(),ga()),l},l.useStrict=function(a){if(0===arguments.length)return d.useStrict;if("boolean"!=typeof a)throw new TypeError("useStrict() expects a boolean parameter");return d.useStrict=a,l},l.sideBySide=function(a){if(0===arguments.length)return d.sideBySide;if("boolean"!=typeof a)throw new TypeError("sideBySide() expects a boolean parameter");return d.sideBySide=a,o&&(ba(),ga()),l},l.viewMode=function(a){if(0===arguments.length)return d.viewMode;if("string"!=typeof a)throw new TypeError("viewMode() expects a string parameter");if(r.indexOf(a)===-1)throw new TypeError("viewMode() parameter must be one of ("+r.join(", ")+") value");return d.viewMode=a,k=Math.max(r.indexOf(a),p),L(),l},l.toolbarPlacement=function(a){if(0===arguments.length)return d.toolbarPlacement;if("string"!=typeof a)throw new TypeError("toolbarPlacement() expects a string parameter");if(u.indexOf(a)===-1)throw new TypeError("toolbarPlacement() parameter must be one of ("+u.join(", ")+") value");return d.toolbarPlacement=a,o&&(ba(),ga()),l},l.widgetPositioning=function(b){if(0===arguments.length)return a.extend({},d.widgetPositioning);if("[object Object]"!=={}.toString.call(b))throw new TypeError("widgetPositioning() expects an object variable");if(b.horizontal){if("string"!=typeof b.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(b.horizontal=b.horizontal.toLowerCase(),t.indexOf(b.horizontal)===-1)throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+t.join(", ")+")");d.widgetPositioning.horizontal=b.horizontal}if(b.vertical){if("string"!=typeof b.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(b.vertical=b.vertical.toLowerCase(),s.indexOf(b.vertical)===-1)throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+s.join(", ")+")");d.widgetPositioning.vertical=b.vertical}return _(),l},l.calendarWeeks=function(a){if(0===arguments.length)return d.calendarWeeks;if("boolean"!=typeof a)throw new TypeError("calendarWeeks() expects parameter to be a boolean value");return d.calendarWeeks=a,_(),l},l.showTodayButton=function(a){if(0===arguments.length)return d.showTodayButton;if("boolean"!=typeof a)throw new TypeError("showTodayButton() expects a boolean parameter");return d.showTodayButton=a,o&&(ba(),ga()),l},l.showClear=function(a){if(0===arguments.length)return d.showClear;if("boolean"!=typeof a)throw new TypeError("showClear() expects a boolean parameter");return d.showClear=a,o&&(ba(),ga()),l},l.widgetParent=function(b){if(0===arguments.length)return d.widgetParent;if("string"==typeof b&&(b=a(b)),null!==b&&"string"!=typeof b&&!(b instanceof a))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");return d.widgetParent=b,o&&(ba(),ga()),l},l.keepOpen=function(a){if(0===arguments.length)return d.keepOpen;if("boolean"!=typeof a)throw new TypeError("keepOpen() expects a boolean parameter");return d.keepOpen=a,l},l.focusOnShow=function(a){if(0===arguments.length)return d.focusOnShow;if("boolean"!=typeof a)throw new TypeError("focusOnShow() expects a boolean parameter");return d.focusOnShow=a,l},l.inline=function(a){if(0===arguments.length)return d.inline;if("boolean"!=typeof a)throw new TypeError("inline() expects a boolean parameter");return d.inline=a,l},l.clear=function(){return ca(),l},l.keyBinds=function(a){return 0===arguments.length?d.keyBinds:(d.keyBinds=a,l)},l.getMoment=function(a){return y(a)},l.debug=function(a){if("boolean"!=typeof a)throw new TypeError("debug() expects a boolean parameter");return d.debug=a,l},l.allowInputToggle=function(a){if(0===arguments.length)return d.allowInputToggle;if("boolean"!=typeof a)throw new TypeError("allowInputToggle() expects a boolean parameter");return d.allowInputToggle=a,l},l.showClose=function(a){if(0===arguments.length)return d.showClose;if("boolean"!=typeof a)throw new TypeError("showClose() expects a boolean parameter");return d.showClose=a,l},l.keepInvalid=function(a){if(0===arguments.length)return d.keepInvalid;if("boolean"!=typeof a)throw new TypeError("keepInvalid() expects a boolean parameter");
605
+ return d.keepInvalid=a,l},l.datepickerInput=function(a){if(0===arguments.length)return d.datepickerInput;if("string"!=typeof a)throw new TypeError("datepickerInput() expects a string parameter");return d.datepickerInput=a,l},l.parseInputDate=function(a){if(0===arguments.length)return d.parseInputDate;if("function"!=typeof a)throw new TypeError("parseInputDate() sholud be as function");return d.parseInputDate=a,l},l.disabledTimeIntervals=function(b){if(0===arguments.length)return d.disabledTimeIntervals?a.extend({},d.disabledTimeIntervals):d.disabledTimeIntervals;if(!b)return d.disabledTimeIntervals=!1,_(),l;if(!(b instanceof Array))throw new TypeError("disabledTimeIntervals() expects an array parameter");return d.disabledTimeIntervals=b,_(),l},l.disabledHours=function(b){if(0===arguments.length)return d.disabledHours?a.extend({},d.disabledHours):d.disabledHours;if(!b)return d.disabledHours=!1,_(),l;if(!(b instanceof Array))throw new TypeError("disabledHours() expects an array parameter");if(d.disabledHours=oa(b),d.enabledHours=!1,d.useCurrent&&!d.keepInvalid){for(var c=0;!R(e,"h");){if(e.add(1,"h"),24===c)throw"Tried 24 times to find a valid date";c++}aa(e)}return _(),l},l.enabledHours=function(b){if(0===arguments.length)return d.enabledHours?a.extend({},d.enabledHours):d.enabledHours;if(!b)return d.enabledHours=!1,_(),l;if(!(b instanceof Array))throw new TypeError("enabledHours() expects an array parameter");if(d.enabledHours=oa(b),d.disabledHours=!1,d.useCurrent&&!d.keepInvalid){for(var c=0;!R(e,"h");){if(e.add(1,"h"),24===c)throw"Tried 24 times to find a valid date";c++}aa(e)}return _(),l},l.viewDate=function(a){if(0===arguments.length)return f.clone();if(!a)return f=e.clone(),l;if(!("string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("viewDate() parameter must be one of [string, moment or Date]");return f=da(a),K(),l},c.is("input"))g=c;else if(g=c.find(d.datepickerInput),0===g.length)g=c.find("input");else if(!g.is("input"))throw new Error('CSS class "'+d.datepickerInput+'" cannot be applied to non input element');if(c.hasClass("input-group")&&(n=0===c.find(".datepickerbutton").length?c.find(".input-group-addon"):c.find(".datepickerbutton")),!d.inline&&!g.is("input"))throw new Error("Could not initialize DateTimePicker without an input element");return e=y(),f=e.clone(),a.extend(!0,d,H()),l.options(d),pa(),la(),g.prop("disabled")&&l.disable(),g.is("input")&&0!==g.val().trim().length?aa(da(g.val().trim())):d.defaultDate&&void 0===g.attr("placeholder")&&aa(d.defaultDate),d.inline&&ga(),l};return a.fn.datetimepicker=function(b){b=b||{};var d,e=Array.prototype.slice.call(arguments,1),f=!0,g=["destroy","hide","show","toggle"];if("object"==typeof b)return this.each(function(){var d,e=a(this);e.data("DateTimePicker")||(d=a.extend(!0,{},a.fn.datetimepicker.defaults,b),e.data("DateTimePicker",c(e,d)))});if("string"==typeof b)return this.each(function(){var c=a(this),g=c.data("DateTimePicker");if(!g)throw new Error('bootstrap-datetimepicker("'+b+'") method was called on an element that is not using DateTimePicker');d=g[b].apply(g,e),f=d===g}),f||a.inArray(b,g)>-1?this:d;throw new TypeError("Invalid arguments for DateTimePicker: "+b)},a.fn.datetimepicker.defaults={timeZone:"",format:!1,dayViewHeaderFormat:"MMMM YYYY",extraFormats:!1,stepping:1,minDate:!1,maxDate:!1,useCurrent:!0,collapse:!0,locale:b.locale(),defaultDate:!1,disabledDates:!1,enabledDates:!1,icons:{time:"glyphicon glyphicon-time",date:"glyphicon glyphicon-calendar",up:"glyphicon glyphicon-chevron-up",down:"glyphicon glyphicon-chevron-down",previous:"glyphicon glyphicon-chevron-left",next:"glyphicon glyphicon-chevron-right",today:"glyphicon glyphicon-screenshot",clear:"glyphicon glyphicon-trash",close:"glyphicon glyphicon-remove"},tooltips:{today:"Go to today",clear:"Clear selection",close:"Close the picker",selectMonth:"Select Month",prevMonth:"Previous Month",nextMonth:"Next Month",selectYear:"Select Year",prevYear:"Previous Year",nextYear:"Next Year",selectDecade:"Select Decade",prevDecade:"Previous Decade",nextDecade:"Next Decade",prevCentury:"Previous Century",nextCentury:"Next Century",pickHour:"Pick Hour",incrementHour:"Increment Hour",decrementHour:"Decrement Hour",pickMinute:"Pick Minute",incrementMinute:"Increment Minute",decrementMinute:"Decrement Minute",pickSecond:"Pick Second",incrementSecond:"Increment Second",decrementSecond:"Decrement Second",togglePeriod:"Toggle Period",selectTime:"Select Time"},useStrict:!1,sideBySide:!1,daysOfWeekDisabled:!1,calendarWeeks:!1,viewMode:"days",toolbarPlacement:"default",showTodayButton:!1,showClear:!1,showClose:!1,widgetPositioning:{horizontal:"auto",vertical:"auto"},widgetParent:null,ignoreReadonly:!1,keepOpen:!1,focusOnShow:!0,inline:!1,keepInvalid:!1,datepickerInput:".datepickerinput",keyBinds:{up:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().subtract(7,"d")):this.date(b.clone().add(this.stepping(),"m"))}},down:function(a){if(!a)return void this.show();var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().add(7,"d")):this.date(b.clone().subtract(this.stepping(),"m"))},"control up":function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().subtract(1,"y")):this.date(b.clone().add(1,"h"))}},"control down":function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().add(1,"y")):this.date(b.clone().subtract(1,"h"))}},left:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().subtract(1,"d"))}},right:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().add(1,"d"))}},pageUp:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().subtract(1,"M"))}},pageDown:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().add(1,"M"))}},enter:function(){this.hide()},escape:function(){this.hide()},"control space":function(a){a&&a.find(".timepicker").is(":visible")&&a.find('.btn[data-action="togglePeriod"]').click()},t:function(){this.date(this.getMoment())},delete:function(){this.clear()}},debug:!1,allowInputToggle:!1,disabledTimeIntervals:!1,disabledHours:!1,enabledHours:!1,viewDate:!1},a.fn.datetimepicker});
606
+
607
+ /*!
608
+ * clipboard.js v2.0.4
609
+ * https://zenorocha.github.io/clipboard.js
610
+ *
611
+ * Licensed MIT © Zeno Rocha
612
+ */
613
+ !function (t, e) { "object" == typeof exports && "object" == typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define([], e) : "object" == typeof exports ? exports.ClipboardJS = e() : t.ClipboardJS = e() }(this, function () { return function (n) { var o = {}; function r(t) { if (o[t]) return o[t].exports; var e = o[t] = { i: t, l: !1, exports: {} }; return n[t].call(e.exports, e, e.exports, r), e.l = !0, e.exports } return r.m = n, r.c = o, r.d = function (t, e, n) { r.o(t, e) || Object.defineProperty(t, e, { enumerable: !0, get: n }) }, r.r = function (t) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(t, "__esModule", { value: !0 }) }, r.t = function (e, t) { if (1 & t && (e = r(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var n = Object.create(null); if (r.r(n), Object.defineProperty(n, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var o in e) r.d(n, o, function (t) { return e[t] }.bind(null, o)); return n }, r.n = function (t) { var e = t && t.__esModule ? function () { return t.default } : function () { return t }; return r.d(e, "a", e), e }, r.o = function (t, e) { return Object.prototype.hasOwnProperty.call(t, e) }, r.p = "", r(r.s = 0) }([function (t, e, n) { "use strict"; var r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t }, i = function () { function o(t, e) { for (var n = 0; n < e.length; n++) { var o = e[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(t, o.key, o) } } return function (t, e, n) { return e && o(t.prototype, e), n && o(t, n), t } }(), a = o(n(1)), c = o(n(3)), u = o(n(4)); function o(t) { return t && t.__esModule ? t : { default: t } } var l = function (t) { function o(t, e) { !function (t, e) { if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") }(this, o); var n = function (t, e) { if (!t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !e || "object" != typeof e && "function" != typeof e ? t : e }(this, (o.__proto__ || Object.getPrototypeOf(o)).call(this)); return n.resolveOptions(e), n.listenClick(t), n } return function (t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function, not " + typeof e); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, enumerable: !1, writable: !0, configurable: !0 } }), e && (Object.setPrototypeOf ? Object.setPrototypeOf(t, e) : t.__proto__ = e) }(o, c.default), i(o, [{ key: "resolveOptions", value: function () { var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {}; this.action = "function" == typeof t.action ? t.action : this.defaultAction, this.target = "function" == typeof t.target ? t.target : this.defaultTarget, this.text = "function" == typeof t.text ? t.text : this.defaultText, this.container = "object" === r(t.container) ? t.container : document.body } }, { key: "listenClick", value: function (t) { var e = this; this.listener = (0, u.default)(t, "click", function (t) { return e.onClick(t) }) } }, { key: "onClick", value: function (t) { var e = t.delegateTarget || t.currentTarget; this.clipboardAction && (this.clipboardAction = null), this.clipboardAction = new a.default({ action: this.action(e), target: this.target(e), text: this.text(e), container: this.container, trigger: e, emitter: this }) } }, { key: "defaultAction", value: function (t) { return s("action", t) } }, { key: "defaultTarget", value: function (t) { var e = s("target", t); if (e) return document.querySelector(e) } }, { key: "defaultText", value: function (t) { return s("text", t) } }, { key: "destroy", value: function () { this.listener.destroy(), this.clipboardAction && (this.clipboardAction.destroy(), this.clipboardAction = null) } }], [{ key: "isSupported", value: function () { var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : ["copy", "cut"], e = "string" == typeof t ? [t] : t, n = !!document.queryCommandSupported; return e.forEach(function (t) { n = n && !!document.queryCommandSupported(t) }), n } }]), o }(); function s(t, e) { var n = "data-clipboard-" + t; if (e.hasAttribute(n)) return e.getAttribute(n) } t.exports = l }, function (t, e, n) { "use strict"; var o, r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t }, i = function () { function o(t, e) { for (var n = 0; n < e.length; n++) { var o = e[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(t, o.key, o) } } return function (t, e, n) { return e && o(t.prototype, e), n && o(t, n), t } }(), a = n(2), c = (o = a) && o.__esModule ? o : { default: o }; var u = function () { function e(t) { !function (t, e) { if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") }(this, e), this.resolveOptions(t), this.initSelection() } return i(e, [{ key: "resolveOptions", value: function () { var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {}; this.action = t.action, this.container = t.container, this.emitter = t.emitter, this.target = t.target, this.text = t.text, this.trigger = t.trigger, this.selectedText = "" } }, { key: "initSelection", value: function () { this.text ? this.selectFake() : this.target && this.selectTarget() } }, { key: "selectFake", value: function () { var t = this, e = "rtl" == document.documentElement.getAttribute("dir"); this.removeFake(), this.fakeHandlerCallback = function () { return t.removeFake() }, this.fakeHandler = this.container.addEventListener("click", this.fakeHandlerCallback) || !0, this.fakeElem = document.createElement("textarea"), this.fakeElem.style.fontSize = "12pt", this.fakeElem.style.border = "0", this.fakeElem.style.padding = "0", this.fakeElem.style.margin = "0", this.fakeElem.style.position = "absolute", this.fakeElem.style[e ? "right" : "left"] = "-9999px"; var n = window.pageYOffset || document.documentElement.scrollTop; this.fakeElem.style.top = n + "px", this.fakeElem.setAttribute("readonly", ""), this.fakeElem.value = this.text, this.container.appendChild(this.fakeElem), this.selectedText = (0, c.default)(this.fakeElem), this.copyText() } }, { key: "removeFake", value: function () { this.fakeHandler && (this.container.removeEventListener("click", this.fakeHandlerCallback), this.fakeHandler = null, this.fakeHandlerCallback = null), this.fakeElem && (this.container.removeChild(this.fakeElem), this.fakeElem = null) } }, { key: "selectTarget", value: function () { this.selectedText = (0, c.default)(this.target), this.copyText() } }, { key: "copyText", value: function () { var e = void 0; try { e = document.execCommand(this.action) } catch (t) { e = !1 } this.handleResult(e) } }, { key: "handleResult", value: function (t) { this.emitter.emit(t ? "success" : "error", { action: this.action, text: this.selectedText, trigger: this.trigger, clearSelection: this.clearSelection.bind(this) }) } }, { key: "clearSelection", value: function () { this.trigger && this.trigger.focus(), window.getSelection().removeAllRanges() } }, { key: "destroy", value: function () { this.removeFake() } }, { key: "action", set: function () { var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : "copy"; if (this._action = t, "copy" !== this._action && "cut" !== this._action) throw new Error('Invalid "action" value, use either "copy" or "cut"') }, get: function () { return this._action } }, { key: "target", set: function (t) { if (void 0 !== t) { if (!t || "object" !== (void 0 === t ? "undefined" : r(t)) || 1 !== t.nodeType) throw new Error('Invalid "target" value, use a valid Element'); if ("copy" === this.action && t.hasAttribute("disabled")) throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); if ("cut" === this.action && (t.hasAttribute("readonly") || t.hasAttribute("disabled"))) throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); this._target = t } }, get: function () { return this._target } }]), e }(); t.exports = u }, function (t, e) { t.exports = function (t) { var e; if ("SELECT" === t.nodeName) t.focus(), e = t.value; else if ("INPUT" === t.nodeName || "TEXTAREA" === t.nodeName) { var n = t.hasAttribute("readonly"); n || t.setAttribute("readonly", ""), t.select(), t.setSelectionRange(0, t.value.length), n || t.removeAttribute("readonly"), e = t.value } else { t.hasAttribute("contenteditable") && t.focus(); var o = window.getSelection(), r = document.createRange(); r.selectNodeContents(t), o.removeAllRanges(), o.addRange(r), e = o.toString() } return e } }, function (t, e) { function n() { } n.prototype = { on: function (t, e, n) { var o = this.e || (this.e = {}); return (o[t] || (o[t] = [])).push({ fn: e, ctx: n }), this }, once: function (t, e, n) { var o = this; function r() { o.off(t, r), e.apply(n, arguments) } return r._ = e, this.on(t, r, n) }, emit: function (t) { for (var e = [].slice.call(arguments, 1), n = ((this.e || (this.e = {}))[t] || []).slice(), o = 0, r = n.length; o < r; o++)n[o].fn.apply(n[o].ctx, e); return this }, off: function (t, e) { var n = this.e || (this.e = {}), o = n[t], r = []; if (o && e) for (var i = 0, a = o.length; i < a; i++)o[i].fn !== e && o[i].fn._ !== e && r.push(o[i]); return r.length ? n[t] = r : delete n[t], this } }, t.exports = n }, function (t, e, n) { var d = n(5), h = n(6); t.exports = function (t, e, n) { if (!t && !e && !n) throw new Error("Missing required arguments"); if (!d.string(e)) throw new TypeError("Second argument must be a String"); if (!d.fn(n)) throw new TypeError("Third argument must be a Function"); if (d.node(t)) return s = e, f = n, (l = t).addEventListener(s, f), { destroy: function () { l.removeEventListener(s, f) } }; if (d.nodeList(t)) return a = t, c = e, u = n, Array.prototype.forEach.call(a, function (t) { t.addEventListener(c, u) }), { destroy: function () { Array.prototype.forEach.call(a, function (t) { t.removeEventListener(c, u) }) } }; if (d.string(t)) return o = t, r = e, i = n, h(document.body, o, r, i); throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList"); var o, r, i, a, c, u, l, s, f } }, function (t, n) { n.node = function (t) { return void 0 !== t && t instanceof HTMLElement && 1 === t.nodeType }, n.nodeList = function (t) { var e = Object.prototype.toString.call(t); return void 0 !== t && ("[object NodeList]" === e || "[object HTMLCollection]" === e) && "length" in t && (0 === t.length || n.node(t[0])) }, n.string = function (t) { return "string" == typeof t || t instanceof String }, n.fn = function (t) { return "[object Function]" === Object.prototype.toString.call(t) } }, function (t, e, n) { var a = n(7); function i(t, e, n, o, r) { var i = function (e, n, t, o) { return function (t) { t.delegateTarget = a(t.target, n), t.delegateTarget && o.call(e, t) } }.apply(this, arguments); return t.addEventListener(n, i, r), { destroy: function () { t.removeEventListener(n, i, r) } } } t.exports = function (t, e, n, o, r) { return "function" == typeof t.addEventListener ? i.apply(null, arguments) : "function" == typeof n ? i.bind(null, document).apply(null, arguments) : ("string" == typeof t && (t = document.querySelectorAll(t)), Array.prototype.map.call(t, function (t) { return i(t, e, n, o, r) })) } }, function (t, e) { if ("undefined" != typeof Element && !Element.prototype.matches) { var n = Element.prototype; n.matches = n.matchesSelector || n.mozMatchesSelector || n.msMatchesSelector || n.oMatchesSelector || n.webkitMatchesSelector } t.exports = function (t, e) { for (; t && 9 !== t.nodeType;) { if ("function" == typeof t.matches && t.matches(e)) return t; t = t.parentNode } } }]) });
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: vasyltech
3
  Tags: access control, membership, backend menu, user role, restricted content, security, jwt
4
  Requires at least: 4.0
5
  Tested up to: 5.1
6
- Stable tag: 5.9.1.1
7
 
8
  All you need to manage access to you WordPress websites on frontend, backend and API levels for any role, user or visitors.
9
 
@@ -37,6 +37,7 @@ https://www.youtube.com/watch?v=mj5Xa_Wc16Y
37
 
38
  * [free] Manage Backend Menu. Manage access to the backend menu for any user or role. Find out more from [How to manage WordPress backend menu](https://aamplugin.com/article/how-to-manage-wordpress-backend-menu) article;
39
  * [free] Manage Roles & Capabilities. Manage all your WordPress role and capabilities.
 
40
  * [free] Create temporary user accounts. Create and manage temporary user accounts. Find out more from [How to create temporary WordPress user account](https://aamplugin.com/article/how-to-create-temporary-wordpress-user-account);
41
  * [limited] Content access. Very granular access to unlimited number of post, page or custom post type ([19 different options](https://aamplugin.com/reference/plugin#posts-terms)). With premium [Plus Package](https://aamplugin.com/extension/plus-package) extension also manage access to hierarchical taxonomies or setup the default access to all post types and taxonomies. Find out more from [How to manage access to the WordPress content](https://aamplugin.com/article/how-to-manage-access-to-the-wordpress-content) article;
42
  * [free] Manage Admin Toolbar. Filter out unnecessary items from the top admin toolbar for any role or user.
@@ -79,6 +80,22 @@ https://www.youtube.com/watch?v=mj5Xa_Wc16Y
79
 
80
  == Changelog ==
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  = 5.9.1.1 =
83
  * Fixed the bug with saving Metaboxes & Widgets settings
84
  * Fixed the bug with saving Access Policy that has backward slashes in it
3
  Tags: access control, membership, backend menu, user role, restricted content, security, jwt
4
  Requires at least: 4.0
5
  Tested up to: 5.1
6
+ Stable tag: 5.9.2
7
 
8
  All you need to manage access to you WordPress websites on frontend, backend and API levels for any role, user or visitors.
9
 
37
 
38
  * [free] Manage Backend Menu. Manage access to the backend menu for any user or role. Find out more from [How to manage WordPress backend menu](https://aamplugin.com/article/how-to-manage-wordpress-backend-menu) article;
39
  * [free] Manage Roles & Capabilities. Manage all your WordPress role and capabilities.
40
+ * [free] All necessary set of tools to manage JWT authentication [Ultimate guide to WordPress JWT Authentication](https://aamplugin.com/article/ultimate-guide-to-wordpress-jwt-authentication)
41
  * [free] Create temporary user accounts. Create and manage temporary user accounts. Find out more from [How to create temporary WordPress user account](https://aamplugin.com/article/how-to-create-temporary-wordpress-user-account);
42
  * [limited] Content access. Very granular access to unlimited number of post, page or custom post type ([19 different options](https://aamplugin.com/reference/plugin#posts-terms)). With premium [Plus Package](https://aamplugin.com/extension/plus-package) extension also manage access to hierarchical taxonomies or setup the default access to all post types and taxonomies. Find out more from [How to manage access to the WordPress content](https://aamplugin.com/article/how-to-manage-access-to-the-wordpress-content) article;
43
  * [free] Manage Admin Toolbar. Filter out unnecessary items from the top admin toolbar for any role or user.
80
 
81
  == Changelog ==
82
 
83
+ = 5.9.2 =
84
+ * Fixed the bug with Access Policy access control
85
+ * Fixed the bug with Access Policy tab shows only 10 last Policies
86
+ * Fixed the bug where AAM was not determining correct max user level
87
+ * Fixed the bug where user was able to manage his roles on the profile page
88
+ * Fixed the bug with Access Policy "Between" condition
89
+ * Optimized AAM to support unusual access capabilities for custom post types https://forum.aamplugin.com/d/99-custom-post-type-does-not-honor-edit-delete-publish-overrides/5
90
+ * Enhanced Access Policy with few new features. The complete reference is here https://aamplugin.com/reference/policy
91
+ * Enabled 'JWT Authentication' by default
92
+ * Significantly improved AAM UI page security
93
+ * Added new JWT Tokens feature to the list of AAM features https://aamplugin.com/reference/plugin#jwt-tokens
94
+ * Added new capability aam_manage_jwt
95
+ * Added "Add New Policies" submenu to fix WordPress core bug with managing access to submenus
96
+ * Removed "Role Expiration" feature - it was too confusing to work with
97
+ * Removed allow_ajax_calls capability support - it was too confusing for end users
98
+
99
  = 5.9.1.1 =
100
  * Fixed the bug with saving Metaboxes & Widgets settings
101
  * Fixed the bug with saving Access Policy that has backward slashes in it
vendor/firebase/JWT.php CHANGED
@@ -129,8 +129,11 @@ class JWT
129
  );
130
  }
131
 
 
 
 
132
  // Check if this token has expired.
133
- if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
134
  throw new ExpiredException('Expired token');
135
  }
136
 
129
  );
130
  }
131
 
132
+ // The timestamp simply does not take in consideration the timezone
133
+ $exp = !is_numeric($payload->exp) ? strtotime($payload->exp) : $payload->exp;
134
+
135
  // Check if this token has expired.
136
+ if (isset($exp) && ($timestamp - static::$leeway) >= $exp) {
137
  throw new ExpiredException('Expired token');
138
  }
139