Groups - Version 1.4.1

Version Description

  • Added: Better group-assignment on the Users admin screen, allows to assign/remove multiple users to/from multiple groups along with a better UI.
  • Changed: Groups requires at least WordPress 3.5 now, although this only affects the group-action functionality on the Users admin screen, the restrict_manage_users action which is now used to render the UI elements needed, was introduced with WordPress 3.5.
  • Added: Extensions box in Options.
  • Improved: Groups section in user profile with added description.
Download this release

Release Info

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

Code changes from version 1.4.0 to 1.4.1

css/groups_admin.css CHANGED
@@ -151,6 +151,28 @@ div.groups-footer form {
151
  padding: 1em;
152
  }
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  .groups-bulk-container {
155
  padding-bottom: 1em;
156
  }
151
  padding: 1em;
152
  }
153
 
154
+ #groups-extensions-box {
155
+ position: relative;
156
+ float: right;
157
+ padding: 1em;
158
+ margin: 1em;
159
+ background-color: #eee;
160
+ border-radius: 4px;
161
+ color: #333;
162
+ border: 1px solid #ddd;
163
+ width: 256px;
164
+ }
165
+ #groups-extensions-box a.close {
166
+ color: #ccc;
167
+ line-height: 18px;
168
+ position: absolute;
169
+ right: 6px;
170
+ text-decoration: none;
171
+ top: 6px;
172
+ font-family: sans;
173
+ font-size: 11px;
174
+ }
175
+
176
  .groups-bulk-container {
177
  padding-bottom: 1em;
178
  }
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.4.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.4.0' );
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.4.1
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.4.1' );
31
  define( 'GROUPS_FILE', __FILE__ );
32
  if ( !defined( 'GROUPS_CORE_DIR' ) ) {
33
  define( 'GROUPS_CORE_DIR', WP_PLUGIN_DIR . '/groups' );
lib/admin/class-groups-admin-user-profile.php CHANGED
@@ -88,13 +88,21 @@ class Groups_Admin_User_Profile {
88
  $user_groups = $user->groups;
89
  $groups_table = _groups_get_tablename( 'group' );
90
  if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ) ) {
91
- $output .= '<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple">';
 
 
 
 
 
 
 
92
  foreach( $groups as $group ) {
93
  $is_member = Groups_User_Group::read( $user->ID, $group->group_id ) ? true : false;
94
  $output .= sprintf( '<option value="%d" %s>%s</option>', Groups_Utility::id( $group->group_id ), $is_member ? ' selected="selected" ' : '', wp_filter_nohtml_kses( $group->name ) );
95
  }
96
  $output .= '</select>';
97
  $output .= Groups_UIE::render_select( '#user-groups' );
 
98
  }
99
  echo $output;
100
  }
88
  $user_groups = $user->groups;
89
  $groups_table = _groups_get_tablename( 'group' );
90
  if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ) ) {
91
+ $output .= '<style type="text/css">';
92
+ $output .= '.groups .selectize-input { font-size: inherit; }';
93
+ $output .= '</style>';
94
+ $output .= sprintf(
95
+ '<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
96
+ esc_attr( __( 'Choose groups &hellip;', GROUPS_PLUGIN_DOMAIN ) ) ,
97
+ esc_attr( __( 'Choose groups &hellip;', GROUPS_PLUGIN_DOMAIN ) )
98
+ );
99
  foreach( $groups as $group ) {
100
  $is_member = Groups_User_Group::read( $user->ID, $group->group_id ) ? true : false;
101
  $output .= sprintf( '<option value="%d" %s>%s</option>', Groups_Utility::id( $group->group_id ), $is_member ? ' selected="selected" ' : '', wp_filter_nohtml_kses( $group->name ) );
102
  }
103
  $output .= '</select>';
104
  $output .= Groups_UIE::render_select( '#user-groups' );
105
+ $output .= '<p class="description">' . __( 'The user is a member of the chosen groups.', GROUPS_PLUGIN_DOMAIN ) . '</p>';
106
  }
107
  echo $output;
108
  }
lib/admin/class-groups-admin-users.php CHANGED
@@ -23,9 +23,9 @@
23
  * Users admin integration with Groups.
24
  */
25
  class Groups_Admin_Users {
26
-
27
  const GROUPS = 'groups_user_groups';
28
-
29
  /**
30
  * Hooks into filters to add the Groups column to the users table.
31
  */
@@ -33,7 +33,7 @@ class Groups_Admin_Users {
33
  // we hook this on admin_init so that current_user_can() is available
34
  add_action( 'admin_init', array( __CLASS__, 'setup' ) );
35
  }
36
-
37
  /**
38
  * Adds the filters and actions only for users who have the right
39
  * Groups permissions.
@@ -47,6 +47,9 @@ class Groups_Admin_Users {
47
  }
48
  if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
49
  if ( !is_network_admin() ) {
 
 
 
50
  add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
51
  // allow to add or remove selected users to groups
52
  add_action( 'load-users.php', array( __CLASS__, 'load_users' ) );
@@ -54,10 +57,17 @@ class Groups_Admin_Users {
54
  add_filter( 'views_users', array( __CLASS__, 'views_users' ) );
55
  // modify query to filter users by group
56
  add_filter( 'pre_user_query', array( __CLASS__, 'pre_user_query' ) );
 
 
 
 
 
 
 
57
  }
58
  }
59
  }
60
-
61
  /**
62
  * Modify query to filter users by group.
63
  *
@@ -65,7 +75,7 @@ class Groups_Admin_Users {
65
  * @return WP_User_Query
66
  */
67
  public static function pre_user_query( $user_query ) {
68
- global $pagenow, $wpdb;
69
  if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
70
  if ( isset( $_REQUEST['group'] ) ) {
71
  $group_id = $_REQUEST['group'];
@@ -80,79 +90,128 @@ class Groups_Admin_Users {
80
  } else { // no results
81
  $include[] = 0;
82
  }
83
- $ids = implode( ',', wp_parse_id_list( $include ) );
84
  $user_query->query_where .= " AND $wpdb->users.ID IN ($ids)";
85
  }
86
  }
87
  }
88
  return $user_query;
89
  }
90
-
 
 
 
 
 
 
 
 
 
 
 
 
91
  /**
92
  * Adds the group add/remove buttons after the last action box.
93
  */
94
  public static function admin_head() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  global $pagenow, $wpdb;
 
 
 
96
  if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
97
-
98
  $group_table = _groups_get_tablename( "group" );
99
  // groups select
100
- $groups_select = "<select class='groups' style='float:none;' name='group_id'>";
101
- $groups = $wpdb->get_results( "SELECT * FROM $group_table ORDER BY name" );
102
- foreach( $groups as $group ) {
103
- $groups_select .= "<option value='" . esc_attr( $group->group_id ) . "'>" . wp_filter_nohtml_kses( $group->name ) . "</option>";
 
 
 
 
 
 
 
 
 
104
  }
105
- $groups_select .= "</select>";
106
-
107
- // we add this inside the form that contains the bulk
108
- // action and role change buttons
109
- $box = "<div class='alignleft actions' style='padding-left:1em'>";
110
- $box .= "<input class='button' type='submit' name='add-to-group' value='" . __( "Add", GROUPS_PLUGIN_DOMAIN ) . "'/>";
111
- $box .= "&nbsp;/&nbsp;";
112
- $box .= "<input class='button' type='submit' name='remove-from-group' value='" . __( "Remove", GROUPS_PLUGIN_DOMAIN ) . "'/>";
113
- $box .= "&nbsp;";
114
- $box .= __( 'selected to / from group:', GROUPS_PLUGIN_DOMAIN );
115
- $box .= "&nbsp;";
116
- $box .= "<label class='screen-reader-text' for='group_id'>" . __( 'Group', GROUPS_PLUGIN_DOMAIN ) . "</label>";
117
  $box .= $groups_select;
118
- $box .= "</div>";
119
-
 
 
 
 
 
 
 
 
120
  $nonce = wp_nonce_field( 'user-group', 'bulk-user-group-nonce', true, false );
121
- $nonce = str_replace('"', "'", $nonce );
122
  $box .= $nonce;
123
-
124
- // @todo replace when a hook allows us to add actions to the users table
125
- // Another option is to extend the users table and implement it in extra_tablenav()
126
- // but that would require either to replace the users admin screen with
127
- // that extended one or create our own section, e.g. Groups > Users.
128
- echo '<script type="text/javascript">';
129
- echo '
130
- jQuery(function(){
131
- jQuery(".tablenav.top .alignleft.actions:last").after("' . $box . '");
132
- });
133
- ';
134
- echo '</script>';
135
-
136
- // .subsubsub rule added because with views_users() the list can get long
137
- // icon distinguishes from role links
138
- echo '<style type="text/css">';
139
- echo '.subsubsub { white-space: normal; }';
140
- echo 'a.group { background: url(' . GROUPS_PLUGIN_URL . '/images/groups-grey-8x8.png) transparent no-repeat left center; padding-left: 10px;}';
141
- echo '</style>';
142
  }
 
143
  }
144
-
145
  /**
146
  * Hooked on filter in class-wp-list-table.php to add links that
147
  * filter by group.
148
  * @param array $views
149
  */
150
  public static function views_users( $views ) {
151
- global $pagenow, $wpdb;
152
- if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
153
  $group_table = _groups_get_tablename( "group" );
154
- $user_group_table = _groups_get_tablename( "user_group" );
155
- $groups = $wpdb->get_results( "SELECT * FROM $group_table ORDER BY name" );
156
  foreach( $groups as $group ) {
157
  $group = new Groups_Group( $group->group_id );
158
  // Do not use $user_count = count( $group->users ); here,
@@ -166,42 +225,53 @@ class Groups_Admin_Users {
166
  esc_url( add_query_arg( 'group', $group->group_id, admin_url( 'users.php' ) ) ),
167
  sprintf( '%s Group', wp_filter_nohtml_kses( $group->name ) ),
168
  sprintf( '%s <span class="count">(%s)</span>', wp_filter_nohtml_kses( $group->name ), $user_count )
169
- );
170
- }
171
  }
172
  return $views;
173
  }
174
-
175
  /**
176
  * Adds or removes users to/from groups.
177
  */
178
  public static function load_users() {
179
  if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
180
- $group_id = isset( $_REQUEST['group_id'] ) ? $_REQUEST['group_id'] : null;
181
  $users = isset( $_REQUEST['users'] ) ? $_REQUEST['users'] : null;
182
  $action = null;
183
- if ( !empty( $_REQUEST['add-to-group'] ) ) {
184
- $action = 'add';
185
- } else if ( !empty( $_REQUEST['remove-from-group'] ) ) {
186
- $action = 'remove';
 
 
187
  }
188
- if ( $group_id !== null && $users !== null && $action !== null ) {
189
  if ( wp_verify_nonce( $_REQUEST['bulk-user-group-nonce'], 'user-group' ) ) {
190
  foreach( $users as $user_id ) {
191
  switch ( $action ) {
192
  case 'add':
193
- if ( !Groups_User_Group::read( $user_id, $group_id ) ) {
194
- Groups_User_Group::create(
195
- array(
196
- 'user_id' => $user_id,
197
- 'group_id' => $group_id
198
- )
199
- );
 
 
 
 
 
200
  }
201
  break;
202
  case 'remove':
203
- if ( Groups_User_Group::read( $user_id, $group_id ) ) {
204
- Groups_User_Group::delete( $user_id, $group_id );
 
 
 
 
 
205
  }
206
  break;
207
  }
@@ -216,7 +286,7 @@ class Groups_Admin_Users {
216
  }
217
  }
218
  }
219
-
220
  /**
221
  * Adds a new column to the users table to show the groups that users
222
  * belong to.
@@ -228,7 +298,7 @@ class Groups_Admin_Users {
228
  $column_headers[self::GROUPS] = __( 'Groups', GROUPS_PLUGIN_DOMAIN );
229
  return $column_headers;
230
  }
231
-
232
  /**
233
  * Renders custom column content.
234
  *
@@ -258,15 +328,15 @@ class Groups_Admin_Users {
258
  }
259
  return $output;
260
  }
261
-
262
- /**
263
- * usort helper
264
- * @param Groups_Group $o1
265
- * @param Groups_Group $o2
266
- * @return int strcmp result for group names
267
- */
268
- public static function by_group_name( $o1, $o2 ) {
269
- return strcmp( $o1->name, $o2->name );
270
  }
271
  }
272
  Groups_Admin_Users::init();
23
  * Users admin integration with Groups.
24
  */
25
  class Groups_Admin_Users {
26
+
27
  const GROUPS = 'groups_user_groups';
28
+
29
  /**
30
  * Hooks into filters to add the Groups column to the users table.
31
  */
33
  // we hook this on admin_init so that current_user_can() is available
34
  add_action( 'admin_init', array( __CLASS__, 'setup' ) );
35
  }
36
+
37
  /**
38
  * Adds the filters and actions only for users who have the right
39
  * Groups permissions.
47
  }
48
  if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
49
  if ( !is_network_admin() ) {
50
+ // scripts
51
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
52
+ // styles
53
  add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
54
  // allow to add or remove selected users to groups
55
  add_action( 'load-users.php', array( __CLASS__, 'load_users' ) );
57
  add_filter( 'views_users', array( __CLASS__, 'views_users' ) );
58
  // modify query to filter users by group
59
  add_filter( 'pre_user_query', array( __CLASS__, 'pre_user_query' ) );
60
+ // WP_Users_List_Table implements extra_tablenav() where the restrict_manage_users action is invoked.
61
+ // As the extra_tablenav() method does not define a generic extension point, this is
62
+ // the best shot we get at inserting our group actions block (currently we're at WordPress 3.6.1).
63
+ // We choose to use our own group-actions block instead of re-using the existing bulk-actions,
64
+ // to have a more explicit user interface which makes it clear that these actions
65
+ // are directed at relating users and groups.
66
+ add_action( 'restrict_manage_users', array( __CLASS__, 'restrict_manage_users' ), 0 );
67
  }
68
  }
69
  }
70
+
71
  /**
72
  * Modify query to filter users by group.
73
  *
75
  * @return WP_User_Query
76
  */
77
  public static function pre_user_query( $user_query ) {
78
+ global $pagenow, $wpdb;
79
  if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
80
  if ( isset( $_REQUEST['group'] ) ) {
81
  $group_id = $_REQUEST['group'];
90
  } else { // no results
91
  $include[] = 0;
92
  }
93
+ $ids = implode( ',', wp_parse_id_list( $include ) );
94
  $user_query->query_where .= " AND $wpdb->users.ID IN ($ids)";
95
  }
96
  }
97
  }
98
  return $user_query;
99
  }
100
+
101
+ /**
102
+ * Enqueue scripts the group-actions.
103
+ */
104
+ public static function admin_enqueue_scripts() {
105
+
106
+ global $pagenow;
107
+
108
+ if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
109
+ Groups_UIE::enqueue( 'select' );
110
+ }
111
+ }
112
+
113
  /**
114
  * Adds the group add/remove buttons after the last action box.
115
  */
116
  public static function admin_head() {
117
+
118
+ global $pagenow;
119
+
120
+ if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
121
+
122
+ // .subsubsub rule added because with views_users() the list can get long
123
+ // icon distinguishes from role links
124
+ echo '<style type="text/css">';
125
+ echo '.subsubsub { white-space: normal; }';
126
+ echo 'a.group { background: url(' . GROUPS_PLUGIN_URL . '/images/groups-grey-8x8.png) transparent no-repeat left center; padding-left: 10px;}';
127
+ echo '</style>';
128
+
129
+ // group-actions
130
+ echo '<style type="text/css">';
131
+ echo '.groups-bulk-container { display: inline-block; line-height: 24px; padding-bottom: 1em; vertical-align: top; margin-left: 1em; margin-right: 1em; }';
132
+ echo '.groups-bulk-container .groups-select-container { display: inline-block; vertical-align: top; }';
133
+ echo '.groups-bulk-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; }';
134
+ echo '.groups-bulk-container .selectize-control { min-width: 128px; }';
135
+ echo '.groups-bulk-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; }';
136
+ echo '.groups-bulk-container .selectize-input { font-size: inherit; line-height: 18px; padding: 1px 2px 2px 2px; vertical-align: middle; }';
137
+ echo '.groups-bulk-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; }';
138
+ echo '.groups-bulk-container input.button { margin-top: 1px; vertical-align: top; }';
139
+ echo '.tablenav .actions { overflow: visible; }'; // this is important so that the selectize options aren't hidden
140
+ echo '</style>';
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Renders group actions in the users table's extra_tablenav().
146
+ */
147
+ public static function restrict_manage_users() {
148
+
149
  global $pagenow, $wpdb;
150
+
151
+ $output = '';
152
+
153
  if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
 
154
  $group_table = _groups_get_tablename( "group" );
155
  // groups select
156
+ $groups_table = _groups_get_tablename( 'group' );
157
+ if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ) ) {
158
+ $groups_select = sprintf(
159
+ '<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
160
+ esc_attr( __( 'Choose groups &hellip;', GROUPS_PLUGIN_DOMAIN ) ) ,
161
+ esc_attr( __( 'Choose groups &hellip;', GROUPS_PLUGIN_DOMAIN ) )
162
+ );
163
+ foreach( $groups as $group ) {
164
+ $is_member = false;
165
+ $groups_select .= sprintf( '<option value="%d" %s>%s</option>', Groups_Utility::id( $group->group_id ), $is_member ? ' selected="selected" ' : '', wp_filter_nohtml_kses( $group->name ) );
166
+ }
167
+ $groups_select .= '</select>';
168
+
169
  }
170
+
171
+ // group bulk actions added through extra_tablenav()
172
+ $box = '<div id="group-bulk-actions" class="groups-bulk-container">';
173
+ $box .= '<div class="groups-select-container">';
 
 
 
 
 
 
 
 
174
  $box .= $groups_select;
175
+ $box .= '</div>';
176
+ $box .= '<select class="groups-action" name="groups-action">';
177
+ $box .= '<option selected="selected" value="-1">' . __( 'Group Actions', GROUPS_PLUGIN_DOMAIN ) . '</option>';
178
+ $box .= '<option value="add-group">' . __( 'Add to group', GROUPS_PLUGIN_DOMAIN ) . '</option>';
179
+ $box .= '<option value="remove-group">' . __( 'Remove from group', GROUPS_PLUGIN_DOMAIN ) . '</option>';
180
+ $box .= '</select>';
181
+ $box .= sprintf( '<input class="button" type="submit" name="groups" value="%s" />', __( 'Apply', GROUPS_PLUGIN_DOMAIN ) );
182
+ $box .= '</div>';
183
+ $box = str_replace( '"', "'", $box );
184
+
185
  $nonce = wp_nonce_field( 'user-group', 'bulk-user-group-nonce', true, false );
186
+ $nonce = str_replace( '"', "'", $nonce );
187
  $box .= $nonce;
188
+
189
+ $box .= '<script type="text/javascript">';
190
+ $box .= 'if ( typeof jQuery !== "undefined" ) {';
191
+ $box .= 'jQuery("document").ready(function(){';
192
+ $box .= 'jQuery(".tablenav.top .alignleft.actions:last").after("<div id=\"groups-bulk-actions-block\" class=\"alignleft actions\"></div>");';
193
+ $box .= 'jQuery("#group-bulk-actions").appendTo(jQuery("#groups-bulk-actions-block"));';
194
+ $box .= '});';
195
+ $box .= '}';
196
+ $box .= '</script>';
197
+
198
+ $output .= $box;
199
+ $output .= Groups_UIE::render_select( '#user-groups' );
 
 
 
 
 
 
 
200
  }
201
+ echo $output;
202
  }
203
+
204
  /**
205
  * Hooked on filter in class-wp-list-table.php to add links that
206
  * filter by group.
207
  * @param array $views
208
  */
209
  public static function views_users( $views ) {
210
+ global $pagenow, $wpdb;
211
+ if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
212
  $group_table = _groups_get_tablename( "group" );
213
+ $user_group_table = _groups_get_tablename( "user_group" );
214
+ $groups = $wpdb->get_results( "SELECT * FROM $group_table ORDER BY name" );
215
  foreach( $groups as $group ) {
216
  $group = new Groups_Group( $group->group_id );
217
  // Do not use $user_count = count( $group->users ); here,
225
  esc_url( add_query_arg( 'group', $group->group_id, admin_url( 'users.php' ) ) ),
226
  sprintf( '%s Group', wp_filter_nohtml_kses( $group->name ) ),
227
  sprintf( '%s <span class="count">(%s)</span>', wp_filter_nohtml_kses( $group->name ), $user_count )
228
+ );
229
+ }
230
  }
231
  return $views;
232
  }
233
+
234
  /**
235
  * Adds or removes users to/from groups.
236
  */
237
  public static function load_users() {
238
  if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
 
239
  $users = isset( $_REQUEST['users'] ) ? $_REQUEST['users'] : null;
240
  $action = null;
241
+ if ( !empty( $_REQUEST['groups'] ) ) {
242
+ if ( $_GET['groups-action'] == "add-group" ) {
243
+ $action = 'add';
244
+ } else if ( $_GET['groups-action'] == "remove-group" ) {
245
+ $action = 'remove';
246
+ }
247
  }
248
+ if ( $users !== null && $action !== null ) {
249
  if ( wp_verify_nonce( $_REQUEST['bulk-user-group-nonce'], 'user-group' ) ) {
250
  foreach( $users as $user_id ) {
251
  switch ( $action ) {
252
  case 'add':
253
+ $group_ids = isset( $_GET['group_ids'] ) ? $_GET['group_ids'] : null;
254
+ if ( $group_ids !== null ) {
255
+ foreach ( $group_ids as $group_id ) {
256
+ if ( !Groups_User_Group::read( $user_id, $group_id ) ) {
257
+ Groups_User_Group::create(
258
+ array(
259
+ 'user_id' => $user_id,
260
+ 'group_id' => $group_id
261
+ )
262
+ );
263
+ }
264
+ }
265
  }
266
  break;
267
  case 'remove':
268
+ $group_ids = isset( $_GET['group_ids'] ) ? $_GET['group_ids'] : null;
269
+ if ( $group_ids !== null ) {
270
+ foreach ( $group_ids as $group_id ) {
271
+ if ( Groups_User_Group::read( $user_id, $group_id ) ) {
272
+ Groups_User_Group::delete( $user_id, $group_id );
273
+ }
274
+ }
275
  }
276
  break;
277
  }
286
  }
287
  }
288
  }
289
+
290
  /**
291
  * Adds a new column to the users table to show the groups that users
292
  * belong to.
298
  $column_headers[self::GROUPS] = __( 'Groups', GROUPS_PLUGIN_DOMAIN );
299
  return $column_headers;
300
  }
301
+
302
  /**
303
  * Renders custom column content.
304
  *
328
  }
329
  return $output;
330
  }
331
+
332
+ /**
333
+ * usort helper
334
+ * @param Groups_Group $o1
335
+ * @param Groups_Group $o2
336
+ * @return int strcmp result for group names
337
+ */
338
+ public static function by_group_name( $o1, $o2 ) {
339
+ return strcmp( $o1->name, $o2->name );
340
  }
341
  }
342
  Groups_Admin_Users::init();
lib/admin/groups-admin-options.php CHANGED
@@ -171,9 +171,22 @@ function groups_admin_options() {
171
  }
172
  $caps_table .= '</tbody>';
173
  $caps_table .= '</table>';
174
-
175
  $delete_data = Groups_Options::get_option( 'groups_delete_data', false );
176
-
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  //
178
  // print the options form
179
  //
@@ -182,6 +195,7 @@ function groups_admin_options() {
182
 
183
  '<p>' .
184
  '<input class="button" type="submit" name="submit" value="' . __( 'Save', GROUPS_PLUGIN_DOMAIN ) . '"/>' .
 
185
  '</p>' .
186
 
187
  '<div>' .
171
  }
172
  $caps_table .= '</tbody>';
173
  $caps_table .= '</table>';
174
+
175
  $delete_data = Groups_Options::get_option( 'groups_delete_data', false );
176
+
177
+ if ( isset( $_GET['dismiss-groups-extensions-box'] ) && isset( $_GET['groups-extensions-box-nonce'] ) && wp_verify_nonce( $_GET['groups-extensions-box-nonce'], 'dismiss-box' ) ) {
178
+ Groups_Options::update_user_option( 'show-extensions-box', false );
179
+ }
180
+ $extensions_box = '';
181
+ if ( Groups_Options::get_user_option( 'show-extensions-box', true ) ) {
182
+ $dismiss_url = wp_nonce_url( add_query_arg( 'dismiss-groups-extensions-box', '1', admin_url( 'admin.php?page=groups-admin-options' ) ), 'dismiss-box', 'groups-extensions-box-nonce' );
183
+ $extensions_box =
184
+ '<div id="groups-extensions-box">' .
185
+ __( 'Enhanced functionality is available via official <a href="http://www.itthinx.com/plugins/groups/">Extensions</a> for Groups.', GROUPS_PLUGIN_DOMAIN ) .
186
+ sprintf( '<a class="close" href="%s">x</a>', esc_url( $dismiss_url ) ) .
187
+ '</div>';
188
+ }
189
+
190
  //
191
  // print the options form
192
  //
195
 
196
  '<p>' .
197
  '<input class="button" type="submit" name="submit" value="' . __( 'Save', GROUPS_PLUGIN_DOMAIN ) . '"/>' .
198
+ $extensions_box .
199
  '</p>' .
200
 
201
  '<div>' .
readme.txt CHANGED
@@ -2,32 +2,26 @@
2
  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.6.1
7
- Stable tag: 1.4.0
8
  License: GPLv3
9
 
10
- Groups provides group-based user membership management, group-based capabilities and content access control.
11
 
12
  == Description ==
13
 
14
- Groups provides group-based user membership management, group-based capabilities and content access control.
15
- It integrates standard WordPress capabilities and application-specific capabilities along with an extensive API.
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.
24
- - [Groups Newsletters](http://www.itthinx.com/plugins/groups-newsletters/) Newsletter Campaigns for Subscribers and Groups.
25
- - [Groups Notifications](http://www.itthinx.com/plugins/groups-notifications/) Adds customizable notifications for events related to Groups.
26
- - [Groups PayPal](http://www.itthinx.com/plugins/groups-paypal/) Groups for PayPal allows to sell memberships and subscriptions with Groups.
27
- - [Groups Subscriptions](http://www.itthinx.com/plugins/groups-subscriptions/) A subscription framework for Groups used by other extensions.
28
- - [Groups WooCommerce](http://www.woothemes.com/extension/groups-woocommerce/) Groups for WooCommerce is a WordPress plugin that allows you to sell memberships.
29
- - [Groups 2 MailChimp](http://eggemplo.com/plugins/groups2mailchimp/) Synchronizes groups with MailChimp lists.
30
- - [Groups 404 Redirect](http://wordpress.org/extend/plugins/groups-404-redirect/) Redirects 404's caused by hits on pages that are protected by Groups.
31
 
32
  ### Features ###
33
 
@@ -113,188 +107,6 @@ Please try to solve problems there before you rate this plugin or say it doesn't
113
 
114
  Many thanks for your help!
115
 
116
-
117
- ### Introduction ###
118
-
119
- #### Content Access Control ####
120
-
121
- ##### Access restrictions on posts ####
122
-
123
- On posts an pages (and custom content types) a new meta box titled *Access restrictions* appears.
124
- 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.
125
- You need to assign this capability to a group and make users members of that group to allow them to see those posts.
126
-
127
- #### Content visibility for members and non-members ####
128
-
129
- The [groups_member] and [groups_non_member] shortcodes are used 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.
130
-
131
- Example: Limiting visibility of enclosed content to registered users.
132
-
133
- [groups_member group="Registered"]
134
-
135
- Only registered users can see this text.
136
-
137
- [/groups_member]
138
-
139
- #### Content visibility based on capabilities ####
140
-
141
- The [groups_can] and [groups_can_not] shortcodes limit visibility of enclosed content to those users who *have* the capability or those who *do not have* it. Multiple capabilities can be given.
142
-
143
- Example: Showing enclosed content to users who can edit_posts (standard WordPress capability).
144
-
145
- [groups_can capability="edit_posts"]
146
-
147
- You can see this only if you have the edit_posts capability.
148
-
149
- [/groups_can]
150
-
151
- ### Integration in the 'Users' menu: ###
152
-
153
- Users - group membership is managed from the standard Users admin view.
154
- Users are automatically added to the _Registered_ group. You can add multiple users to other groups here and also remove them.
155
-
156
- ### Integration in user profiles: ###
157
-
158
- Group memberships can be shown on the user profile page and edited by users who can *Administer groups*.
159
-
160
- This option is disabled by default and can be enabled under *Groups > Options > User profiles*.
161
-
162
- ### Sections in the 'Groups' menu: ###
163
-
164
- #### Groups ####
165
-
166
- Here you can:
167
-
168
- - add groups
169
- - remove groups
170
- - assign capabilities to groups
171
-
172
- #### Capabilities ####
173
-
174
- This is where you add, remove and manage capabilities.
175
-
176
- Capabilities can be assigned to groups and users (1). These capabilities include
177
- the *standard WordPress capabilities* but you can also define additional
178
- capabilities for your web-application.
179
-
180
- Groups defines the `groups_read_post` capability by default which can be
181
- used to restrict access to certain posts or pages to groups (and users)
182
- with that capability only. Additional capabilities can be identified on the
183
- *Groups > Options* admin screen that may be used to limit access.
184
-
185
- A user *must* be a member of a group that has the desired capability to restrict access. For example, in order to apply the `groups_read_post` capability, the user must belong to a group which has that capability assigned.
186
-
187
- (1) Assigning capabilities to users is not integrated in the user interface yet but can be done through API calls.
188
-
189
- #### Options ####
190
-
191
- ##### Administrator override #####
192
-
193
- Administrator overrides can be turned off.
194
-
195
- ##### Access restrictions #####
196
-
197
- Post types : Access restrictions can be enabled or disabled for standard (Post, Page and Media) and custom post types.
198
- Capabilities : Here specific capabilities can be enabled or disabled to restrict access to posts. The standard `groups_read_post` capability is enabled by default.
199
-
200
- Note that to apply an access restriction on a post, the user must belong to a group which has that capability.
201
-
202
- ##### User profiles #####
203
-
204
- Groups can be shown in user profiles, users who can *Administer groups* can edit group memberships on a user's profile page.
205
-
206
- ##### Tree view #####
207
-
208
- The tree view adds a menu item to the Groups menu which shows the group hierarchy.
209
-
210
- ##### Permissions #####
211
-
212
- For each role these permissions can be set:
213
-
214
- * Access Groups: see information related to Groups.
215
- * Administer Groups: complete control over everything related to Groups.
216
- * Administer Groups plugin options: grants access to make changes on the *Groups > Options* admin section.
217
-
218
- ##### Testing the plugin #####
219
-
220
- A convenient option is provided to delete all data that has been stored by the Groups plugin.
221
- This option is useful if you just need to start from fresh after you have been testing the plugin.
222
-
223
- ### Shortcodes ###
224
-
225
- #### Limit content visibility ####
226
-
227
- These shortcodes are used to limit the visibility of the content they enclose:
228
-
229
- - [groups_member]
230
- - [groups_non_member]
231
- - [groups_can]
232
- - [groups_can_not]
233
-
234
- See above for examples and descriptions.
235
-
236
- #### Show group information ####
237
-
238
- - [groups_group_info]
239
-
240
- This shortcode takes the following attributes to show information about a group:
241
-
242
- - _group_ : (required) the group ID or name
243
- - _show_ : (required) what to show, accepted values are: _name_, _description_, _count_, _users_
244
- - _single_ : (optional) used when show="count" and there is 1 member in the group
245
- - _plural_ : (optional) used when show="count" and there is more than 1 member in the group, must contain %d to show the number of members
246
-
247
- Examples:
248
-
249
- * [groups_group_info group="Registered" show="count"]
250
-
251
- * There [groups_group_info group="1" show="count" single="is one member" plural="are %d members"] in the [groups_group_info group="1" show="name"] group.
252
-
253
- #### Let a user join a group ####
254
-
255
- - [groups_join]
256
-
257
- This shortcode takes the following attributes to let a user join a specific group:
258
-
259
- - _group_ : (required) the group ID or name
260
- - _display_message_ : (optional) whether to show a confirmation after joining the group; accepted values: _true_, _false_; defaults to _true_
261
- - _display_is_member_ : (optional) whether to show that the user is a member of the group; accepted values: _true_, _false_; defaults to _false_
262
- - _submit_text_ : (optional) specify to change the button text; must contain %s to show the group name
263
-
264
- Example:
265
-
266
- * [group_join group="Cool"]
267
-
268
- #### Let a user leave a group ####
269
-
270
- - [groups_leave]
271
-
272
- This shortcode takes the following attributes to let a user leave a specific group:
273
-
274
- - _group_ : (required) the group ID or name
275
- - _display_message_ : (optional) whether to show a confirmation after leaving the group; accepted values: _true_, _false_; defaults to _true_
276
- - _submit_text_ : (optional) specify to change the button text; must contain %s to show the group name
277
-
278
- Example:
279
-
280
- * [groups_leave group="Cool"]
281
-
282
- #### Show user groups ####
283
-
284
- - [groups_user_groups]
285
-
286
- This shortcode lists the current user's or a specific user's groups.
287
-
288
- For detailed information about this shortcode, please refer to the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
289
-
290
- #### Show site groups ####
291
-
292
- - [groups_groups]
293
-
294
- This shortcode lists the site's groups.
295
-
296
- For detailed information about this shortcode, please refer to the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
297
-
298
  == Installation ==
299
 
300
  1. Upload or extract the `groups` folder to your site's `/wp-content/plugins/` directory. You can also use the *Add new* option found in the *Plugins* menu in WordPress.
@@ -304,14 +116,11 @@ For detailed information about this shortcode, please refer to the [Groups plugi
304
 
305
  = Where is the documentation? =
306
 
307
- Most of the features are currently documented at the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
308
-
309
- The official Groups documentation root is at the [Groups Documentation](http://www.itthinx.com/documentation/groups/) page.
310
- 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.
311
 
312
  = I have a question, where do I ask? =
313
 
314
- You can leave a comment at the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
315
 
316
  = I want Advanced and Premium members, where the Premium members can access everything that Advanced members can access. How can I do that? =
317
 
@@ -368,6 +177,12 @@ See also [Groups](http://www.itthinx.com/plugins/groups/)
368
 
369
  == Changelog ==
370
 
 
 
 
 
 
 
371
  = 1.4.0 =
372
  * Added: Groups > Groups > Add / Edit group screens, allow to assign/modify the capabilities assigned to the group.
373
  * Added: Groups > Groups screen, allow to assign/remove multiple capabilities to multiple groups.
@@ -527,6 +342,9 @@ Some installations wouldn't work correctly, showing no capabilities and making i
527
 
528
  == Upgrade Notice ==
529
 
 
 
 
530
  = 1.4.0 =
531
  * This release brings User Interface improvements mainly directed at working with groups and capabilities. This includes bulk actions for groups and capabilities and capability assignments when editing groups along with other improvements.
532
 
2
  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.5
6
  Tested up to: 3.6.1
7
+ Stable tag: 1.4.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.
11
 
12
  == Description ==
13
 
14
+ Groups is designed as an efficient, powerful and flexible solution for group-oriented membership and content access control.
 
15
 
16
+ It provides group-based user membership management, group-based capabilities and access control for content, built on solid principles.
17
 
18
+ Groups is light-weight and offers an easy user interface, while it acts as a framework and integrates standard WordPress capabilities and application-specific capabilities along with an extensive API.
19
 
20
+ Enhanced functionality is available via official [extensions](http://www.itthinx.com/plugins/groups/) for Groups.
21
+
22
+ ### Documentation ###
23
+
24
+ The official documentation is located at the [Groups documentation pages](http://www.itthinx.com/documentation/groups/).
 
 
 
 
 
 
25
 
26
  ### Features ###
27
 
107
 
108
  Many thanks for your help!
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  == Installation ==
111
 
112
  1. Upload or extract the `groups` folder to your site's `/wp-content/plugins/` directory. You can also use the *Add new* option found in the *Plugins* menu in WordPress.
116
 
117
  = Where is the documentation? =
118
 
119
+ The official documentation is located at the [Groups documentation pages](http://www.itthinx.com/documentation/groups/).
 
 
 
120
 
121
  = I have a question, where do I ask? =
122
 
123
+ For questions directly related to Groups, you can leave a comment at the [Groups plugin page](http://www.itthinx.com/plugins/groups/).
124
 
125
  = I want Advanced and Premium members, where the Premium members can access everything that Advanced members can access. How can I do that? =
126
 
177
 
178
  == Changelog ==
179
 
180
+ = 1.4.1 =
181
+ * Added: Better group-assignment on the Users admin screen, allows to assign/remove multiple users to/from multiple groups along with a better UI.
182
+ * Changed: Groups requires at least WordPress 3.5 now, although this only affects the group-action functionality on the Users admin screen, the restrict_manage_users action which is now used to render the UI elements needed, was introduced with WordPress 3.5.
183
+ * Added: Extensions box in Options.
184
+ * Improved: Groups section in user profile with added description.
185
+
186
  = 1.4.0 =
187
  * Added: Groups > Groups > Add / Edit group screens, allow to assign/modify the capabilities assigned to the group.
188
  * Added: Groups > Groups screen, allow to assign/remove multiple capabilities to multiple groups.
342
 
343
  == Upgrade Notice ==
344
 
345
+ = 1.4.1 =
346
+ * From this release on, Groups requires at least WordPress 3.5. It includes improved group-actions for the Users admin screen, where multiple users can now be added to or removed from multiple groups at once.
347
+
348
  = 1.4.0 =
349
  * This release brings User Interface improvements mainly directed at working with groups and capabilities. This includes bulk actions for groups and capabilities and capability assignments when editing groups along with other improvements.
350