Advanced Access Manager - Version 6.0.2

Version Description

  • Fixed Bug: https://forum.aamplugin.com/d/361-uncaught-error-call-to-a-member-function-settimezone-on-boolean
  • Fixed Bug: https://forum.aamplugin.com/d/378-aam-6-0-1-conflict-with-acf-advanced-custom-fields
  • Fixed Bug: Migration script, fixed couple more minor bugs that were causing warnings
Download this release

Release Info

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

Code changes from version 6.0.1 to 6.0.2

aam.php CHANGED
@@ -3,7 +3,7 @@
3
  /**
4
  * Plugin Name: Advanced Access Manager
5
  * Description: Collection of features to manage your WordPress website authentication, authorization and monitoring
6
- * Version: 6.0.1
7
  * Author: Vasyl Martyniuk <vasyl@vasyltech.com>
8
  * Author URI: https://vasyltech.com
9
  * Text Domain: advanced-access-manager
@@ -264,7 +264,7 @@ if (defined('ABSPATH')) {
264
  //define few common constants
265
  define('AAM_MEDIA', plugins_url('/media', __FILE__));
266
  define('AAM_KEY', 'advanced-access-manager');
267
- define('AAM_VERSION', '6.0.1');
268
  define('AAM_BASEDIR', __DIR__);
269
 
270
  //load vendor
3
  /**
4
  * Plugin Name: Advanced Access Manager
5
  * Description: Collection of features to manage your WordPress website authentication, authorization and monitoring
6
+ * Version: 6.0.2
7
  * Author: Vasyl Martyniuk <vasyl@vasyltech.com>
8
  * Author URI: https://vasyltech.com
9
  * Text Domain: advanced-access-manager
264
  //define few common constants
265
  define('AAM_MEDIA', plugins_url('/media', __FILE__));
266
  define('AAM_KEY', 'advanced-access-manager');
267
+ define('AAM_VERSION', '6.0.2');
268
  define('AAM_BASEDIR', __DIR__);
269
 
270
  //load vendor
application/Core/Object/Post.php CHANGED
@@ -5,15 +5,17 @@
5
  * LICENSE: This file is subject to the terms and conditions defined in *
6
  * file 'license.txt', which is part of this source code package. *
7
  * ======================================================================
8
- *
9
- * @version 6.0.0
10
  */
11
 
12
  /**
13
  * Post object
14
  *
 
 
 
 
15
  * @package AAM
16
- * @version 6.0.0
17
  */
18
  class AAM_Core_Object_Post extends AAM_Core_Object
19
  {
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
  * Post object
12
  *
13
+ * @since 6.0.1 Added new method isDefined that is used to determine if access option
14
+ * is defined
15
+ * @since 6.0.0 Initial implementation of the class
16
+ *
17
  * @package AAM
18
+ * @version 6.0.1
19
  */
20
  class AAM_Core_Object_Post extends AAM_Core_Object
21
  {
application/Core/Subject/User.php CHANGED
@@ -5,15 +5,16 @@
5
  * LICENSE: This file is subject to the terms and conditions defined in *
6
  * file 'license.txt', which is part of this source code package. *
7
  * ======================================================================
8
- *
9
- * @version 6.0.0
10
  */
11
 
12
  /**
13
  * User subject
14
  *
 
 
 
15
  * @package AAM
16
- * @version 6.0.0
17
  */
18
  class AAM_Core_Subject_User extends AAM_Core_Subject
19
  {
@@ -308,17 +309,30 @@ class AAM_Core_Subject_User extends AAM_Core_Subject
308
  *
309
  * @return array|null
310
  *
 
 
 
 
311
  * @access public
312
- * @version 6.0.0
313
  */
314
  public function getUserExpiration()
315
  {
316
  $response = get_user_option(self::EXPIRATION_OPTION, $this->getId());
317
 
318
  if (!empty($response)) {
319
- $response['expires'] = new DateTime(
320
- '@' . $response['expires'], new DateTimeZone('UTC')
321
- );
 
 
 
 
 
 
 
 
 
322
  }
323
 
324
  return $response;
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
  * User subject
12
  *
13
+ * @since 6.0.2 Enhanced stability of the code
14
+ * @since 6.0.0 Initial implementation of the class
15
+ *
16
  * @package AAM
17
+ * @version 6.0.2
18
  */
19
  class AAM_Core_Subject_User extends AAM_Core_Subject
20
  {
309
  *
310
  * @return array|null
311
  *
312
+ * @since 6.0.2 Making sure that we are covering scenario when expiration flag
313
+ * contains corrupted data in the database
314
+ * @since 6.0.0 Initial implementation of the method
315
+ *
316
  * @access public
317
+ * @version 6.0.2
318
  */
319
  public function getUserExpiration()
320
  {
321
  $response = get_user_option(self::EXPIRATION_OPTION, $this->getId());
322
 
323
  if (!empty($response)) {
324
+ try {
325
+ $response['expires'] = new DateTime(
326
+ '@' . $response['expires'], new DateTimeZone('UTC')
327
+ );
328
+ } catch (Exception $e) {
329
+ _doing_it_wrong(
330
+ __CLASS__ . '::' . __METHOD__,
331
+ $e->getMessage(),
332
+ AAM_VERSION
333
+ );
334
+ $response['expires'] = new DateTime('now', new DateTimeZone('UTC'));
335
+ }
336
  }
337
 
338
  return $response;
application/Migration/2019_06_30-base.php CHANGED
@@ -28,13 +28,14 @@ use WP_Error,
28
  *
29
  * The main purpose for this class is to eliminate AAM_Core_Compatibility
30
  *
 
31
  * @since 6.0.1 Slightly refactored the way errors are collected during the migration
32
  * execution. Fixed fatal error when incorrectly defined "Expire" post
33
  * option
34
  * @since 6.0.0 Initial implementation of the class
35
  *
36
  * @package AAM
37
- * @version 6.0.1
38
  */
39
  class Migration600 implements AAM_Core_Contract_MigrationInterface
40
  {
@@ -294,10 +295,6 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
294
 
295
  if (!is_null($xpath)) {
296
  AAM_Core_AccessSettings::getInstance()->set($xpath, $value);
297
-
298
- // Delete legacy option
299
- // TODO: Enable in the 6.0.2 release
300
- // delete_post_meta($option->post_id, $option->meta_key);
301
  } else {
302
  $this->errors[] = new WP_Error(
303
  'migration_error',
@@ -315,6 +312,7 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
315
  *
316
  * @return void
317
  *
 
318
  * @since 6.0.1 Any errors are pushed directly to the $this->errors array instead
319
  * of returning them. Skipping wp_aam_capability option
320
  * @since 6.0.0 Initialize implementation of the method
@@ -326,6 +324,12 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
326
  {
327
  global $wpdb;
328
 
 
 
 
 
 
 
329
  foreach($options as $option) {
330
  // e.g. "wp_aam_type_post", "wp_aam_term_1|category"
331
  $regex = '/^' . $wpdb->prefix . 'aam_([a-z]+)_?([a-z0-9_\-\|]*)$/i';
@@ -356,10 +360,6 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
356
  }
357
 
358
  AAM_Core_AccessSettings::getInstance()->set($xpath, $options);
359
-
360
- // Delete legacy meta
361
- // TODO: Enable in the 6.0.2 release
362
- // delete_user_meta($option->user_id, $option->meta_key);
363
  } elseif (!in_array($match[1], array('capability'), true)) {
364
  $this->errors[] = new WP_Error(
365
  'migration_error',
@@ -367,11 +367,9 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
367
  $option
368
  );
369
  }
370
- }elseif ($option->meta_key === 'aam-jwt') {
371
  // Just delete it. AAM v5 JWT tokens are no longer valid due to the
372
  // new way to calculate exp property
373
- // TODO: Enable in the 6.0.2 release
374
- // delete_user_meta($option->user_id, $option->meta_key);
375
  } else {
376
  $this->errors[] = new WP_Error(
377
  'migration_error',
@@ -444,11 +442,7 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
444
  {
445
  $result = AAM_Core_ConfigPress::getInstance()->save($option->option_value);
446
 
447
- if ($result === true) {
448
- // Delete legacy option
449
- // TODO: Enable in the 6.0.2 release
450
- // AAM_Core_API::deleteOption($option->option_name);
451
- } else {
452
  $this->errors[] = new WP_Error(
453
  'migration_error', 'Failed to convert ConfigPress settings', $option
454
  );
@@ -475,11 +469,7 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
475
  AAM_Addon_Repository::DB_OPTION, $option->option_value, 'site'
476
  );
477
 
478
- if ($result === true) {
479
- // Delete legacy option
480
- // TODO: Enable in the 6.0.2 release
481
- // AAM_Core_API::deleteOption($option->option_name);
482
- } else {
483
  $this->errors[] = new WP_Error(
484
  'migration_error', 'Failed to convert Addon settings', $option
485
  );
@@ -546,11 +536,7 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
546
  );
547
  }
548
 
549
- if ($result === true) {
550
- // Delete legacy option
551
- // TODO: Enable in the 6.0.2 release
552
- // AAM_Core_API::deleteOption($option->option_name);
553
- } else {
554
  $this->errors[] = new WP_Error(
555
  'migration_error', 'Failed to convert core settings', $option
556
  );
@@ -650,10 +636,6 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
650
  AAM_Core_AccessSettings::getInstance()->set($xpath, $options);
651
  }
652
  }
653
-
654
- // Delete legacy option
655
- // TODO: Enable in the 6.0.2 release
656
- // AAM_Core_API::deleteOption($option->option_name);
657
  }
658
 
659
  /**
@@ -835,6 +817,7 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
835
  *
836
  * @return array
837
  *
 
838
  * @since 6.0.1 Improved code formating. Fixed the error when unexpected datetime
839
  * is set for "Expire" option (Uncaught Error: Call to a member
840
  * function setTimezone() on boolean)
@@ -932,17 +915,24 @@ class Migration600 implements AAM_Core_Contract_MigrationInterface
932
  break;
933
 
934
  case 'expire':
935
- $datetime = new \DateTime(
936
- '@' . strtotime($prepped['expire_datetime'])
937
- );
938
-
939
- if ($datetime) { // Cover any unexpected issues with the option
940
- $datetime->setTimezone(new \DateTimeZone('UTC'));
941
-
942
- $converted[$ns . 'ceased'] = array(
943
- 'enabled' => filter_var($val, FILTER_VALIDATE_BOOLEAN),
944
- 'after' => $datetime->getTimestamp()
945
- );
 
 
 
 
 
 
 
946
  } else {
947
  $this->errors[] = new WP_Error(
948
  'migration_error',
28
  *
29
  * The main purpose for this class is to eliminate AAM_Core_Compatibility
30
  *
31
+ * @since 6.0.2 Bug fixing
32
  * @since 6.0.1 Slightly refactored the way errors are collected during the migration
33
  * execution. Fixed fatal error when incorrectly defined "Expire" post
34
  * option
35
  * @since 6.0.0 Initial implementation of the class
36
  *
37
  * @package AAM
38
+ * @version 6.0.2
39
  */
40
  class Migration600 implements AAM_Core_Contract_MigrationInterface
41
  {
295
 
296
  if (!is_null($xpath)) {
297
  AAM_Core_AccessSettings::getInstance()->set($xpath, $value);
 
 
 
 
298
  } else {
299
  $this->errors[] = new WP_Error(
300
  'migration_error',
312
  *
313
  * @return void
314
  *
315
+ * @since 6.0.2 Added list of known options that should be ignored
316
  * @since 6.0.1 Any errors are pushed directly to the $this->errors array instead
317
  * of returning them. Skipping wp_aam_capability option
318
  * @since 6.0.0 Initialize implementation of the method
324
  {
325
  global $wpdb;
326
 
327
+ $ignored = array(
328
+ 'aam-jwt',
329
+ "{$wpdb->prefix}aam-original-roles",
330
+ "{$wpdb->prefix}aam-role-expires"
331
+ );
332
+
333
  foreach($options as $option) {
334
  // e.g. "wp_aam_type_post", "wp_aam_term_1|category"
335
  $regex = '/^' . $wpdb->prefix . 'aam_([a-z]+)_?([a-z0-9_\-\|]*)$/i';
360
  }
361
 
362
  AAM_Core_AccessSettings::getInstance()->set($xpath, $options);
 
 
 
 
363
  } elseif (!in_array($match[1], array('capability'), true)) {
364
  $this->errors[] = new WP_Error(
365
  'migration_error',
367
  $option
368
  );
369
  }
370
+ }elseif (in_array($option->meta_key, $ignored, true)) {
371
  // Just delete it. AAM v5 JWT tokens are no longer valid due to the
372
  // new way to calculate exp property
 
 
373
  } else {
374
  $this->errors[] = new WP_Error(
375
  'migration_error',
442
  {
443
  $result = AAM_Core_ConfigPress::getInstance()->save($option->option_value);
444
 
445
+ if ($result !== true) {
 
 
 
 
446
  $this->errors[] = new WP_Error(
447
  'migration_error', 'Failed to convert ConfigPress settings', $option
448
  );
469
  AAM_Addon_Repository::DB_OPTION, $option->option_value, 'site'
470
  );
471
 
472
+ if ($result !== true) {
 
 
 
 
473
  $this->errors[] = new WP_Error(
474
  'migration_error', 'Failed to convert Addon settings', $option
475
  );
536
  );
537
  }
538
 
539
+ if ($result !== true) {
 
 
 
 
540
  $this->errors[] = new WP_Error(
541
  'migration_error', 'Failed to convert core settings', $option
542
  );
636
  AAM_Core_AccessSettings::getInstance()->set($xpath, $options);
637
  }
638
  }
 
 
 
 
639
  }
640
 
641
  /**
817
  *
818
  * @return array
819
  *
820
+ * @since 6.0.2 Fixed another fatal error with "Expire" setting
821
  * @since 6.0.1 Improved code formating. Fixed the error when unexpected datetime
822
  * is set for "Expire" option (Uncaught Error: Call to a member
823
  * function setTimezone() on boolean)
915
  break;
916
 
917
  case 'expire':
918
+ $time = strtotime($prepped['expire_datetime']);
919
+
920
+ if ($time) { // Cover any unexpected issues with the option
921
+ try {
922
+ $datetime = new \DateTime('@' . $time);
923
+ $datetime->setTimezone(new \DateTimeZone('UTC'));
924
+
925
+ $converted[$ns . 'ceased'] = array(
926
+ 'enabled' => filter_var($val, FILTER_VALIDATE_BOOLEAN),
927
+ 'after' => $datetime->getTimestamp()
928
+ );
929
+ } catch (\Exception $e) {
930
+ _doing_it_wrong(
931
+ __CLASS__ . '::' . __METHOD__,
932
+ $e->getMessage(),
933
+ AAM_VERSION
934
+ );
935
+ }
936
  } else {
937
  $this->errors[] = new WP_Error(
938
  'migration_error',
application/Migration/2019_11_20-base.php CHANGED
@@ -123,6 +123,9 @@ class Migration601 implements AAM_Core_Contract_MigrationInterface
123
  *
124
  * @return void
125
  *
 
 
 
126
  * @access private
127
  * @version 6.0.1
128
  */
@@ -134,9 +137,13 @@ class Migration601 implements AAM_Core_Contract_MigrationInterface
134
  if (strpos($id, '|') === false && is_numeric($id)) {
135
  $settings->delete("{$prefix}.post.{$id}");
136
  $post = get_post($id);
137
- $settings->set(
138
- "{$prefix}.post.{$post->ID}|{$post->post_type}", $options
139
- );
 
 
 
 
140
  }
141
  }
142
  }
123
  *
124
  * @return void
125
  *
126
+ * @since 6.0.2 Making sure that get_post returns actually a post object
127
+ * @since 6.0.1 Initial implementation of the method
128
+ *
129
  * @access private
130
  * @version 6.0.1
131
  */
137
  if (strpos($id, '|') === false && is_numeric($id)) {
138
  $settings->delete("{$prefix}.post.{$id}");
139
  $post = get_post($id);
140
+
141
+ // Making sure that we have actually a post object
142
+ if (is_a($post, 'WP_Post')) {
143
+ $settings->set(
144
+ "{$prefix}.post.{$post->ID}|{$post->post_type}", $options
145
+ );
146
+ }
147
  }
148
  }
149
  }
application/Service/Content.php CHANGED
@@ -5,15 +5,18 @@
5
  * LICENSE: This file is subject to the terms and conditions defined in *
6
  * file 'license.txt', which is part of this source code package. *
7
  * ======================================================================
8
- *
9
- * @version 6.0.0
10
  */
11
 
12
  /**
13
  * Posts & Terms service
14
  *
 
 
 
 
 
15
  * @package AAM
16
- * @version 6.0.0
17
  */
18
  class AAM_Service_Content
19
  {
@@ -34,6 +37,22 @@ class AAM_Service_Content
34
  */
35
  const POST_COUNTER_DB_OPTION = 'aam_post_%s_access_counter';
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  /**
38
  * Constructor
39
  *
@@ -133,6 +152,7 @@ class AAM_Service_Content
133
  *
134
  * @return void
135
  *
 
136
  * @since 6.0.1 Fixed bug related to enabling commenting on all posts
137
  * @since 6.0.0 Initial implementation of the method
138
  *
@@ -163,9 +183,6 @@ class AAM_Service_Content
163
  // Filter post content
164
  add_filter('the_content', array($this, 'filterPostContent'), 999);
165
 
166
- // Working with post types
167
- add_action('registered_post_type', array($this, 'registerPostType'), 999, 2);
168
-
169
  // Check if user has ability to perform certain task based on provided
170
  // capability and meta data
171
  add_filter('map_meta_cap', array($this, 'filterMetaMaps'), 999, 4);
@@ -186,69 +203,31 @@ class AAM_Service_Content
186
  // REST API action authorization. Triggered before call is dispatched
187
  add_filter('rest_request_before_callbacks', array($this, 'beforeDispatch'), 10, 3);
188
 
189
- // Cover any kind of surprize things by other funky plugins
190
- add_filter('pre_update_option', array($this, 'updateOption'), 10, 2);
191
- add_filter('role_has_cap', array($this, 'roleHasCap'), 1, 3);
192
- }
193
-
194
- /**
195
- * Hook into option update process
196
- *
197
- * Filter out AAM dynamically modified post type capabilities before they get into
198
- * the database. Some plugins really like the idea to force custom capability
199
- * creation during CPT registration. Some themes cause even infinite loop if those
200
- * capabilities are not stored in the _user_roles option.
201
- *
202
- * @param mixed $value
203
- * @param string $option
204
- *
205
- * @return mixed
206
- *
207
- * @access public
208
- * @global $wpdb
209
- * @version 6.0.0
210
- */
211
- public function updateOption($value, $option)
212
- {
213
- global $wpdb;
214
-
215
- if ($option === $wpdb->prefix . 'user_roles') {
216
- // Remove all pseudo capabilities from list of caps
217
- foreach ($value as &$role) {
218
- foreach ($role['capabilities'] as $cap => $granted) {
219
- if (strpos($cap, 'aam|') === 0) {
220
- $parts = explode('|', $cap);
221
- unset($role['capabilities'][$cap]);
222
- $role['capabilities'][$parts[2]] = $granted;
223
  }
224
  }
225
- }
226
- }
227
 
228
- return $value;
229
- }
230
 
231
- /**
232
- * Hook into role has capability check
233
- *
234
- * @param array $caps
235
- * @param string $cap
236
- *
237
- * @return array
238
- *
239
- * @access public
240
- * @version 6.0.0
241
- */
242
- public function roleHasCap($caps, $cap)
243
- {
244
- if (strpos($cap, 'aam|') === 0) {
245
- $parts = explode('|', $cap);
246
- if (isset($caps[$parts[2]])) {
247
- $caps[$cap] = $caps[$parts[2]];
248
  }
249
- }
250
-
251
- return $caps;
252
  }
253
 
254
  /**
@@ -260,8 +239,11 @@ class AAM_Service_Content
260
  *
261
  * @return mixed
262
  *
 
 
 
263
  * @access public
264
- * @version 6.0.0
265
  */
266
  public function beforeDispatch($response, $handler, $request)
267
  {
@@ -275,10 +257,11 @@ class AAM_Service_Content
275
  $callback = (!empty($attrs['callback'][0]) ? $attrs['callback'][0] : null);
276
 
277
  if (is_a($callback, 'WP_REST_Posts_Controller')) {
278
- $post = get_post($request['id']);
 
279
 
280
  // Honor the manually defined password on the post
281
- if (empty($post->post_password) && isset($request['password'])) {
282
  $request['_password'] = $request['password'];
283
  unset($request['password']);
284
  }
@@ -595,33 +578,6 @@ class AAM_Service_Content
595
  return $content;
596
  }
597
 
598
- /**
599
- * Hook into post type registration process
600
- *
601
- * @param string $type
602
- * @param WP_Post_Type $object
603
- *
604
- * @return void
605
- *
606
- * @access public
607
- * @version 6.0.0
608
- */
609
- public function registerPostType($type, $object)
610
- {
611
- if (is_a($object, 'WP_Post_Type')) { // Work only with WP 4.6.0 or higher
612
- // The list of capabilities to override
613
- $override = array(
614
- 'edit_post', 'delete_post', 'read_post', 'publish_posts'
615
- );
616
-
617
- foreach ($object->cap as $type => $capability) {
618
- if (in_array($type, $override, true)) {
619
- $object->cap->{$type} = "aam|{$type}|{$capability}";
620
- }
621
- }
622
- }
623
- }
624
-
625
  /**
626
  * Check user capability
627
  *
@@ -637,6 +593,12 @@ class AAM_Service_Content
637
  *
638
  * @return array
639
  *
 
 
 
 
 
 
640
  * @access public
641
  * @version 6.0.0
642
  */
@@ -644,21 +606,69 @@ class AAM_Service_Content
644
  {
645
  global $post;
646
 
647
- $objectId = (isset($args[0]) ? $args[0] : null);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
648
 
649
- // First of all delete all artificial capabilities from the $caps
650
- foreach ($caps as $i => $capability) {
651
- if (strpos($capability, 'aam|') === 0) {
652
- // Remove this capability from the mapped array and let WP Core
653
- // handle the correct mapping
654
- unset($caps[$i]);
655
  }
656
  }
657
 
658
- // This part needs to stay to cover scenarios where WP_Post_Type->cap->...
659
- // is not used but rather the hard-coded capability
660
- switch ($cap) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  case 'edit_post':
 
662
  // Cover the scenario when user uses Bulk Action or Quick Edit to
663
  // change the Status to Published and post is not allowed to be
664
  // published
@@ -669,40 +679,30 @@ class AAM_Service_Content
669
  in_array($action, array('edit', 'inline-save', true))
670
  && $status === 'publish'
671
  ) {
672
- $caps = $this->mapPublishPostCaps($caps, $objectId);
673
  } else {
674
- $caps = $this->mapEditPostCaps($caps, $objectId);
675
  }
676
  break;
677
 
678
  case 'delete_post':
679
- $caps = $this->mapDeletePostCaps($caps, $objectId);
 
680
  break;
681
 
682
  case 'read_post':
683
- $caps = $this->mapReadPostCaps(
684
- $caps, $objectId, (isset($args[1]) ? $args[1] : null)
685
- );
686
  break;
687
 
688
-
689
  case 'publish_post':
 
690
  case 'publish_posts':
691
- case 'publish_pages':
692
- // There is a bug in WP core that instead of checking if user has
693
- // ability to publish_post, it checks for edit_post. That is why
694
- // user has to be on the edit
695
- if (is_a($post, 'WP_Post')) {
696
- $caps = $this->mapPublishPostCaps($caps, $post->ID);
697
- }
698
  break;
699
 
700
  default:
701
- if (strpos($cap, 'aam|') === 0) {
702
- $caps = $this->checkPostTypePermission(
703
- $caps, $cap, $user_id, $objectId
704
- );
705
- }
706
  break;
707
  }
708
 
@@ -1130,49 +1130,6 @@ class AAM_Service_Content
1130
  return $result;
1131
  }
1132
 
1133
- /**
1134
- * Check is user has capability attached to post type
1135
- *
1136
- * @param array $caps
1137
- * @param string $cap
1138
- * @param int $user_id
1139
- * @param int $object
1140
- *
1141
- * @return array
1142
- *
1143
- * @access protected
1144
- * @version 6.0.0
1145
- */
1146
- protected function checkPostTypePermission($caps, $cap, $user_id, $object = null)
1147
- {
1148
- // Expecting to have:
1149
- // [0] === aam
1150
- // [1] === WP_Post_Type->cap key
1151
- // [2] === The capability
1152
- $parts = explode('|', $cap);
1153
-
1154
- // Build the argument array for the current_user_can
1155
- $args = array($parts[2]);
1156
- if (!is_null($object)) {
1157
- $args[] = $object;
1158
- }
1159
-
1160
- if (call_user_func_array('current_user_can', $args)) {
1161
- if ($parts[1] !== $parts[2]) {
1162
- $caps = $this->filterMetaMaps(
1163
- $caps,
1164
- $parts[1],
1165
- $user_id,
1166
- array($object)
1167
- );
1168
- }
1169
- } else {
1170
- $caps[] = 'do_not_allow';
1171
- }
1172
-
1173
- return $caps;
1174
- }
1175
-
1176
  }
1177
 
1178
  if (defined('AAM_KEY')) {
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
  * Posts & Terms service
12
  *
13
+ * @since 6.0.2 Refactored the way access to posts is managed. No more pseudo caps
14
+ * aam|...
15
+ * @since 6.0.1 Bug fixing
16
+ * @since 6.0.0 Initial implementation of the class
17
+ *
18
  * @package AAM
19
+ * @version 6.0.2
20
  */
21
  class AAM_Service_Content
22
  {
37
  */
38
  const POST_COUNTER_DB_OPTION = 'aam_post_%s_access_counter';
39
 
40
+ /**
41
+ * Collection of post type caps
42
+ *
43
+ * This is a collection of post type capabilities for optimization reasons. It
44
+ * is used by filterMetaMaps method to determine if additional check needs to be
45
+ * perform
46
+ *
47
+ * @var array
48
+ *
49
+ * @access protected
50
+ * @version 6.0.2
51
+ */
52
+ protected $postTypeCaps = array(
53
+ 'edit_post', 'edit_page', 'read_post', 'read_page', 'publish_post'
54
+ );
55
+
56
  /**
57
  * Constructor
58
  *
152
  *
153
  * @return void
154
  *
155
+ * @since 6.0.2 Removed invocation for the pseudo-cap mapping for post types
156
  * @since 6.0.1 Fixed bug related to enabling commenting on all posts
157
  * @since 6.0.0 Initial implementation of the method
158
  *
183
  // Filter post content
184
  add_filter('the_content', array($this, 'filterPostContent'), 999);
185
 
 
 
 
186
  // Check if user has ability to perform certain task based on provided
187
  // capability and meta data
188
  add_filter('map_meta_cap', array($this, 'filterMetaMaps'), 999, 4);
203
  // REST API action authorization. Triggered before call is dispatched
204
  add_filter('rest_request_before_callbacks', array($this, 'beforeDispatch'), 10, 3);
205
 
206
+ // REST API. Control if user is allowed to publish content
207
+ add_action('registered_post_type', function($post_type, $obj) {
208
+ add_filter("rest_pre_insert_{$post_type}", function($post, $request) {
209
+ $status = (isset($request['status']) ? $request['status'] : null);
210
+
211
+ if (in_array($status, array('publish', 'future'), true)) {
212
+ if ($this->isAuthorizedToPublishPost($request['id']) === false) {
213
+ $post = new WP_Error(
214
+ 'rest_cannot_publish',
215
+ __('You are not allowed to publish this content', AAM_KEY),
216
+ array('status' => rest_authorization_required_code())
217
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  }
219
  }
 
 
220
 
221
+ return $post;
222
+ }, 10, 2);
223
 
224
+ // Populate the collection of post type caps
225
+ foreach($obj->cap as $cap) {
226
+ if (!in_array($cap, $this->postTypeCaps, true)) {
227
+ $this->postTypeCaps[] = $cap;
228
+ }
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
+ }, 10, 2);
 
 
231
  }
232
 
233
  /**
239
  *
240
  * @return mixed
241
  *
242
+ * @since 6.0.2 Making sure that get_post returns actual post object
243
+ * @since 6.0.0 Initial implementation of the method
244
+ *
245
  * @access public
246
+ * @version 6.0.2
247
  */
248
  public function beforeDispatch($response, $handler, $request)
249
  {
257
  $callback = (!empty($attrs['callback'][0]) ? $attrs['callback'][0] : null);
258
 
259
  if (is_a($callback, 'WP_REST_Posts_Controller')) {
260
+ $post = get_post($request['id']);
261
+ $has_pass = isset($request['password']);
262
 
263
  // Honor the manually defined password on the post
264
+ if (is_a($post, 'WP_Post') && empty($post->post_password) && $has_pass) {
265
  $request['_password'] = $request['password'];
266
  unset($request['password']);
267
  }
578
  return $content;
579
  }
580
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  /**
582
  * Check user capability
583
  *
593
  *
594
  * @return array
595
  *
596
+ * @since 6.0.2 Completely rewrote this method to fixed loop caused by mapped
597
+ * aam|... post type capability
598
+ * @since 6.0.0 Initial implementation of the method
599
+ *
600
+ * @link https://forum.aamplugin.com/d/378-aam-6-0-1-conflict-with-acf-advanced-custom-fields
601
+ *
602
  * @access public
603
  * @version 6.0.0
604
  */
606
  {
607
  global $post;
608
 
609
+ // For optimization reasons, check only caps that belong to registered post
610
+ // types
611
+ if (in_array($cap, $this->postTypeCaps, true)) {
612
+ // Critical part of the implementation. We do not know ahead what
613
+ // capability is responsible for what action when it comes to post types.
614
+ if (isset($args[0])) {
615
+ $objectId = intval($args[0]);
616
+ } elseif (is_a($post, 'WP_Post')) {
617
+ $objectId = $post->ID;
618
+ } else {
619
+ $objectId = null;
620
+ }
621
+
622
+ // If object ID is not empty, then, potentially we are checking for perms
623
+ // to perform one of the action against a post
624
+ if (!empty($objectId)) {
625
+ $requested = get_post($objectId);
626
+
627
+ if (is_a($requested, 'WP_Post')) {
628
+ $post_type = get_post_type_object($requested->post_type);
629
 
630
+ if (is_a($post_type, 'WP_Post_Type')) {
631
+ $caps = $this->__mapPostTypeCaps(
632
+ $post_type, $cap, $caps, $requested, $args
633
+ );
634
+ }
635
+ }
636
  }
637
  }
638
 
639
+ return $caps;
640
+ }
641
+
642
+ /**
643
+ * Map post type capability based on set permissions
644
+ *
645
+ * @param WP_Post_Type $post_type
646
+ * @param string $cap
647
+ * @param array $caps
648
+ * @param WP_Post $post
649
+ * @param array $args
650
+ *
651
+ * @return array
652
+ *
653
+ * @access private
654
+ * @version 6.0.2
655
+ */
656
+ private function __mapPostTypeCaps(
657
+ WP_Post_Type $post_type, $cap, $caps, WP_Post $post, $args
658
+ ) {
659
+
660
+ // Cover the scenario when $cap is not part of the post type capabilities
661
+ // There is a bug in the WP core when user is checked for 'publish_post'
662
+ // capability
663
+ $primitive_cap = array_search($cap, (array) $post_type->cap);
664
+
665
+ if ($primitive_cap === false) {
666
+ $primitive_cap = $cap;
667
+ }
668
+
669
+ switch ($primitive_cap) {
670
  case 'edit_post':
671
+ case 'edit_page':
672
  // Cover the scenario when user uses Bulk Action or Quick Edit to
673
  // change the Status to Published and post is not allowed to be
674
  // published
679
  in_array($action, array('edit', 'inline-save', true))
680
  && $status === 'publish'
681
  ) {
682
+ $caps = $this->mapPublishPostCaps($caps, $post->ID);
683
  } else {
684
+ $caps = $this->mapEditPostCaps($caps, $post->ID);
685
  }
686
  break;
687
 
688
  case 'delete_post':
689
+ case 'delete_page':
690
+ $caps = $this->mapDeletePostCaps($caps, $post->ID);
691
  break;
692
 
693
  case 'read_post':
694
+ case 'read_page':
695
+ $password = (isset($args[1]) ? $args[1] : null);
696
+ $caps = $this->mapReadPostCaps($caps, $post->ID, $password);
697
  break;
698
 
 
699
  case 'publish_post':
700
+ case 'publish_page':
701
  case 'publish_posts':
702
+ $caps = $this->mapPublishPostCaps($caps, $post->ID);
 
 
 
 
 
 
703
  break;
704
 
705
  default:
 
 
 
 
 
706
  break;
707
  }
708
 
1130
  return $result;
1131
  }
1132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1133
  }
1134
 
1135
  if (defined('AAM_KEY')) {
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: access control, membership, backend menu, user role, restricted content, s
4
  Requires at least: 4.7.0
5
  Requires PHP: 5.6.0
6
  Tested up to: 5.3
7
- Stable tag: 6.0.1
8
 
9
  All you need to manage access to WordPress websites on the frontend, backend and API levels for any role, user or visitors.
10
 
@@ -91,6 +91,11 @@ We take security and privacy very seriously, that is why there are several non-n
91
 
92
  == Changelog ==
93
 
 
 
 
 
 
94
  = 6.0.1 =
95
  * Fixed Bug: Numerous bugs fixed in the migration script. New script prepared to do additional clean-up and fix corrupted data
96
  * Fixed Bug: https://forum.aamplugin.com/d/369-notice-undefined-offset-1-service-content-php-on-line-509
4
  Requires at least: 4.7.0
5
  Requires PHP: 5.6.0
6
  Tested up to: 5.3
7
+ Stable tag: 6.0.2
8
 
9
  All you need to manage access to WordPress websites on the frontend, backend and API levels for any role, user or visitors.
10
 
91
 
92
  == Changelog ==
93
 
94
+ = 6.0.2 =
95
+ * Fixed Bug: https://forum.aamplugin.com/d/361-uncaught-error-call-to-a-member-function-settimezone-on-boolean
96
+ * Fixed Bug: https://forum.aamplugin.com/d/378-aam-6-0-1-conflict-with-acf-advanced-custom-fields
97
+ * Fixed Bug: Migration script, fixed couple more minor bugs that were causing warnings
98
+
99
  = 6.0.1 =
100
  * Fixed Bug: Numerous bugs fixed in the migration script. New script prepared to do additional clean-up and fix corrupted data
101
  * Fixed Bug: https://forum.aamplugin.com/d/369-notice-undefined-offset-1-service-content-php-on-line-509