| @@ -22,8 +22,10 @@ | ||
| 22 | 22 | if ( !defined( 'ABSPATH' ) ) { |
| 23 | 23 | exit; |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | +// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 27 | + | |
| 26 | 28 | /** |
| 27 | 29 | * Show group info on user profile pages and let admins edit group membership. |
| 28 | 30 | */ |
| 29 | 31 | class Groups_Admin_User_Profile { |
| @@ -31,8 +33,10 @@ | ||
| 31 | 33 | /** |
| 32 | 34 | * Adds user profile actions. |
| 33 | 35 | */ |
| 34 | 36 | public static function init() { |
| 37 | + add_action( 'user_new_form', array( __CLASS__, 'user_new_form' ) ); | |
| 38 | + add_action( 'user_register', array( __CLASS__, 'user_register' ) ); | |
| 35 | 39 | add_action( 'show_user_profile', array( __CLASS__, 'show_user_profile' ) ); |
| 36 | 40 | add_action( 'edit_user_profile', array( __CLASS__, 'edit_user_profile' ) ); |
| 37 | 41 | add_action( 'personal_options_update', array( __CLASS__, 'personal_options_update' ) ); |
| 38 | 42 | add_action( 'edit_user_profile_update', array( __CLASS__, 'edit_user_profile_update' ) ); |
| @@ -44,9 +48,10 @@ | ||
| 44 | 48 | */ |
| 45 | 49 | public static function admin_enqueue_scripts() { |
| 46 | 50 | $screen = get_current_screen(); |
| 47 | 51 | if ( isset( $screen->id ) ) { |
| 48 | - switch( $screen->id ) { | |
| 52 | + switch ( $screen->id ) { | |
| 53 | + case 'user' : // creating a new user | |
| 49 | 54 | case 'user-edit' : |
| 50 | 55 | case 'profile' : |
| 51 | 56 | require_once GROUPS_VIEWS_LIB . '/class-groups-uie.php'; |
| 52 | 57 | Groups_UIE::enqueue( 'select' ); |
| @@ -55,61 +60,191 @@ | ||
| 55 | 60 | } |
| 56 | 61 | } |
| 57 | 62 | |
| 58 | 63 | /** |
| 64 | + * Hook for the form to create a new user. | |
| 65 | + * | |
| 66 | + * See wp-admin/user-new.php | |
| 67 | + * | |
| 68 | + * @param string $type form context, expecting 'add-existing-user' (Multisite), or 'add-new-user' (single site and network admin) | |
| 69 | + */ | |
| 70 | + public static function user_new_form( $type = null ) { | |
| 71 | + global $wpdb; | |
| 72 | + if ( $type == 'add-new-user' ) { | |
| 73 | + if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 74 | + $output = '<h3>' . esc_html_x( 'Groups', 'Groups section heading (add user)', 'groups' ) . '</h3>'; | |
| 75 | + $groups_table = _groups_get_tablename( 'group' ); | |
| 76 | + /** | |
| 77 | + * Allow to filter the groups. | |
| 78 | + * | |
| 79 | + * @since 2.20.0 | |
| 80 | + * | |
| 81 | + * @param array $groups | |
| 82 | + * @param string $type form context | |
| 83 | + * | |
| 84 | + * @return array | |
| 85 | + */ | |
| 86 | + $groups = apply_filters( | |
| 87 | + 'groups_admin_user_profile_user_new_form_groups', | |
| 88 | + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 89 | + $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ), | |
| 90 | + $type | |
| 91 | + ); | |
| 92 | + if ( $groups ) { | |
| 93 | + $output .= '<style type="text/css">'; | |
| 94 | + $output .= '.groups .selectize-input { font-size: inherit; }'; | |
| 95 | + $output .= '</style>'; | |
| 96 | + $output .= sprintf( | |
| 97 | + '<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">', | |
| 98 | + esc_attr__( 'Choose groups …', 'groups' ), | |
| 99 | + esc_attr__( 'Choose groups …', 'groups' ) | |
| 100 | + ); | |
| 101 | + foreach ( $groups as $group ) { | |
| 102 | + $output .= sprintf( | |
| 103 | + '<option value="%d">%s</option>', | |
| 104 | + Groups_Utility::id( $group->group_id ), | |
| 105 | + $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '' | |
| 106 | + ); | |
| 107 | + } | |
| 108 | + $output .= '</select>'; | |
| 109 | + $output .= Groups_UIE::render_select( '#user-groups' ); | |
| 110 | + $output .= '<p class="description">' . esc_html__( 'The user is a member of the chosen groups.', 'groups' ) . '</p>'; | |
| 111 | + } | |
| 112 | + echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 113 | + } | |
| 114 | + } | |
| 115 | + } | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * Adds the new user to chosen groups when creating a new user account | |
| 119 | + * from the admin side. | |
| 120 | + * | |
| 121 | + * @param int $user_id | |
| 122 | + */ | |
| 123 | + public static function user_register( $user_id ) { | |
| 124 | + | |
| 125 | + global $wpdb; | |
| 126 | + | |
| 127 | + if ( is_admin() ) { | |
| 128 | + if ( function_exists( 'get_current_screen' ) ) { | |
| 129 | + $screen = get_current_screen(); | |
| 130 | + if ( isset( $screen->id ) && $screen->id === 'user' ) { | |
| 131 | + if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 132 | + $groups_table = _groups_get_tablename( 'group' ); | |
| 133 | + /** | |
| 134 | + * Allow to filter the groups offered. | |
| 135 | + * | |
| 136 | + * @since 2.20.0 | |
| 137 | + * | |
| 138 | + * @param array $groups | |
| 139 | + * @param int $user_id | |
| 140 | + * | |
| 141 | + * @return array | |
| 142 | + */ | |
| 143 | + $groups = apply_filters( | |
| 144 | + 'groups_admin_user_profile_user_register_groups', | |
| 145 | + $wpdb->get_results( "SELECT * FROM $groups_table" ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 146 | + $user_id | |
| 147 | + ); | |
| 148 | + if ( $groups ) { | |
| 149 | + $user_group_ids = groups_sanitize_post( 'group_ids' ); | |
| 150 | + if ( !is_array( $user_group_ids ) ) { | |
| 151 | + $user_group_ids = array(); | |
| 152 | + } | |
| 153 | + foreach ( $groups as $group ) { | |
| 154 | + if ( in_array( $group->group_id, $user_group_ids ) ) { | |
| 155 | + // Do NOT use Groups_User::user_is_member( ... ) here, as this must not be filtered: | |
| 156 | + if ( !Groups_User_Group::read( $user_id, $group->group_id ) ) { | |
| 157 | + Groups_User_Group::create( array( 'user_id' => $user_id, 'group_id' => $group->group_id ) ); | |
| 158 | + } | |
| 159 | + } | |
| 160 | + } | |
| 161 | + } | |
| 162 | + } | |
| 163 | + } | |
| 164 | + } | |
| 165 | + } | |
| 166 | + } | |
| 167 | + | |
| 168 | + /** | |
| 59 | 169 | * Own profile. |
| 170 | + * | |
| 60 | 171 | * @param WP_User $user |
| 61 | 172 | */ |
| 62 | 173 | public static function show_user_profile( $user ) { |
| 63 | - if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 174 | + if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 64 | 175 | self::edit_user_profile( $user ); |
| 65 | 176 | } else { |
| 66 | - $output = '<h3>' . __( 'Groups', GROUPS_PLUGIN_DOMAIN ) . '</h3>'; | |
| 177 | + $output = '<h3>' . esc_html_x( 'Groups', 'Groups section heading (user profile)', 'groups' ) . '</h3>'; | |
| 67 | 178 | $user = new Groups_User( $user->ID ); |
| 68 | - $groups = $user->groups; | |
| 179 | + $groups = $user->get_groups(); | |
| 69 | 180 | if ( is_array( $groups ) ) { |
| 70 | 181 | if ( count( $groups ) > 0 ) { |
| 71 | 182 | usort( $groups, array( __CLASS__, 'by_group_name' ) ); |
| 72 | 183 | $output .= '<ul>'; |
| 73 | - foreach( $groups as $group ) { | |
| 74 | - $output .= '<li>' . wp_filter_nohtml_kses( $group->name ) . '</li>'; | |
| 184 | + foreach ( $groups as $group ) { | |
| 185 | + $output .= '<li>'; | |
| 186 | + $output .= $group->get_name() ? stripslashes( wp_filter_nohtml_kses( $group->get_name() ) ) : ''; | |
| 187 | + $output .= '</li>'; | |
| 75 | 188 | } |
| 76 | 189 | $output .= '</ul>'; |
| 77 | 190 | } |
| 78 | 191 | } |
| 79 | - echo $output; | |
| 192 | + echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 80 | 193 | } |
| 81 | 194 | } |
| 82 | 195 | |
| 83 | 196 | /** |
| 84 | 197 | * Editing a user profile. |
| 198 | + * | |
| 85 | 199 | * @param WP_User $user |
| 86 | 200 | */ |
| 87 | 201 | public static function edit_user_profile( $user ) { |
| 88 | 202 | global $wpdb; |
| 89 | - if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 90 | - $output = '<h3>' . __( 'Groups', GROUPS_PLUGIN_DOMAIN ) . '</h3>'; | |
| 203 | + if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 204 | + $output = '<h3>' . esc_html_x( 'Groups', 'Groups section heading (edit user)', 'groups' ) . '</h3>'; | |
| 91 | 205 | $user = new Groups_User( $user->ID ); |
| 92 | - $user_groups = $user->groups; | |
| 93 | 206 | $groups_table = _groups_get_tablename( 'group' ); |
| 94 | - if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ) ) { | |
| 207 | + /** | |
| 208 | + * Allow to filter the groups offered. | |
| 209 | + * | |
| 210 | + * @since 2.20.0 | |
| 211 | + * | |
| 212 | + * @param array $groups | |
| 213 | + * @param int $user_id | |
| 214 | + * | |
| 215 | + * @return array | |
| 216 | + */ | |
| 217 | + $groups = apply_filters( | |
| 218 | + 'groups_admin_user_profile_edit_user_profile_groups', | |
| 219 | + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 220 | + $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ), | |
| 221 | + $user->get_user()->ID | |
| 222 | + ); | |
| 223 | + if ( $groups ) { | |
| 95 | 224 | $output .= '<style type="text/css">'; |
| 96 | 225 | $output .= '.groups .selectize-input { font-size: inherit; }'; |
| 97 | 226 | $output .= '</style>'; |
| 98 | 227 | $output .= sprintf( |
| 99 | 228 | '<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">', |
| 100 | - esc_attr( __( 'Choose groups …', GROUPS_PLUGIN_DOMAIN ) ) , | |
| 101 | - esc_attr( __( 'Choose groups …', GROUPS_PLUGIN_DOMAIN ) ) | |
| 229 | + esc_attr__( 'Choose groups …', 'groups' ), | |
| 230 | + esc_attr__( 'Choose groups …', 'groups' ) | |
| 102 | 231 | ); |
| 103 | - foreach( $groups as $group ) { | |
| 104 | - $is_member = Groups_User_Group::read( $user->ID, $group->group_id ) ? true : false; | |
| 105 | - $output .= sprintf( '<option value="%d" %s>%s</option>', Groups_Utility::id( $group->group_id ), $is_member ? ' selected="selected" ' : '', wp_filter_nohtml_kses( $group->name ) ); | |
| 232 | + foreach ( $groups as $group ) { | |
| 233 | + // Do NOT use Groups_User::user_is_member( ... ) here, as this must not be filtered: | |
| 234 | + $is_member = Groups_User_Group::read( $user->get_user_id(), $group->group_id ) ? true : false; | |
| 235 | + $output .= sprintf( | |
| 236 | + '<option value="%d" %s>%s</option>', | |
| 237 | + Groups_Utility::id( $group->group_id ), | |
| 238 | + $is_member ? ' selected="selected" ' : '', | |
| 239 | + $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '' | |
| 240 | + ); | |
| 106 | 241 | } |
| 107 | 242 | $output .= '</select>'; |
| 108 | 243 | $output .= Groups_UIE::render_select( '#user-groups' ); |
| 109 | - $output .= '<p class="description">' . __( 'The user is a member of the chosen groups.', GROUPS_PLUGIN_DOMAIN ) . '</p>'; | |
| 244 | + $output .= '<p class="description">' . esc_html__( 'The user is a member of the chosen groups.', 'groups' ) . '</p>'; | |
| 110 | 245 | } |
| 111 | - echo $output; | |
| 246 | + echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 112 | 247 | } |
| 113 | 248 | } |
| 114 | 249 | |
| 115 | 250 | /** |
| @@ -114,16 +249,17 @@ | ||
| 114 | 249 | |
| 115 | 250 | /** |
| 116 | 251 | * Updates the group membership when a user's own profile is saved - but |
| 117 | 252 | * for group admins on their own profile page only. |
| 118 | - * | |
| 253 | + * | |
| 119 | 254 | * @param int $user_id |
| 255 | + * | |
| 120 | 256 | * @see Groups_Admin_User_Profile::edit_user_profile_update() |
| 121 | 257 | */ |
| 122 | 258 | public static function personal_options_update( $user_id ) { |
| 123 | 259 | // We're using the same method as for editing another user's profile, |
| 124 | - // but let's check for group admin here as well. | |
| 125 | - if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 260 | + // but let's check for group admin here as well. | |
| 261 | + if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 126 | 262 | self::edit_user_profile_update( $user_id ); |
| 127 | 263 | } |
| 128 | 264 | } |
| 129 | 265 | |
| @@ -128,22 +264,34 @@ | ||
| 128 | 264 | } |
| 129 | 265 | |
| 130 | 266 | /** |
| 131 | 267 | * Updates the group membership. |
| 268 | + * | |
| 132 | 269 | * @param int $user_id |
| 133 | 270 | */ |
| 134 | 271 | public static function edit_user_profile_update( $user_id ) { |
| 135 | 272 | global $wpdb; |
| 136 | - if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 273 | + if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { | |
| 137 | 274 | $groups_table = _groups_get_tablename( 'group' ); |
| 138 | - if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table" ) ) { | |
| 139 | - $user_group_ids = isset( $_POST['group_ids'] ) && is_array( $_POST['group_ids'] ) ? $_POST['group_ids'] : array(); | |
| 140 | - foreach( $groups as $group ) { | |
| 275 | + $groups = apply_filters( | |
| 276 | + 'groups_admin_user_profile_edit_user_profile_update_groups', | |
| 277 | + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 278 | + $wpdb->get_results( "SELECT * FROM $groups_table" ), | |
| 279 | + $user_id | |
| 280 | + ); | |
| 281 | + if ( $groups ) { | |
| 282 | + $user_group_ids = groups_sanitize_post( 'group_ids' ); | |
| 283 | + if ( !is_array( $user_group_ids ) ) { | |
| 284 | + $user_group_ids = array(); | |
| 285 | + } | |
| 286 | + foreach ( $groups as $group ) { | |
| 141 | 287 | if ( in_array( $group->group_id, $user_group_ids ) ) { |
| 288 | + // Do NOT use Groups_User::user_is_member( ... ) here, as this must not be filtered: | |
| 142 | 289 | if ( !Groups_User_Group::read( $user_id, $group->group_id ) ) { |
| 143 | 290 | Groups_User_Group::create( array( 'user_id' => $user_id, 'group_id' => $group->group_id ) ); |
| 144 | 291 | } |
| 145 | 292 | } else { |
| 293 | + // Do NOT use Groups_User::user_is_member( ... ) here, as this must not be filtered: | |
| 146 | 294 | if ( Groups_User_Group::read( $user_id, $group->group_id ) ) { |
| 147 | 295 | Groups_User_Group::delete( $user_id, $group->group_id ); |
| 148 | 296 | } |
| 149 | 297 | } |
| @@ -153,14 +301,16 @@ | ||
| 153 | 301 | } |
| 154 | 302 | |
| 155 | 303 | /** |
| 156 | 304 | * usort helper |
| 305 | + * | |
| 157 | 306 | * @param Groups_Group $o1 |
| 158 | 307 | * @param Groups_Group $o2 |
| 308 | + * | |
| 159 | 309 | * @return int strcmp result for group names |
| 160 | 310 | */ |
| 161 | 311 | public static function by_group_name( $o1, $o2 ) { |
| 162 | - return strcmp( $o1->name, $o2->name ); | |
| 312 | + return strcmp( $o1->get_name(), $o2->get_name() ); | |
| 163 | 313 | } |
| 164 | 314 | |
| 165 | 315 | } |
| 166 | 316 | Groups_Admin_User_Profile::init(); |