Make WordPress Core

Ticket #19867: 19867.3.diff

File 19867.3.diff, 17.1 KB (added by helen, 10 years ago)
  • src/wp-admin/includes/ajax-actions.php

     
    187187}
    188188
    189189function wp_ajax_autocomplete_user() {
    190         if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
     190        /**
     191         * Filter the minimum autocomplete search term length.
     192         *
     193         * The search term length is also specified in JS.
     194         *
     195         * @since 3.9.0
     196         * @param int    $length  Minimum length for the autocomplete search term.
     197         * @param string $context The context for the autocomplete search.
     198         */
     199        if ( trim( strlen( $_REQUEST['term'] ) ) < apply_filters( 'autocomplete_term_length', 2, 'users' ) ) {
    191200                wp_die( -1 );
     201        }
    192202
    193         /** This filter is documented in wp-admin/user-new.php */
    194         if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
    195                 wp_die( -1 );
    196 
    197         $return = array();
    198 
    199203        // Check the type of request
    200204        // Current allowed values are `add` and `search`
    201205        if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) {
     
    204208                $type = 'add';
    205209        }
    206210
     211        // Adding users needs the right permissions,
     212        // shouldn't run the search on large networks,
     213        // and requires that you are a superadmin.
     214        // If in the future a `search` type is used at the network level,
     215        // this will need a bit of a rethink for passing context.
     216        if ( is_multisite() && 'add' === $type ) {
     217                if ( ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) ) {
     218                        wp_die( -1 );
     219                }
     220
     221                /** This filter is documented in wp-admin/user-new.php */
     222                if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) ) {
     223                        wp_die( -1 );
     224                }
     225        }
     226
     227        $return = array();
     228
    207229        // Check the desired field for value
    208         // Current allowed values are `user_email` and `user_login`
     230        // Current allowed values are `user_email`, `user_login`, and `id`
    209231        if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) {
    210232                $field = $_REQUEST['autocomplete_field'];
     233        } elseif ( 'user_id' === $_REQUEST['autocomplete_field'] ) {
     234                $field = 'id';
    211235        } else {
    212236                $field = 'user_login';
    213237        }
    214238
    215         // Exclude current users of this blog
    216         if ( isset( $_REQUEST['site_id'] ) ) {
    217                 $id = absint( $_REQUEST['site_id'] );
     239        // Get the return label template.
     240        if ( isset( $_REQUEST['autocomplete_label'] ) && ! empty( $_REQUEST['autocomplete_label' ] ) ) {
     241                $label_template = $_REQUEST['autocomplete_label'];
    218242        } else {
    219                 $id = get_current_blog_id();
     243                // default label
     244                $label_template = '{{display_name}}';
    220245        }
    221246
    222         $include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
    223         $exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
     247        // Email addresses should only be shown to (super)admins.
     248        if ( ! is_super_admin() ) {
     249                $label_template = str_replace( '{{user_email}}', '', $label_template );
     250        }
    224251
     252        $id = false;
     253        $include_blog_users = $exclude_blog_users = array();
     254        $search_columns = array( 'user_login', 'user_nicename', 'display_name' );
     255
     256        // Exclude current users of this blog in multisite in add mode
     257        if ( is_multisite() ) {
     258                if ( isset( $_REQUEST['site_id'] ) ) {
     259                        $id = absint( $_REQUEST['site_id'] );
     260                } else {
     261                        $id = get_current_blog_id();
     262                }
     263
     264                if ( 'add' === $type ) {
     265                        $exclude_blog_users = get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) );
     266                        // set the blog ID to false because we want the rest of the network's users
     267                        $id = false;
     268                }
     269
     270                // Include email in search columns - at this point the user must be a superadmin
     271                $search_columns[] = 'user_email';
     272        }
     273
    225274        $users = get_users( array(
    226                 'blog_id' => false,
     275                'blog_id' => $id,
    227276                'search'  => '*' . $_REQUEST['term'] . '*',
    228                 'include' => $include_blog_users,
    229277                'exclude' => $exclude_blog_users,
    230                 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
     278                'search_columns' => $search_columns,
    231279        ) );
    232280
    233281        foreach ( $users as $user ) {
     282                // Replace a given set of tokens. The rest can be handled with the filter later.
     283                $label = $label_template;
     284                foreach( array( 'user_id', 'user_login', 'user_email', 'display_name' ) as $token ) {
     285                        $label = str_replace( '{{' . $token . '}}', $user->$token, $label );
     286                }
     287
     288                // If the label ends up empty, show user login in parens.
     289                if ( empty( $label ) ) {
     290                        $label = "({$user->user_login})";
     291                }
     292
    234293                $return[] = array(
    235                         /* translators: 1: user_login, 2: user_email */
    236                         'label' => sprintf( __( '%1$s (%2$s)' ), $user->user_login, $user->user_email ),
     294                        'label' => esc_html( $label ),
    237295                        'value' => $user->$field,
    238296                );
    239297        }
    240298
     299        /**
     300         * Filter the user autocomplete results array.
     301         *
     302         * Useful if using custom tokens in the label.
     303         *
     304         * @since 3.9.0
     305         * @param array $return Array of results.
     306         */
     307        $return = apply_filters( 'autocomplete_user_results', $return );
     308
    241309        wp_die( json_encode( $return ) );
    242310}
    243311
  • src/wp-admin/js/user-suggest.js

     
    1212                        var $this = $( this ),
    1313                                autocompleteType = ( typeof $this.data( 'autocompleteType' ) !== 'undefined' ) ? $this.data( 'autocompleteType' ) : 'add',
    1414                                autocompleteField = ( typeof $this.data( 'autocompleteField' ) !== 'undefined' ) ? $this.data( 'autocompleteField' ) : 'user_login';
     15                                autocompleteLabel = ( typeof $this.data( 'autocompleteLabel' ) !== 'undefined' ) ? $this.data( 'autocompleteLabel' ) : '';
    1516
    1617                        $this.autocomplete({
    17                                 source:    ajaxurl + '?action=autocomplete-user&autocomplete_type=' + autocompleteType + '&autocomplete_field=' + autocompleteField + id,
     18                                source:    ajaxurl + '?action=autocomplete-user&autocomplete_type=' + autocompleteType + '&autocomplete_field=' + autocompleteField + '&autocomplete_label=' + autocompleteLabel + id,
    1819                                delay:     500,
    1920                                minLength: 2,
    2021                                position:  position,
     
    2324                                },
    2425                                close: function() {
    2526                                        $( this ).removeClass( 'open' );
     27                                },
     28                                focus: function( e, ui ) {
     29                                        $el = $( this );
     30                                        if ( typeof $el.data( 'autocompleteHelper' ) !== 'undefined' ) {
     31                                                $el.val( ui.item.label );
     32                                        } else {
     33                                                $el.val( ui.item.value );
     34                                        }
     35                                        return false;
     36                                },
     37                                select: function( e, ui ) {
     38                                        $el = $( this );
     39                                        if ( typeof $el.data( 'autocompleteHelper' ) !== 'undefined' ) {
     40                                                $el.next( '.wp-suggest-user-helper' ).val( ui.item.value );
     41                                                $el.val( ui.item.label );
     42                                        } else {
     43                                                $el.val( ui.item.value );
     44                                        }
     45                                        return false;
    2646                                }
    2747                        });
    2848                });
  • src/wp-admin/network/site-new.php

     
    140140                </tr>
    141141                <tr class="form-field form-required">
    142142                        <th scope="row"><?php _e( 'Admin Email' ) ?></th>
    143                         <td><input name="blog[email]" type="text" class="regular-text wp-suggest-user" data-autocomplete-type="search" data-autocomplete-field="user_email" title="<?php esc_attr_e( 'Email' ) ?>"/></td>
     143                        <td><input name="blog[email]" type="text" class="regular-text wp-suggest-user" data-autocomplete-type="search" data-autocomplete-field="user_email" data-autocomplete-label="{{user_login}} ({{user_email}})" title="<?php esc_attr_e( 'Email' ) ?>"/></td>
    144144                </tr>
    145145                <tr class="form-field">
    146146                        <td colspan="2"><?php _e( 'A new user will be created if the above email address is not in the database.' ) ?><br /><?php _e( 'The username and password will be mailed to this email address.' ) ?></td>
  • src/wp-admin/network/site-users.php

     
    265265        <table class="form-table">
    266266                <tr>
    267267                        <th scope="row"><?php _e( 'Username' ); ?></th>
    268                         <td><input type="text" class="regular-text wp-suggest-user" name="newuser" id="newuser" /></td>
     268                        <td><input type="text" class="regular-text wp-suggest-user" name="newuser" id="newuser" data-autocomplete-label="{{user_login}} ({{user_email}})" /></td>
    269269                </tr>
    270270                <tr>
    271271                        <th scope="row"><?php _e( 'Role' ); ?></th>
  • src/wp-admin/user-new.php

     
    293293<table class="form-table">
    294294        <tr class="form-field form-required">
    295295                <th scope="row"><label for="adduser-email"><?php echo $label; ?></label></th>
    296                 <td><input name="email" type="text" id="adduser-email" class="wp-suggest-user" value="" /></td>
     296                <td><input name="email" type="text" id="adduser-email" class="wp-suggest-user" value="" data-autocomplete-label="{{user_login}} ({{user_email}})" /></td>
    297297        </tr>
    298298        <tr class="form-field">
    299299                <th scope="row"><label for="adduser-role"><?php _e('Role'); ?></label></th>
  • src/wp-includes/user.php

     
    11291129 * need to be used; all users will be displayed in that case. Only one can be
    11301130 * used, either 'include' or 'exclude', but not both.
    11311131 *
    1132  * The available arguments are as follows:
    1133  * <ol>
    1134  * <li>show_option_all - Text to show all and whether HTML option exists.</li>
    1135  * <li>show_option_none - Text for show none and whether HTML option exists.</li>
    1136  * <li>hide_if_only_one_author - Don't create the dropdown if there is only one user.</li>
    1137  * <li>orderby - SQL order by clause for what order the users appear. Default is 'display_name'.</li>
    1138  * <li>order - Default is 'ASC'. Can also be 'DESC'.</li>
    1139  * <li>include - User IDs to include.</li>
    1140  * <li>exclude - User IDs to exclude.</li>
    1141  * <li>multi - Default is 'false'. Whether to skip the ID attribute on the 'select' element. A 'true' value is overridden when id argument is set.</li>
    1142  * <li>show - Default is 'display_name'. User table column to display. If the selected item is empty then the user_login will be displayed in parentheses</li>
    1143  * <li>echo - Default is '1'. Whether to display or retrieve content.</li>
    1144  * <li>selected - Which User ID is selected.</li>
    1145  * <li>include_selected - Always include the selected user ID in the dropdown. Default is false.</li>
    1146  * <li>name - Default is 'user'. Name attribute of select element.</li>
    1147  * <li>id - Default is the value of the 'name' parameter. ID attribute of select element.</li>
    1148  * <li>class - Class attribute of select element.</li>
    1149  * <li>blog_id - ID of blog (Multisite only). Defaults to ID of current blog.</li>
    1150  * <li>who - Which users to query. Currently only 'authors' is supported. Default is all users.</li>
    1151  * </ol>
     1132 * @param array $args {
     1133 *     An array of arguments. Optional.
    11521134 *
     1135 *     @string $show_option_all Text to show all and whether HTML option exists.
     1136 *     @string $show_option_none Text for show none and whether HTML option exists.
     1137 *     @bool   $hide_if_only_one_author Hide the dropdown if there is only one user.
     1138 *     @string $orderby SQL order by clause for what order the users appear.
     1139 *                      Default is 'display_name'.
     1140 *     @string $order Default is 'ASC'. Accepts 'ASC', 'DESC'.
     1141 *     @array  $include User IDs to include.
     1142 *     @array  $exclude User IDs to exclude.
     1143 *     @bool   $multi Whether to skip the ID attribute on the 'select' element.
     1144 *                    A 'true' value is overridden when id argument is set.
     1145 *                    Default is 'false'.
     1146 *     @string $show User table column to display. Default is 'display_name'.
     1147 *                   If the selected item is empty then the user_login will be
     1148 *                   displayed in parentheses
     1149 *     @bool   $echo Default is '1'. Whether to display or retrieve content.
     1150 *     @int    $selected Which User ID is selected.
     1151 *     @bool   $include_selected Always include the selected user ID in the dropdown.
     1152 *                               Default is false.
     1153 *     @string $name Name attribute of select element. Default is 'user'.
     1154 *     @string $id ID attribute of select element.
     1155 *                 Default is the value of the 'name' parameter.
     1156 *     @string $class Class attribute of select element.
     1157 *     @int    $blog_id ID of blog (Multisite only). Defaults to ID of current blog.
     1158 *     @array  $who Which users to query. Currently only 'authors' is supported.
     1159 *                  Default is all users.
     1160 *     @bool   $autocomplete Whether to use jQuery UI autocomplete instead of a select.
     1161 *                           Default false.
     1162 * }
     1163 *
    11531164 * @since 2.3.0
    11541165 * @uses $wpdb WordPress database object for queries
    11551166 *
     
    11631174                'include' => '', 'exclude' => '', 'multi' => 0,
    11641175                'show' => 'display_name', 'echo' => 1,
    11651176                'selected' => 0, 'name' => 'user', 'class' => '', 'id' => '',
    1166                 'blog_id' => $GLOBALS['blog_id'], 'who' => '', 'include_selected' => false
     1177                'blog_id' => $GLOBALS['blog_id'], 'who' => '', 'include_selected' => false,
     1178                'autocomplete' => false
    11671179        );
    11681180
    11691181        $defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0;
     
    11751187        $query_args['fields'] = array( 'ID', 'user_login', $show );
    11761188        $users = get_users( $query_args );
    11771189
     1190        /**
     1191         * Filter whether to enable autocomplete for the user select in the admin.
     1192         *
     1193         * wp_dropdown_users() defaults to using jQuery UI autocomplete in the admin
     1194         * when there are over 100 users. To use autocomplete on the front-end,
     1195         * set the arg to true when calling the function.
     1196         *
     1197         * @since 3.9.0
     1198         * @param bool  $autocomplete Whether or not to use autocomplete for the dropdown.
     1199         * @param array $users        The results of the get_users() call. Most useful for count().
     1200         * @param array $args         The passed args to wp_dropdown_users().
     1201         */
     1202        if ( is_admin() ) {
     1203                $autocomplete = apply_filters( 'admin_user_dropdown_autocomplete', count( $users ) > 100, $users, $args );
     1204        }
     1205
    11781206        $output = '';
    11791207        if ( !empty($users) && ( empty($hide_if_only_one_author) || count($users) > 1 ) ) {
    11801208                $name = esc_attr( $name );
     
    11831211                else
    11841212                        $id = $id ? " id='" . esc_attr( $id ) . "'" : " id='$name'";
    11851213
    1186                 $output = "<select name='{$name}'{$id} class='$class'>\n";
     1214                if ( $autocomplete ) {
     1215                        wp_enqueue_script( 'user-suggest' );
    11871216
    1188                 if ( $show_option_all )
    1189                         $output .= "\t<option value='0'>$show_option_all</option>\n";
     1217                        $display = '';
    11901218
    1191                 if ( $show_option_none ) {
    1192                         $_selected = selected( -1, $selected, false );
    1193                         $output .= "\t<option value='-1'$_selected>$show_option_none</option>\n";
    1194                 }
     1219                        if ( $selected > 0 ) {
     1220                                $user = get_user_by( 'id', $selected );
     1221                                $display = ! empty( $user->$show ) ? $user->$show : '('. $user->user_login . ')';
     1222                        }
    11951223
    1196                 $found_selected = false;
    1197                 foreach ( (array) $users as $user ) {
    1198                         $user->ID = (int) $user->ID;
    1199                         $_selected = selected( $user->ID, $selected, false );
    1200                         if ( $_selected )
    1201                                 $found_selected = true;
    1202                         $display = !empty($user->$show) ? $user->$show : '('. $user->user_login . ')';
    1203                         $output .= "\t<option value='$user->ID'$_selected>" . esc_html($display) . "</option>\n";
    1204                 }
     1224                        $output = '<input type="text" class="' . $class . 'wp-suggest-user hide-if-no-js" data-autocomplete-type="search" data-autocomplete-field="user_id" data-autocomplete-helper="y" data-autocomplete-label="{{' . $show . '}}" value="' . $display . '" />';
    12051225
    1206                 if ( $include_selected && ! $found_selected && ( $selected > 0 ) ) {
    1207                         $user = get_userdata( $selected );
    1208                         $_selected = selected( $user->ID, $selected, false );
    1209                         $display = !empty($user->$show) ? $user->$show : '('. $user->user_login . ')';
    1210                         $output .= "\t<option value='$user->ID'$_selected>" . esc_html($display) . "</option>\n";
     1226                        $output .= "<input type='text' name='{$name}'{$id} class='{$class} wp-suggest-user-helper hide-if-js' value='{$selected}' />";
     1227                } else {
     1228                        $output = "<select name='{$name}'{$id} class='$class'>\n";
     1229
     1230                        if ( $show_option_all )
     1231                                $output .= "\t<option value='0'>$show_option_all</option>\n";
     1232
     1233                        if ( $show_option_none ) {
     1234                                $_selected = selected( -1, $selected, false );
     1235                                $output .= "\t<option value='-1'$_selected>$show_option_none</option>\n";
     1236                        }
     1237
     1238                        $found_selected = false;
     1239                        foreach ( (array) $users as $user ) {
     1240                                $user->ID = (int) $user->ID;
     1241                                $_selected = selected( $user->ID, $selected, false );
     1242                                if ( $_selected )
     1243                                        $found_selected = true;
     1244                                $display = !empty($user->$show) ? $user->$show : '('. $user->user_login . ')';
     1245                                $output .= "\t<option value='$user->ID'$_selected>" . esc_html($display) . "</option>\n";
     1246                        }
     1247
     1248                        if ( $include_selected && ! $found_selected && ( $selected > 0 ) ) {
     1249                                $user = get_userdata( $selected );
     1250                                $_selected = selected( $user->ID, $selected, false );
     1251                                $display = !empty($user->$show) ? $user->$show : '('. $user->user_login . ')';
     1252                                $output .= "\t<option value='$user->ID'$_selected>" . esc_html($display) . "</option>\n";
     1253                        }
     1254
     1255                        $output .= "</select>";
    12111256                }
    1212 
    1213                 $output .= "</select>";
    12141257        }
    12151258
    12161259        /**