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 | Groups |
Version | 1.9.0 |
Comparing to | |
See all releases |
Code changes from version 1.8.1 to 1.9.0
- groups.php +2 -2
- lib/access/class-groups-access-meta-boxes.php +2 -2
- lib/access/class-groups-post-access.php +6 -4
- lib/auto/class-groups-registered.php +5 -4
- lib/core/class-groups-cache-object.php +88 -0
- lib/core/class-groups-cache.php +82 -0
- lib/core/class-groups-capability.php +10 -8
- lib/core/class-groups-controller.php +3 -3
- lib/core/class-groups-group.php +10 -8
- lib/core/class-groups-user-group.php +7 -4
- lib/core/class-groups-user.php +123 -58
- lib/core/wp-init.php +3 -0
- readme.txt +10 -3
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.
|
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.
|
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 |
-
|
59 |
-
|
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 |
-
$
|
406 |
-
|
407 |
-
|
|
|
|
|
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 |
-
|
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
|
165 |
-
* @param
|
|
|
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 |
-
|
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 |
-
$
|
201 |
-
|
202 |
-
|
|
|
|
|
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 |
-
|
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 |
-
|
263 |
}
|
264 |
if ( !empty( $old_capability_capability ) ) {
|
265 |
-
|
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 |
-
|
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 (
|
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 (
|
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 (
|
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 |
-
|
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 |
-
$
|
329 |
-
|
330 |
-
|
|
|
|
|
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 |
-
|
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 |
-
|
425 |
}
|
426 |
if ( !empty( $old_group ) && !empty( $old_group->name ) ) {
|
427 |
-
|
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 |
-
|
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 |
-
|
98 |
-
if ( !empty( $group_id ) ) {
|
99 |
// make sure user and group exist
|
100 |
-
if (
|
|
|
|
|
|
|
|
|
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
|
35 |
-
const CAPABILITIES
|
36 |
-
const
|
37 |
-
const
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
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 |
-
$
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
$
|
130 |
-
|
131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
}
|
|
|
133 |
}
|
134 |
break;
|
135 |
|
136 |
case 'capability_ids_deep' :
|
137 |
if ( $this->user !== null ) {
|
138 |
-
$
|
139 |
-
if ( $
|
|
|
|
|
|
|
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 |
-
$
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
149 |
"SELECT group_id FROM $user_group_table WHERE user_id = %d",
|
150 |
Groups_Utility::id( $this->user->ID )
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
|
|
156 |
}
|
|
|
157 |
}
|
158 |
break;
|
159 |
|
160 |
case 'group_ids_deep' :
|
161 |
if ( $this->user !== null ) {
|
162 |
-
$
|
163 |
-
if ( $
|
|
|
|
|
|
|
164 |
$this->init_cache( $capability_ids, $capabilities, $group_ids );
|
165 |
}
|
166 |
$result = $group_ids;
|
167 |
}
|
168 |
break;
|
169 |
|
170 |
-
case
|
171 |
-
$
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
$
|
178 |
-
|
179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
}
|
|
|
181 |
}
|
182 |
break;
|
183 |
|
184 |
case 'capabilities_deep' :
|
185 |
if ( $this->user !== null ) {
|
186 |
-
$
|
187 |
-
if ( $
|
|
|
|
|
|
|
188 |
$this->init_cache( $capability_ids, $capabilities );
|
189 |
}
|
190 |
$result = $capabilities;
|
191 |
}
|
192 |
break;
|
193 |
|
194 |
-
case
|
195 |
-
$
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
$result = array();
|
202 |
-
foreach( $
|
203 |
-
$result[] = new Groups_Group( $
|
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 |
-
$
|
236 |
-
if ( $
|
|
|
|
|
|
|
237 |
$this->init_cache( $capability_ids );
|
238 |
}
|
239 |
$result = in_array( $capability_id, $capability_ids );
|
240 |
} else if ( is_string( $capability ) ) {
|
241 |
-
$
|
242 |
-
if ( $
|
|
|
|
|
|
|
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 ) && (
|
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 |
-
|
366 |
-
|
367 |
-
|
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 |
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.
|
494 |
-
|
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.
|