Groups - Version 1.3.8

Version Description

  • Fix: using substitute wp_cache_switch_to_blog instead of deprecated function wp_cache_reset when available (from 3.5.0)
  • Fix: don't show access restriction meta box on attachments, the option is added with the attachment fields (3.5 uses common post edit screen but save_post isn't triggered on attachments)
  • Improvement: limiting choice of access restrictions to those the current user has
  • Fix: restrict access to edit or delete posts based on the post's access restrictions
  • Feature: added option to refresh capabilities
  • Fix: replaced use of get_user_by() (memory leaks on large user sets) with query & added batch limit when adding users to Registered group on activation
Download this release

Release Info

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

Code changes from version 1.3.7 to 1.3.8

css/groups_admin.css CHANGED
@@ -40,6 +40,18 @@
40
  height: 20px;
41
  vertical-align: middle;
42
  }
 
 
 
 
 
 
 
 
 
 
 
 
43
  div.field {
44
  padding-bottom: 0.62em;
45
  }
@@ -113,3 +125,10 @@ div.groups-footer form {
113
  .groups-options .groups-permissions td.checkbox {
114
  text-align: center;
115
  }
 
 
 
 
 
 
 
40
  height: 20px;
41
  vertical-align: middle;
42
  }
43
+ .manage a.refresh {
44
+ text-decoration: none;
45
+ cursor: pointer;
46
+ float: right;
47
+ }
48
+ .manage a.refresh .icon {
49
+ vertical-align: middle;
50
+ }
51
+ .manage a.refresh .label {
52
+ height: 20px;
53
+ vertical-align: middle;
54
+ }
55
  div.field {
56
  padding-bottom: 0.62em;
57
  }
125
  .groups-options .groups-permissions td.checkbox {
126
  text-align: center;
127
  }
128
+ .info {
129
+ background-color: #eec;
130
+ border: 1px solid #cca;
131
+ border-radius: 4px;
132
+ margin: 1em;
133
+ padding: 1em;
134
+ }
groups.php CHANGED
@@ -21,13 +21,13 @@
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.3.7
25
  * Author: itthinx
26
  * Author URI: http://www.itthinx.com
27
  * Donate-Link: http://www.itthinx.com
28
  * License: GPLv3
29
  */
30
- define( 'GROUPS_CORE_VERSION', '1.3.7' );
31
  define( 'GROUPS_FILE', __FILE__ );
32
  if ( !defined( 'GROUPS_CORE_DIR' ) ) {
33
  define( 'GROUPS_CORE_DIR', WP_PLUGIN_DIR . '/groups' );
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.3.8
25
  * Author: itthinx
26
  * Author URI: http://www.itthinx.com
27
  * Donate-Link: http://www.itthinx.com
28
  * License: GPLv3
29
  */
30
+ define( 'GROUPS_CORE_VERSION', '1.3.8' );
31
  define( 'GROUPS_FILE', __FILE__ );
32
  if ( !defined( 'GROUPS_CORE_DIR' ) ) {
33
  define( 'GROUPS_CORE_DIR', WP_PLUGIN_DIR . '/groups' );
images/refresh.png ADDED
Binary file
lib/access/class-groups-access-meta-boxes.php CHANGED
@@ -48,7 +48,7 @@ class Groups_Access_Meta_Boxes {
48
  public static function add_meta_boxes( $post_type, $post = null ) {
49
  global $wp_version;
50
  $post_type_object = get_post_type_object( $post_type );
51
- if ( $post_type_object ) {
52
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
53
  if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
54
  if ( $wp_version < 3.3 ) {
@@ -102,33 +102,39 @@ class Groups_Access_Meta_Boxes {
102
  }
103
  }
104
 
105
- $output .= __( "Enforce read access", GROUPS_PLUGIN_DOMAIN );
106
- $read_caps = get_post_meta( $post_id, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY );
107
- $valid_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
108
- $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
109
- $output .= '<ul>';
110
- foreach( $valid_read_caps as $valid_read_cap ) {
111
- if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
112
- $checked = in_array( $capability->capability, $read_caps ) ? ' checked="checked" ' : '';
113
- $output .= '<li>';
114
- $output .= '<label>';
115
- $output .= '<input name="' . self::CAPABILITY . '[]" ' . $checked . ' type="checkbox" value="' . esc_attr( $capability->capability_id ) . '" />';
116
- $output .= wp_filter_nohtml_kses( $capability->capability );
117
- $output .= '</label>';
118
- $output .= '</li>';
 
 
119
  }
 
 
 
 
 
 
 
 
 
 
 
120
  }
121
- $output .= '</ul>';
122
- $output .= '</div>';
123
-
124
- $output .= '<p class="description">';
125
- $output .= sprintf( __( "Only groups or users that have one of the selected capabilities are allowed to read this %s.", GROUPS_PLUGIN_DOMAIN ), $post_singular_name );
126
- $output .= '</p>';
127
- $output .= wp_nonce_field( self::SET_CAPABILITY, self::NONCE, true, false );
128
 
129
  echo $output;
130
  }
131
-
132
  /**
133
  * Save capability options.
134
  *
@@ -140,7 +146,7 @@ class Groups_Access_Meta_Boxes {
140
  } else {
141
  $post_type = get_post_type( $post_id );
142
  $post_type_object = get_post_type_object( $post_type );
143
- if ( $post_type_object ) {
144
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
145
  if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
146
  if ( isset( $_POST[self::NONCE] ) && wp_verify_nonce( $_POST[self::NONCE], self::SET_CAPABILITY ) ) {
@@ -151,14 +157,19 @@ class Groups_Access_Meta_Boxes {
151
  // If the post ID is not provided, it will throw:
152
  // PHP Notice: Undefined offset: 0 in /var/www/groups-forums/wp-includes/capabilities.php on line 1067
153
  if ( current_user_can( 'edit_'.$post_type, $post_id ) ) {
154
- Groups_Post_Access::delete( $post_id, null );
155
- if ( !empty( $_POST[self::CAPABILITY] ) ) {
156
- foreach ( $_POST[self::CAPABILITY] as $capability_id ) {
157
- if ( $capability = Groups_Capability::read( $capability_id ) ) {
158
- Groups_Post_Access::create( array(
159
- 'post_id' => $post_id,
160
- 'capability' => $capability->capability
161
- ) );
 
 
 
 
 
162
  }
163
  }
164
  }
@@ -169,7 +180,7 @@ class Groups_Access_Meta_Boxes {
169
  }
170
  }
171
  }
172
-
173
  /**
174
  * Render capabilities box for attachment post type (Media).
175
  * @param array $form_fields
@@ -177,45 +188,45 @@ class Groups_Access_Meta_Boxes {
177
  * @return array
178
  */
179
  public static function attachment_fields_to_edit( $form_fields, $post ) {
180
-
181
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
182
  if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
183
-
184
- $output = "";
185
- $post_singular_name = __( 'Media', GROUPS_PLUGIN_DOMAIN );
186
 
187
- $output .= __( "Enforce read access", GROUPS_PLUGIN_DOMAIN );
188
- $read_caps = get_post_meta( $post->ID, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY );
189
- $valid_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
190
- $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
191
- $output .= '<ul>';
192
- foreach( $valid_read_caps as $valid_read_cap ) {
193
- if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
194
- $checked = in_array( $capability->capability, $read_caps ) ? ' checked="checked" ' : '';
195
- $output .= '<li>';
196
- $output .= '<label>';
197
- $output .= '<input name="attachments[' . $post->ID . '][' . self::CAPABILITY . '][]" ' . $checked . ' type="checkbox" value="' . esc_attr( $capability->capability_id ) . '" />';
198
- $output .= wp_filter_nohtml_kses( $capability->capability );
199
- $output .= '</label>';
200
- $output .= '</li>';
 
201
  }
202
- }
203
- $output .= '</ul>';
204
- $output .= '</div>';
205
-
206
- $output .= '<p class="description">';
207
- $output .= sprintf( __( "Only groups or users that have one of the selected capabilities are allowed to read this %s.", GROUPS_PLUGIN_DOMAIN ), $post_singular_name );
208
- $output .= '</p>';
209
 
210
- $form_fields['groups_access'] = array(
211
- 'label' => __( 'Access restrictions', GROUPS_PLUGIN_DOMAIN ),
212
- 'input' => 'html',
213
- 'html' => $output
214
- );
 
 
 
 
 
215
  }
216
  return $form_fields;
217
  }
218
-
219
  /**
220
  * Save capabilities for attachment post type (Media).
221
  * When multiple attachments are saved, this is called once for each.
@@ -226,21 +237,43 @@ class Groups_Access_Meta_Boxes {
226
  public static function attachment_fields_to_save( $post, $attachment ) {
227
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
228
  if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
229
- if ( current_user_can( 'edit_attachment' ) ) {
230
- Groups_Post_Access::delete( $post['ID'], null );
231
- if ( !empty( $attachment[self::CAPABILITY] ) ) {
232
- foreach ( $attachment[self::CAPABILITY] as $capability_id ) {
233
- if ( $capability = Groups_Capability::read( $capability_id ) ) {
234
- Groups_Post_Access::create( array(
235
- 'post_id' => $post['ID'],
236
- 'capability' => $capability->capability
237
- ) );
 
 
238
  }
239
- }
240
  }
241
  }
242
  }
243
  return $post;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  }
245
 
246
  }
48
  public static function add_meta_boxes( $post_type, $post = null ) {
49
  global $wp_version;
50
  $post_type_object = get_post_type_object( $post_type );
51
+ if ( $post_type_object && $post_type != 'attachment' ) {
52
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
53
  if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
54
  if ( $wp_version < 3.3 ) {
102
  }
103
  }
104
 
105
+ if ( self::user_can_restrict() ) {
106
+ $output .= __( "Enforce read access", GROUPS_PLUGIN_DOMAIN );
107
+ $read_caps = get_post_meta( $post_id, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY );
108
+ $valid_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
109
+ $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
110
+ $output .= '<ul>';
111
+ foreach( $valid_read_caps as $valid_read_cap ) {
112
+ if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
113
+ $checked = in_array( $capability->capability, $read_caps ) ? ' checked="checked" ' : '';
114
+ $output .= '<li>';
115
+ $output .= '<label>';
116
+ $output .= '<input name="' . self::CAPABILITY . '[]" ' . $checked . ' type="checkbox" value="' . esc_attr( $capability->capability_id ) . '" />';
117
+ $output .= wp_filter_nohtml_kses( $capability->capability );
118
+ $output .= '</label>';
119
+ $output .= '</li>';
120
+ }
121
  }
122
+ $output .= '</ul>';
123
+ $output .= '</div>';
124
+
125
+ $output .= '<p class="description">';
126
+ $output .= sprintf( __( "Only groups or users that have one of the selected capabilities are allowed to read this %s.", GROUPS_PLUGIN_DOMAIN ), $post_singular_name );
127
+ $output .= '</p>';
128
+ $output .= wp_nonce_field( self::SET_CAPABILITY, self::NONCE, true, false );
129
+ } else {
130
+ $output .= '<p class="description">';
131
+ $output .= sprintf( __( 'You cannot set any access restrictions.', GROUPS_PLUGIN_DOMAIN ), $post_singular_name );
132
+ $output .= '</p>';
133
  }
 
 
 
 
 
 
 
134
 
135
  echo $output;
136
  }
137
+
138
  /**
139
  * Save capability options.
140
  *
146
  } else {
147
  $post_type = get_post_type( $post_id );
148
  $post_type_object = get_post_type_object( $post_type );
149
+ if ( $post_type_object && $post_type != 'attachment' ) {
150
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
151
  if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
152
  if ( isset( $_POST[self::NONCE] ) && wp_verify_nonce( $_POST[self::NONCE], self::SET_CAPABILITY ) ) {
157
  // If the post ID is not provided, it will throw:
158
  // PHP Notice: Undefined offset: 0 in /var/www/groups-forums/wp-includes/capabilities.php on line 1067
159
  if ( current_user_can( 'edit_'.$post_type, $post_id ) ) {
160
+ if ( self::user_can_restrict() ) {
161
+ Groups_Post_Access::delete( $post_id, null );
162
+ $user = new Groups_User( get_current_user_id() );
163
+ if ( !empty( $_POST[self::CAPABILITY] ) ) {
164
+ foreach ( $_POST[self::CAPABILITY] as $capability_id ) {
165
+ if ( $capability = Groups_Capability::read( $capability_id ) ) {
166
+ if ( $user->can( $capability->capability_id ) ) {
167
+ Groups_Post_Access::create( array(
168
+ 'post_id' => $post_id,
169
+ 'capability' => $capability->capability
170
+ ) );
171
+ }
172
+ }
173
  }
174
  }
175
  }
180
  }
181
  }
182
  }
183
+
184
  /**
185
  * Render capabilities box for attachment post type (Media).
186
  * @param array $form_fields
188
  * @return array
189
  */
190
  public static function attachment_fields_to_edit( $form_fields, $post ) {
 
191
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
192
  if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
193
+ if ( self::user_can_restrict() ) {
194
+ $output = "";
195
+ $post_singular_name = __( 'Media', GROUPS_PLUGIN_DOMAIN );
196
 
197
+ $output .= __( "Enforce read access", GROUPS_PLUGIN_DOMAIN );
198
+ $read_caps = get_post_meta( $post->ID, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY );
199
+ $valid_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
200
+ $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
201
+ $output .= '<ul>';
202
+ foreach( $valid_read_caps as $valid_read_cap ) {
203
+ if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
204
+ $checked = in_array( $capability->capability, $read_caps ) ? ' checked="checked" ' : '';
205
+ $output .= '<li>';
206
+ $output .= '<label>';
207
+ $output .= '<input name="attachments[' . $post->ID . '][' . self::CAPABILITY . '][]" ' . $checked . ' type="checkbox" value="' . esc_attr( $capability->capability_id ) . '" />';
208
+ $output .= wp_filter_nohtml_kses( $capability->capability );
209
+ $output .= '</label>';
210
+ $output .= '</li>';
211
+ }
212
  }
213
+ $output .= '</ul>';
214
+ $output .= '</div>';
 
 
 
 
 
215
 
216
+ $output .= '<p class="description">';
217
+ $output .= sprintf( __( "Only groups or users that have one of the selected capabilities are allowed to read this %s.", GROUPS_PLUGIN_DOMAIN ), $post_singular_name );
218
+ $output .= '</p>';
219
+
220
+ $form_fields['groups_access'] = array(
221
+ 'label' => __( 'Access restrictions', GROUPS_PLUGIN_DOMAIN ),
222
+ 'input' => 'html',
223
+ 'html' => $output
224
+ );
225
+ }
226
  }
227
  return $form_fields;
228
  }
229
+
230
  /**
231
  * Save capabilities for attachment post type (Media).
232
  * When multiple attachments are saved, this is called once for each.
237
  public static function attachment_fields_to_save( $post, $attachment ) {
238
  $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
239
  if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
240
+ if ( current_user_can( 'edit_attachment' ) ) {
241
+ if ( self::user_can_restrict() ) {
242
+ Groups_Post_Access::delete( $post['ID'], null );
243
+ if ( !empty( $attachment[self::CAPABILITY] ) ) {
244
+ foreach ( $attachment[self::CAPABILITY] as $capability_id ) {
245
+ if ( $capability = Groups_Capability::read( $capability_id ) ) {
246
+ Groups_Post_Access::create( array(
247
+ 'post_id' => $post['ID'],
248
+ 'capability' => $capability->capability
249
+ ) );
250
+ }
251
  }
252
+ }
253
  }
254
  }
255
  }
256
  return $post;
257
+ }
258
+
259
+ /**
260
+ * Returns true if the current user has at least one of the capabilities
261
+ * that can be used to restrict access to posts.
262
+ * @return boolean
263
+ */
264
+ private static function user_can_restrict() {
265
+ $has_read_cap = false;
266
+ $user = new Groups_User( get_current_user_id() );
267
+ $valid_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
268
+ foreach( $valid_read_caps as $valid_read_cap ) {
269
+ if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
270
+ if ( $user->can( $capability->capability_id ) ) {
271
+ $has_read_cap = true;
272
+ break;
273
+ }
274
+ }
275
+ }
276
+ return $has_read_cap;
277
  }
278
 
279
  }
lib/access/class-groups-post-access.php CHANGED
@@ -45,7 +45,10 @@ class Groups_Post_Access {
45
  __( "Read Post", GROUPS_PLUGIN_DOMAIN );
46
  }
47
  }
48
-
 
 
 
49
  public static function init() {
50
  // post access
51
  add_filter( 'get_pages', array( __CLASS__, "get_pages" ), 1 );
@@ -54,12 +57,38 @@ class Groups_Post_Access {
54
  // content access
55
  add_filter( "get_the_excerpt", array( __CLASS__, "get_the_excerpt" ), 1 );
56
  add_filter( "the_content", array( __CLASS__, "the_content" ), 1 );
 
 
57
  // @todo these could be interesting to add later ...
58
  // add_filter( "plugin_row_meta", array( __CLASS__, "plugin_row_meta" ), 1 );
59
  // add_filter( "posts_join_paged", array( __CLASS__, "posts_join_paged" ), 1 );
60
  // add_filter( "posts_where_paged", array( __CLASS__, "posts_where_paged" ), 1 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
-
 
 
63
  /**
64
  * Filter pages by access capability.
65
  *
45
  __( "Read Post", GROUPS_PLUGIN_DOMAIN );
46
  }
47
  }
48
+
49
+ /**
50
+ * Sets up filters to restrict access.
51
+ */
52
  public static function init() {
53
  // post access
54
  add_filter( 'get_pages', array( __CLASS__, "get_pages" ), 1 );
57
  // content access
58
  add_filter( "get_the_excerpt", array( __CLASS__, "get_the_excerpt" ), 1 );
59
  add_filter( "the_content", array( __CLASS__, "the_content" ), 1 );
60
+ // edit & delete post
61
+ add_filter( 'map_meta_cap', array( __CLASS__, 'map_meta_cap' ), 10, 4 );
62
  // @todo these could be interesting to add later ...
63
  // add_filter( "plugin_row_meta", array( __CLASS__, "plugin_row_meta" ), 1 );
64
  // add_filter( "posts_join_paged", array( __CLASS__, "posts_join_paged" ), 1 );
65
  // add_filter( "posts_where_paged", array( __CLASS__, "posts_where_paged" ), 1 );
66
+ }
67
+
68
+ /**
69
+ * Restrict access to edit or delete posts based on the post's access restrictions.
70
+ * @param array $caps
71
+ * @param string $cap
72
+ * @param int $user_id
73
+ * @param array $args
74
+ * @return array
75
+ */
76
+ public static function map_meta_cap( $caps, $cap, $user_id, $args ) {
77
+ if ( isset( $args[0] ) ) {
78
+ if ( strpos( $cap, 'edit_' ) === 0 || strpos( $cap, 'delete_' ) === 0 ) {
79
+ if ( $post_type = get_post_type( $args[0] ) ) {
80
+ if ( $cap === "edit_$post_type" || $cap === "delete_$post_type" ) {
81
+ $post_id = $args[0];
82
+ if ( !self::user_can_read_post( $post_id, $user_id ) ) {
83
+ $caps[] = 'do_not_allow';
84
+ }
85
+ }
86
+ }
87
+ }
88
  }
89
+ return $caps;
90
+ }
91
+
92
  /**
93
  * Filter pages by access capability.
94
  *
lib/admin/groups-admin-capabilities.php CHANGED
@@ -99,6 +99,18 @@ function groups_admin_capabilities() {
99
  return groups_admin_capabilities_remove( $_GET['capability_id'] );
100
  }
101
  break;
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
103
  }
104
 
@@ -166,10 +178,11 @@ function groups_admin_capabilities() {
166
  __( 'Capabilities', GROUPS_PLUGIN_DOMAIN ) .
167
  '</h2>' .
168
  '</div>';
169
-
170
  $output .=
171
  '<div class="manage">' .
172
- "<a title='" . __( 'Click to add a new capability', GROUPS_PLUGIN_DOMAIN ) . "' class='add button' href='" . esc_url( $current_url ) . "&action=add'><img class='icon' alt='" . __( 'Add', GROUPS_PLUGIN_DOMAIN) . "' src='". GROUPS_PLUGIN_URL ."images/add.png'/><span class='label'>" . __( 'New Capability', GROUPS_PLUGIN_DOMAIN) . "</span></a>" .
 
173
  '</div>';
174
 
175
  $row_count = isset( $_POST['row_count'] ) ? intval( $_POST['row_count'] ) : 0;
99
  return groups_admin_capabilities_remove( $_GET['capability_id'] );
100
  }
101
  break;
102
+ case 'refresh' :
103
+ if ( check_admin_referer( 'refresh' ) ) {
104
+ $n = Groups_WordPress::refresh_capabilities();
105
+ if ( $n > 0 ) {
106
+ $output .= '<div class="info">' . sprintf( _n( 'One capability has been added.', '%d capabilities have been added.', $n, GROUPS_PLUGIN_DOMAIN ), $n ) . '</div>';
107
+ } else {
108
+ $output .= '<div class="info">' . __( 'No new capabilities have been found.', GROUPS_PLUGIN_DOMAIN ) . '</div>';
109
+ }
110
+ } else {
111
+ wp_die( __( 'A Duck!', GROUPS_PLUGIN_DOMAIN ) );
112
+ }
113
+ break;
114
  }
115
  }
116
 
178
  __( 'Capabilities', GROUPS_PLUGIN_DOMAIN ) .
179
  '</h2>' .
180
  '</div>';
181
+
182
  $output .=
183
  '<div class="manage">' .
184
+ "<a title='" . __( 'Click to add a new capability', GROUPS_PLUGIN_DOMAIN ) . "' class='add button' href='" . esc_url( $current_url ) . "&action=add'><img class='icon' alt='" . __( 'Add', GROUPS_PLUGIN_DOMAIN) . "' src='". GROUPS_PLUGIN_URL . "images/add.png'/><span class='label'>" . __( 'New Capability', GROUPS_PLUGIN_DOMAIN) . "</span></a>" .
185
+ "<a title='" . __( 'Click to refresh capabilities', GROUPS_PLUGIN_DOMAIN ) . "' class='refresh button' href='" . esc_url( wp_nonce_url( $current_url, 'refresh' ) ) . "&action=refresh'><img class='icon' alt='" . __( 'Refresh', GROUPS_PLUGIN_DOMAIN) . "' src='". GROUPS_PLUGIN_URL . "images/refresh.png'/><span class='label'>" . __( '', GROUPS_PLUGIN_DOMAIN) . "</span></a>" .
186
  '</div>';
187
 
188
  $row_count = isset( $_POST['row_count'] ) ? intval( $_POST['row_count'] ) : 0;
lib/auto/class-groups-registered.php CHANGED
@@ -26,27 +26,35 @@ class Groups_Registered {
26
 
27
  const REGISTERED_GROUP_NAME = 'Registered';
28
 
 
 
29
  /**
30
  * Creates groups for registered users.
31
  * Must be called explicitly or hooked into activation.
32
  */
33
  public static function activate() {
34
-
35
  global $wpdb;
36
-
37
  // create a group for the blog if it doesn't exist
38
  if ( !( $group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME ) ) ) {
39
  $group_id = Groups_Group::create( array( "name" => self::REGISTERED_GROUP_NAME ) );
40
  } else {
41
  $group_id = $group->group_id;
42
  }
43
-
44
- $users = $wpdb->get_results( "SELECT ID FROM $wpdb->users" );
45
- foreach( $users as $user ) {
46
- // add the user to the group
47
- if ( $group_id ) {
48
- if ( !Groups_User_Group::read( $user->ID, $group_id ) ) {
49
- Groups_User_Group::create( array( "user_id" => $user->ID, "group_id" => $group_id ) );
 
 
 
 
 
 
50
  }
51
  }
52
  }
26
 
27
  const REGISTERED_GROUP_NAME = 'Registered';
28
 
29
+ const BATCH_LIMIT = 100;
30
+
31
  /**
32
  * Creates groups for registered users.
33
  * Must be called explicitly or hooked into activation.
34
  */
35
  public static function activate() {
36
+
37
  global $wpdb;
38
+
39
  // create a group for the blog if it doesn't exist
40
  if ( !( $group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME ) ) ) {
41
  $group_id = Groups_Group::create( array( "name" => self::REGISTERED_GROUP_NAME ) );
42
  } else {
43
  $group_id = $group->group_id;
44
  }
45
+ if ( $group_id ) {
46
+ $n = $wpdb->get_var( "SELECT COUNT(ID) FROM $wpdb->users" );
47
+ for ( $i = 0; $i < $n; $i += self::BATCH_LIMIT ) {
48
+ $users = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->users LIMIT %d, %d", $i, self::BATCH_LIMIT ) );
49
+ foreach( $users as $user ) {
50
+ // add the user to the group
51
+ if ( !Groups_User_Group::read( $user->ID, $group_id ) ) {
52
+ Groups_User_Group::create( array( "user_id" => $user->ID, "group_id" => $group_id ) );
53
+ }
54
+ }
55
+ unset( $users );
56
+ if ( function_exists( 'gc_collect_cycles' ) ) {
57
+ gc_collect_cycles();
58
  }
59
  }
60
  }
lib/core/class-groups-controller.php CHANGED
@@ -36,7 +36,11 @@ class Groups_Controller {
36
  */
37
  public static function switch_to_blog( $blog_id ) {
38
  switch_to_blog( $blog_id );
39
- wp_cache_reset();
 
 
 
 
40
  }
41
 
42
  /**
36
  */
37
  public static function switch_to_blog( $blog_id ) {
38
  switch_to_blog( $blog_id );
39
+ if ( function_exists( 'wp_cache_switch_to_blog' ) ) {
40
+ wp_cache_switch_to_blog( $blog_id ); // introduced in WP 3.5.0
41
+ } else {
42
+ wp_cache_reset(); // deprecated in WP 3.5.0
43
+ }
44
  }
45
 
46
  /**
lib/core/class-groups-user-group.php CHANGED
@@ -93,7 +93,7 @@ class Groups_User_Group {
93
  // if ( !empty( $user_id ) && !empty( $group_id ) ) {
94
  if ( !empty( $group_id ) ) {
95
  // make sure user and group exist
96
- if ( ( false !== Groups_Utility::id( $user_id ) ) && get_user_by( "id", $user_id ) && ( $group = Groups_Group::read( $group_id ) ) ) {
97
  // only allow to add users to groups if they belong to the
98
  // group's blog or we have the anonymous user
99
  if ( is_user_member_of_blog( Groups_Utility::id( $user_id ) ) || ( Groups_Utility::id( $user_id ) === 0 ) ) {
93
  // if ( !empty( $user_id ) && !empty( $group_id ) ) {
94
  if ( !empty( $group_id ) ) {
95
  // make sure user and group exist
96
+ 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 ) ) ) {
97
  // only allow to add users to groups if they belong to the
98
  // group's blog or we have the anonymous user
99
  if ( is_user_member_of_blog( Groups_Utility::id( $user_id ) ) || ( Groups_Utility::id( $user_id ) === 0 ) ) {
lib/wp/class-groups-wordpress.php CHANGED
@@ -89,30 +89,40 @@ class Groups_WordPress {
89
  * @see Groups_Controller::activate()
90
  */
91
  public static function activate() {
92
- global $wp_roles;
93
- $capabilities = array();
94
-
95
- if ( !isset( $wp_roles ) ) {
96
- // just trigger initialization
97
- get_role( 'administrator' );
98
- }
99
- $roles = $wp_roles->roles;
100
- if ( is_array( $roles ) ) {
101
- foreach ( $roles as $rolename => $atts ) {
102
- if ( isset( $atts['capabilities'] ) && is_array( $atts['capabilities'] ) ) {
103
- foreach ( $atts['capabilities'] as $capability => $value ) {
104
- if ( !in_array( $capability, $capabilities ) ) {
105
- $capabilities[] = $capability;
106
- }
107
- }
108
- }
109
- }
110
- }
111
- foreach ( $capabilities as $capability ) {
112
- if ( !Groups_Capability::read_by_capability( $capability ) ) {
 
 
 
 
 
 
 
 
113
  Groups_Capability::create( array( 'capability' => $capability ) );
114
- }
 
115
  }
 
116
  }
117
  }
118
  Groups_WordPress::init();
89
  * @see Groups_Controller::activate()
90
  */
91
  public static function activate() {
92
+ self::refresh_capabilities();
93
+ }
94
+
95
+ /**
96
+ * Refreshes Groups capabilities based on WordPress capabilities.
97
+ * @return int number of capabilities added
98
+ */
99
+ public static function refresh_capabilities() {
100
+ global $wp_roles;
101
+ $capabilities = array();
102
+ $count = 0;
103
+ if ( !isset( $wp_roles ) ) {
104
+ // just trigger initialization
105
+ get_role( 'administrator' );
106
+ }
107
+ $roles = $wp_roles->roles;
108
+ if ( is_array( $roles ) ) {
109
+ foreach ( $roles as $rolename => $atts ) {
110
+ if ( isset( $atts['capabilities'] ) && is_array( $atts['capabilities'] ) ) {
111
+ foreach ( $atts['capabilities'] as $capability => $value ) {
112
+ if ( !in_array( $capability, $capabilities ) ) {
113
+ $capabilities[] = $capability;
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ foreach ( $capabilities as $capability ) {
120
+ if ( !Groups_Capability::read_by_capability( $capability ) ) {
121
  Groups_Capability::create( array( 'capability' => $capability ) );
122
+ $count++;
123
+ }
124
  }
125
+ return $count;
126
  }
127
  }
128
  Groups_WordPress::init();
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: itthinx
3
  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: 3.3
6
- Tested up to: 3.5
7
- Stable tag: 1.3.7
8
  License: GPLv3
9
 
10
  Groups provides group-based user membership management, group-based capabilities and content access control.
@@ -16,6 +16,8 @@ It integrates standard WordPress capabilities and application-specific capabilit
16
 
17
  ### Extensions ###
18
 
 
 
19
  - [Groups File Access](http://www.itthinx.com/plugins/groups-file-access/) Groups File Access is an extension that allows to provide file download links for authorized users. Access to files is restricted to users by their group membership.
20
  - [Groups Forums](http://www.itthinx.com/plugins/groups-forums/) A powerful and yet light-weight forum system.
21
  - [Groups Jigoshop](http://jigoshop.com/product/subscriptions/) Groups integration for Jigoshop that supports memberships and subscriptions.
@@ -324,6 +326,14 @@ See also [Groups](http://www.itthinx.com/plugins/groups/)
324
 
325
  == Changelog ==
326
 
 
 
 
 
 
 
 
 
327
  = 1.3.7 =
328
  * Fix: missing argument for meta box when saving a post
329
  * Fix: Groups conflicting with other plugins adding columns to the Users screen (in the manage_users_custom_column filter) thanks to [Erwin](http://www.transpontine.com) who spotted this :)
@@ -430,6 +440,9 @@ Some installations wouldn't work correctly, showing no capabilities and making i
430
 
431
  == Upgrade Notice ==
432
 
 
 
 
433
  = 1.3.7 =
434
  * Please update, this includes fixes: missing argument for meta box when saving a post; Groups conflicting with other plugins adding columns to the Users screen.
435
 
3
  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: 3.3
6
+ Tested up to: 3.5.1
7
+ Stable tag: 1.3.8
8
  License: GPLv3
9
 
10
  Groups provides group-based user membership management, group-based capabilities and content access control.
16
 
17
  ### Extensions ###
18
 
19
+
20
+ - [Groups Blog Protect](http://wordpress.org/extend/plugins/groups-blog-protect/) Protect access to blogs by group membership.
21
  - [Groups File Access](http://www.itthinx.com/plugins/groups-file-access/) Groups File Access is an extension that allows to provide file download links for authorized users. Access to files is restricted to users by their group membership.
22
  - [Groups Forums](http://www.itthinx.com/plugins/groups-forums/) A powerful and yet light-weight forum system.
23
  - [Groups Jigoshop](http://jigoshop.com/product/subscriptions/) Groups integration for Jigoshop that supports memberships and subscriptions.
326
 
327
  == Changelog ==
328
 
329
+ = 1.3.8 =
330
+ * Fix: using substitute wp_cache_switch_to_blog instead of deprecated function wp_cache_reset when available (from 3.5.0)
331
+ * Fix: don't show access restriction meta box on attachments, the option is added with the attachment fields (3.5 uses common post edit screen but save_post isn't triggered on attachments)
332
+ * Improvement: limiting choice of access restrictions to those the current user has
333
+ * Fix: restrict access to edit or delete posts based on the post's access restrictions
334
+ * Feature: added option to refresh capabilities
335
+ * Fix: replaced use of get_user_by() (memory leaks on large user sets) with query & added batch limit when adding users to Registered group on activation
336
+
337
  = 1.3.7 =
338
  * Fix: missing argument for meta box when saving a post
339
  * Fix: Groups conflicting with other plugins adding columns to the Users screen (in the manage_users_custom_column filter) thanks to [Erwin](http://www.transpontine.com) who spotted this :)
440
 
441
  == Upgrade Notice ==
442
 
443
+ = 1.3.8 =
444
+ * This release includes several fixes and improvements, including more limiting features for access restrictions.
445
+
446
  = 1.3.7 =
447
  * Please update, this includes fixes: missing argument for meta box when saving a post; Groups conflicting with other plugins adding columns to the Users screen.
448