Groups - Version 1.2.0

Version Description

  • Access control is no longer restricted to the groups_read_post capability: now any capability can be used to limit access to posts so that different groups can be granted access to different sets of posts.
Download this release

Release Info

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

Code changes from version 1.1.5 to 1.2.0

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.1.5
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.1.5' );
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.2.0
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.2.0' );
31
  define( 'GROUPS_FILE', __FILE__ );
32
  if ( !defined( 'GROUPS_CORE_DIR' ) ) {
33
  define( 'GROUPS_CORE_DIR', WP_PLUGIN_DIR . '/groups' );
lib/access/class-groups-access-meta-boxes.php CHANGED
@@ -29,6 +29,7 @@ class Groups_Access_Meta_Boxes {
29
  const NONCE = 'groups-meta-box-nonce';
30
  const SET_CAPABILITY = 'set-capability';
31
  const READ_ACCESS = 'read-access';
 
32
 
33
  /**
34
  * Hooks for capabilities meta box and saving options.
@@ -76,9 +77,11 @@ class Groups_Access_Meta_Boxes {
76
  * @param Object $box
77
  */
78
  public static function capability( $object = null, $box = null ) {
79
-
 
 
80
  $output = "";
81
-
82
  $post_id = isset( $object->ID ) ? $object->ID : null;
83
  $post_type = isset( $object->post_type ) ? $object->post_type : null;
84
  $post_singular_name = __( "Post", GROUPS_PLUGIN_DOMAIN );
@@ -91,25 +94,28 @@ class Groups_Access_Meta_Boxes {
91
  }
92
  }
93
  }
94
- $read_access = false;
95
- if ( $post_id !== null ) {
96
- if ( Groups_Post_Access::read( $post_id, Groups_Post_Access::READ_POST_CAPABILITY ) ) {
97
- $read_access = true;
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
  }
100
- if ( $read_access ) {
101
- $checked = ' checked="checked" ';
102
- } else {
103
- $checked = '';
104
- }
105
-
106
- $output .= '<input type="checkbox" name="' . self::READ_ACCESS . '" ' . $checked . ' />';
107
- $output .= '&nbsp;';
108
- $output .= '<label for="' . self::READ_ACCESS . '">';
109
- $output .= __( "Enforce read access", GROUPS_PLUGIN_DOMAIN );
110
- $output .= '</label> ';
111
  $output .= '<p class="description">';
112
- $output .= sprintf( __( "Only groups or users that have the <b>%s</b> capability are allowed to read this %s.", GROUPS_PLUGIN_DOMAIN ), Groups_Post_Access::READ_POST_CAPABILITY, $post_singular_name );
113
  $output .= '</p>';
114
  $output .= wp_nonce_field( self::SET_CAPABILITY, self::NONCE, true, false );
115
  echo $output;
@@ -128,11 +134,16 @@ class Groups_Access_Meta_Boxes {
128
  $post_type = isset( $_POST["post_type"] ) ? $_POST["post_type"] : null;
129
  if ( $post_type !== null ) {
130
  if ( current_user_can( 'edit_'.$post_type ) ) {
131
- $read_access = isset( $_POST[self::READ_ACCESS] ) ? !empty( $_POST[self::READ_ACCESS] ) : false;
132
- if ( $read_access ) {
133
- Groups_Post_Access::create( array( "post_id" => $post_id ) );
134
- } else {
135
- Groups_Post_Access::delete( $post_id );
 
 
 
 
 
136
  }
137
  }
138
  }
29
  const NONCE = 'groups-meta-box-nonce';
30
  const SET_CAPABILITY = 'set-capability';
31
  const READ_ACCESS = 'read-access';
32
+ const CAPABILITY = 'capability';
33
 
34
  /**
35
  * Hooks for capabilities meta box and saving options.
77
  * @param Object $box
78
  */
79
  public static function capability( $object = null, $box = null ) {
80
+
81
+ global $wpdb;
82
+
83
  $output = "";
84
+
85
  $post_id = isset( $object->ID ) ? $object->ID : null;
86
  $post_type = isset( $object->post_type ) ? $object->post_type : null;
87
  $post_singular_name = __( "Post", GROUPS_PLUGIN_DOMAIN );
94
  }
95
  }
96
  }
97
+
98
+ $output .= __( "Enforce read access", GROUPS_PLUGIN_DOMAIN );
99
+ $read_caps = get_post_meta( $post_id, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY );
100
+ $valid_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
101
+ $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
102
+ $output .= '<ul>';
103
+ foreach( $valid_read_caps as $valid_read_cap ) {
104
+ if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
105
+ $checked = in_array( $capability->capability, $read_caps ) ? ' checked="checked" ' : '';
106
+ $output .= '<li>';
107
+ $output .= '<label>';
108
+ $output .= '<input name="' . self::CAPABILITY . '[]" ' . $checked . ' type="checkbox" value="' . esc_attr( $capability->capability_id ) . '" />';
109
+ $output .= wp_filter_nohtml_kses( $capability->capability );
110
+ $output .= '</label>';
111
+ $output .= '</li>';
112
  }
113
  }
114
+ $output .= '</ul>';
115
+ $output .= '</div>';
116
+
 
 
 
 
 
 
 
 
117
  $output .= '<p class="description">';
118
+ $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 );
119
  $output .= '</p>';
120
  $output .= wp_nonce_field( self::SET_CAPABILITY, self::NONCE, true, false );
121
  echo $output;
134
  $post_type = isset( $_POST["post_type"] ) ? $_POST["post_type"] : null;
135
  if ( $post_type !== null ) {
136
  if ( current_user_can( 'edit_'.$post_type ) ) {
137
+ Groups_Post_Access::delete( $post_id, null );
138
+ if ( !empty( $_POST[self::CAPABILITY] ) ) {
139
+ foreach ( $_POST[self::CAPABILITY] as $capability_id ) {
140
+ if ( $capability = Groups_Capability::read( $capability_id ) ) {
141
+ Groups_Post_Access::create( array(
142
+ 'post_id' => $post_id,
143
+ 'capability' => $capability->capability
144
+ ) );
145
+ }
146
+ }
147
  }
148
  }
149
  }
lib/access/class-groups-post-access.php CHANGED
@@ -28,6 +28,7 @@ class Groups_Post_Access {
28
 
29
  const READ_POST_CAPABILITY = "groups_read_post";
30
  const READ_POST_CAPABILITY_NAME = "Read Post";
 
31
 
32
  /**
33
  * Create needed capabilities on plugin activation.
@@ -36,24 +37,22 @@ class Groups_Post_Access {
36
  public static function activate() {
37
  if ( !Groups_Capability::read_by_capability( self::READ_POST_CAPABILITY ) ) {
38
  Groups_Capability::create( array( "capability" => self::READ_POST_CAPABILITY ) );
 
 
 
 
 
39
  }
40
  }
41
 
42
  public static function init() {
43
-
44
- // for translation
45
- // @see self::READ_POST_CAPABILITY_NAME
46
- __( "Read Post", GROUPS_PLUGIN_DOMAIN );
47
-
48
  // post access
49
  add_filter( 'get_pages', array( __CLASS__, "get_pages" ), 1 );
50
  add_filter( 'the_posts', array( __CLASS__, "the_posts" ), 1, 2 );
51
  add_filter( 'wp_get_nav_menu_items', array( __CLASS__, "wp_get_nav_menu_items" ), 1, 3 );
52
-
53
  // content access
54
  add_filter( "get_the_excerpt", array( __CLASS__, "get_the_excerpt" ), 1 );
55
  add_filter( "the_content", array( __CLASS__, "the_content" ), 1 );
56
-
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 );
@@ -66,22 +65,14 @@ class Groups_Post_Access {
66
  * @param array $pages
67
  */
68
  public static function get_pages( $pages ) {
69
-
70
  $result = array();
 
71
  foreach ( $pages as $page ) {
72
- $read_post_capability = self::read( $page->ID, self::READ_POST_CAPABILITY );
73
- if ( $read_post_capability ) {
74
- $user_id = get_current_user_id();
75
- $groups_user = new Groups_User( $user_id );
76
- if ( $groups_user->can( self::READ_POST_CAPABILITY ) ) {
77
- $result[] = $page;
78
- }
79
- } else {
80
  $result[] = $page;
81
  }
82
  }
83
  return $result;
84
-
85
  }
86
 
87
  /**
@@ -91,17 +82,10 @@ class Groups_Post_Access {
91
  * @param WP_Query $query
92
  */
93
  public static function the_posts( $posts, $query ) {
94
-
95
  $result = array();
 
96
  foreach ( $posts as $post ) {
97
- $read_post_capability = self::read( $post->ID, self::READ_POST_CAPABILITY );
98
- if ( $read_post_capability ) {
99
- $user_id = get_current_user_id();
100
- $groups_user = new Groups_User( $user_id );
101
- if ( $groups_user->can( self::READ_POST_CAPABILITY ) ) {
102
- $result[] = $post;
103
- }
104
- } else {
105
  $result[] = $post;
106
  }
107
  }
@@ -111,23 +95,19 @@ class Groups_Post_Access {
111
  /**
112
  * Filter menu items by access capability.
113
  *
 
 
114
  * @param array $items
115
  * @param mixed $menu
116
  * @param array $args
117
  */
118
  public static function wp_get_nav_menu_items( $items = null, $menu = null, $args = null ) {
119
  $result = array();
 
120
  foreach ( $items as $item ) {
121
  // @todo might want to check $item->object and $item->type first,
122
  // for example these are 'page' and 'post_type' for a page
123
- $read_post_capability = self::read( $item->object_id, self::READ_POST_CAPABILITY );
124
- if ( $read_post_capability ) {
125
- $user_id = get_current_user_id();
126
- $groups_user = new Groups_User( $user_id );
127
- if ( $groups_user->can( self::READ_POST_CAPABILITY ) ) {
128
- $result[] = $item;
129
- }
130
- } else {
131
  $result[] = $item;
132
  }
133
  }
@@ -144,14 +124,7 @@ class Groups_Post_Access {
144
  global $post;
145
  $result = '';
146
  if ( isset( $post->ID ) ) {
147
- $read_post_capability = self::read( $post->ID, self::READ_POST_CAPABILITY );
148
- if ( $read_post_capability ) {
149
- $user_id = get_current_user_id();
150
- $groups_user = new Groups_User( $user_id );
151
- if ( $groups_user->can( self::READ_POST_CAPABILITY ) ) {
152
- $result = $output;
153
- }
154
- } else {
155
  $result = $output;
156
  }
157
  }
@@ -168,14 +141,7 @@ class Groups_Post_Access {
168
  global $post;
169
  $result = '';
170
  if ( isset( $post->ID ) ) {
171
- $read_post_capability = self::read( $post->ID, self::READ_POST_CAPABILITY );
172
- if ( $read_post_capability ) {
173
- $user_id = get_current_user_id();
174
- $groups_user = new Groups_User( $user_id );
175
- if ( $groups_user->can( self::READ_POST_CAPABILITY ) ) {
176
- $result = $output;
177
- }
178
- } else {
179
  $result = $output;
180
  }
181
  }
@@ -200,9 +166,13 @@ class Groups_Post_Access {
200
  if ( !isset( $capability ) ) {
201
  $capability = self::READ_POST_CAPABILITY;
202
  }
203
-
204
  if ( !empty( $post_id ) && !empty( $capability) ) {
205
- $result = update_post_meta( $post_id, self::POSTMETA_PREFIX . $capability, true );
 
 
 
 
206
  }
207
  return $result;
208
  }
@@ -218,7 +188,12 @@ class Groups_Post_Access {
218
  * @return true if the capability is required, otherwise false
219
  */
220
  public static function read( $post_id, $capability = self::READ_POST_CAPABILITY ) {
221
- return get_post_meta( $post_id, self::POSTMETA_PREFIX . $capability );
 
 
 
 
 
222
  }
223
 
224
  /**
@@ -235,13 +210,56 @@ class Groups_Post_Access {
235
  * Removes a capability requirement from a post.
236
  *
237
  * @param int $post_id
238
- * @param string $capability
239
  * @return true on success, otherwise false
240
  */
241
  public static function delete( $post_id, $capability = self::READ_POST_CAPABILITY ) {
242
  $result = false;
243
- if ( !empty( $post_id ) && !empty( $capability) ) {
244
- $result = delete_post_meta( $post_id, self::POSTMETA_PREFIX . $capability );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
246
  return $result;
247
  }
28
 
29
  const READ_POST_CAPABILITY = "groups_read_post";
30
  const READ_POST_CAPABILITY_NAME = "Read Post";
31
+ const READ_POST_CAPABILITIES = 'read_post_capabilities';
32
 
33
  /**
34
  * Create needed capabilities on plugin activation.
37
  public static function activate() {
38
  if ( !Groups_Capability::read_by_capability( self::READ_POST_CAPABILITY ) ) {
39
  Groups_Capability::create( array( "capability" => self::READ_POST_CAPABILITY ) );
40
+ // default read caps
41
+ Groups_Options::update_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
42
+ // for translation
43
+ // @see self::READ_POST_CAPABILITY_NAME
44
+ __( "Read Post", GROUPS_PLUGIN_DOMAIN );
45
  }
46
  }
47
 
48
  public static function init() {
 
 
 
 
 
49
  // post access
50
  add_filter( 'get_pages', array( __CLASS__, "get_pages" ), 1 );
51
  add_filter( 'the_posts', array( __CLASS__, "the_posts" ), 1, 2 );
52
  add_filter( 'wp_get_nav_menu_items', array( __CLASS__, "wp_get_nav_menu_items" ), 1, 3 );
 
53
  // content access
54
  add_filter( "get_the_excerpt", array( __CLASS__, "get_the_excerpt" ), 1 );
55
  add_filter( "the_content", array( __CLASS__, "the_content" ), 1 );
 
56
  // @todo these could be interesting to add later ...
57
  // add_filter( "plugin_row_meta", array( __CLASS__, "plugin_row_meta" ), 1 );
58
  // add_filter( "posts_join_paged", array( __CLASS__, "posts_join_paged" ), 1 );
65
  * @param array $pages
66
  */
67
  public static function get_pages( $pages ) {
 
68
  $result = array();
69
+ $user_id = get_current_user_id();
70
  foreach ( $pages as $page ) {
71
+ if ( self::user_can_read_post( $page->ID, $user_id ) ) {
 
 
 
 
 
 
 
72
  $result[] = $page;
73
  }
74
  }
75
  return $result;
 
76
  }
77
 
78
  /**
82
  * @param WP_Query $query
83
  */
84
  public static function the_posts( $posts, $query ) {
 
85
  $result = array();
86
+ $user_id = get_current_user_id();
87
  foreach ( $posts as $post ) {
88
+ if ( self::user_can_read_post( $post->ID, $user_id ) ) {
 
 
 
 
 
 
 
89
  $result[] = $post;
90
  }
91
  }
95
  /**
96
  * Filter menu items by access capability.
97
  *
98
+ * @todo admin section: this won't inhibit the items being offered to be added, although when they're added they won't show up in the menu
99
+ *
100
  * @param array $items
101
  * @param mixed $menu
102
  * @param array $args
103
  */
104
  public static function wp_get_nav_menu_items( $items = null, $menu = null, $args = null ) {
105
  $result = array();
106
+ $user_id = get_current_user_id();
107
  foreach ( $items as $item ) {
108
  // @todo might want to check $item->object and $item->type first,
109
  // for example these are 'page' and 'post_type' for a page
110
+ if ( self::user_can_read_post( $item->object_id, $user_id ) ) {
 
 
 
 
 
 
 
111
  $result[] = $item;
112
  }
113
  }
124
  global $post;
125
  $result = '';
126
  if ( isset( $post->ID ) ) {
127
+ if ( self::user_can_read_post( $post->ID ) ) {
 
 
 
 
 
 
 
128
  $result = $output;
129
  }
130
  }
141
  global $post;
142
  $result = '';
143
  if ( isset( $post->ID ) ) {
144
+ if ( self::user_can_read_post( $post->ID ) ) {
 
 
 
 
 
 
 
145
  $result = $output;
146
  }
147
  }
166
  if ( !isset( $capability ) ) {
167
  $capability = self::READ_POST_CAPABILITY;
168
  }
169
+
170
  if ( !empty( $post_id ) && !empty( $capability) ) {
171
+ if ( Groups_Capability::read_by_capability( $capability ) ) {
172
+ if ( !in_array( $capability, get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY ) ) ) {
173
+ $result = add_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY, $capability );
174
+ }
175
+ }
176
  }
177
  return $result;
178
  }
188
  * @return true if the capability is required, otherwise false
189
  */
190
  public static function read( $post_id, $capability = self::READ_POST_CAPABILITY ) {
191
+ $result = false;
192
+ $caps = get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY );
193
+ if ( $caps ) {
194
+ $result = in_array( $capability, $caps );
195
+ }
196
+ return $result;
197
  }
198
 
199
  /**
210
  * Removes a capability requirement from a post.
211
  *
212
  * @param int $post_id
213
+ * @param string $capability defaults to groups_read_post, removes all if null is given
214
  * @return true on success, otherwise false
215
  */
216
  public static function delete( $post_id, $capability = self::READ_POST_CAPABILITY ) {
217
  $result = false;
218
+ if ( !empty( $post_id ) ) {
219
+ if ( !empty( $capability ) ) {
220
+ $result = delete_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY, $capability );
221
+ } else {
222
+ $result = delete_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY );
223
+ }
224
+ }
225
+ return $result;
226
+ }
227
+
228
+ /**
229
+ * Returns a list of capabilities that grant access to the post.
230
+ *
231
+ * @param int $post_id
232
+ * @return array of string, capabilities
233
+ */
234
+ public static function get_read_post_capabilities( $post_id ) {
235
+ return get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY );
236
+ }
237
+
238
+ /**
239
+ * Returns true if the user has any of the capabilities that grant access to the post.
240
+ *
241
+ * @param int $post_id post id
242
+ * @param int $user_id user id or null for current user
243
+ * @return boolean true if user can read the post
244
+ */
245
+ public static function user_can_read_post( $post_id, $user_id = null ) {
246
+ $result = false;
247
+ if ( !empty( $post_id ) ) {
248
+ if ( $user_id === null ) {
249
+ $user_id = get_current_user_id();
250
+ }
251
+ $groups_user = new Groups_User( $user_id );
252
+ $read_caps = self::get_read_post_capabilities( $post_id );
253
+ if ( !empty( $read_caps ) ) {
254
+ foreach( $read_caps as $read_cap ) {
255
+ if ( $groups_user->can( $read_cap ) ) {
256
+ $result = true;
257
+ break;
258
+ }
259
+ }
260
+ } else {
261
+ $result = true;
262
+ }
263
  }
264
  return $result;
265
  }
lib/admin/groups-admin-options.php CHANGED
@@ -44,9 +44,9 @@ function groups_admin_options() {
44
 
45
  echo
46
  '<div>' .
47
- '<h2>' .
48
- __( 'Groups options', GROUPS_PLUGIN_DOMAIN ) .
49
- '</h2>' .
50
  '</div>';
51
 
52
  $caps = array(
@@ -71,6 +71,17 @@ function groups_admin_options() {
71
  add_option( GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE, $admin_override ); // WP 3.3.1 : update alone wouldn't create the option when value is false
72
  update_option( GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE, $admin_override );
73
 
 
 
 
 
 
 
 
 
 
 
 
74
  // tree view
75
  if ( !empty( $_POST[GROUPS_SHOW_TREE_VIEW] ) ) {
76
  Groups_Options::update_option( GROUPS_SHOW_TREE_VIEW, true );
@@ -155,45 +166,68 @@ function groups_admin_options() {
155
  //
156
  echo
157
  '<form action="" name="options" method="post">' .
158
- '<div>' .
159
- '<h3>' . __( 'Administrator Access Override', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
160
- '<p>' .
161
- '<input name="' . GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE . '" type="checkbox" ' . ( $admin_override ? 'checked="checked"' : '' ) . '/>' .
162
- '<label for="' . GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE . '">' . __( 'Administrators override all access permissions derived from Groups capabilities.', GROUPS_PLUGIN_DOMAIN ) . '</label>' .
163
- '</p>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
  echo
166
- '<h3>' . __( 'Tree view', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
167
- '<p>' .
168
- '<input name="' . GROUPS_SHOW_TREE_VIEW . '" type="checkbox" ' . ( $show_tree_view ? 'checked="checked"' : '' ) . '/>' .
169
- '<label for="' . GROUPS_SHOW_TREE_VIEW . '">' . __( 'Show the Groups tree view.', GROUPS_PLUGIN_DOMAIN ) . '</label>' .
170
- '</p>';
171
  echo
172
- '<h3>' . __( 'Permissions', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
173
- '<p>' . __( 'These permissions apply to Groups management. They do not apply to access permissions derived from Groups capabilities.', GROUPS_PLUGIN_DOMAIN ) . '</p>' .
174
- $caps_table .
175
- '<p class="description">' .
176
- __( 'A minimum set of permissions will be preserved.', GROUPS_PLUGIN_DOMAIN ) .
177
- '<br/>' .
178
- __( 'If you lock yourself out, please ask an administrator to help.', GROUPS_PLUGIN_DOMAIN ) .
179
- '</p>';
180
  if ( !$is_sitewide_plugin ) {
181
  echo
182
- '<h3>' . __( 'Deactivation and data persistence', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
183
- '<p>' .
184
- '<input name="delete-data" type="checkbox" ' . ( $delete_data ? 'checked="checked"' : '' ) . '/>' .
185
- '<label for="delete-data">' . __( 'Delete all Groups plugin data on deactivation', GROUPS_PLUGIN_DOMAIN ) . '</label>' .
186
- '</p>' .
187
- '<p class="description warning">' .
188
- __( 'CAUTION: If this option is active while the plugin is deactivated, ALL plugin settings and data will be DELETED. If you are going to use this option, now would be a good time to make a backup. By enabling this option you agree to be solely responsible for any loss of data or any other consequences thereof.', GROUPS_PLUGIN_DOMAIN ) .
189
- '</p>';
190
  }
191
  echo
192
- '<p>' .
193
- wp_nonce_field( 'admin', GROUPS_ADMIN_OPTIONS_NONCE, true, false ) .
194
- '<input type="submit" name="submit" value="' . __( 'Save', GROUPS_PLUGIN_DOMAIN ) . '"/>' .
195
- '</p>' .
196
- '</div>' .
197
  '</form>';
198
  Groups_Help::footer();
199
  }
44
 
45
  echo
46
  '<div>' .
47
+ '<h2>' .
48
+ __( 'Groups options', GROUPS_PLUGIN_DOMAIN ) .
49
+ '</h2>' .
50
  '</div>';
51
 
52
  $caps = array(
71
  add_option( GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE, $admin_override ); // WP 3.3.1 : update alone wouldn't create the option when value is false
72
  update_option( GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE, $admin_override );
73
 
74
+ $valid_read_caps = array( Groups_Post_Access::READ_POST_CAPABILITY );
75
+ if ( !empty( $_POST[GROUPS_READ_POST_CAPABILITIES] ) ) {
76
+ $read_caps = $_POST[GROUPS_READ_POST_CAPABILITIES];
77
+ foreach( $read_caps as $read_cap ) {
78
+ if ( !in_array( $read_cap, $valid_read_caps ) && ( $valid_cap = Groups_Capability::read( $read_cap ) ) ) {
79
+ $valid_read_caps[] = $valid_cap->capability;
80
+ }
81
+ }
82
+ }
83
+ Groups_Options::update_option( Groups_Post_Access::READ_POST_CAPABILITIES, $valid_read_caps );
84
+
85
  // tree view
86
  if ( !empty( $_POST[GROUPS_SHOW_TREE_VIEW] ) ) {
87
  Groups_Options::update_option( GROUPS_SHOW_TREE_VIEW, true );
166
  //
167
  echo
168
  '<form action="" name="options" method="post">' .
169
+ '<div>' .
170
+ '<h3>' . __( 'Administrator Access Override', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
171
+ '<p>' .
172
+ '<input name="' . GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE . '" type="checkbox" ' . ( $admin_override ? 'checked="checked"' : '' ) . '/>' .
173
+ '<label for="' . GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE . '">' . __( 'Administrators override all access permissions derived from Groups capabilities.', GROUPS_PLUGIN_DOMAIN ) . '</label>' .
174
+ '</p>';
175
+
176
+ echo '<h3>' . __( 'Access restricions', GROUPS_PLUGIN_DOMAIN ) . '</h3>';
177
+
178
+ echo '<p class="description">' .
179
+ __( 'Include these capabilities to enforce read access on posts. The selected capabilities will be offered to restrict access to posts.', GROUPS_PLUGIN_DOMAIN ) .
180
+ '</p>';
181
+
182
+ $capability_table = _groups_get_tablename( "capability" );
183
+ $capabilities = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $capability_table ORDER BY capability" ) );
184
+ $applicable_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
185
+ foreach( $capabilities as $capability ) {
186
+ $checked = in_array( $capability->capability, $applicable_read_caps ) ? ' checked="checked" ' : '';
187
+ if ( $capability->capability == Groups_Post_Access::READ_POST_CAPABILITY ) {
188
+ $checked .= ' readonly="readonly" disabled="disabled" ';
189
+ }
190
+ echo '<label>';
191
+ echo '<input name="' . GROUPS_READ_POST_CAPABILITIES . '[]" ' . $checked . ' type="checkbox" value="' . esc_attr( $capability->capability_id ) . '" />';
192
+ echo wp_filter_nohtml_kses( $capability->capability );
193
+ echo '</label>';
194
+ echo ' ';
195
+ echo '<span class="description">' . wp_filter_nohtml_kses( $capability->description ) . '</span>';
196
+ echo '<br/>';
197
+ }
198
 
199
  echo
200
+ '<h3>' . __( 'Tree view', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
201
+ '<p>' .
202
+ '<input name="' . GROUPS_SHOW_TREE_VIEW . '" type="checkbox" ' . ( $show_tree_view ? 'checked="checked"' : '' ) . '/>' .
203
+ '<label for="' . GROUPS_SHOW_TREE_VIEW . '">' . __( 'Show the Groups tree view.', GROUPS_PLUGIN_DOMAIN ) . '</label>' .
204
+ '</p>';
205
  echo
206
+ '<h3>' . __( 'Permissions', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
207
+ '<p>' . __( 'These permissions apply to Groups management. They do not apply to access permissions derived from Groups capabilities.', GROUPS_PLUGIN_DOMAIN ) . '</p>' .
208
+ $caps_table .
209
+ '<p class="description">' .
210
+ __( 'A minimum set of permissions will be preserved.', GROUPS_PLUGIN_DOMAIN ) .
211
+ '<br/>' .
212
+ __( 'If you lock yourself out, please ask an administrator to help.', GROUPS_PLUGIN_DOMAIN ) .
213
+ '</p>';
214
  if ( !$is_sitewide_plugin ) {
215
  echo
216
+ '<h3>' . __( 'Deactivation and data persistence', GROUPS_PLUGIN_DOMAIN ) . '</h3>' .
217
+ '<p>' .
218
+ '<input name="delete-data" type="checkbox" ' . ( $delete_data ? 'checked="checked"' : '' ) . '/>' .
219
+ '<label for="delete-data">' . __( 'Delete all Groups plugin data on deactivation', GROUPS_PLUGIN_DOMAIN ) . '</label>' .
220
+ '</p>' .
221
+ '<p class="description warning">' .
222
+ __( 'CAUTION: If this option is active while the plugin is deactivated, ALL plugin settings and data will be DELETED. If you are going to use this option, now would be a good time to make a backup. By enabling this option you agree to be solely responsible for any loss of data or any other consequences thereof.', GROUPS_PLUGIN_DOMAIN ) .
223
+ '</p>';
224
  }
225
  echo
226
+ '<p>' .
227
+ wp_nonce_field( 'admin', GROUPS_ADMIN_OPTIONS_NONCE, true, false ) .
228
+ '<input type="submit" name="submit" value="' . __( 'Save', GROUPS_PLUGIN_DOMAIN ) . '"/>' .
229
+ '</p>' .
230
+ '</div>' .
231
  '</form>';
232
  Groups_Help::footer();
233
  }
lib/core/class-groups-controller.php CHANGED
@@ -128,7 +128,7 @@ class Groups_Controller {
128
 
129
  // create WP capabilities
130
  Groups_Controller::set_default_capabilities();
131
-
132
  $charset_collate = '';
133
  if ( ! empty( $wpdb->charset ) ) {
134
  $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
@@ -256,6 +256,12 @@ class Groups_Controller {
256
  }
257
  break;
258
  default :
 
 
 
 
 
 
259
  } // switch
260
  foreach ( $queries as $query ) {
261
  if ( $wpdb->query( $query ) === false ) {
128
 
129
  // create WP capabilities
130
  Groups_Controller::set_default_capabilities();
131
+
132
  $charset_collate = '';
133
  if ( ! empty( $wpdb->charset ) ) {
134
  $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
256
  }
257
  break;
258
  default :
259
+ if ( !empty( $previous_version ) ) {
260
+ if ( strcmp( $previous_version, '1.1.6' ) < 0 ) {
261
+ Groups_Options::update_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
262
+ $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 ) );
263
+ }
264
+ }
265
  } // switch
266
  foreach ( $queries as $query ) {
267
  if ( $wpdb->query( $query ) === false ) {
lib/core/constants.php CHANGED
@@ -77,6 +77,11 @@ define( 'GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE', 'groups-admin-override' );
77
  */
78
  define( 'GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE_DEFAULT', true );
79
 
 
 
 
 
 
80
  /**
81
  * Tree view option
82
  * @var string
77
  */
78
  define( 'GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE_DEFAULT', true );
79
 
80
+ /**
81
+ * @var string read post capabilities option
82
+ */
83
+ define( 'GROUPS_READ_POST_CAPABILITIES', 'groups-read-post-capabilities' );
84
+
85
  /**
86
  * Tree view option
87
  * @var string
readme.txt CHANGED
@@ -4,7 +4,8 @@ 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, permission, permissions
5
  Requires at least: 3.0
6
  Tested up to: 3.3.2
7
- Stable tag: 1.1.5
 
8
 
9
  Groups provides group-based user membership management, group-based capabilities and content access control.
10
 
@@ -62,9 +63,17 @@ It integrates standard WordPress capabilities and application-specific capabilit
62
 
63
  #### Access Control ####
64
 
65
- Groups defines some capabilities of its own. The groups_read_post capability
66
- is used to restrict access to certain posts or pages to groups (and users)
67
- with that capability only.
 
 
 
 
 
 
 
 
68
 
69
  #### Framework ####
70
 
@@ -92,8 +101,8 @@ Please try to solve problems there before you rate this plugin or say it doesn't
92
 
93
  ##### Access restrictions on posts ####
94
 
95
- On posts an pages (and custom content types) a new meta box titles *Access restrictions* appears.
96
- By checking *Enforce read access*, you can restrict access to the post to groups and users who have the *groups_read_post* capability.
97
  You need to assign this capability to a group and make users members of that group to allow them to see those posts.
98
 
99
  #### Content visibility for members and non-members ####
@@ -143,9 +152,10 @@ Capabilities can be assigned to groups and users (1). These capabilities include
143
  the *standard WordPress capabilities* but you can also define additional
144
  capabilities for your web-application.
145
 
146
- Groups defines some capabilities of its own. The *groups_read_post* capability
147
- is used to restrict access to certain posts or pages to groups (and users)
148
- with that capability only.
 
149
 
150
  (1) Assigning capabilities to users is not integrated in the user interface yet but can be done through API calls.
151
 
@@ -223,6 +233,8 @@ For detailed information about this shortcode, please refer to the [Groups plugi
223
 
224
  = Where is the documentation? =
225
 
 
 
226
  The official Groups documentation root is at the [Groups Documentation](http://www.itthinx.com/documentation/groups/) page.
227
  The documentation is a work in progress, if you don't find anything there yet but want to know about the API, please look at the code as it provides useful documentation on all functions.
228
 
@@ -230,6 +242,30 @@ The documentation is a work in progress, if you don't find anything there yet bu
230
 
231
  You can leave a comment at the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
232
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  == Screenshots ==
234
 
235
  See also [Groups](http://www.itthinx.com/plugins/groups/)
@@ -237,13 +273,17 @@ See also [Groups](http://www.itthinx.com/plugins/groups/)
237
  1. Groups - this is where you add and remove groups and assign capabilities to groups.
238
  2. Capabilities - here you get an overview of the capabilities that are defined and you can add and remove capabilities as well.
239
  3. Users - group membership is managed from the standard Users admin view.
240
- 4. Access restrictions meta box - on pages and posts (or custom content types) you can restrict access to users who are part of a group with the *groups_read_post* capability.
241
  5. Usage of the [groups_member] and [groups_non_member] shortcodes to limit visibility of content to users who are members of a group or users who are not members of a group. Multiple comma-separated groups can be specified.
242
  6. Usage of the [groups_can] and [groups_can_not] shortcodes. Limits visibility of enclosed content to those users who have the capability or those who do not. Multiple capabilities can be given.
243
  7. Options - you can adjust the plugin's settings here.
 
244
 
245
  == Changelog ==
246
 
 
 
 
247
  = 1.1.5 =
248
  * Added shortcode & API functions [groups_user_group] / [groups_user_groups] that allows to show the list of groups the current user or a specific user belongs to
249
  * Added shortcode & API functions [groups_groups]to show the site's list of groups
@@ -294,8 +334,11 @@ Some installations wouldn't work correctly, showing no capabilities and making i
294
 
295
  == Upgrade Notice ==
296
 
 
 
 
297
  = 1.1.5 =
298
- ...
299
 
300
  = 1.1.4 =
301
  * Several bug fixes and improvements.
4
  Tags: access, access control, capability, capabilities, content, download, downloads, file, file access, files, group, groups, member, members, membership, permission, permissions
5
  Requires at least: 3.0
6
  Tested up to: 3.3.2
7
+ Stable tag: 1.2.0
8
+ License: GPLv3
9
 
10
  Groups provides group-based user membership management, group-based capabilities and content access control.
11
 
63
 
64
  #### Access Control ####
65
 
66
+ Access to posts and pages can be restricted by capability.
67
+
68
+ Any capability can be used to restrict access, including new capabilities.
69
+
70
+ If access to a post is restricted, only users who belong to a group with that
71
+ capability may access the post.
72
+
73
+ Groups defines the groups_read_post capability by default, which can be
74
+ used to restrict access to certain posts or pages to groups
75
+ with that capability only. Any other capability (including new ones) can be
76
+ used to limit access as well.
77
 
78
  #### Framework ####
79
 
101
 
102
  ##### Access restrictions on posts ####
103
 
104
+ On posts an pages (and custom content types) a new meta box titled *Access restrictions* appears.
105
+ By checking a capability under *Enforce read access*, you can restrict access to the post to groups and users who are members of a group with that capability.
106
  You need to assign this capability to a group and make users members of that group to allow them to see those posts.
107
 
108
  #### Content visibility for members and non-members ####
152
  the *standard WordPress capabilities* but you can also define additional
153
  capabilities for your web-application.
154
 
155
+ Groups defines the *groups_read_post* capability by default which can be
156
+ used to restrict access to certain posts or pages to groups (and users)
157
+ with that capability only. Additional capabilities can be identified on the
158
+ *Groups > Options* admin screen that may be used to limit access.
159
 
160
  (1) Assigning capabilities to users is not integrated in the user interface yet but can be done through API calls.
161
 
233
 
234
  = Where is the documentation? =
235
 
236
+ Most of the features are currently documented at the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
237
+
238
  The official Groups documentation root is at the [Groups Documentation](http://www.itthinx.com/documentation/groups/) page.
239
  The documentation is a work in progress, if you don't find anything there yet but want to know about the API, please look at the code as it provides useful documentation on all functions.
240
 
242
 
243
  You can leave a comment at the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
244
 
245
+ = I want Advanced and Premium members, where the Premium members can access everything that Advanced members can access. How can I do that? =
246
+
247
+ Example: Advanced and Premium members
248
+
249
+ 1. Go to *Groups > Capabilities* and define two new capabilities, let's call them *advanced* and *premium*.
250
+ 2. Go to *Groups > Groups* and define two new groups, let's call them *Advanced Members* and *Premium Members* - select *Advanced Members* as the *Parent* for the *Premium Members* group.
251
+ 3. Assign the *advanced* capability to the *Advanced Members* group and the *premium* capability to the *Premium Members* group.
252
+ 4. Go to *Groups > Options* and tick the checkboxes for *advanced* and *premium* under _Access restrictions_ and hit the *Save* button at the end of the page.
253
+ 5. Now create an example post that only members of the *Advanced Members* group should be able to see and tick the *advanced* checkbox under _Access restrictions_.
254
+ 6. Create another post for *Premium Members* and tick the *premium* checkbox for that post.
255
+ 7. Assign test users to both groups, log in as each user in turn and see which posts will be accessible.
256
+
257
+ = How do I limit access to posts so that users in group A can not read the same as those in group B and vice-versa? =
258
+
259
+ Example: Green and Red members
260
+
261
+ 1. Go to *Groups > Capabilities* and define two new capabilities, call them *green* and *red*.
262
+ 2. Go to *Groups > Groups* and define two new groups, let's call them *Green Members* and *Red Members*
263
+ 3. Assign the *green* capability to the *Green Members* group and the *red* capability to the *Red Members* group.
264
+ 4. Go to *Groups > Options* and tick the checkboxes for *green* and *red* under _Access restrictions_ and hit the *Save* button at the end of the page.
265
+ 5. Now create an example post that only members of the *Green Members* group should be able to see and tick the *green* checkbox under _Access restrictions_.
266
+ 6. Create another post for *Red Members* and tick the *red* checkbox for that post.
267
+ 7. Assign a test user to any of the above groups, log in as that user and the post will be accessible.
268
+
269
  == Screenshots ==
270
 
271
  See also [Groups](http://www.itthinx.com/plugins/groups/)
273
  1. Groups - this is where you add and remove groups and assign capabilities to groups.
274
  2. Capabilities - here you get an overview of the capabilities that are defined and you can add and remove capabilities as well.
275
  3. Users - group membership is managed from the standard Users admin view.
276
+ 4. Access restrictions meta box - on pages and posts (or custom content types) you can restrict access to users who are part of a group with capabilities.
277
  5. Usage of the [groups_member] and [groups_non_member] shortcodes to limit visibility of content to users who are members of a group or users who are not members of a group. Multiple comma-separated groups can be specified.
278
  6. Usage of the [groups_can] and [groups_can_not] shortcodes. Limits visibility of enclosed content to those users who have the capability or those who do not. Multiple capabilities can be given.
279
  7. Options - you can adjust the plugin's settings here.
280
+ 8. More options.
281
 
282
  == Changelog ==
283
 
284
+ = 1.2.0 =
285
+ * Access control is no longer restricted to the groups_read_post capability: now any capability can be used to limit access to posts so that different groups can be granted access to different sets of posts.
286
+
287
  = 1.1.5 =
288
  * Added shortcode & API functions [groups_user_group] / [groups_user_groups] that allows to show the list of groups the current user or a specific user belongs to
289
  * Added shortcode & API functions [groups_groups]to show the site's list of groups
334
 
335
  == Upgrade Notice ==
336
 
337
+ = 1.2.0 =
338
+ * New: Different groups can be granted access to different sets of pages or posts: Any capability - including custom capabilities - can be used to limit access.
339
+
340
  = 1.1.5 =
341
+ * New shortcodes.
342
 
343
  = 1.1.4 =
344
  * Several bug fixes and improvements.
screenshot-4.png CHANGED
Binary file
screenshot-5.png CHANGED
Binary file
screenshot-6.png CHANGED
Binary file
screenshot-7.png CHANGED
Binary file
screenshot-8.png ADDED
Binary file