WordPress.org

Making WordPress.org

Changeset 2777


Ignore:
Timestamp:
03/22/2016 06:21:26 AM (3 years ago)
Author:
dd32
Message:

Plugin Directory: Introduce the Plugin Committer/Review & Admin roles. Use the capabilities throughout the core flows of the admin.
This also has a few hacks to make plugin committers/authors only see plugins which they can manage, although a few core bugs remain which cause non-owner committers not to be able to edit plugins properly.

See #1571

Location:
sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/class-customizations.php

    r2776 r2777  
    22namespace WordPressdotorg\Plugin_Directory\Admin;
    33use \WordPressdotorg\Plugin_Directory;
     4use \WordPressdotorg\Plugin_Directory\Tools;
    45use \WordPressdotorg\Plugin_Directory\Admin\List_Table\Plugin_Posts;
    56
     
    2829        add_action( 'do_meta_boxes', array( $this, 'replace_title_global' ) );
    2930
     31        add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
    3032        add_action( 'save_post_plugin', array( $this, 'save_plugin_post' ), 10, 2 );
     33        add_filter( 'views_edit-plugin', array( $this, 'list_table_views' ) );
    3134
    3235        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
     
    4043        add_filter( 'wp_ajax_add-committer',    array( __NAMESPACE__ . '\Metabox\Committers', 'add_committer'    ) );
    4144        add_filter( 'wp_ajax_delete-committer', array( __NAMESPACE__ . '\Metabox\Committers', 'remove_committer' ) );
     45        add_action( 'admin_menu', array( $this, 'admin_menu' ) );
     46
    4247    }
    4348
     
    8287            }
    8388        }
     89    }
     90
     91    public function admin_menu() {
     92        // WordPress requires that the plugin post_type have at least one submenu accessible *other* than itself.
     93        // If it doesn't have at least one submenu then users who cannot also publish posts will not be able to access the post type.
     94        add_submenu_page( 'edit.php?post_type=plugin', 'Plugin Handbook', 'Plugin Handbook', 'read', 'handbook', function() {} );
     95        add_submenu_page( 'edit.php?post_type=plugin', 'Readme Validator', 'Readme Validator', 'read', 'readme_validator', function() {} );
     96
     97        remove_menu_page( 'index.php' );
     98        remove_menu_page( 'profile.php' );
     99    }
     100
     101    /**
     102     * Filter the query in wp-admin to list only
     103     */
     104    public function pre_get_posts( $query ) {
     105        global $wpdb;
     106        if ( ! $query->is_main_query() ) {
     107            return;
     108        }
     109
     110        if ( ! current_user_can( 'plugin_edit_others' ) || ( isset( $query->query['author'] ) && $query->query['author'] == get_current_user_id() ) ) {
     111            $query->query_vars['author'] = get_current_user_id();
     112            $plugins = Tools::get_users_write_access_plugins( get_current_user_id() );
     113            if ( $plugins ) {
     114                $query->query_vars['post_name__in'] = $plugins;
     115                add_filter( 'posts_where', array( $this, 'pre_get_posts_sql_name_or_user' ) );
     116            }
     117        }
     118    }
     119
     120    /**
     121     * Custom callback for pre_get_posts to use an OR query between post_name & post_author
     122     *
     123     * @ignore
     124     */
     125    public function pre_get_posts_sql_name_or_user( $where ) {
     126        remove_filter( 'posts_where', array( $this, 'pre_get_posts_sql_name_or_user' ) );
     127
     128        // Replace `post_name IN(..) AND post_author IN (..)`
     129        // With `( post_name IN() OR post_author IN() )`
     130
     131        $where = preg_replace( "!\s(\S+\.post_name IN .+?)\s*AND\s*(\s\S+\.post_author.+?)AND!i", ' ( $1 OR $2 ) AND', $where );
     132        return $where;
     133    }
     134
     135    public function list_table_views( $views ) {
     136        global $wp_query;
     137        if ( current_user_can( 'plugin_edit_others' ) ) {
     138            return $views;
     139        }
     140        // The only view the user needs, is their own.
     141        return array(
     142            sprintf(
     143                '<a href="#" class="current">%s</a>',
     144                sprintf(
     145                    _nx(
     146                        'Mine <span class="count">(%s)</span>',
     147                        'Mine <span class="count">(%s)</span>',
     148                        $wp_query->found_posts,
     149                        'posts',
     150                        'wporg-posts'
     151                    ),
     152                    number_format_i18n( $wp_query->found_posts )
     153                )
     154            )
     155        );
    84156    }
    85157
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/list-table/class-committers.php

    r2764 r2777  
    3333     */
    3434    public function ajax_user_can() {
    35         return current_user_can( 'manage_committers' );
     35        return current_user_can( 'plugin_remove_committer' );
    3636    }
    3737
     
    150150
    151151        // Check if the committer for this row is removable.
    152         if ( current_user_can( 'list_users' ) ) {
    153             $post_id = get_post()->ID;
     152        $post_id = get_post()->ID;
     153        if ( current_user_can( 'plugin_remove_committer', $post_id ) && $user_object->ID != get_current_user_id() ) {
    154154            $actions['delete'] = "<a class='submitremove' data-wp-lists='delete:the-committer-list:committer-{$user_object->ID}:faafaa:post_id={$post_id}' href='" . wp_nonce_url( 'users.php?action=remove&amp;committer=' . $user_object->ID, "remove-committer-{$user_object->ID}" ) . "'>" . __( 'Remove', 'wporg-plugins' ) . "</a>";
    155155        }
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/metabox/class-committers.php

    r2763 r2777  
    22namespace WordPressdotorg\Plugin_Directory\Admin\Metabox;
    33use WordPressdotorg\Plugin_Directory\Admin\List_Table;
     4use WordPressdotorg\Plugin_Directory\Tools;
    45
    56/**
     
    3536     */
    3637    public static function add_committer() {
     38        $login    = isset( $_POST['add_committer'] ) ? sanitize_user( $_POST['add_committer'] ) : '';
     39        $post_id  = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0;
     40
    3741        check_ajax_referer( 'add-committer' );
    3842
    39         $login    = isset( $_POST['add_committer'] ) ? sanitize_user( $_POST['add_committer'] ) : '';
    40         $post_id  = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0;
    41         $response = new \WP_Ajax_Response();
     43        $response    = new \WP_Ajax_Response();
     44        $plugin_slug = get_post( $post_id )->post_name;
    4245
    4346        if ( ! $committer = get_user_by( 'login', $login ) ) {
     
    4952        }
    5053
    51         // @todo: Capabilities.
    52         if ( ! current_user_can( 'add_committers', $post_id ) ) {
    53             //  wp_die( -1 );
     54        if ( ! current_user_can( 'plugin_add_committer', $post_id ) ) {
     55                wp_die( -1 );
    5456        }
    55         global $post, $wpdb;
    5657
    57         $post   = get_post( $post_id );
    58         $result = $wpdb->insert( PLUGINS_TABLE_PREFIX . 'svn_access', array(
    59             'path'   => "/{$post->post_name}",
    60             'user'   => $login,
    61             'access' => 'rw',
    62         ) );
     58        $result = Tools::grant_plugin_committer( $plugin_slug, $committer );
     59
    6360        if ( ! $result ) {
    64             if ( 'Duplicate entry' === substr( $wpdb->last_error, 0, 15 ) ) {
    65                 $message = __( 'Duplicate committer detected.', 'wporg-plugins' );
    66             } else {
    67                 $message = __( 'An error has occurred. Please reload the page and try again.', 'wporg-plugins' );
    68             }
     61            $message = __( 'An error has occurred. Please reload the page and try again.', 'wporg-plugins' );
    6962
    7063            $response->add( array(
     
    9588        check_ajax_referer( "remove-committer-$id" );
    9689
    97         $response = new \WP_Ajax_Response();
     90        $response    = new \WP_Ajax_Response();
     91        $plugin_slug = get_post( $post_id )->post_name;
    9892
    9993        if ( ! $committer = get_user_by( 'id', $id ) ) {
     
    10599        }
    106100
    107         // @todo: Capabilities.
    108         if ( ! current_user_can( 'remove_committers', $post_id ) ) {
    109             //  wp_die( -1 );
     101        if ( ! current_user_can( 'plugin_remove_committer', $post_id ) ) {
     102                wp_die( -1 );
    110103        }
    111104
    112         $plugin_slug = get_post( $post_id )->post_name;
    113 
    114         $result = $GLOBALS['wpdb']->delete( PLUGINS_TABLE_PREFIX . 'svn_access', array(
    115             'path' => "/{$plugin_slug}",
    116             'user' => $committer->user_login,
    117         ) );
     105        $result = Tools::revoke_plugin_committer( $plugin_slug, $committer );
    118106
    119107        wp_die( $result );
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-directory.php

    r2735 r2777  
    2626        add_filter( 'the_content', array( $this, 'filter_post_content_to_correct_page' ), 1 );
    2727
     28        add_filter( 'map_meta_cap', array( __NAMESPACE__ . '\Capabilities', 'map_meta_cap' ), 10, 4 );
     29
    2830        // Load all Admin-specific items.
    29         add_action( 'admin_init', array( __NAMESPACE__ . '\Admin\Customizations', 'instance' ) );
     31        // Cannot be included on `admin_init` to allow access to menu hooks
     32        if ( defined( 'WP_ADMIN' ) && WP_ADMIN ) {
     33            Admin\Customizations::instance();
     34        }
    3035
    3136        register_activation_hook( PLUGIN_FILE, array( $this, 'activate' ) );
     
    6065            'rewrite'         => false,
    6166            'menu_icon'       => 'dashicons-admin-plugins',
    62             'capability_type' => array( 'post', 'posts' ), // TODO roles & capabilities
    63             'map_meta_cap'    => true,
    6467            'capabilities'    => array(
    65                 'create_posts' => 'do_not_allow'
     68                'edit_post'          => 'plugin_edit',
     69                'read_post'          => 'read',
     70                'edit_posts'         => 'plugin_dashboard_access',
     71                'edit_others_posts'  => 'plugin_edit_others',
     72                'read_private_posts' => 'do_not_allow',
     73                'delete_posts'       => 'do_not_allow',
     74                'create_posts'       => 'do_not_allow'
    6675            )
    6776        ) );
     
    7180            'query_var'         => 'plugin_category',
    7281            'rewrite'           => false,
    73             'public'            => true,
    74             'show_ui'           => true,
    75             'show_admin_column' => true,
     82            'public'            => false,
     83            'show_ui'           => current_user_can( 'plugin_set_category' ),
     84            'show_admin_column' => current_user_can( 'plugin_set_category' ),
    7685            'meta_box_cb'       => 'post_categories_meta_box',
    7786            'capabilities'      => array(
     
    102111            'show_admin_column' => true,
    103112            'meta_box_cb'       => array( __NAMESPACE__ . '\Admin\Metabox\Plugin_Tags', 'display' ),
    104             'capabilities'      => array()
     113            'capabilities'      => array(
     114                'assign_terms' => 'plugin_set_tags'
     115            )
    105116        ) );
    106117
     
    108119            'label'                     => _x( 'Pending', 'plugin status', 'wporg-plugins' ),
    109120            'public'                    => false,
    110             'show_in_admin_status_list' => true,
     121            'show_in_admin_status_list' => current_user_can( 'plugin_approve' ),
    111122            'label_count'               => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>', 'wporg-plugins' ),
    112123        ) );
     
    114125            'label'                     => _x( 'Disabled', 'plugin status', 'wporg-plugins' ),
    115126            'public'                    => false,
    116             'show_in_admin_status_list' => true,
     127            'show_in_admin_status_list' => current_user_can( 'plugin_disable' ),
    117128            'label_count'               => _n_noop( 'Disabled <span class="count">(%s)</span>', 'Disabled <span class="count">(%s)</span>', 'wporg-plugins' ),
    118129        ) );
     
    120131            'label'                     => _x( 'Closed', 'plugin status', 'wporg-plugins' ),
    121132            'public'                    => false,
    122             'show_in_admin_status_list' => true,
     133            'show_in_admin_status_list' => current_user_can( 'plugin_close' ),
    123134            'label_count'               => _n_noop( 'Closed <span class="count">(%s)</span>', 'Closed <span class="count">(%s)</span>', 'wporg-plugins' ),
    124135        ) );
     
    126137            'label'                     => _x( 'Rejected', 'plugin status', 'wporg-plugins' ),
    127138            'public'                    => false,
    128             'show_in_admin_status_list' => true,
     139            'show_in_admin_status_list' => current_user_can( 'plugin_reject' ),
    129140            'label_count'               => _n_noop( 'Rejected <span class="count">(%s)</span>', 'Rejected <span class="count">(%s)</span>', 'wporg-plugins' ),
    130141        ) );
     
    141152        add_rewrite_endpoint( 'developers',   EP_PERMALINK );
    142153        add_rewrite_endpoint( 'other_notes',  EP_PERMALINK );
     154
     155        // If changing capabilities around, uncomment this.
     156        //Capabilities::add_roles();
    143157
    144158        // When this plugin is used in the context of a Rosetta site, handle it gracefully
     
    278292     */
    279293    public function use_plugins_in_query( $wp_query ) {
    280         if ( ! $wp_query->is_main_query() ) {
     294        if ( is_admin() || ! $wp_query->is_main_query() ) {
    281295            return;
    282296        }
    283297
    284         if ( empty( $wp_query->query_vars['pagename'] ) && ( empty( $wp_query->query_vars['post_type'] ) || 'posts' == $wp_query->query_vars['post_type'] ) ) {
     298        if ( empty( $wp_query->query_vars['pagename'] ) && ( empty( $wp_query->query_vars['post_type'] ) || 'post' == $wp_query->query_vars['post_type'] ) ) {
    285299            $wp_query->query_vars['post_type'] = array( 'plugin' );
    286300        }
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-tools.php

    r2638 r2777  
    4141    }
    4242
     43    public static function get_users_write_access_plugins( $user ) {
     44        global $wpdb;
     45        if ( ! $user instanceof \WP_User ) {
     46            $user = new \WP_User( $user );
     47        }
     48        if ( ! $user->exists() ) {
     49            return false;
     50        }
     51
     52        $plugins = $wpdb->get_col( $wpdb->prepare( 'SELECT path FROM `' . PLUGINS_TABLE_PREFIX . 'svn_access' . '` WHERE user = %s', $user->user_login ) );
     53        $plugins = array_map( function( $plugin ) { return trim( $plugin, '/' ); }, $plugins );
     54
     55        return $plugins;
     56
     57    }
     58
    4359    /**
    4460     * Grant a user RW access to a plugin.
     
    5571        }
    5672
    57         if ( ! $user->exists() || ! Plugin_Directory::instance()->get_plugin_post( $plugin_slug ) ) {
     73        if ( ! $user->exists() || ! Plugin_Directory::get_plugin_post( $plugin_slug ) ) {
    5874            return false;
    5975        }
    6076
    61         $existing_committers = wp_list_pluck( self::get_plugin_committers( $plugin_slug ), 'user_login' );
     77        $existing_committers = self::get_plugin_committers( $plugin_slug );
    6278        if ( in_array( $user->user_login, $existing_committers, true ) ) {
    6379            // User already has write access
     
    89105        }
    90106
    91         if ( ! $user->exists() || ! Plugin_Directory::instance()->get_plugin_post( $plugin_slug ) ) {
     107        if ( ! $user->exists() || ! Plugin_Directory::get_plugin_post( $plugin_slug ) ) {
    92108            return false;
    93109        }
Note: See TracChangeset for help on using the changeset viewer.