Groups - Version 1.9.0

Version Description

  • Added own cache encapsulation to guard against flaws in outdated cache implementations.
  • Added new user group property to obtain hierarchical groups.
  • Improved user properties to use own caching.
  • Fixed media field filters wrongly declared as actions.
  • Fixed the version comparison operation.
Download this release

Release Info

Developer itthinx
Plugin Icon 128x128 Groups
Version 1.9.0
Comparing to
See all releases

Code changes from version 1.8.1 to 1.9.0

groups.php CHANGED
@@ -21,7 +21,7 @@
21
  * Plugin Name: Groups
22
  * Plugin URI: http://www.itthinx.com/plugins/groups
23
  * Description: Groups provides group-based user membership management, group-based capabilities and content access control.
24
- * Version: 1.8.1
25
  * Author: itthinx
26
  * Author URI: http://www.itthinx.com
27
  * Donate-Link: http://www.itthinx.com
@@ -30,7 +30,7 @@
30
  if ( !defined( 'ABSPATH' ) ) {
31
  exit;
32
  }
33
- define( 'GROUPS_CORE_VERSION', '1.8.1' );
34
  define( 'GROUPS_FILE', __FILE__ );
35
  if ( !defined( 'GROUPS_CORE_DIR' ) ) {
36
  define( 'GROUPS_CORE_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
21
  * Plugin Name: Groups
22
  * Plugin URI: http://www.itthinx.com/plugins/groups
23
  * Description: Groups provides group-based user membership management, group-based capabilities and content access control.
24
+ * Version: 1.9.0
25
  * Author: itthinx
26
  * Author URI: http://www.itthinx.com
27
  * Donate-Link: http://www.itthinx.com
30
  if ( !defined( 'ABSPATH' ) ) {
31
  exit;
32
  }
33
+ define( 'GROUPS_CORE_VERSION', '1.9.0' );
34
  define( 'GROUPS_FILE', __FILE__ );
35
  if ( !defined( 'GROUPS_CORE_DIR' ) ) {
36
  define( 'GROUPS_CORE_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
lib/access/class-groups-access-meta-boxes.php CHANGED
@@ -55,8 +55,8 @@ class Groups_Access_Meta_Boxes {
55
  add_action( 'save_post', array( __CLASS__, "save_post" ), 10, 2 );
56
  add_filter( 'wp_insert_post_empty_content', array( __CLASS__, 'wp_insert_post_empty_content' ), 10, 2 );
57
 
58
- add_action( 'attachment_fields_to_edit', array( __CLASS__, 'attachment_fields_to_edit' ), 10, 2 );
59
- add_action( 'attachment_fields_to_save', array( __CLASS__, 'attachment_fields_to_save' ), 10, 2 );
60
  }
61
  }
62
 
55
  add_action( 'save_post', array( __CLASS__, "save_post" ), 10, 2 );
56
  add_filter( 'wp_insert_post_empty_content', array( __CLASS__, 'wp_insert_post_empty_content' ), 10, 2 );
57
 
58
+ add_filter( 'attachment_fields_to_edit', array( __CLASS__, 'attachment_fields_to_edit' ), 10, 2 );
59
+ add_filter( 'attachment_fields_to_save', array( __CLASS__, 'attachment_fields_to_save' ), 10, 2 );
60
  }
61
  }
62
 
lib/access/class-groups-post-access.php CHANGED
@@ -402,9 +402,11 @@ class Groups_Post_Access {
402
  if ( $user_id === null ) {
403
  $user_id = get_current_user_id();
404
  }
405
- $found = false;
406
- $result = wp_cache_get( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, self::CACHE_GROUP, false, $found );
407
- if ( $found === false ) {
 
 
408
  $groups_user = new Groups_User( $user_id );
409
  $read_caps = self::get_read_post_capabilities( $post_id );
410
  if ( !empty( $read_caps ) ) {
@@ -418,7 +420,7 @@ class Groups_Post_Access {
418
  $result = true;
419
  }
420
  $result = apply_filters( 'groups_post_access_user_can_read_post', $result, $post_id, $user_id );
421
- wp_cache_set( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, $result, self::CACHE_GROUP );
422
  }
423
  }
424
  return $result;
402
  if ( $user_id === null ) {
403
  $user_id = get_current_user_id();
404
  }
405
+ $cached = Groups_Cache::get( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, self::CACHE_GROUP );
406
+ if ( $cached !== null ) {
407
+ $result = $cached->value;
408
+ unset( $cached ) ;
409
+ } else {
410
  $groups_user = new Groups_User( $user_id );
411
  $read_caps = self::get_read_post_capabilities( $post_id );
412
  if ( !empty( $read_caps ) ) {
420
  $result = true;
421
  }
422
  $result = apply_filters( 'groups_post_access_user_can_read_post', $result, $post_id, $user_id );
423
+ Groups_Cache::set( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, $result, self::CACHE_GROUP );
424
  }
425
  }
426
  return $result;
lib/auto/class-groups-registered.php CHANGED
@@ -160,11 +160,12 @@ class Groups_Registered {
160
 
161
  /**
162
  * Assign a user to its "Registered" group for the given blog.
163
- *
164
- * @param int $user_id
165
- * @param WP_string $role
 
166
  */
167
- function add_user_to_blog( $user_id, $role, $blog_id ) {
168
 
169
  if ( is_multisite() ) {
170
  Groups_Controller::switch_to_blog( $blog_id );
160
 
161
  /**
162
  * Assign a user to its "Registered" group for the given blog.
163
+ *
164
+ * @param int $user_id User ID.
165
+ * @param string $role User role.
166
+ * @param int $blog_id Blog ID.
167
  */
168
+ public static function add_user_to_blog( $user_id, $role, $blog_id ) {
169
 
170
  if ( is_multisite() ) {
171
  Groups_Controller::switch_to_blog( $blog_id );
lib/core/class-groups-cache-object.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * class-groups-cache-object.php
4
+ *
5
+ * Copyright (c) "kento" Karim Rahimpur www.itthinx.com
6
+ *
7
+ * This code is released under the GNU General Public License.
8
+ * See COPYRIGHT.txt and LICENSE.txt.
9
+ *
10
+ * This code is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * This header and all notices must be kept intact.
16
+ *
17
+ * @author Karim Rahimpur
18
+ * @package groups
19
+ * @since groups 1.9.0
20
+ */
21
+
22
+ if ( !defined( 'ABSPATH' ) ) {
23
+ exit;
24
+ }
25
+
26
+ /**
27
+ * Cache entry encapsulation.
28
+ *
29
+ * @property string $key
30
+ * @property mixed $value
31
+ */
32
+ class Groups_Cache_Object {
33
+
34
+ /**
35
+ * Cache key.
36
+ * @var string
37
+ */
38
+ private $key = null;
39
+
40
+ /**
41
+ * Cached value.
42
+ * @var mixed
43
+ */
44
+ private $value = null;
45
+
46
+ /**
47
+ * Create a cache entry object that holds a value for the given key.
48
+ *
49
+ * @param string $key
50
+ * @param mixed $value
51
+ */
52
+ public function __construct( $key, $value ) {
53
+ $this->key = $key;
54
+ $this->value = $value;
55
+ }
56
+
57
+ /**
58
+ * Getter implementation for key and value properties.
59
+ *
60
+ * @param string $name
61
+ * @return property value or null
62
+ */
63
+ public function __get( $name ) {
64
+ $result = null;
65
+ switch ( $name ) {
66
+ case 'key' :
67
+ case 'value' :
68
+ $result = $this->$name;
69
+ break;
70
+ }
71
+ return $result;
72
+ }
73
+
74
+ /**
75
+ * Setter for key and value properties.
76
+ *
77
+ * @param string $name
78
+ * @param mixed $value
79
+ */
80
+ public function __set( $name, $value ) {
81
+ switch( $name ) {
82
+ case 'key' :
83
+ case 'value' :
84
+ $this->$name = $value;
85
+ break;
86
+ }
87
+ }
88
+ }
lib/core/class-groups-cache.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * class-groups-cache.php
4
+ *
5
+ * Copyright (c) "kento" Karim Rahimpur www.itthinx.com
6
+ *
7
+ * This code is released under the GNU General Public License.
8
+ * See COPYRIGHT.txt and LICENSE.txt.
9
+ *
10
+ * This code is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * This header and all notices must be kept intact.
16
+ *
17
+ * @author Karim Rahimpur
18
+ * @package groups
19
+ * @since groups 1.9.0
20
+ */
21
+
22
+ if ( !defined( 'ABSPATH' ) ) {
23
+ exit;
24
+ }
25
+
26
+ /**
27
+ * Cache service.
28
+ *
29
+ * Uses cache objects to encapsulate cached data.
30
+ *
31
+ * This makes us completely independent from the problems related to
32
+ * incomplete cache implementations that ignore the $found parameter used
33
+ * to disambiguate cache misses with wp_cache_get() when false is retrieved.
34
+ */
35
+ class Groups_Cache {
36
+
37
+ /**
38
+ * Default cache group.
39
+ * @var string
40
+ */
41
+ const CACHE_GROUP = 'groups';
42
+
43
+ /**
44
+ * Retrieve an entry from cache.
45
+ *
46
+ * @param string $key
47
+ * @param string $group
48
+ * @return Groups_Cache_Object|null returns a cache object on hit, null on cache miss
49
+ */
50
+ public static function get( $key, $group = self::CACHE_GROUP ) {
51
+ $found = null;
52
+ $value = wp_cache_get( $key, $group, false, $found );
53
+ if ( !( $value instanceof Groups_Cache_Object ) ) {
54
+ $value = null;
55
+ }
56
+ return $value;
57
+ }
58
+
59
+ /**
60
+ * Store an entry in cache.
61
+ *
62
+ * @param string $key
63
+ * @param string $value
64
+ * @param string $group
65
+ * @return true if successful, otherwise false
66
+ */
67
+ public static function set( $key, $value, $group = self::CACHE_GROUP ) {
68
+ $object = new Groups_Cache_Object( $key, $value );
69
+ return wp_cache_set( $key, $object, $group );
70
+ }
71
+
72
+ /**
73
+ * Delete a cache entry.
74
+ *
75
+ * @param string $key
76
+ * @param string $group
77
+ * @return true if successful, otherwise false
78
+ */
79
+ public static function delete( $key, $group = self::CACHE_GROUP ) {
80
+ return wp_cache_delete( $key, $group );
81
+ }
82
+ }
lib/core/class-groups-capability.php CHANGED
@@ -156,7 +156,7 @@ class Groups_Capability {
156
  if ( $wpdb->insert( $capability_table, $data, $formats ) ) {
157
  if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
158
  // read_by_capability above created a cache entry which needs to be reset
159
- wp_cache_delete( self::READ_BY_CAPABILITY . '_' . $capability, self::CACHE_GROUP );
160
  do_action( "groups_created_capability", $result );
161
  }
162
  }
@@ -197,9 +197,11 @@ class Groups_Capability {
197
  public static function read_by_capability( $capability ) {
198
  global $wpdb;
199
  $_capability = $capability;
200
- $found = false;
201
- $result = wp_cache_get( self::READ_BY_CAPABILITY . '_' . $_capability, self::CACHE_GROUP, false, $found );
202
- if ( $found === false ) {
 
 
203
  $result = false;
204
  $capability_table = _groups_get_tablename( 'capability' );
205
  $capability = $wpdb->get_row( $wpdb->prepare(
@@ -209,7 +211,7 @@ class Groups_Capability {
209
  if ( isset( $capability->capability_id ) ) {
210
  $result = $capability;
211
  }
212
- wp_cache_set( self::READ_BY_CAPABILITY . '_' . $_capability, $result, self::CACHE_GROUP );
213
  }
214
  return $result;
215
  }
@@ -259,10 +261,10 @@ class Groups_Capability {
259
  if ( ( $rows !== false ) ) {
260
  $result = $capability_id;
261
  if ( !empty( $old_capability ) && !empty( $old_capability->capability ) ) {
262
- wp_cache_delete( self::READ_BY_CAPABILITY . '_' . $old_capability->capability, self::CACHE_GROUP );
263
  }
264
  if ( !empty( $old_capability_capability ) ) {
265
- wp_cache_delete( self::READ_BY_CAPABILITY . '_' . $old_capability_capability, self::CACHE_GROUP );
266
  }
267
  do_action( "groups_updated_capability", $result );
268
  }
@@ -292,7 +294,7 @@ class Groups_Capability {
292
  ) ) ) {
293
  $result = $capability_id;
294
  if ( !empty( $capability->capability ) ) {
295
- wp_cache_delete( self::READ_BY_CAPABILITY . '_' . $capability->capability, self::CACHE_GROUP );
296
  do_action( 'groups_deleted_capability_capability', $capability->capability );
297
  }
298
  do_action( "groups_deleted_capability", $result );
156
  if ( $wpdb->insert( $capability_table, $data, $formats ) ) {
157
  if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
158
  // read_by_capability above created a cache entry which needs to be reset
159
+ Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $capability, self::CACHE_GROUP );
160
  do_action( "groups_created_capability", $result );
161
  }
162
  }
197
  public static function read_by_capability( $capability ) {
198
  global $wpdb;
199
  $_capability = $capability;
200
+ $cached = Groups_Cache::get( self::READ_BY_CAPABILITY . '_' . $_capability, self::CACHE_GROUP );
201
+ if ( $cached !== null ) {
202
+ $result = $cached->value;
203
+ unset( $cached );
204
+ } else {
205
  $result = false;
206
  $capability_table = _groups_get_tablename( 'capability' );
207
  $capability = $wpdb->get_row( $wpdb->prepare(
211
  if ( isset( $capability->capability_id ) ) {
212
  $result = $capability;
213
  }
214
+ Groups_Cache::set( self::READ_BY_CAPABILITY . '_' . $_capability, $result, self::CACHE_GROUP );
215
  }
216
  return $result;
217
  }
261
  if ( ( $rows !== false ) ) {
262
  $result = $capability_id;
263
  if ( !empty( $old_capability ) && !empty( $old_capability->capability ) ) {
264
+ Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $old_capability->capability, self::CACHE_GROUP );
265
  }
266
  if ( !empty( $old_capability_capability ) ) {
267
+ Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $old_capability_capability, self::CACHE_GROUP );
268
  }
269
  do_action( "groups_updated_capability", $result );
270
  }
294
  ) ) ) {
295
  $result = $capability_id;
296
  if ( !empty( $capability->capability ) ) {
297
+ Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $capability->capability, self::CACHE_GROUP );
298
  do_action( 'groups_deleted_capability_capability', $capability->capability );
299
  }
300
  do_action( "groups_deleted_capability", $result );
lib/core/class-groups-controller.php CHANGED
@@ -220,7 +220,7 @@ class Groups_Controller {
220
  global $groups_version, $groups_admin_messages;
221
  $previous_version = get_option( 'groups_plugin_version', null );
222
  $groups_version = GROUPS_CORE_VERSION;
223
- if ( strcmp( $previous_version, $groups_version ) < 0 ) {
224
  if ( self::update( $previous_version ) ) {
225
  update_option( 'groups_plugin_version', $groups_version );
226
  } else {
@@ -266,11 +266,11 @@ class Groups_Controller {
266
  break;
267
  default :
268
  if ( !empty( $previous_version ) ) {
269
- if ( strcmp( $previous_version, '1.1.6' ) < 0 ) {
270
  Groups_Options::update_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
271
  $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->postmeta SET meta_value = %s WHERE meta_key = %s", Groups_Post_Access::READ_POST_CAPABILITY, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY ) );
272
  }
273
- if ( strcmp( $previous_version, '1.5.1' ) < 0 ) {
274
  $capability_table = _groups_get_tablename( 'capability' );
275
  $queries[] = "ALTER TABLE $capability_table DROP INDEX capability, ADD UNIQUE INDEX capability(capability(100));";
276
  }
220
  global $groups_version, $groups_admin_messages;
221
  $previous_version = get_option( 'groups_plugin_version', null );
222
  $groups_version = GROUPS_CORE_VERSION;
223
+ if ( version_compare( $previous_version, $groups_version ) < 0 ) {
224
  if ( self::update( $previous_version ) ) {
225
  update_option( 'groups_plugin_version', $groups_version );
226
  } else {
266
  break;
267
  default :
268
  if ( !empty( $previous_version ) ) {
269
+ if ( version_compare( $previous_version, '1.1.6' ) < 0 ) {
270
  Groups_Options::update_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
271
  $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->postmeta SET meta_value = %s WHERE meta_key = %s", Groups_Post_Access::READ_POST_CAPABILITY, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY ) );
272
  }
273
+ if ( version_compare( $previous_version, '1.5.1' ) < 0 ) {
274
  $capability_table = _groups_get_tablename( 'capability' );
275
  $queries[] = "ALTER TABLE $capability_table DROP INDEX capability, ADD UNIQUE INDEX capability(capability(100));";
276
  }
lib/core/class-groups-group.php CHANGED
@@ -287,7 +287,7 @@ class Groups_Group implements I_Capable {
287
  if ( $wpdb->insert( $group_table, $data, $formats ) ) {
288
  if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
289
  // must clear cache for this name in case it has been requested previously as it now exists
290
- wp_cache_delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
291
  do_action( "groups_created_group", $result );
292
  }
293
  }
@@ -325,9 +325,11 @@ class Groups_Group implements I_Capable {
325
  */
326
  public static function read_by_name( $name ) {
327
  global $wpdb;
328
- $found = false;
329
- $result = wp_cache_get( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP, false, $found );
330
- if ( $found === false ) {
 
 
331
  $result = false;
332
  $group_table = _groups_get_tablename( 'group' );
333
  $group = $wpdb->get_row( $wpdb->prepare(
@@ -337,7 +339,7 @@ class Groups_Group implements I_Capable {
337
  if ( isset( $group->group_id ) ) {
338
  $result = $group;
339
  }
340
- wp_cache_set( self::READ_BY_NAME . '_' . $name, $result, self::CACHE_GROUP );
341
  }
342
  return $result;
343
  }
@@ -421,10 +423,10 @@ class Groups_Group implements I_Capable {
421
  }
422
  $result = $group_id;
423
  if ( !empty( $name ) ) {
424
- wp_cache_delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
425
  }
426
  if ( !empty( $old_group ) && !empty( $old_group->name ) ) {
427
- wp_cache_delete( self::READ_BY_NAME . '_' . $old_group->name, self::CACHE_GROUP );
428
  }
429
  do_action( "groups_updated_group", $result );
430
  }
@@ -472,7 +474,7 @@ class Groups_Group implements I_Capable {
472
  ) ) ) {
473
  $result = $group->group_id;
474
  if ( !empty( $group->name ) ) {
475
- wp_cache_delete( self::READ_BY_NAME . '_' . $group->name, self::CACHE_GROUP );
476
  }
477
  do_action( "groups_deleted_group", $result );
478
  }
287
  if ( $wpdb->insert( $group_table, $data, $formats ) ) {
288
  if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
289
  // must clear cache for this name in case it has been requested previously as it now exists
290
+ Groups_Cache::delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
291
  do_action( "groups_created_group", $result );
292
  }
293
  }
325
  */
326
  public static function read_by_name( $name ) {
327
  global $wpdb;
328
+ $cached = Groups_Cache::get( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
329
+ if ( $cached !== null ) {
330
+ $result = $cached->value;
331
+ unset( $cached );
332
+ } else {
333
  $result = false;
334
  $group_table = _groups_get_tablename( 'group' );
335
  $group = $wpdb->get_row( $wpdb->prepare(
339
  if ( isset( $group->group_id ) ) {
340
  $result = $group;
341
  }
342
+ Groups_Cache::set( self::READ_BY_NAME . '_' . $name, $result, self::CACHE_GROUP );
343
  }
344
  return $result;
345
  }
423
  }
424
  $result = $group_id;
425
  if ( !empty( $name ) ) {
426
+ Groups_Cache::delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
427
  }
428
  if ( !empty( $old_group ) && !empty( $old_group->name ) ) {
429
+ Groups_Cache::delete( self::READ_BY_NAME . '_' . $old_group->name, self::CACHE_GROUP );
430
  }
431
  do_action( "groups_updated_group", $result );
432
  }
474
  ) ) ) {
475
  $result = $group->group_id;
476
  if ( !empty( $group->name ) ) {
477
+ Groups_Cache::delete( self::READ_BY_NAME . '_' . $group->name, self::CACHE_GROUP );
478
  }
479
  do_action( "groups_deleted_group", $result );
480
  }
lib/core/class-groups-user-group.php CHANGED
@@ -88,16 +88,19 @@ class Groups_User_Group {
88
  * @return true on success, otherwise false
89
  */
90
  public static function create( $map ) {
91
-
92
  global $wpdb;
93
  extract( $map );
94
  $result = false;
95
 
96
  // avoid nonsense requests
97
- // if ( !empty( $user_id ) && !empty( $group_id ) ) {
98
- if ( !empty( $group_id ) ) {
99
  // make sure user and group exist
100
- if ( ( false !== Groups_Utility::id( $user_id ) ) && ( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->users WHERE ID = %d", $user_id ) ) > 0 ) && ( $group = Groups_Group::read( $group_id ) ) ) {
 
 
 
 
101
  // only allow to add users to groups if they belong to the
102
  // group's blog or we have the anonymous user
103
  if ( is_user_member_of_blog( Groups_Utility::id( $user_id ) ) || ( Groups_Utility::id( $user_id ) === 0 ) ) {
88
  * @return true on success, otherwise false
89
  */
90
  public static function create( $map ) {
91
+
92
  global $wpdb;
93
  extract( $map );
94
  $result = false;
95
 
96
  // avoid nonsense requests
97
+ if ( !empty( $group_id ) ) {
 
98
  // make sure user and group exist
99
+ if (
100
+ ( false !== Groups_Utility::id( $user_id ) ) &&
101
+ ( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->users WHERE ID = %d", $user_id ) ) > 0 ) &&
102
+ ( $group = Groups_Group::read( $group_id ) )
103
+ ) {
104
  // only allow to add users to groups if they belong to the
105
  // group's blog or we have the anonymous user
106
  if ( is_user_member_of_blog( Groups_Utility::id( $user_id ) ) || ( Groups_Utility::id( $user_id ) === 0 ) ) {
lib/core/class-groups-user.php CHANGED
@@ -31,10 +31,15 @@ require_once( GROUPS_CORE_LIB . "/class-groups-capability.php" );
31
  */
32
  class Groups_User implements I_Capable {
33
 
34
- const CACHE_GROUP = 'groups';
35
- const CAPABILITIES = 'capabilities';
36
- const CAPABILITY_IDS = 'capability_ids';
37
- const GROUP_IDS = 'group_ids';
 
 
 
 
 
38
 
39
  /**
40
  * User object.
@@ -64,9 +69,14 @@ class Groups_User implements I_Capable {
64
  */
65
  public static function clear_cache( $user_id ) {
66
  // be lazy, clear the entries so they are rebuilt when requested
67
- wp_cache_delete( self::CAPABILITIES . $user_id, self::CACHE_GROUP );
68
- wp_cache_delete( self::CAPABILITY_IDS . $user_id, self::CACHE_GROUP );
69
- wp_cache_delete( self::GROUP_IDS . $user_id, self::CACHE_GROUP );
 
 
 
 
 
70
  }
71
 
72
  /**
@@ -113,30 +123,38 @@ class Groups_User implements I_Capable {
113
  global $wpdb;
114
  $result = null;
115
 
116
- // @todo Do we need to maintain the current semantics of "capabilities" and "groups" as direct properties of the object?
117
-
118
  if ( $this->user !== null ) {
119
 
120
  switch ( $name ) {
121
 
122
  case 'capability_ids' :
123
- $user_capability_table = _groups_get_tablename( "user_capability" );
124
- $rows = $wpdb->get_results( $wpdb->prepare(
125
- "SELECT capability_id FROM $user_capability_table WHERE user_id = %d",
126
- Groups_Utility::id( $this->user->ID )
127
- ) );
128
- if ( $rows ) {
129
- $result = array();
130
- foreach ( $rows as $row ) {
131
- $result[] = $row->capability_id;
 
 
 
 
 
 
132
  }
 
133
  }
134
  break;
135
 
136
  case 'capability_ids_deep' :
137
  if ( $this->user !== null ) {
138
- $capability_ids = wp_cache_get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
139
- if ( $capability_ids === false ) {
 
 
 
140
  $this->init_cache( $capability_ids );
141
  }
142
  $result = $capability_ids;
@@ -144,64 +162,105 @@ class Groups_User implements I_Capable {
144
  break;
145
 
146
  case 'group_ids' :
147
- $user_group_table = _groups_get_tablename( "user_group" );
148
- $rows = $wpdb->get_results( $wpdb->prepare(
 
 
 
 
 
149
  "SELECT group_id FROM $user_group_table WHERE user_id = %d",
150
  Groups_Utility::id( $this->user->ID )
151
- ) );
152
- if ( $rows ) {
153
- $result = array();
154
- foreach( $rows as $row ) {
155
- $result[] = $row->group_id;
 
156
  }
 
157
  }
158
  break;
159
 
160
  case 'group_ids_deep' :
161
  if ( $this->user !== null ) {
162
- $group_ids = wp_cache_get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP );
163
- if ( $group_ids === false ) {
 
 
 
164
  $this->init_cache( $capability_ids, $capabilities, $group_ids );
165
  }
166
  $result = $group_ids;
167
  }
168
  break;
169
 
170
- case "capabilities" :
171
- $user_capability_table = _groups_get_tablename( "user_capability" );
172
- $rows = $wpdb->get_results( $wpdb->prepare(
173
- "SELECT capability_id FROM $user_capability_table WHERE user_id = %d",
174
- Groups_Utility::id( $this->user->ID )
175
- ) );
176
- if ( $rows ) {
177
- $result = array();
178
- foreach ( $rows as $row ) {
179
- $result[] = new Groups_Capability( $row->capability_id );
 
 
 
 
 
 
180
  }
 
181
  }
182
  break;
183
 
184
  case 'capabilities_deep' :
185
  if ( $this->user !== null ) {
186
- $capabilities = wp_cache_get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP );
187
- if ( $capabilities === false ) {
 
 
 
188
  $this->init_cache( $capability_ids, $capabilities );
189
  }
190
  $result = $capabilities;
191
  }
192
  break;
193
 
194
- case "groups" :
195
- $user_group_table = _groups_get_tablename( "user_group" );
196
- $rows = $wpdb->get_results( $wpdb->prepare(
197
- "SELECT group_id FROM $user_group_table WHERE user_id = %d",
198
- Groups_Utility::id( $this->user->ID )
199
- ) );
200
- if ( $rows ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  $result = array();
202
- foreach( $rows as $row ) {
203
- $result[] = new Groups_Group( $row->group_id );
204
  }
 
205
  }
206
  break;
207
 
@@ -232,14 +291,20 @@ class Groups_User implements I_Capable {
232
  $capability_id = null;
233
  if ( is_numeric( $capability ) ) {
234
  $capability_id = Groups_Utility::id( $capability );
235
- $capability_ids = wp_cache_get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
236
- if ( $capability_ids === false ) {
 
 
 
237
  $this->init_cache( $capability_ids );
238
  }
239
  $result = in_array( $capability_id, $capability_ids );
240
  } else if ( is_string( $capability ) ) {
241
- $capabilities = wp_cache_get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP );
242
- if ( $capabilities === false ) {
 
 
 
243
  $this->init_cache( $capability_ids, $capabilities );
244
  }
245
  $result = in_array( $capability, $capabilities );
@@ -267,7 +332,7 @@ class Groups_User implements I_Capable {
267
  $capability_ids = array();
268
  $group_ids = array();
269
 
270
- if ( ( $this->user !== null ) && ( wp_cache_get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP ) === false ) ) {
271
  $group_table = _groups_get_tablename( "group" );
272
  $capability_table = _groups_get_tablename( "capability" );
273
  $group_capability_table = _groups_get_tablename( "group_capability" );
@@ -362,9 +427,9 @@ class Groups_User implements I_Capable {
362
 
363
  }
364
  }
365
- wp_cache_set( self::CAPABILITIES . $this->user->ID, $capabilities, self::CACHE_GROUP );
366
- wp_cache_set( self::CAPABILITY_IDS . $this->user->ID, $capability_ids, self::CACHE_GROUP );
367
- wp_cache_set( self::GROUP_IDS . $this->user->ID, $group_ids, self::CACHE_GROUP );
368
  }
369
  }
370
 
31
  */
32
  class Groups_User implements I_Capable {
33
 
34
+ const CACHE_GROUP = 'groups';
35
+ const CAPABILITIES = 'capabilities';
36
+ const CAPABILITIES_BASE = 'capabilities_base';
37
+ const CAPABILITY_IDS = 'capability_ids';
38
+ const CAPABILITY_IDS_BASE = 'capability_ids_base';
39
+ const GROUP_IDS = 'group_ids';
40
+ const GROUP_IDS_BASE = 'group_ids_base';
41
+ const GROUPS = 'groups';
42
+ const GROUPS_BASE = 'groups_base';
43
 
44
  /**
45
  * User object.
69
  */
70
  public static function clear_cache( $user_id ) {
71
  // be lazy, clear the entries so they are rebuilt when requested
72
+ Groups_Cache::delete( self::CAPABILITIES . $user_id, self::CACHE_GROUP );
73
+ Groups_Cache::delete( self::CAPABILITIES_BASE . $user_id, self::CACHE_GROUP );
74
+ Groups_Cache::delete( self::CAPABILITY_IDS . $user_id, self::CACHE_GROUP );
75
+ Groups_Cache::delete( self::CAPABILITY_IDS_BASE . $user_id, self::CACHE_GROUP );
76
+ Groups_Cache::delete( self::GROUP_IDS . $user_id, self::CACHE_GROUP );
77
+ Groups_Cache::delete( self::GROUP_IDS_BASE . $user_id, self::CACHE_GROUP );
78
+ Groups_Cache::delete( self::GROUPS . $user_id, self::CACHE_GROUP );
79
+ Groups_Cache::delete( self::GROUPS_BASE . $user_id, self::CACHE_GROUP );
80
  }
81
 
82
  /**
123
  global $wpdb;
124
  $result = null;
125
 
 
 
126
  if ( $this->user !== null ) {
127
 
128
  switch ( $name ) {
129
 
130
  case 'capability_ids' :
131
+ $cached = Groups_Cache::get( self::CAPABILITY_IDS_BASE . $this->user->ID, self::CACHE_GROUP );
132
+ if ( $cached !== null ) {
133
+ $result = $cached->value;
134
+ unset( $cached );
135
+ } else {
136
+ $user_capability_table = _groups_get_tablename( "user_capability" );
137
+ $rows = $wpdb->get_results( $wpdb->prepare(
138
+ "SELECT capability_id FROM $user_capability_table WHERE user_id = %d",
139
+ Groups_Utility::id( $this->user->ID )
140
+ ) );
141
+ if ( $rows ) {
142
+ $result = array();
143
+ foreach ( $rows as $row ) {
144
+ $result[] = $row->capability_id;
145
+ }
146
  }
147
+ Groups_Cache::set( self::CAPABILITY_IDS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
148
  }
149
  break;
150
 
151
  case 'capability_ids_deep' :
152
  if ( $this->user !== null ) {
153
+ $cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
154
+ if ( $cached !== null ) {
155
+ $capability_ids = $cached->value;
156
+ unset( $cached );
157
+ } else {
158
  $this->init_cache( $capability_ids );
159
  }
160
  $result = $capability_ids;
162
  break;
163
 
164
  case 'group_ids' :
165
+ $cached = Groups_Cache::get( self::GROUP_IDS_BASE . $this->user->ID, self::CACHE_GROUP );
166
+ if ( $cached !== null ) {
167
+ $result = $cached->value;
168
+ unset( $cached );
169
+ } else {
170
+ $user_group_table = _groups_get_tablename( "user_group" );
171
+ $rows = $wpdb->get_results( $wpdb->prepare(
172
  "SELECT group_id FROM $user_group_table WHERE user_id = %d",
173
  Groups_Utility::id( $this->user->ID )
174
+ ) );
175
+ if ( $rows ) {
176
+ $result = array();
177
+ foreach( $rows as $row ) {
178
+ $result[] = $row->group_id;
179
+ }
180
  }
181
+ Groups_Cache::set( self::GROUP_IDS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
182
  }
183
  break;
184
 
185
  case 'group_ids_deep' :
186
  if ( $this->user !== null ) {
187
+ $cached = Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP );
188
+ if ( $cached !== null ) {
189
+ $group_ids = $cached->value;
190
+ unset( $cached );
191
+ } else {
192
  $this->init_cache( $capability_ids, $capabilities, $group_ids );
193
  }
194
  $result = $group_ids;
195
  }
196
  break;
197
 
198
+ case 'capabilities' :
199
+ $cached = Groups_Cache::get( self::CAPABILITIES_BASE . $this->user->ID, self::CACHE_GROUP );
200
+ if ( $cached !== null ) {
201
+ $result = $cached->value;
202
+ unset( $cached );
203
+ } else {
204
+ $user_capability_table = _groups_get_tablename( "user_capability" );
205
+ $rows = $wpdb->get_results( $wpdb->prepare(
206
+ "SELECT capability_id FROM $user_capability_table WHERE user_id = %d",
207
+ Groups_Utility::id( $this->user->ID )
208
+ ) );
209
+ if ( $rows ) {
210
+ $result = array();
211
+ foreach ( $rows as $row ) {
212
+ $result[] = new Groups_Capability( $row->capability_id );
213
+ }
214
  }
215
+ Groups_Cache::set( self::CAPABILITIES_BASE . $this->user->ID, $result, self::CACHE_GROUP );
216
  }
217
  break;
218
 
219
  case 'capabilities_deep' :
220
  if ( $this->user !== null ) {
221
+ $cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP );
222
+ if ( $cached !== null ) {
223
+ $capabilities = $cached->value;
224
+ unset( $cached );
225
+ } else {
226
  $this->init_cache( $capability_ids, $capabilities );
227
  }
228
  $result = $capabilities;
229
  }
230
  break;
231
 
232
+ case 'groups' :
233
+ $cached = Groups_Cache::get( self::GROUPS_BASE . $this->user->ID, self::CACHE_GROUP );
234
+ if ( $cached !== null ) {
235
+ $result = $cached->value;
236
+ unset( $cached );
237
+ } else {
238
+ $user_group_table = _groups_get_tablename( "user_group" );
239
+ $rows = $wpdb->get_results( $wpdb->prepare(
240
+ "SELECT group_id FROM $user_group_table WHERE user_id = %d",
241
+ Groups_Utility::id( $this->user->ID )
242
+ ) );
243
+ if ( $rows ) {
244
+ $result = array();
245
+ foreach( $rows as $row ) {
246
+ $result[] = new Groups_Group( $row->group_id );
247
+ }
248
+ }
249
+ Groups_Cache::set( self::GROUPS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
250
+ }
251
+ break;
252
+
253
+ case 'groups_deep' :
254
+ $cached = Groups_Cache::get( self::GROUPS . $this->user->ID, self::CACHE_GROUP );
255
+ if ( $cached !== null ) {
256
+ $result = $cached->value;
257
+ unset( $cached );
258
+ } else {
259
  $result = array();
260
+ foreach( $this->group_ids_deep as $group_id ) {
261
+ $result[] = new Groups_Group( $group_id );
262
  }
263
+ Groups_Cache::set( self::GROUPS . $this->user->ID, $result, self::CACHE_GROUP );
264
  }
265
  break;
266
 
291
  $capability_id = null;
292
  if ( is_numeric( $capability ) ) {
293
  $capability_id = Groups_Utility::id( $capability );
294
+ $cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
295
+ if ( $cached !== null ) {
296
+ $capability_ids = $cached->value;
297
+ unset( $cached );
298
+ } else {
299
  $this->init_cache( $capability_ids );
300
  }
301
  $result = in_array( $capability_id, $capability_ids );
302
  } else if ( is_string( $capability ) ) {
303
+ $cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP );
304
+ if ( $cached !== null ) {
305
+ $capabilities = $cached->value;
306
+ unset( $cached );
307
+ } else {
308
  $this->init_cache( $capability_ids, $capabilities );
309
  }
310
  $result = in_array( $capability, $capabilities );
332
  $capability_ids = array();
333
  $group_ids = array();
334
 
335
+ if ( ( $this->user !== null ) && ( Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP ) === null ) ) {
336
  $group_table = _groups_get_tablename( "group" );
337
  $capability_table = _groups_get_tablename( "capability" );
338
  $group_capability_table = _groups_get_tablename( "group_capability" );
427
 
428
  }
429
  }
430
+ Groups_Cache::set( self::CAPABILITIES . $this->user->ID, $capabilities, self::CACHE_GROUP );
431
+ Groups_Cache::set( self::CAPABILITY_IDS . $this->user->ID, $capability_ids, self::CACHE_GROUP );
432
+ Groups_Cache::set( self::GROUP_IDS . $this->user->ID, $group_ids, self::CACHE_GROUP );
433
  }
434
  }
435
 
lib/core/wp-init.php CHANGED
@@ -45,6 +45,9 @@ if ( !function_exists( 'is_user_member_of_blog' ) ) {
45
  * Load core :
46
  */
47
 
 
 
 
48
  require_once( GROUPS_CORE_LIB . '/class-groups-utility.php' );
49
 
50
  // options
45
  * Load core :
46
  */
47
 
48
+ require_once GROUPS_CORE_LIB . '/class-groups-cache.php';
49
+ require_once GROUPS_CORE_LIB . '/class-groups-cache-object.php';
50
+
51
  require_once( GROUPS_CORE_LIB . '/class-groups-utility.php' );
52
 
53
  // options
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://www.itthinx.com/plugins/groups
4
  Tags: access, access control, capability, capabilities, content, download, downloads, file, file access, files, group, groups, member, members, membership, memberships, paypal, permission, permissions, subscription, subscriptions, woocommerce
5
  Requires at least: 4.0
6
  Tested up to: 4.3.1
7
- Stable tag: 1.8.1
8
  License: GPLv3
9
 
10
  Groups is an efficient and powerful solution, providing group-based user membership management, group-based capabilities and content access control.
@@ -181,6 +181,13 @@ See also [Groups](http://www.itthinx.com/plugins/groups/)
181
 
182
  == Changelog ==
183
 
 
 
 
 
 
 
 
184
  = 1.8.1 =
185
  * Fixed potential XSS vulnerabilities related to the unescaped use of the $_SERVER['REQUEST_URI'] in some forms.
186
 
@@ -490,5 +497,5 @@ Some installations wouldn't work correctly, showing no capabilities and making i
490
 
491
  == Upgrade Notice ==
492
 
493
- = 1.8.1 =
494
- Security Release : This release fixes potential XSS vulnerabilities.
4
  Tags: access, access control, capability, capabilities, content, download, downloads, file, file access, files, group, groups, member, members, membership, memberships, paypal, permission, permissions, subscription, subscriptions, woocommerce
5
  Requires at least: 4.0
6
  Tested up to: 4.3.1
7
+ Stable tag: 1.9.0
8
  License: GPLv3
9
 
10
  Groups is an efficient and powerful solution, providing group-based user membership management, group-based capabilities and content access control.
181
 
182
  == Changelog ==
183
 
184
+ = 1.9.0 =
185
+ * Added own cache encapsulation to guard against flaws in outdated cache implementations.
186
+ * Added new user group property to obtain hierarchical groups.
187
+ * Improved user properties to use own caching.
188
+ * Fixed media field filters wrongly declared as actions.
189
+ * Fixed the version comparison operation.
190
+
191
  = 1.8.1 =
192
  * Fixed potential XSS vulnerabilities related to the unescaped use of the $_SERVER['REQUEST_URI'] in some forms.
193
 
497
 
498
  == Upgrade Notice ==
499
 
500
+ = 1.9.0 =
501
+ This release fixes bugs and introduces improved caching mechanisms which may result in performance improvements for some setups.