WordPress.org

Make WordPress Core

Ticket #38073: 38073.patch

File 38073.patch, 490.0 KB (added by davideferre, 2 years ago)
  • src/wp-admin/comment.php

    diff --git a/src/wp-admin/comment.php b/src/wp-admin/comment.php
    index c2eca11ec3..01081116b0 100644
    a b require_once( dirname( __FILE__ ) . '/admin.php' ); 
    1212$parent_file = 'edit-comments.php';
    1313$submenu_file = 'edit-comments.php';
    1414
    15 /**
    16  * @global string $action
    17  */
    18 global $action;
    19 wp_reset_vars( array('action') );
     15$action = wp_assign_request_var('action');
    2016
    2117if ( isset( $_POST['deletecomment'] ) )
    22         $action = 'deletecomment';
     18    $action = 'deletecomment';
    2319
    2420if ( 'cdc' == $action )
    25         $action = 'delete';
     21    $action = 'delete';
    2622elseif ( 'mac' == $action )
    27         $action = 'approve';
     23    $action = 'approve';
    2824
    2925if ( isset( $_GET['dt'] ) ) {
    30         if ( 'spam' == $_GET['dt'] )
    31                 $action = 'spam';
    32         elseif ( 'trash' == $_GET['dt'] )
    33                 $action = 'trash';
     26    if ( 'spam' == $_GET['dt'] )
     27        $action = 'spam';
     28    elseif ( 'trash' == $_GET['dt'] )
     29        $action = 'trash';
    3430}
    3531
    3632switch( $action ) {
    3733
    3834case 'editcomment' :
    39         $title = __('Edit Comment');
     35    $title = __('Edit Comment');
    4036
    41         get_current_screen()->add_help_tab( array(
    42                 'id'      => 'overview',
    43                 'title'   => __('Overview'),
    44                 'content' =>
    45                         '<p>' . __( 'You can edit the information left in a comment if needed. This is often useful when you notice that a commenter has made a typographical error.' ) . '</p>' .
    46                         '<p>' . __( 'You can also moderate the comment from this screen using the Status box, where you can also change the timestamp of the comment.' ) . '</p>'
    47         ) );
     37    get_current_screen()->add_help_tab( array(
     38        'id'      => 'overview',
     39        'title'   => __('Overview'),
     40        'content' =>
     41            '<p>' . __( 'You can edit the information left in a comment if needed. This is often useful when you notice that a commenter has made a typographical error.' ) . '</p>' .
     42            '<p>' . __( 'You can also moderate the comment from this screen using the Status box, where you can also change the timestamp of the comment.' ) . '</p>'
     43    ) );
    4844
    49         get_current_screen()->set_help_sidebar(
    50         '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
    51         '<p>' . __( '<a href="https://codex.wordpress.org/Administration_Screens#Comments">Documentation on Comments</a>' ) . '</p>' .
    52         '<p>' . __( '<a href="https://wordpress.org/support/">Support Forums</a>' ) . '</p>'
    53         );
     45    get_current_screen()->set_help_sidebar(
     46    '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
     47    '<p>' . __( '<a href="https://codex.wordpress.org/Administration_Screens#Comments">Documentation on Comments</a>' ) . '</p>' .
     48    '<p>' . __( '<a href="https://wordpress.org/support/">Support Forums</a>' ) . '</p>'
     49    );
    5450
    55         wp_enqueue_script('comment');
    56         require_once( ABSPATH . 'wp-admin/admin-header.php' );
     51    wp_enqueue_script('comment');
     52    require_once( ABSPATH . 'wp-admin/admin-header.php' );
    5753
    58         $comment_id = absint( $_GET['c'] );
     54    $comment_id = absint( $_GET['c'] );
    5955
    60         if ( !$comment = get_comment( $comment_id ) )
    61                 comment_footer_die( __( 'Invalid comment ID.' ) . sprintf(' <a href="%s">' . __('Go back') . '</a>.', 'javascript:history.go(-1)') );
     56    if ( !$comment = get_comment( $comment_id ) )
     57        comment_footer_die( __( 'Invalid comment ID.' ) . sprintf(' <a href="%s">' . __('Go back') . '</a>.', 'javascript:history.go(-1)') );
    6258
    63         if ( !current_user_can( 'edit_comment', $comment_id ) )
    64                 comment_footer_die( __('Sorry, you are not allowed to edit this comment.') );
     59    if ( !current_user_can( 'edit_comment', $comment_id ) )
     60        comment_footer_die( __('Sorry, you are not allowed to edit this comment.') );
    6561
    66         if ( 'trash' == $comment->comment_approved )
    67                 comment_footer_die( __('This comment is in the Trash. Please move it out of the Trash if you want to edit it.') );
     62    if ( 'trash' == $comment->comment_approved )
     63        comment_footer_die( __('This comment is in the Trash. Please move it out of the Trash if you want to edit it.') );
    6864
    69         $comment = get_comment_to_edit( $comment_id );
     65    $comment = get_comment_to_edit( $comment_id );
    7066
    71         include( ABSPATH . 'wp-admin/edit-form-comment.php' );
     67    include( ABSPATH . 'wp-admin/edit-form-comment.php' );
    7268
    73         break;
     69    break;
    7470
    7571case 'delete'  :
    7672case 'approve' :
    7773case 'trash'   :
    7874case 'spam'    :
    7975
    80         $title = __('Moderate Comment');
     76    $title = __('Moderate Comment');
    8177
    82         $comment_id = absint( $_GET['c'] );
     78    $comment_id = absint( $_GET['c'] );
    8379
    84         if ( ! $comment = get_comment( $comment_id ) ) {
    85                 wp_redirect( admin_url('edit-comments.php?error=1') );
    86                 die();
    87         }
     80    if ( ! $comment = get_comment( $comment_id ) ) {
     81        wp_redirect( admin_url('edit-comments.php?error=1') );
     82        die();
     83    }
    8884
    89         if ( !current_user_can( 'edit_comment', $comment->comment_ID ) ) {
    90                 wp_redirect( admin_url('edit-comments.php?error=2') );
    91                 die();
    92         }
     85    if ( !current_user_can( 'edit_comment', $comment->comment_ID ) ) {
     86        wp_redirect( admin_url('edit-comments.php?error=2') );
     87        die();
     88    }
    9389
    94         // No need to re-approve/re-trash/re-spam a comment.
    95         if ( $action == str_replace( '1', 'approve', $comment->comment_approved ) ) {
    96                 wp_redirect( admin_url( 'edit-comments.php?same=' . $comment_id ) );
    97                 die();
    98         }
     90    // No need to re-approve/re-trash/re-spam a comment.
     91    if ( $action == str_replace( '1', 'approve', $comment->comment_approved ) ) {
     92        wp_redirect( admin_url( 'edit-comments.php?same=' . $comment_id ) );
     93        die();
     94     }
    9995
    100         require_once( ABSPATH . 'wp-admin/admin-header.php' );
     96    require_once( ABSPATH . 'wp-admin/admin-header.php' );
    10197
    102         $formaction    = $action . 'comment';
    103         $nonce_action  = 'approve' == $action ? 'approve-comment_' : 'delete-comment_';
    104         $nonce_action .= $comment_id;
     98    $formaction    = $action . 'comment';
     99    $nonce_action  = 'approve' == $action ? 'approve-comment_' : 'delete-comment_';
     100    $nonce_action .= $comment_id;
    105101
    106102?>
    107103<div class="wrap">
    case 'spam' : 
    110106
    111107<?php
    112108switch ( $action ) {
    113         case 'spam' :
    114                 $caution_msg = __('You are about to mark the following comment as spam:');
    115                 $button      = _x( 'Mark as Spam', 'comment' );
    116                 break;
    117         case 'trash' :
    118                 $caution_msg = __('You are about to move the following comment to the Trash:');
    119                 $button      = __('Move to Trash');
    120                 break;
    121         case 'delete' :
    122                 $caution_msg = __('You are about to delete the following comment:');
    123                 $button      = __('Permanently Delete Comment');
    124                 break;
    125         default :
    126                 $caution_msg = __('You are about to approve the following comment:');
    127                 $button      = __('Approve Comment');
    128                 break;
     109    case 'spam' :
     110        $caution_msg = __('You are about to mark the following comment as spam:');
     111        $button      = _x( 'Mark as Spam', 'comment' );
     112        break;
     113    case 'trash' :
     114        $caution_msg = __('You are about to move the following comment to the Trash:');
     115        $button      = __('Move to Trash');
     116        break;
     117    case 'delete' :
     118        $caution_msg = __('You are about to delete the following comment:');
     119        $button      = __('Permanently Delete Comment');
     120        break;
     121    default :
     122        $caution_msg = __('You are about to approve the following comment:');
     123        $button      = __('Approve Comment');
     124        break;
    129125}
    130126
    131127if ( $comment->comment_approved != '0' ) { // if not unapproved
    132         $message = '';
    133         switch ( $comment->comment_approved ) {
    134                 case '1' :
    135                         $message = __('This comment is currently approved.');
    136                         break;
    137                 case 'spam' :
    138                         $message  = __('This comment is currently marked as spam.');
    139                         break;
    140                 case 'trash' :
    141                         $message  = __('This comment is currently in the Trash.');
    142                         break;
    143         }
    144         if ( $message ) {
    145                 echo '<div id="message" class="notice notice-info"><p>' . $message . '</p></div>';
    146         }
     128    $message = '';
     129    switch ( $comment->comment_approved ) {
     130        case '1' :
     131            $message = __('This comment is currently approved.');
     132            break;
     133        case 'spam' :
     134            $message  = __('This comment is currently marked as spam.');
     135            break;
     136        case 'trash' :
     137            $message  = __('This comment is currently in the Trash.');
     138            break;
     139    }
     140    if ( $message ) {
     141        echo '<div id="message" class="notice notice-info"><p>' . $message . '</p></div>';
     142    }
    147143}
    148144?>
    149145<div id="message" class="notice notice-warning"><p><strong><?php _e( 'Caution:' ); ?></strong> <?php echo $caution_msg; ?></p></div>
    if ( $comment->comment_approved != '0' ) { // if not unapproved 
    166162</tr>
    167163<?php } ?>
    168164<tr>
    169         <th scope="row"><?php /* translators: column name or table row header */ _e( 'In Response To' ); ?></th>
    170         <td>
    171         <?php
    172                 $post_id = $comment->comment_post_ID;
    173                 if ( current_user_can( 'edit_post', $post_id ) ) {
    174                         $post_link = "<a href='" . esc_url( get_edit_post_link( $post_id ) ) . "'>";
    175                         $post_link .= esc_html( get_the_title( $post_id ) ) . '</a>';
    176                 } else {
    177                         $post_link = esc_html( get_the_title( $post_id ) );
    178                 }
    179                 echo $post_link;
    180 
    181                 if ( $comment->comment_parent ) {
    182                         $parent      = get_comment( $comment->comment_parent );
    183                         $parent_link = esc_url( get_comment_link( $parent ) );
    184                         $name        = get_comment_author( $parent );
    185                         printf(
    186                                 /* translators: %s: comment link */
    187                                 ' | ' . __( 'In reply to %s.' ),
    188                                 '<a href="' . $parent_link . '">' . $name . '</a>'
    189                         );
    190                 }
    191         ?>
    192         </td>
     165    <th scope="row"><?php /* translators: column name or table row header */ _e( 'In Response To' ); ?></th>
     166    <td>
     167    <?php
     168        $post_id = $comment->comment_post_ID;
     169        if ( current_user_can( 'edit_post', $post_id ) ) {
     170            $post_link = "<a href='" . esc_url( get_edit_post_link( $post_id ) ) . "'>";
     171            $post_link .= esc_html( get_the_title( $post_id ) ) . '</a>';
     172        } else {
     173            $post_link = esc_html( get_the_title( $post_id ) );
     174        }
     175        echo $post_link;
     176
     177        if ( $comment->comment_parent ) {
     178            $parent      = get_comment( $comment->comment_parent );
     179            $parent_link = esc_url( get_comment_link( $parent ) );
     180            $name        = get_comment_author( $parent );
     181            printf(
     182                /* translators: %s: comment link */
     183                ' | ' . __( 'In reply to %s.' ),
     184                '<a href="' . $parent_link . '">' . $name . '</a>'
     185            );
     186        }
     187    ?>
     188    </td>
    193189</tr>
    194190<tr>
    195         <th scope="row"><?php _e( 'Submitted on' ); ?></th>
    196         <td>
    197         <?php
    198                 /* translators: 1: comment date, 2: comment time */
    199                 $submitted = sprintf( __( '%1$s at %2$s' ),
    200                         /* translators: comment date format. See https://secure.php.net/date */
    201                         get_comment_date( __( 'Y/m/d' ), $comment ),
    202                         get_comment_date( __( 'g:i a' ), $comment )
    203                 );
    204                 if ( 'approved' === wp_get_comment_status( $comment ) && ! empty ( $comment->comment_post_ID ) ) {
    205                         echo '<a href="' . esc_url( get_comment_link( $comment ) ) . '">' . $submitted . '</a>';
    206                 } else {
    207                         echo $submitted;
    208                 }
    209         ?>
    210         </td>
     191    <th scope="row"><?php _e( 'Submitted on' ); ?></th>
     192    <td>
     193    <?php
     194        /* translators: 1: comment date, 2: comment time */
     195        $submitted = sprintf( __( '%1$s at %2$s' ),
     196            /* translators: comment date format. See https://secure.php.net/date */
     197            get_comment_date( __( 'Y/m/d' ), $comment ),
     198            get_comment_date( __( 'g:i a' ), $comment )
     199        );
     200        if ( 'approved' === wp_get_comment_status( $comment ) && ! empty ( $comment->comment_post_ID ) ) {
     201            echo '<a href="' . esc_url( get_comment_link( $comment ) ) . '">' . $submitted . '</a>';
     202        } else {
     203            echo $submitted;
     204        }
     205    ?>
     206    </td>
    211207</tr>
    212208<tr>
    213209<th scope="row"><?php /* translators: field name in comment form */ _ex('Comment', 'noun'); ?></th>
    214210<td class="comment-content">
    215         <?php comment_text( $comment ); ?>
    216         <p class="edit-comment"><a href="<?php echo admin_url( "comment.php?action=editcomment&amp;c={$comment->comment_ID}" ); ?>"><?php esc_html_e( 'Edit' ); ?></a></p>
     211    <?php comment_text( $comment ); ?>
     212    <p class="edit-comment"><a href="<?php echo admin_url( "comment.php?action=editcomment&amp;c={$comment->comment_ID}" ); ?>"><?php esc_html_e( 'Edit' ); ?></a></p>
    217213</td>
    218214</tr>
    219215</table>
    if ( $comment->comment_approved != '0' ) { // if not unapproved 
    221217<form action="comment.php" method="get" class="comment-ays-submit">
    222218
    223219<p>
    224         <?php submit_button( $button, 'primary', 'submit', false ); ?>
    225         <a href="<?php echo admin_url('edit-comments.php'); ?>" class="button-cancel"><?php esc_html_e( 'Cancel' ); ?></a>
     220    <?php submit_button( $button, 'primary', 'submit', false ); ?>
     221    <a href="<?php echo admin_url('edit-comments.php'); ?>" class="button-cancel"><?php esc_html_e( 'Cancel' ); ?></a>
    226222</p>
    227223
    228224<?php wp_nonce_field( $nonce_action ); ?>
    if ( $comment->comment_approved != '0' ) { // if not unapproved 
    233229
    234230</div>
    235231<?php
    236         break;
     232    break;
    237233
    238234case 'deletecomment'    :
    239235case 'trashcomment'     :
    case 'spamcomment' : 
    242238case 'unspamcomment'    :
    243239case 'approvecomment'   :
    244240case 'unapprovecomment' :
    245         $comment_id = absint( $_REQUEST['c'] );
    246 
    247         if ( in_array( $action, array( 'approvecomment', 'unapprovecomment' ) ) )
    248                 check_admin_referer( 'approve-comment_' . $comment_id );
    249         else
    250                 check_admin_referer( 'delete-comment_' . $comment_id );
    251 
    252         $noredir = isset($_REQUEST['noredir']);
    253 
    254         if ( !$comment = get_comment($comment_id) )
    255                 comment_footer_die( __( 'Invalid comment ID.' ) . sprintf(' <a href="%s">' . __('Go back') . '</a>.', 'edit-comments.php') );
    256         if ( !current_user_can( 'edit_comment', $comment->comment_ID ) )
    257                 comment_footer_die( __('Sorry, you are not allowed to edit comments on this post.') );
    258 
    259         if ( '' != wp_get_referer() && ! $noredir && false === strpos(wp_get_referer(), 'comment.php') )
    260                 $redir = wp_get_referer();
    261         elseif ( '' != wp_get_original_referer() && ! $noredir )
    262                 $redir = wp_get_original_referer();
    263         elseif ( in_array( $action, array( 'approvecomment', 'unapprovecomment' ) ) )
    264                 $redir = admin_url('edit-comments.php?p=' . absint( $comment->comment_post_ID ) );
    265         else
    266                 $redir = admin_url('edit-comments.php');
    267 
    268         $redir = remove_query_arg( array('spammed', 'unspammed', 'trashed', 'untrashed', 'deleted', 'ids', 'approved', 'unapproved'), $redir );
    269 
    270         switch ( $action ) {
    271                 case 'deletecomment' :
    272                         wp_delete_comment( $comment );
    273                         $redir = add_query_arg( array('deleted' => '1'), $redir );
    274                         break;
    275                 case 'trashcomment' :
    276                         wp_trash_comment( $comment );
    277                         $redir = add_query_arg( array('trashed' => '1', 'ids' => $comment_id), $redir );
    278                         break;
    279                 case 'untrashcomment' :
    280                         wp_untrash_comment( $comment );
    281                         $redir = add_query_arg( array('untrashed' => '1'), $redir );
    282                         break;
    283                 case 'spamcomment' :
    284                         wp_spam_comment( $comment );
    285                         $redir = add_query_arg( array('spammed' => '1', 'ids' => $comment_id), $redir );
    286                         break;
    287                 case 'unspamcomment' :
    288                         wp_unspam_comment( $comment );
    289                         $redir = add_query_arg( array('unspammed' => '1'), $redir );
    290                         break;
    291                 case 'approvecomment' :
    292                         wp_set_comment_status( $comment, 'approve' );
    293                         $redir = add_query_arg( array( 'approved' => 1 ), $redir );
    294                         break;
    295                 case 'unapprovecomment' :
    296                         wp_set_comment_status( $comment, 'hold' );
    297                         $redir = add_query_arg( array( 'unapproved' => 1 ), $redir );
    298                         break;
    299         }
    300 
    301         wp_redirect( $redir );
    302         die;
     241    $comment_id = absint( $_REQUEST['c'] );
     242
     243    if ( in_array( $action, array( 'approvecomment', 'unapprovecomment' ) ) )
     244        check_admin_referer( 'approve-comment_' . $comment_id );
     245    else
     246        check_admin_referer( 'delete-comment_' . $comment_id );
     247
     248    $noredir = isset($_REQUEST['noredir']);
     249
     250    if ( !$comment = get_comment($comment_id) )
     251        comment_footer_die( __( 'Invalid comment ID.' ) . sprintf(' <a href="%s">' . __('Go back') . '</a>.', 'edit-comments.php') );
     252    if ( !current_user_can( 'edit_comment', $comment->comment_ID ) )
     253        comment_footer_die( __('Sorry, you are not allowed to edit comments on this post.') );
     254
     255    if ( '' != wp_get_referer() && ! $noredir && false === strpos(wp_get_referer(), 'comment.php') )
     256        $redir = wp_get_referer();
     257    elseif ( '' != wp_get_original_referer() && ! $noredir )
     258        $redir = wp_get_original_referer();
     259    elseif ( in_array( $action, array( 'approvecomment', 'unapprovecomment' ) ) )
     260        $redir = admin_url('edit-comments.php?p=' . absint( $comment->comment_post_ID ) );
     261    else
     262        $redir = admin_url('edit-comments.php');
     263
     264    $redir = remove_query_arg( array('spammed', 'unspammed', 'trashed', 'untrashed', 'deleted', 'ids', 'approved', 'unapproved'), $redir );
     265
     266    switch ( $action ) {
     267        case 'deletecomment' :
     268            wp_delete_comment( $comment );
     269            $redir = add_query_arg( array('deleted' => '1'), $redir );
     270            break;
     271        case 'trashcomment' :
     272            wp_trash_comment( $comment );
     273            $redir = add_query_arg( array('trashed' => '1', 'ids' => $comment_id), $redir );
     274            break;
     275        case 'untrashcomment' :
     276            wp_untrash_comment( $comment );
     277            $redir = add_query_arg( array('untrashed' => '1'), $redir );
     278            break;
     279        case 'spamcomment' :
     280            wp_spam_comment( $comment );
     281            $redir = add_query_arg( array('spammed' => '1', 'ids' => $comment_id), $redir );
     282            break;
     283        case 'unspamcomment' :
     284            wp_unspam_comment( $comment );
     285            $redir = add_query_arg( array('unspammed' => '1'), $redir );
     286            break;
     287        case 'approvecomment' :
     288            wp_set_comment_status( $comment, 'approve' );
     289            $redir = add_query_arg( array( 'approved' => 1 ), $redir );
     290            break;
     291        case 'unapprovecomment' :
     292            wp_set_comment_status( $comment, 'hold' );
     293            $redir = add_query_arg( array( 'unapproved' => 1 ), $redir );
     294            break;
     295    }
     296
     297    wp_redirect( $redir );
     298    die;
    303299
    304300case 'editedcomment' :
    305301
    306         $comment_id = absint( $_POST['comment_ID'] );
    307         $comment_post_id = absint( $_POST['comment_post_ID'] );
     302    $comment_id = absint( $_POST['comment_ID'] );
     303    $comment_post_id = absint( $_POST['comment_post_ID'] );
    308304
    309         check_admin_referer( 'update-comment_' . $comment_id );
     305    check_admin_referer( 'update-comment_' . $comment_id );
    310306
    311         edit_comment();
     307    edit_comment();
    312308
    313         $location = ( empty( $_POST['referredby'] ) ? "edit-comments.php?p=$comment_post_id" : $_POST['referredby'] ) . '#comment-' . $comment_id;
     309    $location = ( empty( $_POST['referredby'] ) ? "edit-comments.php?p=$comment_post_id" : $_POST['referredby'] ) . '#comment-' . $comment_id;
    314310
    315         /**
    316         * Filters the URI the user is redirected to after editing a comment in the admin.
    317         *
    318         * @since 2.1.0
    319         *
    320         * @param string $location The URI the user will be redirected to.
    321         * @param int $comment_id The ID of the comment being edited.
    322         */
    323         $location = apply_filters( 'comment_edit_redirect', $location, $comment_id );
    324         wp_redirect( $location );
     311    /**
     312    * Filters the URI the user is redirected to after editing a comment in the admin.
     313    *
     314    * @since 2.1.0
     315    *
     316    * @param string $location The URI the user will be redirected to.
     317    * @param int $comment_id The ID of the comment being edited.
     318    */
     319    $location = apply_filters( 'comment_edit_redirect', $location, $comment_id );
     320    wp_redirect( $location );
    325321
    326         exit();
     322    exit();
    327323
    328324default:
    329         wp_die( __('Unknown action.') );
     325    wp_die( __('Unknown action.') );
    330326
    331327} // end switch
    332328
  • src/wp-admin/customize.php

    diff --git a/src/wp-admin/customize.php b/src/wp-admin/customize.php
    index f1bf8aa9a1..2401ada6d0 100644
    a b define( 'IFRAME_REQUEST', true ); 
    1313require_once( dirname( __FILE__ ) . '/admin.php' );
    1414
    1515if ( ! current_user_can( 'customize' ) ) {
    16         wp_die(
    17                 '<h1>' . __( 'Cheatin&#8217; uh?' ) . '</h1>' .
    18                 '<p>' . __( 'Sorry, you are not allowed to customize this site.' ) . '</p>',
    19                 403
    20         );
     16    wp_die(
     17        '<h1>' . __( 'Cheatin&#8217; uh?' ) . '</h1>' .
     18        '<p>' . __( 'Sorry, you are not allowed to customize this site.' ) . '</p>',
     19        403
     20    );
    2121}
    2222
    2323/**
    if ( ! current_user_can( 'customize' ) ) { 
    2727global $wp_scripts, $wp_customize;
    2828
    2929if ( $wp_customize->changeset_post_id() ) {
    30         if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->edit_post, $wp_customize->changeset_post_id() ) ) {
    31                 wp_die(
    32                         '<h1>' . __( 'Cheatin&#8217; uh?' ) . '</h1>' .
    33                         '<p>' . __( 'Sorry, you are not allowed to edit this changeset.' ) . '</p>',
    34                         403
    35                 );
    36         }
    37         if ( in_array( get_post_status( $wp_customize->changeset_post_id() ), array( 'publish', 'trash' ), true ) ) {
    38                 wp_die(
    39                         '<h1>' . __( 'Cheatin&#8217; uh?' ) . '</h1>' .
    40                         '<p>' . __( 'This changeset has already been published and cannot be further modified.' ) . '</p>' .
    41                         '<p><a href="' . esc_url( remove_query_arg( 'changeset_uuid' ) ) . '">' . __( 'Customize New Changes' ) . '</a></p>',
    42                         403
    43                 );
    44         }
     30    if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->edit_post, $wp_customize->changeset_post_id() ) ) {
     31        wp_die(
     32            '<h1>' . __( 'Cheatin&#8217; uh?' ) . '</h1>' .
     33            '<p>' . __( 'Sorry, you are not allowed to edit this changeset.' ) . '</p>',
     34            403
     35        );
     36    }
     37    if ( in_array( get_post_status( $wp_customize->changeset_post_id() ), array( 'publish', 'trash' ), true ) ) {
     38        wp_die(
     39            '<h1>' . __( 'Cheatin&#8217; uh?' ) . '</h1>' .
     40            '<p>' . __( 'This changeset has already been published and cannot be further modified.' ) . '</p>' .
     41            '<p><a href="' . esc_url( remove_query_arg( 'changeset_uuid' ) ) . '">' . __( 'Customize New Changes' ) . '</a></p>',
     42            403
     43        );
     44    }
    4545}
    4646
    47 
    48 wp_reset_vars( array( 'url', 'return', 'autofocus' ) );
     47$url = wp_assign_request_var('url');
    4948if ( ! empty( $url ) ) {
    50         $wp_customize->set_preview_url( wp_unslash( $url ) );
     49    $wp_customize->set_preview_url( wp_unslash( $url ) );
    5150}
     51
     52$return = wp_assign_request_var('return');
    5253if ( ! empty( $return ) ) {
    53         $wp_customize->set_return_url( wp_unslash( $return ) );
     54    $wp_customize->set_return_url( wp_unslash( $return ) );
    5455}
     56
     57$autofocus = wp_assign_request_var('autofocus');
    5558if ( ! empty( $autofocus ) && is_array( $autofocus ) ) {
    56         $wp_customize->set_autofocus( wp_unslash( $autofocus ) );
     59    $wp_customize->set_autofocus( wp_unslash( $autofocus ) );
    5760}
    5861
    5962$registered = $wp_scripts->registered;
    _wp_admin_html_begin(); 
    9093$body_class = 'wp-core-ui wp-customizer js';
    9194
    9295if ( wp_is_mobile() ) :
    93         $body_class .= ' mobile';
     96    $body_class .= ' mobile';
    9497
    95         ?><meta name="viewport" id="viewport-meta" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=1.2" /><?php
     98    ?><meta name="viewport" id="viewport-meta" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=1.2" /><?php
    9699endif;
    97100
    98101if ( $wp_customize->is_ios() ) {
    99         $body_class .= ' ios';
     102    $body_class .= ' ios';
    100103}
    101104
    102105if ( is_rtl() ) {
    103         $body_class .= ' rtl';
     106    $body_class .= ' rtl';
    104107}
    105108$body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
    106109
    do_action( 'customize_controls_print_scripts' ); 
    130133</head>
    131134<body class="<?php echo esc_attr( $body_class ); ?>">
    132135<div class="wp-full-overlay expanded">
    133         <form id="customize-controls" class="wrap wp-full-overlay-sidebar">
    134                 <div id="customize-header-actions" class="wp-full-overlay-header">
    135                         <?php
    136                         $save_text = $wp_customize->is_theme_active() ? __( 'Save &amp; Publish' ) : __( 'Save &amp; Activate' );
    137                         $save_attrs = array();
    138                         if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) {
    139                                 $save_attrs['style'] = 'display: none';
    140                         }
    141                         submit_button( $save_text, 'primary save', 'save', false, $save_attrs );
    142                         ?>
    143                         <span class="spinner"></span>
    144                         <button type="button" class="customize-controls-preview-toggle">
    145                                 <span class="controls"><?php _e( 'Customize' ); ?></span>
    146                                 <span class="preview"><?php _e( 'Preview' ); ?></span>
    147                         </button>
    148                         <a class="customize-controls-close" href="<?php echo esc_url( $wp_customize->get_return_url() ); ?>">
    149                                 <span class="screen-reader-text"><?php _e( 'Close the Customizer and go back to the previous page' ); ?></span>
    150                         </a>
    151                 </div>
    152 
    153                 <div id="widgets-right" class="wp-clearfix"><!-- For Widget Customizer, many widgets try to look for instances under div#widgets-right, so we have to add that ID to a container div in the Customizer for compat -->
    154                 <div class="wp-full-overlay-sidebar-content" tabindex="-1">
    155                         <div id="customize-info" class="accordion-section customize-info">
    156                                 <div class="accordion-section-title">
    157                                         <span class="preview-notice"><?php
    158                                                 echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title site-title">' . get_bloginfo( 'name', 'display' ) . '</strong>' );
    159                                         ?></span>
    160                                         <button type="button" class="customize-help-toggle dashicons dashicons-editor-help" aria-expanded="false"><span class="screen-reader-text"><?php _e( 'Help' ); ?></span></button>
    161                                 </div>
    162                                 <div class="customize-panel-description"><?php
    163                                         _e( 'The Customizer allows you to preview changes to your site before publishing them. You can navigate to different pages on your site within the preview. Edit shortcuts are shown for some editable elements.' );
    164                                 ?></div>
    165                         </div>
    166 
    167                         <div id="customize-theme-controls">
    168                                 <ul class="customize-pane-parent"><?php // Panels and sections are managed here via JavaScript ?></ul>
    169                         </div>
    170                 </div>
    171                 </div>
    172 
    173                 <div id="customize-footer-actions" class="wp-full-overlay-footer">
    174                         <button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php echo esc_attr( _x( 'Hide Controls', 'label for hide controls button without length constraints' ) ); ?>">
    175                                 <span class="collapse-sidebar-arrow"></span>
    176                                 <span class="collapse-sidebar-label"><?php _ex( 'Hide Controls', 'short (~12 characters) label for hide controls button' ); ?></span>
    177                         </button>
    178                         <?php $previewable_devices = $wp_customize->get_previewable_devices(); ?>
    179                         <?php if ( ! empty( $previewable_devices ) ) : ?>
    180                         <div class="devices-wrapper">
    181                                 <div class="devices">
    182                                         <?php foreach ( (array) $previewable_devices as $device => $settings ) : ?>
    183                                                 <?php
    184                                                 if ( empty( $settings['label'] ) ) {
    185                                                         continue;
    186                                                 }
    187                                                 $active = ! empty( $settings['default'] );
    188                                                 $class = 'preview-' . $device;
    189                                                 if ( $active ) {
    190                                                         $class .= ' active';
    191                                                 }
    192                                                 ?>
    193                                                 <button type="button" class="<?php echo esc_attr( $class ); ?>" aria-pressed="<?php echo esc_attr( $active ) ?>" data-device="<?php echo esc_attr( $device ); ?>">
    194                                                         <span class="screen-reader-text"><?php echo esc_html( $settings['label'] ); ?></span>
    195                                                 </button>
    196                                         <?php endforeach; ?>
    197                                 </div>
    198                         </div>
    199                         <?php endif; ?>
    200                 </div>
    201         </form>
    202         <div id="customize-preview" class="wp-full-overlay-main"></div>
    203         <?php
    204 
    205         /**
    206         * Prints templates, control scripts, and settings in the footer.
    207         *
    208         * @since 3.4.0
    209         */
    210         do_action( 'customize_controls_print_footer_scripts' );
    211         ?>
     136    <form id="customize-controls" class="wrap wp-full-overlay-sidebar">
     137        <div id="customize-header-actions" class="wp-full-overlay-header">
     138            <?php
     139            $save_text = $wp_customize->is_theme_active() ? __( 'Save &amp; Publish' ) : __( 'Save &amp; Activate' );
     140            $save_attrs = array();
     141            if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) {
     142                $save_attrs['style'] = 'display: none';
     143            }
     144            submit_button( $save_text, 'primary save', 'save', false, $save_attrs );
     145            ?>
     146            <span class="spinner"></span>
     147            <button type="button" class="customize-controls-preview-toggle">
     148                <span class="controls"><?php _e( 'Customize' ); ?></span>
     149                <span class="preview"><?php _e( 'Preview' ); ?></span>
     150            </button>
     151            <a class="customize-controls-close" href="<?php echo esc_url( $wp_customize->get_return_url() ); ?>">
     152                <span class="screen-reader-text"><?php _e( 'Close the Customizer and go back to the previous page' ); ?></span>
     153            </a>
     154        </div>
     155
     156        <div id="widgets-right" class="wp-clearfix"><!-- For Widget Customizer, many widgets try to look for instances under div#widgets-right, so we have to add that ID to a container div in the Customizer for compat -->
     157        <div class="wp-full-overlay-sidebar-content" tabindex="-1">
     158            <div id="customize-info" class="accordion-section customize-info">
     159                <div class="accordion-section-title">
     160                    <span class="preview-notice"><?php
     161                        echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title site-title">' . get_bloginfo( 'name', 'display' ) . '</strong>' );
     162                    ?></span>
     163                    <button type="button" class="customize-help-toggle dashicons dashicons-editor-help" aria-expanded="false"><span class="screen-reader-text"><?php _e( 'Help' ); ?></span></button>
     164                </div>
     165                <div class="customize-panel-description"><?php
     166                    _e( 'The Customizer allows you to preview changes to your site before publishing them. You can navigate to different pages on your site within the preview. Edit shortcuts are shown for some editable elements.' );
     167                ?></div>
     168            </div>
     169
     170            <div id="customize-theme-controls">
     171                <ul class="customize-pane-parent"><?php // Panels and sections are managed here via JavaScript ?></ul>
     172            </div>
     173        </div>
     174        </div>
     175
     176        <div id="customize-footer-actions" class="wp-full-overlay-footer">
     177            <button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php echo esc_attr( _x( 'Hide Controls', 'label for hide controls button without length constraints' ) ); ?>">
     178                <span class="collapse-sidebar-arrow"></span>
     179                <span class="collapse-sidebar-label"><?php _ex( 'Hide Controls', 'short (~12 characters) label for hide controls button' ); ?></span>
     180            </button>
     181            <?php $previewable_devices = $wp_customize->get_previewable_devices(); ?>
     182            <?php if ( ! empty( $previewable_devices ) ) : ?>
     183            <div class="devices-wrapper">
     184                <div class="devices">
     185                    <?php foreach ( (array) $previewable_devices as $device => $settings ) : ?>
     186                        <?php
     187                        if ( empty( $settings['label'] ) ) {
     188                            continue;
     189                        }
     190                        $active = ! empty( $settings['default'] );
     191                        $class = 'preview-' . $device;
     192                        if ( $active ) {
     193                            $class .= ' active';
     194                        }
     195                        ?>
     196                        <button type="button" class="<?php echo esc_attr( $class ); ?>" aria-pressed="<?php echo esc_attr( $active ) ?>" data-device="<?php echo esc_attr( $device ); ?>">
     197                            <span class="screen-reader-text"><?php echo esc_html( $settings['label'] ); ?></span>
     198                        </button>
     199                    <?php endforeach; ?>
     200                </div>
     201            </div>
     202            <?php endif; ?>
     203        </div>
     204    </form>
     205    <div id="customize-preview" class="wp-full-overlay-main"></div>
     206    <?php
     207
     208    /**
     209    * Prints templates, control scripts, and settings in the footer.
     210    *
     211    * @since 3.4.0
     212    */
     213    do_action( 'customize_controls_print_footer_scripts' );
     214    ?>
    212215</div>
    213216</body>
    214217</html>
  • src/wp-admin/edit-tag-form.php

    diff --git a/src/wp-admin/edit-tag-form.php b/src/wp-admin/edit-tag-form.php
    index e3fb222bc9..3108186446 100644
    a b  
    88
    99// don't load directly
    1010if ( ! defined( 'ABSPATH' ) ) {
    11         die( '-1' );
     11    die( '-1' );
    1212}
    1313
    1414// Back compat hooks
    1515if ( 'category' == $taxonomy ) {
    16         /**
    17          * Fires before the Edit Category form.
    18         *
    19         * @since 2.1.0
    20         * @deprecated 3.0.0 Use {$taxonomy}_pre_edit_form instead.
    21         *
    22         * @param object $tag Current category term object.
    23         */
    24         do_action( 'edit_category_form_pre', $tag );
     16    /**
     17     * Fires before the Edit Category form.
     18    *
     19    * @since 2.1.0
     20    * @deprecated 3.0.0 Use {$taxonomy}_pre_edit_form instead.
     21    *
     22    * @param object $tag Current category term object.
     23    */
     24    do_action( 'edit_category_form_pre', $tag );
    2525} elseif ( 'link_category' == $taxonomy ) {
    26         /**
    27         * Fires before the Edit Link Category form.
    28         *
    29         * @since 2.3.0
    30         * @deprecated 3.0.0 Use {$taxonomy}_pre_edit_form instead.
    31         *
    32         * @param object $tag Current link category term object.
    33         */
    34         do_action( 'edit_link_category_form_pre', $tag );
     26    /**
     27    * Fires before the Edit Link Category form.
     28    *
     29    * @since 2.3.0
     30    * @deprecated 3.0.0 Use {$taxonomy}_pre_edit_form instead.
     31    *
     32    * @param object $tag Current link category term object.
     33    */
     34    do_action( 'edit_link_category_form_pre', $tag );
    3535} else {
    36         /**
    37         * Fires before the Edit Tag form.
    38         *
    39         * @since 2.5.0
    40         * @deprecated 3.0.0 Use {$taxonomy}_pre_edit_form instead.
    41         *
    42         * @param object $tag Current tag term object.
    43         */
    44         do_action( 'edit_tag_form_pre', $tag );
     36    /**
     37    * Fires before the Edit Tag form.
     38    *
     39    * @since 2.5.0
     40    * @deprecated 3.0.0 Use {$taxonomy}_pre_edit_form instead.
     41    *
     42    * @param object $tag Current tag term object.
     43    */
     44    do_action( 'edit_tag_form_pre', $tag );
    4545}
    4646
    47 /**
    48  * Use with caution, see https://codex.wordpress.org/Function_Reference/wp_reset_vars
    49  */
    50 wp_reset_vars( array( 'wp_http_referer' ) );
     47$wp_http_referer = wp_assign_request_var('$wp_http_referer');
    5148
    5249$wp_http_referer = remove_query_arg( array( 'action', 'message', 'tag_ID' ), $wp_http_referer );
    5350
    do_action( "{$taxonomy}_pre_edit_form", $tag, $taxonomy ); ?> 
    7269
    7370<?php if ( $message ) : ?>
    7471<div id="message" class="updated">
    75         <p><strong><?php echo $message; ?></strong></p>
    76         <?php if ( $wp_http_referer ) { ?>
    77         <p><a href="<?php echo esc_url( $wp_http_referer ); ?>"><?php
    78                 /* translators: %s: taxonomy name */
    79                 printf( _x( '&larr; Back to %s', 'admin screen' ), $tax->labels->name );
    80         ?></a></p>
    81         <?php } ?>
     72    <p><strong><?php echo $message; ?></strong></p>
     73    <?php if ( $wp_http_referer ) { ?>
     74    <p><a href="<?php echo esc_url( $wp_http_referer ); ?>"><?php
     75        /* translators: %s: taxonomy name */
     76        printf( _x( '&larr; Back to %s', 'admin screen' ), $tax->labels->name );
     77    ?></a></p>
     78    <?php } ?>
    8279</div>
    8380<?php endif; ?>
    8481
    wp_nonce_field( 'update-tag_' . $tag_ID ); 
    115112 */
    116113do_action( "{$taxonomy}_term_edit_form_top", $tag, $taxonomy );
    117114?>
    118         <table class="form-table">
    119                 <tr class="form-field form-required term-name-wrap">
    120                         <th scope="row"><label for="name"><?php _ex( 'Name', 'term name' ); ?></label></th>
    121                         <td><input name="name" id="name" type="text" value="<?php if ( isset( $tag->name ) ) echo esc_attr($tag->name); ?>" size="40" aria-required="true" />
    122                         <p class="description"><?php _e('The name is how it appears on your site.'); ?></p></td>
    123                 </tr>
     115    <table class="form-table">
     116        <tr class="form-field form-required term-name-wrap">
     117            <th scope="row"><label for="name"><?php _ex( 'Name', 'term name' ); ?></label></th>
     118            <td><input name="name" id="name" type="text" value="<?php if ( isset( $tag->name ) ) echo esc_attr($tag->name); ?>" size="40" aria-required="true" />
     119            <p class="description"><?php _e('The name is how it appears on your site.'); ?></p></td>
     120        </tr>
    124121<?php if ( !global_terms_enabled() ) { ?>
    125                 <tr class="form-field term-slug-wrap">
    126                         <th scope="row"><label for="slug"><?php _e( 'Slug' ); ?></label></th>
    127                         <?php
    128                         /**
    129                         * Filters the editable slug.
    130                         *
    131                         * Note: This is a multi-use hook in that it is leveraged both for editable
    132                         * post URIs and term slugs.
    133                         *
    134                         * @since 2.6.0
    135                         * @since 4.4.0 The `$tag` parameter was added.
    136                         *
    137                         * @param string         $slug The editable slug. Will be either a term slug or post URI depending
    138                         *                             upon the context in which it is evaluated.
    139                         * @param object|WP_Post $tag  Term or WP_Post object.
    140                         */
    141                         $slug = isset( $tag->slug ) ? apply_filters( 'editable_slug', $tag->slug, $tag ) : '';
    142                         ?>
    143                         <td><input name="slug" id="slug" type="text" value="<?php echo esc_attr( $slug ); ?>" size="40" />
    144                         <p class="description"><?php _e('The &#8220;slug&#8221; is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.'); ?></p></td>
    145                 </tr>
     122        <tr class="form-field term-slug-wrap">
     123            <th scope="row"><label for="slug"><?php _e( 'Slug' ); ?></label></th>
     124            <?php
     125            /**
     126            * Filters the editable slug.
     127            *
     128            * Note: This is a multi-use hook in that it is leveraged both for editable
     129            * post URIs and term slugs.
     130            *
     131            * @since 2.6.0
     132            * @since 4.4.0 The `$tag` parameter was added.
     133            *
     134            * @param string         $slug The editable slug. Will be either a term slug or post URI depending
     135            *                             upon the context in which it is evaluated.
     136            * @param object|WP_Post $tag  Term or WP_Post object.
     137            */
     138            $slug = isset( $tag->slug ) ? apply_filters( 'editable_slug', $tag->slug, $tag ) : '';
     139            ?>
     140            <td><input name="slug" id="slug" type="text" value="<?php echo esc_attr( $slug ); ?>" size="40" />
     141            <p class="description"><?php _e('The &#8220;slug&#8221; is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.'); ?></p></td>
     142        </tr>
    146143<?php } ?>
    147144<?php if ( is_taxonomy_hierarchical($taxonomy) ) : ?>
    148                 <tr class="form-field term-parent-wrap">
    149                         <th scope="row"><label for="parent"><?php echo esc_html( $tax->labels->parent_item ); ?></label></th>
    150                         <td>
    151                                 <?php
    152                                 $dropdown_args = array(
    153                                         'hide_empty'       => 0,
    154                                         'hide_if_empty'    => false,
    155                                         'taxonomy'         => $taxonomy,
    156                                         'name'             => 'parent',
    157                                         'orderby'          => 'name',
    158                                         'selected'         => $tag->parent,
    159                                         'exclude_tree'     => $tag->term_id,
    160                                         'hierarchical'     => true,
    161                                         'show_option_none' => __( 'None' ),
    162                                 );
     145        <tr class="form-field term-parent-wrap">
     146            <th scope="row"><label for="parent"><?php echo esc_html( $tax->labels->parent_item ); ?></label></th>
     147            <td>
     148                <?php
     149                $dropdown_args = array(
     150                    'hide_empty'       => 0,
     151                    'hide_if_empty'    => false,
     152                    'taxonomy'         => $taxonomy,
     153                    'name'             => 'parent',
     154                    'orderby'          => 'name',
     155                    'selected'         => $tag->parent,
     156                    'exclude_tree'     => $tag->term_id,
     157                    'hierarchical'     => true,
     158                    'show_option_none' => __( 'None' ),
     159                );
    163160
    164                                 /** This filter is documented in wp-admin/edit-tags.php */
    165                                 $dropdown_args = apply_filters( 'taxonomy_parent_dropdown_args', $dropdown_args, $taxonomy, 'edit' );
    166                                 wp_dropdown_categories( $dropdown_args ); ?>
    167                                 <?php if ( 'category' == $taxonomy ) : ?>
    168                                         <p class="description"><?php _e( 'Categories, unlike tags, can have a hierarchy. You might have a Jazz category, and under that have children categories for Bebop and Big Band. Totally optional.' ); ?></p>
    169                                 <?php else : ?>
    170                                         <p class="description"><?php _e( 'Assign a parent term to create a hierarchy. The term Jazz, for example, would be the parent of Bebop and Big Band.' ); ?></p>
    171                                 <?php endif; ?>
    172                         </td>
    173                 </tr>
     161                /** This filter is documented in wp-admin/edit-tags.php */
     162                $dropdown_args = apply_filters( 'taxonomy_parent_dropdown_args', $dropdown_args, $taxonomy, 'edit' );
     163                wp_dropdown_categories( $dropdown_args ); ?>
     164                <?php if ( 'category' == $taxonomy ) : ?>
     165                    <p class="description"><?php _e( 'Categories, unlike tags, can have a hierarchy. You might have a Jazz category, and under that have children categories for Bebop and Big Band. Totally optional.' ); ?></p>
     166                <?php else : ?>
     167                    <p class="description"><?php _e( 'Assign a parent term to create a hierarchy. The term Jazz, for example, would be the parent of Bebop and Big Band.' ); ?></p>
     168                <?php endif; ?>
     169            </td>
     170        </tr>
    174171<?php endif; // is_taxonomy_hierarchical() ?>
    175                 <tr class="form-field term-description-wrap">
    176                         <th scope="row"><label for="description"><?php _e( 'Description' ); ?></label></th>
    177                         <td><textarea name="description" id="description" rows="5" cols="50" class="large-text"><?php echo $tag->description; // textarea_escaped ?></textarea>
    178                         <p class="description"><?php _e('The description is not prominent by default; however, some themes may show it.'); ?></p></td>
    179                 </tr>
    180                 <?php
    181                 // Back compat hooks
    182                 if ( 'category' == $taxonomy ) {
    183                         /**
    184                         * Fires after the Edit Category form fields are displayed.
    185                         *
    186                         * @since 2.9.0
    187                         * @deprecated 3.0.0 Use {$taxonomy}_edit_form_fields instead.
    188                         *
    189                         * @param object $tag Current category term object.
    190                         */
    191                         do_action( 'edit_category_form_fields', $tag );
    192                 } elseif ( 'link_category' == $taxonomy ) {
    193                         /**
    194                         * Fires after the Edit Link Category form fields are displayed.
    195                         *
    196                         * @since 2.9.0
    197                         * @deprecated 3.0.0 Use {$taxonomy}_edit_form_fields instead.
    198                         *
    199                         * @param object $tag Current link category term object.
    200                         */
    201                         do_action( 'edit_link_category_form_fields', $tag );
    202                 } else {
    203                         /**
    204                         * Fires after the Edit Tag form fields are displayed.
    205                         *
    206                         * @since 2.9.0
    207                         * @deprecated 3.0.0 Use {$taxonomy}_edit_form_fields instead.
    208                         *
    209                         * @param object $tag Current tag term object.
    210                         */
    211                         do_action( 'edit_tag_form_fields', $tag );
    212                 }
    213                 /**
    214                 * Fires after the Edit Term form fields are displayed.
    215                 *
    216                 * The dynamic portion of the hook name, `$taxonomy`, refers to
    217                 * the taxonomy slug.
    218                 *
    219                 * @since 3.0.0
    220                 *
    221                 * @param object $tag      Current taxonomy term object.
    222                 * @param string $taxonomy Current taxonomy slug.
    223                 */
    224                 do_action( "{$taxonomy}_edit_form_fields", $tag, $taxonomy );
    225                 ?>
    226         </table>
     172        <tr class="form-field term-description-wrap">
     173            <th scope="row"><label for="description"><?php _e( 'Description' ); ?></label></th>
     174            <td><textarea name="description" id="description" rows="5" cols="50" class="large-text"><?php echo $tag->description; // textarea_escaped ?></textarea>
     175            <p class="description"><?php _e('The description is not prominent by default; however, some themes may show it.'); ?></p></td>
     176        </tr>
     177        <?php
     178        // Back compat hooks
     179        if ( 'category' == $taxonomy ) {
     180            /**
     181            * Fires after the Edit Category form fields are displayed.
     182            *
     183            * @since 2.9.0
     184            * @deprecated 3.0.0 Use {$taxonomy}_edit_form_fields instead.
     185            *
     186            * @param object $tag Current category term object.
     187            */
     188            do_action( 'edit_category_form_fields', $tag );
     189        } elseif ( 'link_category' == $taxonomy ) {
     190            /**
     191            * Fires after the Edit Link Category form fields are displayed.
     192            *
     193            * @since 2.9.0
     194            * @deprecated 3.0.0 Use {$taxonomy}_edit_form_fields instead.
     195            *
     196            * @param object $tag Current link category term object.
     197            */
     198            do_action( 'edit_link_category_form_fields', $tag );
     199        } else {
     200            /**
     201            * Fires after the Edit Tag form fields are displayed.
     202            *
     203            * @since 2.9.0
     204            * @deprecated 3.0.0 Use {$taxonomy}_edit_form_fields instead.
     205            *
     206            * @param object $tag Current tag term object.
     207            */
     208            do_action( 'edit_tag_form_fields', $tag );
     209        }
     210        /**
     211        * Fires after the Edit Term form fields are displayed.
     212        *
     213        * The dynamic portion of the hook name, `$taxonomy`, refers to
     214        * the taxonomy slug.
     215        *
     216        * @since 3.0.0
     217        *
     218        * @param object $tag      Current taxonomy term object.
     219        * @param string $taxonomy Current taxonomy slug.
     220        */
     221        do_action( "{$taxonomy}_edit_form_fields", $tag, $taxonomy );
     222        ?>
     223    </table>
    227224<?php
    228225// Back compat hooks
    229226if ( 'category' == $taxonomy ) {
    230         /** This action is documented in wp-admin/edit-tags.php */
    231         do_action( 'edit_category_form', $tag );
     227    /** This action is documented in wp-admin/edit-tags.php */
     228    do_action( 'edit_category_form', $tag );
    232229} elseif ( 'link_category' == $taxonomy ) {
    233         /** This action is documented in wp-admin/edit-tags.php */
    234         do_action( 'edit_link_category_form', $tag );
     230    /** This action is documented in wp-admin/edit-tags.php */
     231    do_action( 'edit_link_category_form', $tag );
    235232} else {
    236         /**
    237         * Fires at the end of the Edit Term form.
    238         *
    239         * @since 2.5.0
    240         * @deprecated 3.0.0 Use {$taxonomy}_edit_form instead.
    241         *
    242         * @param object $tag Current taxonomy term object.
    243         */
    244         do_action( 'edit_tag_form', $tag );
     233    /**
     234    * Fires at the end of the Edit Term form.
     235    *
     236    * @since 2.5.0
     237    * @deprecated 3.0.0 Use {$taxonomy}_edit_form instead.
     238    *
     239    * @param object $tag Current taxonomy term object.
     240    */
     241    do_action( 'edit_tag_form', $tag );
    245242}
    246243/**
    247244 * Fires at the end of the Edit Term form for all taxonomies.
    do_action( "{$taxonomy}_edit_form", $tag, $taxonomy ); 
    258255
    259256<div class="edit-tag-actions">
    260257
    261         <?php submit_button( __( 'Update' ), 'primary', null, false ); ?>
     258    <?php submit_button( __( 'Update' ), 'primary', null, false ); ?>
    262259
    263         <?php if ( current_user_can( 'delete_term', $tag->term_id ) ) : ?>
    264                 <span id="delete-link">
    265                         <a class="delete" href="<?php echo admin_url( wp_nonce_url( "edit-tags.php?action=delete&taxonomy=$taxonomy&tag_ID=$tag->term_id", 'delete-tag_' . $tag->term_id ) ) ?>"><?php _e( 'Delete' ); ?></a>
    266                 </span>
    267         <?php endif; ?>
     260    <?php if ( current_user_can( 'delete_term', $tag->term_id ) ) : ?>
     261        <span id="delete-link">
     262            <a class="delete" href="<?php echo admin_url( wp_nonce_url( "edit-tags.php?action=delete&taxonomy=$taxonomy&tag_ID=$tag->term_id", 'delete-tag_' . $tag->term_id ) ) ?>"><?php _e( 'Delete' ); ?></a>
     263        </span>
     264    <?php endif; ?>
    268265
    269266</div>
    270267
  • src/wp-admin/includes/class-wp-links-list-table.php

    diff --git a/src/wp-admin/includes/class-wp-links-list-table.php b/src/wp-admin/includes/class-wp-links-list-table.php
    index 222d6dfc2d..97fd848e90 100644
    a b  
    1717 */
    1818class WP_Links_List_Table extends WP_List_Table {
    1919
    20         /**
    21         * Constructor.
    22         *
    23         * @since 3.1.0
    24         * @access public
    25         *
    26         * @see WP_List_Table::__construct() for more information on default arguments.
    27         *
    28         * @param array $args An associative array of arguments.
    29         */
    30         public function __construct( $args = array() ) {
    31                 parent::__construct( array(
    32                         'plural' => 'bookmarks',
    33                         'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
    34                 ) );
    35         }
     20    /**
     21    * Constructor.
     22    *
     23    * @since 3.1.0
     24    * @access public
     25    *
     26    * @see WP_List_Table::__construct() for more information on default arguments.
     27    *
     28    * @param array $args An associative array of arguments.
     29    */
     30    public function __construct( $args = array() ) {
     31        parent::__construct( array(
     32            'plural' => 'bookmarks',
     33            'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
     34        ) );
     35    }
    3636
    37         /**
    38         *
    39         * @return bool
    40         */
    41         public function ajax_user_can() {
    42                 return current_user_can( 'manage_links' );
    43         }
     37    /**
     38    *
     39    * @return bool
     40    */
     41    public function ajax_user_can() {
     42        return current_user_can( 'manage_links' );
     43    }
    4444
    45         /**
    46          *
    47          * @global int    $cat_id
    48          * @global string $s
    49          * @global string $orderby
    50          * @global string $order
    51          */
    52         public function prepare_items() {
    53                 global $cat_id, $s, $orderby, $order;
    5445
    55                 wp_reset_vars( array( 'action', 'cat_id', 'link_id', 'orderby', 'order', 's' ) );
     46    public function prepare_items() {
     47        $cat_id = wp_assign_request_var('cat_id');
     48        $s = wp_assign_request_var('s');
     49        $orderby = wp_assign_request_var('orderby');
     50        $order = wp_assign_request_var('order');
    5651
    57                 $args = array( 'hide_invisible' => 0, 'hide_empty' => 0 );
    5852
    59                 if ( 'all' != $cat_id )
    60                         $args['category'] = $cat_id;
    61                 if ( !empty( $s ) )
    62                         $args['search'] = $s;
    63                 if ( !empty( $orderby ) )
    64                         $args['orderby'] = $orderby;
    65                 if ( !empty( $order ) )
    66                         $args['order'] = $order;
     53        $args = array( 'hide_invisible' => 0, 'hide_empty' => 0 );
    6754
    68                 $this->items = get_bookmarks( $args );
    69         }
     55        if ( 'all' != $cat_id ) {
     56            $args['category'] = $cat_id;
     57        }
     58        if ( !empty( $s ) ) {
     59            $args['search'] = $s;
     60        }
     61        if ( !empty( $orderby ) ) {
     62            $args['orderby'] = $orderby;
     63        }
     64        if ( !empty( $order ) ) {
     65            $args['order'] = $order;
     66        }
    7067
    71         /**
    72          * @access public
    73          */
    74         public function no_items() {
    75                 _e( 'No links found.' );
    76         }
     68        $this->items = get_bookmarks( $args );
     69    }
    7770
    78         /**
    79          *
    80          * @return array
    81          */
    82         protected function get_bulk_actions() {
    83                 $actions = array();
    84                 $actions['delete'] = __( 'Delete' );
     71    /**
     72     * @access public
     73     */
     74    public function no_items() {
     75        _e( 'No links found.' );
     76    }
    8577
    86                 return $actions;
    87         }
     78    /**
     79     *
     80     * @return array
     81     */
     82    protected function get_bulk_actions() {
     83        $actions = array();
     84        $actions['delete'] = __( 'Delete' );
    8885
    89         /**
    90          *
    91          * @global int $cat_id
    92          * @param string $which
    93          */
    94         protected function extra_tablenav( $which ) {
    95                 global $cat_id;
     86        return $actions;
     87    }
    9688
    97                 if ( 'top' != $which )
    98                         return;
     89    /**
     90     *
     91     * @global int $cat_id
     92     * @param string $which
     93     */
     94    protected function extra_tablenav( $which ) {
     95        global $cat_id;
     96
     97        if ( 'top' != $which )
     98            return;
    9999?>
    100                 <div class="alignleft actions">
     100        <div class="alignleft actions">
    101101<?php
    102                         $dropdown_options = array(
    103                                 'selected' => $cat_id,
    104                                 'name' => 'cat_id',
    105                                 'taxonomy' => 'link_category',
    106                                 'show_option_all' => get_taxonomy( 'link_category' )->labels->all_items,
    107                                 'hide_empty' => true,
    108                                 'hierarchical' => 1,
    109                                 'show_count' => 0,
    110                                 'orderby' => 'name',
    111                         );
     102            $dropdown_options = array(
     103                'selected' => $cat_id,
     104                'name' => 'cat_id',
     105                'taxonomy' => 'link_category',
     106                'show_option_all' => get_taxonomy( 'link_category' )->labels->all_items,
     107                'hide_empty' => true,
     108                'hierarchical' => 1,
     109                'show_count' => 0,
     110                'orderby' => 'name',
     111            );
    112112
    113                         echo '<label class="screen-reader-text" for="cat_id">' . __( 'Filter by category' ) . '</label>';
    114                         wp_dropdown_categories( $dropdown_options );
    115                         submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
     113            echo '<label class="screen-reader-text" for="cat_id">' . __( 'Filter by category' ) . '</label>';
     114            wp_dropdown_categories( $dropdown_options );
     115            submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
    116116?>
    117                 </div>
     117        </div>
    118118<?php
    119         }
     119    }
    120120
    121         /**
    122         *
    123         * @return array
    124         */
    125         public function get_columns() {
    126                 return array(
    127                         'cb'         => '<input type="checkbox" />',
    128                         'name'       => _x( 'Name', 'link name' ),
    129                         'url'        => __( 'URL' ),
    130                         'categories' => __( 'Categories' ),
    131                         'rel'        => __( 'Relationship' ),
    132                         'visible'    => __( 'Visible' ),
    133                         'rating'     => __( 'Rating' )
    134                 );
    135         }
     121    /**
     122    *
     123    * @return array
     124    */
     125    public function get_columns() {
     126        return array(
     127            'cb'         => '<input type="checkbox" />',
     128            'name'       => _x( 'Name', 'link name' ),
     129            'url'        => __( 'URL' ),
     130            'categories' => __( 'Categories' ),
     131            'rel'        => __( 'Relationship' ),
     132            'visible'    => __( 'Visible' ),
     133            'rating'     => __( 'Rating' )
     134        );
     135    }
    136136
    137         /**
    138         *
    139         * @return array
    140         */
    141         protected function get_sortable_columns() {
    142                 return array(
    143                         'name'    => 'name',
    144                         'url'     => 'url',
    145                         'visible' => 'visible',
    146                         'rating'  => 'rating'
    147                 );
    148         }
     137    /**
     138    *
     139    * @return array
     140    */
     141    protected function get_sortable_columns() {
     142        return array(
     143            'name'    => 'name',
     144            'url'     => 'url',
     145            'visible' => 'visible',
     146            'rating'  => 'rating'
     147        );
     148    }
    149149
    150         /**
    151         * Get the name of the default primary column.
    152         *
    153         * @since 4.3.0
    154         * @access protected
    155         *
    156         * @return string Name of the default primary column, in this case, 'name'.
    157         */
    158         protected function get_default_primary_column_name() {
    159                 return 'name';
    160         }
     150    /**
     151    * Get the name of the default primary column.
     152    *
     153    * @since 4.3.0
     154    * @access protected
     155    *
     156    * @return string Name of the default primary column, in this case, 'name'.
     157    */
     158    protected function get_default_primary_column_name() {
     159        return 'name';
     160    }
    161161
    162         /**
    163         * Handles the checkbox column output.
    164         *
    165         * @since 4.3.0
    166         * @access public
    167         *
    168         * @param object $link The current link object.
    169         */
    170         public function column_cb( $link ) {
    171                 ?>
    172                 <label class="screen-reader-text" for="cb-select-<?php echo $link->link_id; ?>"><?php echo sprintf( __( 'Select %s' ), $link->link_name ); ?></label>
    173                 <input type="checkbox" name="linkcheck[]" id="cb-select-<?php echo $link->link_id; ?>" value="<?php echo esc_attr( $link->link_id ); ?>" />
    174                 <?php
    175         }
     162    /**
     163    * Handles the checkbox column output.
     164    *
     165    * @since 4.3.0
     166    * @access public
     167    *
     168    * @param object $link The current link object.
     169    */
     170    public function column_cb( $link ) {
     171        ?>
     172        <label class="screen-reader-text" for="cb-select-<?php echo $link->link_id; ?>"><?php echo sprintf( __( 'Select %s' ), $link->link_name ); ?></label>
     173        <input type="checkbox" name="linkcheck[]" id="cb-select-<?php echo $link->link_id; ?>" value="<?php echo esc_attr( $link->link_id ); ?>" />
     174        <?php
     175    }
    176176
    177         /**
    178         * Handles the link name column output.
    179         *
    180         * @since 4.3.0
    181         * @access public
    182         *
    183         * @param object $link The current link object.
    184         */
    185         public function column_name( $link ) {
    186                 $edit_link = get_edit_bookmark_link( $link );
    187                 printf( '<strong><a class="row-title" href="%s" aria-label="%s">%s</a></strong>',
    188                         $edit_link,
    189                         /* translators: %s: link name */
    190                         esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $link->link_name ) ),
    191                         $link->link_name
    192                 );
    193         }
     177    /**
     178    * Handles the link name column output.
     179    *
     180    * @since 4.3.0
     181    * @access public
     182    *
     183    * @param object $link The current link object.
     184    */
     185    public function column_name( $link ) {
     186        $edit_link = get_edit_bookmark_link( $link );
     187        printf( '<strong><a class="row-title" href="%s" aria-label="%s">%s</a></strong>',
     188            $edit_link,
     189            /* translators: %s: link name */
     190            esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $link->link_name ) ),
     191            $link->link_name
     192        );
     193    }
    194194
    195         /**
    196         * Handles the link URL column output.
    197         *
    198         * @since 4.3.0
    199         * @access public
    200         *
    201         * @param object $link The current link object.
    202         */
    203         public function column_url( $link ) {
    204                 $short_url = url_shorten( $link->link_url );
    205                 echo "<a href='$link->link_url'>$short_url</a>";
    206         }
     195    /**
     196    * Handles the link URL column output.
     197    *
     198    * @since 4.3.0
     199    * @access public
     200    *
     201    * @param object $link The current link object.
     202    */
     203    public function column_url( $link ) {
     204        $short_url = url_shorten( $link->link_url );
     205        echo "<a href='$link->link_url'>$short_url</a>";
     206    }
    207207
    208         /**
    209         * Handles the link categories column output.
    210         *
    211         * @since 4.3.0
    212         * @access public
    213         *
    214         * @global $cat_id
    215         *
    216         * @param object $link The current link object.
    217         */
    218         public function column_categories( $link ) {
    219                 global $cat_id;
     208    /**
     209    * Handles the link categories column output.
     210    *
     211    * @since 4.3.0
     212    * @access public
     213    *
     214    * @global $cat_id
     215    *
     216    * @param object $link The current link object.
     217    */
     218    public function column_categories( $link ) {
     219        global $cat_id;
    220220
    221                 $cat_names = array();
    222                 foreach ( $link->link_category as $category ) {
    223                         $cat = get_term( $category, 'link_category', OBJECT, 'display' );
    224                         if ( is_wp_error( $cat ) ) {
    225                                 echo $cat->get_error_message();
    226                         }
    227                         $cat_name = $cat->name;
    228                         if ( $cat_id != $category ) {
    229                                 $cat_name = "<a href='link-manager.php?cat_id=$category'>$cat_name</a>";
    230                         }
    231                         $cat_names[] = $cat_name;
    232                 }
    233                 echo implode( ', ', $cat_names );
    234         }
     221        $cat_names = array();
     222        foreach ( $link->link_category as $category ) {
     223            $cat = get_term( $category, 'link_category', OBJECT, 'display' );
     224            if ( is_wp_error( $cat ) ) {
     225                echo $cat->get_error_message();
     226            }
     227            $cat_name = $cat->name;
     228            if ( $cat_id != $category ) {
     229                $cat_name = "<a href='link-manager.php?cat_id=$category'>$cat_name</a>";
     230            }
     231            $cat_names[] = $cat_name;
     232        }
     233        echo implode( ', ', $cat_names );
     234    }
    235235
    236         /**
    237         * Handles the link relation column output.
    238         *
    239         * @since 4.3.0
    240         * @access public
    241         *
    242         * @param object $link The current link object.
    243         */
    244         public function column_rel( $link ) {
    245                 echo empty( $link->link_rel ) ? '<br />' : $link->link_rel;
    246         }
     236    /**
     237    * Handles the link relation column output.
     238    *
     239    * @since 4.3.0
     240    * @access public
     241    *
     242    * @param object $link The current link object.
     243    */
     244    public function column_rel( $link ) {
     245        echo empty( $link->link_rel ) ? '<br />' : $link->link_rel;
     246    }
    247247
    248         /**
    249         * Handles the link visibility column output.
    250         *
    251         * @since 4.3.0
    252         * @access public
    253         *
    254         * @param object $link The current link object.
    255         */
    256         public function column_visible( $link ) {
    257                 if ( 'Y' === $link->link_visible ) {
    258                         _e( 'Yes' );
    259                 } else {
    260                         _e( 'No' );
    261                 }
    262         }
     248    /**
     249    * Handles the link visibility column output.
     250    *
     251    * @since 4.3.0
     252    * @access public
     253    *
     254    * @param object $link The current link object.
     255    */
     256    public function column_visible( $link ) {
     257        if ( 'Y' === $link->link_visible ) {
     258            _e( 'Yes' );
     259        } else {
     260            _e( 'No' );
     261        }
     262    }
    263263
    264         /**
    265         * Handles the link rating column output.
    266         *
    267         * @since 4.3.0
    268         * @access public
    269         *
    270         * @param object $link The current link object.
    271         */
    272         public function column_rating( $link ) {
    273                 echo $link->link_rating;
    274         }
     264    /**
     265    * Handles the link rating column output.
     266    *
     267    * @since 4.3.0
     268    * @access public
     269    *
     270    * @param object $link The current link object.
     271    */
     272    public function column_rating( $link ) {
     273        echo $link->link_rating;
     274    }
    275275
    276         /**
    277         * Handles the default column output.
    278         *
    279         * @since 4.3.0
    280         * @access public
    281         *
    282         * @param object $link        Link object.
    283         * @param string $column_name Current column name.
    284         */
    285         public function column_default( $link, $column_name ) {
    286                 /**
    287                 * Fires for each registered custom link column.
    288                 *
    289                 * @since 2.1.0
    290                 *
    291                 * @param string $column_name Name of the custom column.
    292                 * @param int    $link_id     Link ID.
    293                 */
    294                 do_action( 'manage_link_custom_column', $column_name, $link->link_id );
    295         }
     276    /**
     277    * Handles the default column output.
     278    *
     279    * @since 4.3.0
     280    * @access public
     281    *
     282    * @param object $link        Link object.
     283    * @param string $column_name Current column name.
     284    */
     285    public function column_default( $link, $column_name ) {
     286        /**
     287        * Fires for each registered custom link column.
     288        *
     289        * @since 2.1.0
     290        *
     291        * @param string $column_name Name of the custom column.
     292        * @param int    $link_id     Link ID.
     293        */
     294        do_action( 'manage_link_custom_column', $column_name, $link->link_id );
     295    }
    296296
    297         public function display_rows() {
    298                 foreach ( $this->items as $link ) {
    299                         $link = sanitize_bookmark( $link );
    300                         $link->link_name = esc_attr( $link->link_name );
    301                         $link->link_category = wp_get_link_cats( $link->link_id );
     297    public function display_rows() {
     298        foreach ( $this->items as $link ) {
     299            $link = sanitize_bookmark( $link );
     300            $link->link_name = esc_attr( $link->link_name );
     301            $link->link_category = wp_get_link_cats( $link->link_id );
    302302?>
    303                 <tr id="link-<?php echo $link->link_id; ?>">
    304                         <?php $this->single_row_columns( $link ) ?>
    305                 </tr>
     303        <tr id="link-<?php echo $link->link_id; ?>">
     304            <?php $this->single_row_columns( $link ) ?>
     305        </tr>
    306306<?php
    307                 }
    308         }
     307        }
     308    }
    309309
    310         /**
    311         * Generates and displays row action links.
    312         *
    313         * @since 4.3.0
    314         * @access protected
    315         *
    316         * @param object $link        Link being acted upon.
    317         * @param string $column_name Current column name.
    318         * @param string $primary     Primary column name.
    319         * @return string Row action output for links.
    320         */
    321         protected function handle_row_actions( $link, $column_name, $primary ) {
    322                 if ( $primary !== $column_name ) {
    323                         return '';
    324                 }
     310    /**
     311    * Generates and displays row action links.
     312    *
     313    * @since 4.3.0
     314    * @access protected
     315    *
     316    * @param object $link        Link being acted upon.
     317    * @param string $column_name Current column name.
     318    * @param string $primary     Primary column name.
     319    * @return string Row action output for links.
     320    */
     321    protected function handle_row_actions( $link, $column_name, $primary ) {
     322        if ( $primary !== $column_name ) {
     323            return '';
     324        }
    325325
    326                 $edit_link = get_edit_bookmark_link( $link );
     326        $edit_link = get_edit_bookmark_link( $link );
    327327
    328                 $actions = array();
    329                 $actions['edit'] = '<a href="' . $edit_link . '">' . __('Edit') . '</a>';
    330                 $actions['delete'] = "<a class='submitdelete' href='" . wp_nonce_url("link.php?action=delete&amp;link_id=$link->link_id", 'delete-bookmark_' . $link->link_id) . "' onclick=\"if ( confirm( '" . esc_js(sprintf(__("You are about to delete this link '%s'\n  'Cancel' to stop, 'OK' to delete."), $link->link_name)) . "' ) ) { return true;}return false;\">" . __('Delete') . "</a>";
    331                 return $this->row_actions( $actions );
    332         }
     328        $actions = array();
     329        $actions['edit'] = '<a href="' . $edit_link . '">' . __('Edit') . '</a>';
     330        $actions['delete'] = "<a class='submitdelete' href='" . wp_nonce_url("link.php?action=delete&amp;link_id=$link->link_id", 'delete-bookmark_' . $link->link_id) . "' onclick=\"if ( confirm( '" . esc_js(sprintf(__("You are about to delete this link '%s'\n  'Cancel' to stop, 'OK' to delete."), $link->link_name)) . "' ) ) { return true;}return false;\">" . __('Delete') . "</a>";
     331        return $this->row_actions( $actions );
     332    }
    333333}
  • src/wp-admin/includes/class-wp-ms-themes-list-table.php

    diff --git a/src/wp-admin/includes/class-wp-ms-themes-list-table.php b/src/wp-admin/includes/class-wp-ms-themes-list-table.php
    index 0f3865a985..0104da97a0 100644
    a b  
    1717 */
    1818class WP_MS_Themes_List_Table extends WP_List_Table {
    1919
    20         public $site_id;
    21         public $is_site_themes;
    22 
    23         private $has_items;
    24 
    25         /**
    26          * Constructor.
    27          *
    28          * @since 3.1.0
    29          * @access public
    30          *
    31          * @see WP_List_Table::__construct() for more information on default arguments.
    32          *
    33          * @global string $status
    34          * @global int    $page
    35          *
    36          * @param array $args An associative array of arguments.
    37          */
    38         public function __construct( $args = array() ) {
    39                 global $status, $page;
    40 
    41                 parent::__construct( array(
    42                         'plural' => 'themes',
    43                         'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
    44                 ) );
    45 
    46                 $status = isset( $_REQUEST['theme_status'] ) ? $_REQUEST['theme_status'] : 'all';
    47                 if ( !in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken' ) ) )
    48                         $status = 'all';
    49 
    50                 $page = $this->get_pagenum();
    51 
    52                 $this->is_site_themes = ( 'site-themes-network' === $this->screen->id ) ? true : false;
    53 
    54                 if ( $this->is_site_themes )
    55                         $this->site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0;
    56         }
    57 
    58         /**
    59          *
    60          * @return array
    61          */
    62         protected function get_table_classes() {
    63                 // todo: remove and add CSS for .themes
    64                 return array( 'widefat', 'plugins' );
    65         }
    66 
    67         /**
    68          *
    69          * @return bool
    70          */
    71         public function ajax_user_can() {
    72                 if ( $this->is_site_themes )
    73                         return current_user_can( 'manage_sites' );
    74                 else
    75                         return current_user_can( 'manage_network_themes' );
    76         }
    77 
    78         /**
    79          *
    80          * @global string $status
    81          * @global array $totals
    82          * @global int $page
    83          * @global string $orderby
    84          * @global string $order
    85          * @global string $s
    86          */
    87         public function prepare_items() {
    88                 global $status, $totals, $page, $orderby, $order, $s;
    89 
    90                 wp_reset_vars( array( 'orderby', 'order', 's' ) );
    91 
    92                 $themes = array(
    93                         /**
    94                          * Filters the full array of WP_Theme objects to list in the Multisite
    95                          * themes list table.
    96                          *
    97                          * @since 3.1.0
    98                          *
    99                          * @param array $all An array of WP_Theme objects to display in the list table.
    100                          */
    101                         'all' => apply_filters( 'all_themes', wp_get_themes() ),
    102                         'search' => array(),
    103                         'enabled' => array(),
    104                         'disabled' => array(),
    105                         'upgrade' => array(),
    106                         'broken' => $this->is_site_themes ? array() : wp_get_themes( array( 'errors' => true ) ),
    107                 );
    108 
    109                 if ( $this->is_site_themes ) {
    110                         $themes_per_page = $this->get_items_per_page( 'site_themes_network_per_page' );
    111                         $allowed_where = 'site';
    112                 } else {
    113                         $themes_per_page = $this->get_items_per_page( 'themes_network_per_page' );
    114                         $allowed_where = 'network';
    115                 }
    116 
    117                 $maybe_update = current_user_can( 'update_themes' ) && ! $this->is_site_themes && $current = get_site_transient( 'update_themes' );
    118 
    119                 foreach ( (array) $themes['all'] as $key => $theme ) {
    120                         if ( $this->is_site_themes && $theme->is_allowed( 'network' ) ) {
    121                                 unset( $themes['all'][ $key ] );
    122                                 continue;
    123                         }
    124 
    125                         if ( $maybe_update && isset( $current->response[ $key ] ) ) {
    126                                 $themes['all'][ $key ]->update = true;
    127                                 $themes['upgrade'][ $key ] = $themes['all'][ $key ];
    128                         }
    129 
    130                         $filter = $theme->is_allowed( $allowed_where, $this->site_id ) ? 'enabled' : 'disabled';
    131                         $themes[ $filter ][ $key ] = $themes['all'][ $key ];
    132                 }
    133 
    134                 if ( $s ) {
    135                         $status = 'search';
    136                         $themes['search'] = array_filter( array_merge( $themes['all'], $themes['broken'] ), array( $this, '_search_callback' ) );
    137                 }
    138 
    139                 $totals = array();
    140                 foreach ( $themes as $type => $list )
    141                         $totals[ $type ] = count( $list );
    142 
    143                 if ( empty( $themes[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) )
    144                         $status = 'all';
    145 
    146                 $this->items = $themes[ $status ];
    147                 WP_Theme::sort_by_name( $this->items );
    148 
    149                 $this->has_items = ! empty( $themes['all'] );
    150                 $total_this_page = $totals[ $status ];
    151 
    152                 wp_localize_script( 'updates', '_wpUpdatesItemCounts', array(
    153                         'themes' => $totals,
    154                         'totals' => wp_get_update_data(),
    155                 ) );
    156 
    157                 if ( $orderby ) {
    158                         $orderby = ucfirst( $orderby );
    159                         $order = strtoupper( $order );
    160 
    161                         if ( $orderby === 'Name' ) {
    162                                 if ( 'ASC' === $order ) {
    163                                         $this->items = array_reverse( $this->items );
    164                                 }
    165                         } else {
    166                                 uasort( $this->items, array( $this, '_order_callback' ) );
    167                         }
    168                 }
    169 
    170                 $start = ( $page - 1 ) * $themes_per_page;
    171 
    172                 if ( $total_this_page > $themes_per_page )
    173                         $this->items = array_slice( $this->items, $start, $themes_per_page, true );
    174 
    175                 $this->set_pagination_args( array(
    176                         'total_items' => $total_this_page,
    177                         'per_page' => $themes_per_page,
    178                 ) );
    179         }
    180 
    181         /**
    182          * @staticvar string $term
    183          * @param WP_Theme $theme
    184          * @return bool
    185          */
    186         public function _search_callback( $theme ) {
    187                 static $term = null;
    188                 if ( is_null( $term ) )
    189                         $term = wp_unslash( $_REQUEST['s'] );
    190 
    191                 foreach ( array( 'Name', 'Description', 'Author', 'Author', 'AuthorURI' ) as $field ) {
    192                         // Don't mark up; Do translate.
    193                         if ( false !== stripos( $theme->display( $field, false, true ), $term ) )
    194                                 return true;
    195                 }
    196 
    197                 if ( false !== stripos( $theme->get_stylesheet(), $term ) )
    198                         return true;
    199 
    200                 if ( false !== stripos( $theme->get_template(), $term ) )
    201                         return true;
    202 
    203                 return false;
    204         }
    205 
    206         // Not used by any core columns.
    207         /**
    208          * @global string $orderby
    209          * @global string $order
    210          * @param array $theme_a
    211          * @param array $theme_b
    212          * @return int
    213          */
    214         public function _order_callback( $theme_a, $theme_b ) {
    215                 global $orderby, $order;
    216 
    217                 $a = $theme_a[ $orderby ];
    218                 $b = $theme_b[ $orderby ];
    219 
    220                 if ( $a == $b )
    221                         return 0;
    222 
    223                 if ( 'DESC' === $order )
    224                         return ( $a < $b ) ? 1 : -1;
    225                 else
    226                         return ( $a < $b ) ? -1 : 1;
    227         }
    228 
    229         /**
    230          * @access public
    231          */
    232         public function no_items() {
    233                 if ( $this->has_items ) {
    234                         _e( 'No themes found.' );
    235                 } else {
    236                         _e( 'You do not appear to have any themes available at this time.' );
    237                 }
    238         }
    239 
    240         /**
    241          *
    242          * @return array
    243          */
    244         public function get_columns() {
    245                 return array(
    246                         'cb'          => '<input type="checkbox" />',
    247                         'name'        => __( 'Theme' ),
    248                         'description' => __( 'Description' ),
    249                 );
    250         }
    251 
    252         /**
    253          *
    254          * @return array
    255          */
    256         protected function get_sortable_columns() {
    257                 return array(
    258                         'name'         => 'name',
    259                 );
    260         }
    261 
    262         /**
    263          * Gets the name of the primary column.
    264          *
    265          * @since 4.3.0
    266          * @access protected
    267          *
    268          * @return string Unalterable name of the primary column name, in this case, 'name'.
    269          */
    270         protected function get_primary_column_name() {
    271                 return 'name';
    272         }
    273 
    274         /**
    275          *
    276          * @global array $totals
    277          * @global string $status
    278          * @return array
    279          */
    280         protected function get_views() {
    281                 global $totals, $status;
    282 
    283                 $status_links = array();
    284                 foreach ( $totals as $type => $count ) {
    285                         if ( !$count )
    286                                 continue;
    287 
    288                         switch ( $type ) {
    289                                 case 'all':
    290                                         $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'themes' );
    291                                         break;
    292                                 case 'enabled':
    293                                         $text = _n( 'Enabled <span class="count">(%s)</span>', 'Enabled <span class="count">(%s)</span>', $count );
    294                                         break;
    295                                 case 'disabled':
    296                                         $text = _n( 'Disabled <span class="count">(%s)</span>', 'Disabled <span class="count">(%s)</span>', $count );
    297                                         break;
    298                                 case 'upgrade':
    299                                         $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count );
    300                                         break;
    301                                 case 'broken' :
    302                                         $text = _n( 'Broken <span class="count">(%s)</span>', 'Broken <span class="count">(%s)</span>', $count );
    303                                         break;
    304                         }
    305 
    306                         if ( $this->is_site_themes )
    307                                 $url = 'site-themes.php?id=' . $this->site_id;
    308                         else
    309                                 $url = 'themes.php';
    310 
    311                         if ( 'search' != $type ) {
    312                                 $status_links[$type] = sprintf( "<a href='%s' %s>%s</a>",
    313                                         esc_url( add_query_arg('theme_status', $type, $url) ),
    314                                         ( $type === $status ) ? ' class="current"' : '',
    315                                         sprintf( $text, number_format_i18n( $count ) )
    316                                 );
    317                         }
    318                 }
    319 
    320                 return $status_links;
    321         }
    322 
    323         /**
    324          * @global string $status
    325          *
    326          * @return array
    327          */
    328         protected function get_bulk_actions() {
    329                 global $status;
    330 
    331                 $actions = array();
    332                 if ( 'enabled' != $status )
    333                         $actions['enable-selected'] = $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' );
    334                 if ( 'disabled' != $status )
    335                         $actions['disable-selected'] = $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' );
    336                 if ( ! $this->is_site_themes ) {
    337                         if ( current_user_can( 'update_themes' ) )
    338                                 $actions['update-selected'] = __( 'Update' );
    339                         if ( current_user_can( 'delete_themes' ) )
    340                                 $actions['delete-selected'] = __( 'Delete' );
    341                 }
    342                 return $actions;
    343         }
    344 
    345         /**
    346          * @access public
    347          */
    348         public function display_rows() {
    349                 foreach ( $this->items as $theme )
    350                         $this->single_row( $theme );
    351         }
    352 
    353         /**
    354          * Handles the checkbox column output.
    355          *
    356          * @since 4.3.0
    357          * @access public
    358          *
    359          * @param WP_Theme $theme The current WP_Theme object.
    360          */
    361         public function column_cb( $theme ) {
    362                 $checkbox_id = 'checkbox_' . md5( $theme->get('Name') );
    363                 ?>
    364                 <input type="checkbox" name="checked[]" value="<?php echo esc_attr( $theme->get_stylesheet() ) ?>" id="<?php echo $checkbox_id ?>" />
    365                 <label class="screen-reader-text" for="<?php echo $checkbox_id ?>" ><?php _e( 'Select' ) ?>  <?php echo $theme->display( 'Name' ) ?></label>
    366                 <?php
    367         }
    368 
    369         /**
    370          * Handles the name column output.
    371          *
    372          * @since 4.3.0
    373          * @access public
    374          *
    375          * @global string $status
    376          * @global int    $page
    377          * @global string $s
    378          *
    379          * @param WP_Theme $theme The current WP_Theme object.
    380          */
    381         public function column_name( $theme ) {
    382                 global $status, $page, $s;
    383 
    384                 $context = $status;
    385 
    386                 if ( $this->is_site_themes ) {
    387                         $url = "site-themes.php?id={$this->site_id}&amp;";
    388                         $allowed = $theme->is_allowed( 'site', $this->site_id );
    389                 } else {
    390                         $url = 'themes.php?';
    391                         $allowed = $theme->is_allowed( 'network' );
    392                 }
    393 
    394                 // Pre-order.
    395                 $actions = array(
    396                         'enable' => '',
    397                         'disable' => '',
    398                         'edit' => '',
    399                         'delete' => ''
    400                 );
    401 
    402                 $stylesheet = $theme->get_stylesheet();
    403                 $theme_key = urlencode( $stylesheet );
    404 
    405                 if ( ! $allowed ) {
    406                         if ( ! $theme->errors() ) {
    407                                 $url = add_query_arg( array(
    408                                         'action' => 'enable',
    409                                         'theme'  => $theme_key,
    410                                         'paged'  => $page,
    411                                         's'      => $s,
    412                                 ), $url );
    413 
    414                                 if ( $this->is_site_themes ) {
    415                                         /* translators: %s: theme name */
    416                                         $aria_label = sprintf( __( 'Enable %s' ), $theme->display( 'Name' ) );
    417                                 } else {
    418                                         /* translators: %s: theme name */
    419                                         $aria_label = sprintf( __( 'Network Enable %s' ), $theme->display( 'Name' ) );
    420                                 }
    421 
    422                                 $actions['enable'] = sprintf( '<a href="%s" class="edit" aria-label="%s">%s</a>',
    423                                         esc_url( wp_nonce_url( $url, 'enable-theme_' . $stylesheet ) ),
    424                                         esc_attr( $aria_label ),
    425                                         ( $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' ) )
    426                                 );
    427                         }
    428                 } else {
    429                         $url = add_query_arg( array(
    430                                 'action' => 'disable',
    431                                 'theme'  => $theme_key,
    432                                 'paged'  => $page,
    433                                 's'      => $s,
    434                         ), $url );
    435 
    436                         if ( $this->is_site_themes ) {
    437                                 /* translators: %s: theme name */
    438                                 $aria_label = sprintf( __( 'Disable %s' ), $theme->display( 'Name' ) );
    439                         } else {
    440                                 /* translators: %s: theme name */
    441                                 $aria_label = sprintf( __( 'Network Disable %s' ), $theme->display( 'Name' ) );
    442                         }
    443 
    444                         $actions['disable'] = sprintf( '<a href="%s" aria-label="%s">%s</a>',
    445                                 esc_url( wp_nonce_url( $url, 'disable-theme_' . $stylesheet ) ),
    446                                 esc_attr( $aria_label ),
    447                                 ( $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' ) )
    448                         );
    449                 }
    450 
    451                 if ( current_user_can('edit_themes') ) {
    452                         $url = add_query_arg( array(
    453                                 'theme' => $theme_key,
    454                         ), 'theme-editor.php' );
    455 
    456                         /* translators: %s: theme name */
    457                         $aria_label = sprintf( __( 'Open %s in the Theme Editor' ), $theme->display( 'Name' ) );
    458 
    459                         $actions['edit'] = sprintf( '<a href="%s" class="edit" aria-label="%s">%s</a>',
    460                                 esc_url( $url ),
    461                                 esc_attr( $aria_label ),
    462                                 __( 'Edit' )
    463                         );
    464                 }
    465 
    466                 if ( ! $allowed && current_user_can( 'delete_themes' ) && ! $this->is_site_themes && $stylesheet != get_option( 'stylesheet' ) && $stylesheet != get_option( 'template' ) ) {
    467                         $url = add_query_arg( array(
    468                                 'action'       => 'delete-selected',
    469                                 'checked[]'    => $theme_key,
    470                                 'theme_status' => $context,
    471                                 'paged'        => $page,
    472                                 's'            => $s,
    473                         ), 'themes.php' );
    474 
    475                         /* translators: %s: theme name */
    476                         $aria_label = sprintf( _x( 'Delete %s', 'theme' ), $theme->display( 'Name' ) );
    477 
    478                         $actions['delete'] = sprintf( '<a href="%s" class="delete" aria-label="%s">%s</a>',
    479                                 esc_url( wp_nonce_url( $url, 'bulk-themes' ) ),
    480                                 esc_attr( $aria_label ),
    481                                 __( 'Delete' )
    482                         );
    483                 }
    484                 /**
    485                  * Filters the action links displayed for each theme in the Multisite
    486                  * themes list table.
    487                  *
    488                  * The action links displayed are determined by the theme's status, and
    489                  * which Multisite themes list table is being displayed - the Network
    490                  * themes list table (themes.php), which displays all installed themes,
    491                  * or the Site themes list table (site-themes.php), which displays the
    492                  * non-network enabled themes when editing a site in the Network admin.
    493                  *
    494                  * The default action links for the Network themes list table include
    495                  * 'Network Enable', 'Network Disable', 'Edit', and 'Delete'.
    496                  *
    497                  * The default action links for the Site themes list table include
    498                  * 'Enable', 'Disable', and 'Edit'.
    499                  *
    500                  * @since 2.8.0
    501                  *
    502                  * @param array    $actions An array of action links.
    503                  * @param WP_Theme $theme   The current WP_Theme object.
    504                  * @param string   $context Status of the theme.
    505                  */
    506                 $actions = apply_filters( 'theme_action_links', array_filter( $actions ), $theme, $context );
    507 
    508                 /**
    509                  * Filters the action links of a specific theme in the Multisite themes
    510                  * list table.
    511                  *
    512                  * The dynamic portion of the hook name, `$stylesheet`, refers to the
    513                  * directory name of the theme, which in most cases is synonymous
    514                  * with the template name.
    515                  *
    516                  * @since 3.1.0
    517                  *
    518                  * @param array    $actions An array of action links.
    519                  * @param WP_Theme $theme   The current WP_Theme object.
    520                  * @param string   $context Status of the theme.
    521                  */
    522                 $actions = apply_filters( "theme_action_links_{$stylesheet}", $actions, $theme, $context );
    523 
    524                 echo $this->row_actions( $actions, true );
    525         }
    526 
    527         /**
    528          * Handles the description column output.
    529          *
    530          * @since 4.3.0
    531          * @access public
    532          *
    533          * @global string $status
    534          * @global array  $totals
    535          *
    536          * @param WP_Theme $theme The current WP_Theme object.
    537          */
    538         public function column_description( $theme ) {
    539                 global $status, $totals;
    540                 if ( $theme->errors() ) {
    541                         $pre = $status === 'broken' ? __( 'Broken Theme:' ) . ' ' : '';
    542                         echo '<p><strong class="error-message">' . $pre . $theme->errors()->get_error_message() . '</strong></p>';
    543                 }
    544 
    545                 if ( $this->is_site_themes ) {
    546                         $allowed = $theme->is_allowed( 'site', $this->site_id );
    547                 } else {
    548                         $allowed = $theme->is_allowed( 'network' );
    549                 }
    550 
    551                 $class = ! $allowed ? 'inactive' : 'active';
    552                 if ( ! empty( $totals['upgrade'] ) && ! empty( $theme->update ) )
    553                         $class .= ' update';
    554 
    555                 echo "<div class='theme-description'><p>" . $theme->display( 'Description' ) . "</p></div>
    556                         <div class='$class second theme-version-author-uri'>";
    557 
    558                 $stylesheet = $theme->get_stylesheet();
    559                 $theme_meta = array();
    560 
    561                 if ( $theme->get('Version') ) {
    562                         $theme_meta[] = sprintf( __( 'Version %s' ), $theme->display('Version') );
    563                 }
    564                 $theme_meta[] = sprintf( __( 'By %s' ), $theme->display('Author') );
    565 
    566                 if ( $theme->get('ThemeURI') ) {
    567                         /* translators: %s: theme name */
    568                         $aria_label = sprintf( __( 'Visit %s homepage' ), $theme->display( 'Name' ) );
    569 
    570                         $theme_meta[] = sprintf( '<a href="%s" aria-label="%s">%s</a>',
    571                                 $theme->display( 'ThemeURI' ),
    572                                 esc_attr( $aria_label ),
    573                                 __( 'Visit Theme Site' )
    574                         );
    575                 }
    576                 /**
    577                  * Filters the array of row meta for each theme in the Multisite themes
    578                  * list table.
    579                  *
    580                  * @since 3.1.0
    581                  *
    582                  * @param array    $theme_meta An array of the theme's metadata,
    583                  *                             including the version, author, and
    584                  *                             theme URI.
    585                  * @param string   $stylesheet Directory name of the theme.
    586                  * @param WP_Theme $theme      WP_Theme object.
    587                  * @param string   $status     Status of the theme.
    588                  */
    589                 $theme_meta = apply_filters( 'theme_row_meta', $theme_meta, $stylesheet, $theme, $status );
    590                 echo implode( ' | ', $theme_meta );
    591 
    592                 echo '</div>';
    593         }
    594 
    595         /**
    596          * Handles default column output.
    597          *
    598          * @since 4.3.0
    599          * @access public
    600          *
    601          * @param WP_Theme $theme       The current WP_Theme object.
    602          * @param string   $column_name The current column name.
    603          */
    604         public function column_default( $theme, $column_name ) {
    605                 $stylesheet = $theme->get_stylesheet();
    606 
    607                 /**
    608                  * Fires inside each custom column of the Multisite themes list table.
    609                  *
    610                  * @since 3.1.0
    611                  *
    612                  * @param string   $column_name Name of the column.
    613                  * @param string   $stylesheet  Directory name of the theme.
    614                  * @param WP_Theme $theme       Current WP_Theme object.
    615                  */
    616                 do_action( 'manage_themes_custom_column', $column_name, $stylesheet, $theme );
    617         }
    618 
    619         /**
    620          * Handles the output for a single table row.
    621          *
    622          * @since 4.3.0
    623          * @access public
    624          *
    625          * @param WP_Theme $item The current WP_Theme object.
    626          */
    627         public function single_row_columns( $item ) {
    628                 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
    629 
    630                 foreach ( $columns as $column_name => $column_display_name ) {
    631                         $extra_classes = '';
    632                         if ( in_array( $column_name, $hidden ) ) {
    633                                 $extra_classes .= ' hidden';
    634                         }
    635 
    636                         switch ( $column_name ) {
    637                                 case 'cb':
    638                                         echo '<th scope="row" class="check-column">';
    639 
    640                                         $this->column_cb( $item );
    641 
    642                                         echo '</th>';
    643                                         break;
    644 
    645                                 case 'name':
    646                                         echo "<td class='theme-title column-primary{$extra_classes}'><strong>" . $item->display('Name') . "</strong>";
    647 
    648                                         $this->column_name( $item );
    649 
    650                                         echo "</td>";
    651                                         break;
    652 
    653                                 case 'description':
    654                                         echo "<td class='column-description desc{$extra_classes}'>";
    655 
    656                                         $this->column_description( $item );
    657 
    658                                         echo '</td>';
    659                                         break;
    660 
    661                                 default:
    662                                         echo "<td class='$column_name column-$column_name{$extra_classes}'>";
    663 
    664                                         $this->column_default( $item, $column_name );
    665 
    666                                         echo "</td>";
    667                                         break;
    668                         }
    669                 }
    670         }
    671 
    672         /**
    673          * @global string $status
    674          * @global array  $totals
    675          *
    676          * @param WP_Theme $theme
    677          */
    678         public function single_row( $theme ) {
    679                 global $status, $totals;
    680 
    681                 if ( $this->is_site_themes ) {
    682                         $allowed = $theme->is_allowed( 'site', $this->site_id );
    683                 } else {
    684                         $allowed = $theme->is_allowed( 'network' );
    685                 }
    686 
    687                 $stylesheet = $theme->get_stylesheet();
    688 
    689                 $class = ! $allowed ? 'inactive' : 'active';
    690                 if ( ! empty( $totals['upgrade'] ) && ! empty( $theme->update ) ) {
    691                         $class .= ' update';
    692                 }
    693 
    694                 printf( '<tr class="%s" data-slug="%s">',
    695                         esc_attr( $class ),
    696                         esc_attr( $stylesheet )
    697                 );
    698 
    699                 $this->single_row_columns( $theme );
    700 
    701                 echo "</tr>";
    702 
    703                 if ( $this->is_site_themes )
    704                         remove_action( "after_theme_row_$stylesheet", 'wp_theme_update_row' );
    705 
    706                 /**
    707                  * Fires after each row in the Multisite themes list table.
    708                  *
    709                  * @since 3.1.0
    710                  *
    711                  * @param string   $stylesheet Directory name of the theme.
    712                  * @param WP_Theme $theme      Current WP_Theme object.
    713                  * @param string   $status     Status of the theme.
    714                  */
    715                 do_action( 'after_theme_row', $stylesheet, $theme, $status );
    716 
    717                 /**
    718                  * Fires after each specific row in the Multisite themes list table.
    719                  *
    720                  * The dynamic portion of the hook name, `$stylesheet`, refers to the
    721                  * directory name of the theme, most often synonymous with the template
    722                  * name of the theme.
    723                  *
    724                  * @since 3.5.0
    725                  *
    726                  * @param string   $stylesheet Directory name of the theme.
    727                  * @param WP_Theme $theme      Current WP_Theme object.
    728                  * @param string   $status     Status of the theme.
    729                  */
    730                 do_action( "after_theme_row_{$stylesheet}", $stylesheet, $theme, $status );
    731         }
     20    public $site_id;
     21    public $is_site_themes;
     22
     23    private $has_items;
     24
     25    /**
     26     * Constructor.
     27     *
     28     * @since 3.1.0
     29     * @access public
     30     *
     31     * @see WP_List_Table::__construct() for more information on default arguments.
     32     *
     33     * @global string $status
     34     * @global int    $page
     35     *
     36     * @param array $args An associative array of arguments.
     37     */
     38    public function __construct( $args = array() ) {
     39        global $status, $page;
     40
     41        parent::__construct( array(
     42            'plural' => 'themes',
     43            'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
     44        ) );
     45
     46        $status = isset( $_REQUEST['theme_status'] ) ? $_REQUEST['theme_status'] : 'all';
     47        if ( !in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken' ) ) )
     48            $status = 'all';
     49
     50        $page = $this->get_pagenum();
     51
     52        $this->is_site_themes = ( 'site-themes-network' === $this->screen->id ) ? true : false;
     53
     54        if ( $this->is_site_themes )
     55            $this->site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0;
     56    }
     57
     58    /**
     59     *
     60     * @return array
     61     */
     62    protected function get_table_classes() {
     63        // todo: remove and add CSS for .themes
     64        return array( 'widefat', 'plugins' );
     65    }
     66
     67    /**
     68     *
     69     * @return bool
     70     */
     71    public function ajax_user_can() {
     72        if ( $this->is_site_themes )
     73            return current_user_can( 'manage_sites' );
     74        else
     75            return current_user_can( 'manage_network_themes' );
     76    }
     77
     78    public function prepare_items() {
     79        $status = wp_assign_request_var('status');
     80        $total = wp_assign_request_var('total');
     81        $page = wp_assign_request_var('page');
     82        $orderby = wp_assign_request_var('orderby');
     83        $order = wp_assign_request_var('order');
     84        $s = wp_assign_request_var('s');
     85
     86        $themes = array(
     87            /**
     88             * Filters the full array of WP_Theme objects to list in the Multisite
     89             * themes list table.
     90             *
     91             * @since 3.1.0
     92             *
     93             * @param array $all An array of WP_Theme objects to display in the list table.
     94             */
     95            'all' => apply_filters( 'all_themes', wp_get_themes() ),
     96            'search' => array(),
     97            'enabled' => array(),
     98            'disabled' => array(),
     99            'upgrade' => array(),
     100            'broken' => $this->is_site_themes ? array() : wp_get_themes( array( 'errors' => true ) ),
     101        );
     102
     103        if ( $this->is_site_themes ) {
     104            $themes_per_page = $this->get_items_per_page( 'site_themes_network_per_page' );
     105            $allowed_where = 'site';
     106        } else {
     107            $themes_per_page = $this->get_items_per_page( 'themes_network_per_page' );
     108            $allowed_where = 'network';
     109        }
     110
     111        $maybe_update = current_user_can( 'update_themes' ) && ! $this->is_site_themes && $current = get_site_transient( 'update_themes' );
     112
     113        foreach ( (array) $themes['all'] as $key => $theme ) {
     114            if ( $this->is_site_themes && $theme->is_allowed( 'network' ) ) {
     115                unset( $themes['all'][ $key ] );
     116                continue;
     117            }
     118
     119            if ( $maybe_update && isset( $current->response[ $key ] ) ) {
     120                $themes['all'][ $key ]->update = true;
     121                $themes['upgrade'][ $key ] = $themes['all'][ $key ];
     122            }
     123
     124            $filter = $theme->is_allowed( $allowed_where, $this->site_id ) ? 'enabled' : 'disabled';
     125            $themes[ $filter ][ $key ] = $themes['all'][ $key ];
     126        }
     127
     128        if ( $s ) {
     129            $status = 'search';
     130            $themes['search'] = array_filter( array_merge( $themes['all'], $themes['broken'] ), array( $this, '_search_callback' ) );
     131        }
     132
     133        $totals = array();
     134        foreach ( $themes as $type => $list )
     135            $totals[ $type ] = count( $list );
     136
     137        if ( empty( $themes[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) )
     138            $status = 'all';
     139
     140        $this->items = $themes[ $status ];
     141        WP_Theme::sort_by_name( $this->items );
     142
     143        $this->has_items = ! empty( $themes['all'] );
     144        $total_this_page = $totals[ $status ];
     145
     146        wp_localize_script( 'updates', '_wpUpdatesItemCounts', array(
     147            'themes' => $totals,
     148            'totals' => wp_get_update_data(),
     149        ) );
     150
     151        if ( $orderby ) {
     152            $orderby = ucfirst( $orderby );
     153            $order = strtoupper( $order );
     154
     155            if ( $orderby === 'Name' ) {
     156                if ( 'ASC' === $order ) {
     157                    $this->items = array_reverse( $this->items );
     158                }
     159            } else {
     160                uasort( $this->items, array( $this, '_order_callback' ) );
     161            }
     162        }
     163
     164        $start = ( $page - 1 ) * $themes_per_page;
     165
     166        if ( $total_this_page > $themes_per_page )
     167            $this->items = array_slice( $this->items, $start, $themes_per_page, true );
     168
     169        $this->set_pagination_args( array(
     170            'total_items' => $total_this_page,
     171            'per_page' => $themes_per_page,
     172        ) );
     173    }
     174
     175    /**
     176     * @staticvar string $term
     177     * @param WP_Theme $theme
     178     * @return bool
     179     */
     180    public function _search_callback( $theme ) {
     181        static $term = null;
     182        if ( is_null( $term ) )
     183            $term = wp_unslash( $_REQUEST['s'] );
     184
     185        foreach ( array( 'Name', 'Description', 'Author', 'Author', 'AuthorURI' ) as $field ) {
     186            // Don't mark up; Do translate.
     187            if ( false !== stripos( $theme->display( $field, false, true ), $term ) )
     188                return true;
     189        }
     190
     191        if ( false !== stripos( $theme->get_stylesheet(), $term ) )
     192            return true;
     193
     194        if ( false !== stripos( $theme->get_template(), $term ) )
     195            return true;
     196
     197        return false;
     198    }
     199
     200    // Not used by any core columns.
     201    /**
     202     * @global string $orderby
     203     * @global string $order
     204     * @param array $theme_a
     205     * @param array $theme_b
     206     * @return int
     207     */
     208    public function _order_callback( $theme_a, $theme_b ) {
     209        global $orderby, $order;
     210
     211        $a = $theme_a[ $orderby ];
     212        $b = $theme_b[ $orderby ];
     213
     214        if ( $a == $b )
     215            return 0;
     216
     217        if ( 'DESC' === $order )
     218            return ( $a < $b ) ? 1 : -1;
     219        else
     220            return ( $a < $b ) ? -1 : 1;
     221    }
     222
     223    /**
     224     * @access public
     225     */
     226    public function no_items() {
     227        if ( $this->has_items ) {
     228            _e( 'No themes found.' );
     229        } else {
     230            _e( 'You do not appear to have any themes available at this time.' );
     231        }
     232    }
     233
     234    /**
     235     *
     236     * @return array
     237     */
     238    public function get_columns() {
     239        return array(
     240            'cb'          => '<input type="checkbox" />',
     241            'name'        => __( 'Theme' ),
     242            'description' => __( 'Description' ),
     243        );
     244    }
     245
     246    /**
     247     *
     248     * @return array
     249     */
     250    protected function get_sortable_columns() {
     251        return array(
     252            'name'         => 'name',
     253        );
     254    }
     255
     256    /**
     257     * Gets the name of the primary column.
     258     *
     259     * @since 4.3.0
     260     * @access protected
     261     *
     262     * @return string Unalterable name of the primary column name, in this case, 'name'.
     263     */
     264    protected function get_primary_column_name() {
     265        return 'name';
     266    }
     267
     268    /**
     269     *
     270     * @global array $totals
     271     * @global string $status
     272     * @return array
     273     */
     274    protected function get_views() {
     275        global $totals, $status;
     276
     277        $status_links = array();
     278        foreach ( $totals as $type => $count ) {
     279            if ( !$count )
     280                continue;
     281
     282            switch ( $type ) {
     283                case 'all':
     284                    $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'themes' );
     285                    break;
     286                case 'enabled':
     287                    $text = _n( 'Enabled <span class="count">(%s)</span>', 'Enabled <span class="count">(%s)</span>', $count );
     288                    break;
     289                case 'disabled':
     290                    $text = _n( 'Disabled <span class="count">(%s)</span>', 'Disabled <span class="count">(%s)</span>', $count );
     291                    break;
     292                case 'upgrade':
     293                    $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count );
     294                    break;
     295                case 'broken' :
     296                    $text = _n( 'Broken <span class="count">(%s)</span>', 'Broken <span class="count">(%s)</span>', $count );
     297                    break;
     298            }
     299
     300            if ( $this->is_site_themes )
     301                $url = 'site-themes.php?id=' . $this->site_id;
     302            else
     303                $url = 'themes.php';
     304
     305            if ( 'search' != $type ) {
     306                $status_links[$type] = sprintf( "<a href='%s' %s>%s</a>",
     307                    esc_url( add_query_arg('theme_status', $type, $url) ),
     308                    ( $type === $status ) ? ' class="current"' : '',
     309                    sprintf( $text, number_format_i18n( $count ) )
     310                );
     311            }
     312        }
     313
     314        return $status_links;
     315    }
     316
     317    /**
     318     * @global string $status
     319     *
     320     * @return array
     321     */
     322    protected function get_bulk_actions() {
     323        global $status;
     324
     325        $actions = array();
     326        if ( 'enabled' != $status )
     327            $actions['enable-selected'] = $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' );
     328        if ( 'disabled' != $status )
     329            $actions['disable-selected'] = $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' );
     330        if ( ! $this->is_site_themes ) {
     331            if ( current_user_can( 'update_themes' ) )
     332                $actions['update-selected'] = __( 'Update' );
     333            if ( current_user_can( 'delete_themes' ) )
     334                $actions['delete-selected'] = __( 'Delete' );
     335        }
     336        return $actions;
     337    }
     338
     339    /**
     340     * @access public
     341     */
     342    public function display_rows() {
     343        foreach ( $this->items as $theme )
     344            $this->single_row( $theme );
     345    }
     346
     347    /**
     348     * Handles the checkbox column output.
     349     *
     350     * @since 4.3.0
     351     * @access public
     352     *
     353     * @param WP_Theme $theme The current WP_Theme object.
     354     */
     355    public function column_cb( $theme ) {
     356        $checkbox_id = 'checkbox_' . md5( $theme->get('Name') );
     357        ?>
     358        <input type="checkbox" name="checked[]" value="<?php echo esc_attr( $theme->get_stylesheet() ) ?>" id="<?php echo $checkbox_id ?>" />
     359        <label class="screen-reader-text" for="<?php echo $checkbox_id ?>" ><?php _e( 'Select' ) ?>  <?php echo $theme->display( 'Name' ) ?></label>
     360        <?php
     361    }
     362
     363    /**
     364     * Handles the name column output.
     365     *
     366     * @since 4.3.0
     367     * @access public
     368     *
     369     * @global string $status
     370     * @global int    $page
     371     * @global string $s
     372     *
     373     * @param WP_Theme $theme The current WP_Theme object.
     374     */
     375    public function column_name( $theme ) {
     376        global $status, $page, $s;
     377
     378        $context = $status;
     379
     380        if ( $this->is_site_themes ) {
     381            $url = "site-themes.php?id={$this->site_id}&amp;";
     382            $allowed = $theme->is_allowed( 'site', $this->site_id );
     383        } else {
     384            $url = 'themes.php?';
     385            $allowed = $theme->is_allowed( 'network' );
     386        }
     387
     388        // Pre-order.
     389        $actions = array(
     390            'enable' => '',
     391            'disable' => '',
     392            'edit' => '',
     393            'delete' => ''
     394        );
     395
     396        $stylesheet = $theme->get_stylesheet();
     397        $theme_key = urlencode( $stylesheet );
     398
     399        if ( ! $allowed ) {
     400            if ( ! $theme->errors() ) {
     401                $url = add_query_arg( array(
     402                    'action' => 'enable',
     403                    'theme'  => $theme_key,
     404                    'paged'  => $page,
     405                    's'      => $s,
     406                ), $url );
     407
     408                if ( $this->is_site_themes ) {
     409                    /* translators: %s: theme name */
     410                    $aria_label = sprintf( __( 'Enable %s' ), $theme->display( 'Name' ) );
     411                } else {
     412                    /* translators: %s: theme name */
     413                    $aria_label = sprintf( __( 'Network Enable %s' ), $theme->display( 'Name' ) );
     414                }
     415
     416                $actions['enable'] = sprintf( '<a href="%s" class="edit" aria-label="%s">%s</a>',
     417                    esc_url( wp_nonce_url( $url, 'enable-theme_' . $stylesheet ) ),
     418                    esc_attr( $aria_label ),
     419                    ( $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' ) )
     420                );
     421            }
     422        } else {
     423            $url = add_query_arg( array(
     424                'action' => 'disable',
     425                'theme'  => $theme_key,
     426                'paged'  => $page,
     427                's'      => $s,
     428            ), $url );
     429
     430            if ( $this->is_site_themes ) {
     431                /* translators: %s: theme name */
     432                $aria_label = sprintf( __( 'Disable %s' ), $theme->display( 'Name' ) );
     433            } else {
     434                /* translators: %s: theme name */
     435                $aria_label = sprintf( __( 'Network Disable %s' ), $theme->display( 'Name' ) );
     436            }
     437
     438            $actions['disable'] = sprintf( '<a href="%s" aria-label="%s">%s</a>',
     439                esc_url( wp_nonce_url( $url, 'disable-theme_' . $stylesheet ) ),
     440                esc_attr( $aria_label ),
     441                ( $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' ) )
     442            );
     443        }
     444
     445        if ( current_user_can('edit_themes') ) {
     446            $url = add_query_arg( array(
     447                'theme' => $theme_key,
     448            ), 'theme-editor.php' );
     449
     450            /* translators: %s: theme name */
     451            $aria_label = sprintf( __( 'Open %s in the Theme Editor' ), $theme->display( 'Name' ) );
     452
     453            $actions['edit'] = sprintf( '<a href="%s" class="edit" aria-label="%s">%s</a>',
     454                esc_url( $url ),
     455                esc_attr( $aria_label ),
     456                __( 'Edit' )
     457            );
     458        }
     459
     460        if ( ! $allowed && current_user_can( 'delete_themes' ) && ! $this->is_site_themes && $stylesheet != get_option( 'stylesheet' ) && $stylesheet != get_option( 'template' ) ) {
     461            $url = add_query_arg( array(
     462                'action'       => 'delete-selected',
     463                'checked[]'    => $theme_key,
     464                'theme_status' => $context,
     465                'paged'        => $page,
     466                's'            => $s,
     467            ), 'themes.php' );
     468
     469            /* translators: %s: theme name */
     470            $aria_label = sprintf( _x( 'Delete %s', 'theme' ), $theme->display( 'Name' ) );
     471
     472            $actions['delete'] = sprintf( '<a href="%s" class="delete" aria-label="%s">%s</a>',
     473                esc_url( wp_nonce_url( $url, 'bulk-themes' ) ),
     474                esc_attr( $aria_label ),
     475                __( 'Delete' )
     476            );
     477        }
     478        /**
     479         * Filters the action links displayed for each theme in the Multisite
     480         * themes list table.
     481         *
     482         * The action links displayed are determined by the theme's status, and
     483         * which Multisite themes list table is being displayed - the Network
     484         * themes list table (themes.php), which displays all installed themes,
     485         * or the Site themes list table (site-themes.php), which displays the
     486         * non-network enabled themes when editing a site in the Network admin.
     487         *
     488         * The default action links for the Network themes list table include
     489         * 'Network Enable', 'Network Disable', 'Edit', and 'Delete'.
     490         *
     491         * The default action links for the Site themes list table include
     492         * 'Enable', 'Disable', and 'Edit'.
     493         *
     494         * @since 2.8.0
     495         *
     496         * @param array    $actions An array of action links.
     497         * @param WP_Theme $theme   The current WP_Theme object.
     498         * @param string   $context Status of the theme.
     499         */
     500        $actions = apply_filters( 'theme_action_links', array_filter( $actions ), $theme, $context );
     501
     502        /**
     503         * Filters the action links of a specific theme in the Multisite themes
     504         * list table.
     505         *
     506         * The dynamic portion of the hook name, `$stylesheet`, refers to the
     507         * directory name of the theme, which in most cases is synonymous
     508         * with the template name.
     509         *
     510         * @since 3.1.0
     511         *
     512         * @param array    $actions An array of action links.
     513         * @param WP_Theme $theme   The current WP_Theme object.
     514         * @param string   $context Status of the theme.
     515         */
     516        $actions = apply_filters( "theme_action_links_{$stylesheet}", $actions, $theme, $context );
     517
     518        echo $this->row_actions( $actions, true );
     519    }
     520
     521    /**
     522     * Handles the description column output.
     523     *
     524     * @since 4.3.0
     525     * @access public
     526     *
     527     * @global string $status
     528     * @global array  $totals
     529     *
     530     * @param WP_Theme $theme The current WP_Theme object.
     531     */
     532    public function column_description( $theme ) {
     533        global $status, $totals;
     534        if ( $theme->errors() ) {
     535            $pre = $status === 'broken' ? __( 'Broken Theme:' ) . ' ' : '';
     536            echo '<p><strong class="error-message">' . $pre . $theme->errors()->get_error_message() . '</strong></p>';
     537        }
     538
     539        if ( $this->is_site_themes ) {
     540            $allowed = $theme->is_allowed( 'site', $this->site_id );
     541        } else {
     542            $allowed = $theme->is_allowed( 'network' );
     543        }
     544
     545        $class = ! $allowed ? 'inactive' : 'active';
     546        if ( ! empty( $totals['upgrade'] ) && ! empty( $theme->update ) )
     547            $class .= ' update';
     548
     549        echo "<div class='theme-description'><p>" . $theme->display( 'Description' ) . "</p></div>
     550            <div class='$class second theme-version-author-uri'>";
     551
     552        $stylesheet = $theme->get_stylesheet();
     553        $theme_meta = array();
     554
     555        if ( $theme->get('Version') ) {
     556            $theme_meta[] = sprintf( __( 'Version %s' ), $theme->display('Version') );
     557        }
     558        $theme_meta[] = sprintf( __( 'By %s' ), $theme->display('Author') );
     559
     560        if ( $theme->get('ThemeURI') ) {
     561            /* translators: %s: theme name */
     562            $aria_label = sprintf( __( 'Visit %s homepage' ), $theme->display( 'Name' ) );
     563
     564            $theme_meta[] = sprintf( '<a href="%s" aria-label="%s">%s</a>',
     565                $theme->display( 'ThemeURI' ),
     566                esc_attr( $aria_label ),
     567                __( 'Visit Theme Site' )
     568            );
     569        }
     570        /**
     571         * Filters the array of row meta for each theme in the Multisite themes
     572         * list table.
     573         *
     574         * @since 3.1.0
     575         *
     576         * @param array    $theme_meta An array of the theme's metadata,
     577         *                             including the version, author, and
     578         *                             theme URI.
     579         * @param string   $stylesheet Directory name of the theme.
     580         * @param WP_Theme $theme      WP_Theme object.
     581         * @param string   $status     Status of the theme.
     582         */
     583        $theme_meta = apply_filters( 'theme_row_meta', $theme_meta, $stylesheet, $theme, $status );
     584        echo implode( ' | ', $theme_meta );
     585
     586        echo '</div>';
     587    }
     588
     589    /**
     590     * Handles default column output.
     591     *
     592     * @since 4.3.0
     593     * @access public
     594     *
     595     * @param WP_Theme $theme       The current WP_Theme object.
     596     * @param string   $column_name The current column name.
     597     */
     598    public function column_default( $theme, $column_name ) {
     599        $stylesheet = $theme->get_stylesheet();
     600
     601        /**
     602         * Fires inside each custom column of the Multisite themes list table.
     603         *
     604         * @since 3.1.0
     605         *
     606         * @param string   $column_name Name of the column.
     607         * @param string   $stylesheet  Directory name of the theme.
     608         * @param WP_Theme $theme       Current WP_Theme object.
     609         */
     610        do_action( 'manage_themes_custom_column', $column_name, $stylesheet, $theme );
     611    }
     612
     613    /**
     614     * Handles the output for a single table row.
     615     *
     616     * @since 4.3.0
     617     * @access public
     618     *
     619     * @param WP_Theme $item The current WP_Theme object.
     620     */
     621    public function single_row_columns( $item ) {
     622        list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
     623
     624        foreach ( $columns as $column_name => $column_display_name ) {
     625            $extra_classes = '';
     626            if ( in_array( $column_name, $hidden ) ) {
     627                $extra_classes .= ' hidden';
     628            }
     629
     630            switch ( $column_name ) {
     631                case 'cb':
     632                    echo '<th scope="row" class="check-column">';
     633
     634                    $this->column_cb( $item );
     635
     636                    echo '</th>';
     637                    break;
     638
     639                case 'name':
     640                    echo "<td class='theme-title column-primary{$extra_classes}'><strong>" . $item->display('Name') . "</strong>";
     641
     642                    $this->column_name( $item );
     643
     644                    echo "</td>";
     645                    break;
     646
     647                case 'description':
     648                    echo "<td class='column-description desc{$extra_classes}'>";
     649
     650                    $this->column_description( $item );
     651
     652                    echo '</td>';
     653                    break;
     654
     655                default:
     656                    echo "<td class='$column_name column-$column_name{$extra_classes}'>";
     657
     658                    $this->column_default( $item, $column_name );
     659
     660                    echo "</td>";
     661                    break;
     662            }
     663        }
     664    }
     665
     666    /**
     667     * @global string $status
     668     * @global array  $totals
     669     *
     670     * @param WP_Theme $theme
     671     */
     672    public function single_row( $theme ) {
     673        global $status, $totals;
     674
     675        if ( $this->is_site_themes ) {
     676            $allowed = $theme->is_allowed( 'site', $this->site_id );
     677        } else {
     678            $allowed = $theme->is_allowed( 'network' );
     679        }
     680
     681        $stylesheet = $theme->get_stylesheet();
     682
     683        $class = ! $allowed ? 'inactive' : 'active';
     684        if ( ! empty( $totals['upgrade'] ) && ! empty( $theme->update ) ) {
     685            $class .= ' update';
     686        }
     687
     688        printf( '<tr class="%s" data-slug="%s">',
     689            esc_attr( $class ),
     690            esc_attr( $stylesheet )
     691        );
     692
     693        $this->single_row_columns( $theme );
     694
     695        echo "</tr>";
     696
     697        if ( $this->is_site_themes )
     698            remove_action( "after_theme_row_$stylesheet", 'wp_theme_update_row' );
     699
     700        /**
     701         * Fires after each row in the Multisite themes list table.
     702         *
     703         * @since 3.1.0
     704         *
     705         * @param string   $stylesheet Directory name of the theme.
     706         * @param WP_Theme $theme      Current WP_Theme object.
     707         * @param string   $status     Status of the theme.
     708         */
     709        do_action( 'after_theme_row', $stylesheet, $theme, $status );
     710
     711        /**
     712         * Fires after each specific row in the Multisite themes list table.
     713         *
     714         * The dynamic portion of the hook name, `$stylesheet`, refers to the
     715         * directory name of the theme, most often synonymous with the template
     716         * name of the theme.
     717         *
     718         * @since 3.5.0
     719         *
     720         * @param string   $stylesheet Directory name of the theme.
     721         * @param WP_Theme $theme      Current WP_Theme object.
     722         * @param string   $status     Status of the theme.
     723         */
     724        do_action( "after_theme_row_{$stylesheet}", $stylesheet, $theme, $status );
     725    }
    732726}
  • src/wp-admin/includes/class-wp-plugin-install-list-table.php

    diff --git a/src/wp-admin/includes/class-wp-plugin-install-list-table.php b/src/wp-admin/includes/class-wp-plugin-install-list-table.php
    index cd718360cb..fd119cc6da 100644
    a b  
    1717 */
    1818class WP_Plugin_Install_List_Table extends WP_List_Table {
    1919
    20         public $order = 'ASC';
    21         public $orderby = null;
    22         public $groups = array();
    23 
    24         private $error;
    25 
    26         /**
    27          *
    28          * @return bool
    29          */
    30         public function ajax_user_can() {
    31                 return current_user_can('install_plugins');
    32         }
    33 
    34         /**
    35          * Return a list of slugs of installed plugins, if known.
    36          *
    37          * Uses the transient data from the updates API to determine the slugs of
    38          * known installed plugins. This might be better elsewhere, perhaps even
    39          * within get_plugins().
    40          *
    41          * @since 4.0.0
    42          * @access protected
    43          *
    44          * @return array
    45          */
    46         protected function get_installed_plugin_slugs() {
    47                 $slugs = array();
    48 
    49                 $plugin_info = get_site_transient( 'update_plugins' );
    50                 if ( isset( $plugin_info->no_update ) ) {
    51                         foreach ( $plugin_info->no_update as $plugin ) {
    52                                 $slugs[] = $plugin->slug;
    53                         }
    54                 }
    55 
    56                 if ( isset( $plugin_info->response ) ) {
    57                         foreach ( $plugin_info->response as $plugin ) {
    58                                 $slugs[] = $plugin->slug;
    59                         }
    60                 }
    61 
    62                 return $slugs;
    63         }
    64 
    65         /**
    66          *
    67          * @global array  $tabs
    68          * @global string $tab
    69          * @global int    $paged
    70          * @global string $type
    71          * @global string $term
    72          */
    73         public function prepare_items() {
    74                 include( ABSPATH . 'wp-admin/includes/plugin-install.php' );
    75 
    76                 global $tabs, $tab, $paged, $type, $term;
    77 
    78                 wp_reset_vars( array( 'tab' ) );
    79 
    80                 $paged = $this->get_pagenum();
    81 
    82                 $per_page = 30;
    83 
    84                 // These are the tabs which are shown on the page
    85                 $tabs = array();
    86 
    87                 if ( 'search' === $tab ) {
    88                         $tabs['search'] = __( 'Search Results' );
    89                 }
    90                 if ( $tab === 'beta' || false !== strpos( get_bloginfo( 'version' ), '-' ) ) {
    91                         $tabs['beta'] = _x( 'Beta Testing', 'Plugin Installer' );
    92                 }
    93                 $tabs['featured']    = _x( 'Featured', 'Plugin Installer' );
    94                 $tabs['popular']     = _x( 'Popular', 'Plugin Installer' );
    95                 $tabs['recommended'] = _x( 'Recommended', 'Plugin Installer' );
    96                 $tabs['favorites']   = _x( 'Favorites', 'Plugin Installer' );
    97                 if ( current_user_can( 'upload_plugins' ) ) {
    98                         // No longer a real tab. Here for filter compatibility.
    99                         // Gets skipped in get_views().
    100                         $tabs['upload'] = __( 'Upload Plugin' );
    101                 }
    102 
    103                 $nonmenu_tabs = array( 'plugin-information' ); // Valid actions to perform which do not have a Menu item.
    104 
    105                 /**
    106                  * Filters the tabs shown on the Plugin Install screen.
    107                  *
    108                  * @since 2.7.0
    109                  *
    110                  * @param array $tabs The tabs shown on the Plugin Install screen. Defaults include 'featured', 'popular',
    111                  *                    'recommended', 'favorites', and 'upload'.
    112                  */
    113                 $tabs = apply_filters( 'install_plugins_tabs', $tabs );
    114 
    115                 /**
    116                  * Filters tabs not associated with a menu item on the Plugin Install screen.
    117                  *
    118                  * @since 2.7.0
    119                  *
    120                  * @param array $nonmenu_tabs The tabs that don't have a Menu item on the Plugin Install screen.
    121                  */
    122                 $nonmenu_tabs = apply_filters( 'install_plugins_nonmenu_tabs', $nonmenu_tabs );
    123 
    124                 // If a non-valid menu tab has been selected, And it's not a non-menu action.
    125                 if ( empty( $tab ) || ( !isset( $tabs[ $tab ] ) && !in_array( $tab, (array) $nonmenu_tabs ) ) )
    126                         $tab = key( $tabs );
    127 
    128                 $args = array(
    129                         'page' => $paged,
    130                         'per_page' => $per_page,
    131                         'fields' => array(
    132                                 'last_updated' => true,
    133                                 'icons' => true,
    134                                 'active_installs' => true
    135                         ),
    136                         // Send the locale and installed plugin slugs to the API so it can provide context-sensitive results.
    137                         'locale' => get_user_locale(),
    138                         'installed_plugins' => $this->get_installed_plugin_slugs(),
    139                 );
    140 
    141                 switch ( $tab ) {
    142                         case 'search':
    143                                 $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term';
    144                                 $term = isset( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : '';
    145 
    146                                 switch ( $type ) {
    147                                         case 'tag':
    148                                                 $args['tag'] = sanitize_title_with_dashes( $term );
    149                                                 break;
    150                                         case 'term':
    151                                                 $args['search'] = $term;
    152                                                 break;
    153                                         case 'author':
    154                                                 $args['author'] = $term;
    155                                                 break;
    156                                 }
    157 
    158                                 break;
    159 
    160                         case 'featured':
    161                                 $args['fields']['group'] = true;
    162                                 $this->orderby = 'group';
    163                                 // No break!
    164                         case 'popular':
    165                         case 'new':
    166                         case 'beta':
    167                         case 'recommended':
    168                                 $args['browse'] = $tab;
    169                                 break;
    170 
    171                         case 'favorites':
    172                                 $action = 'save_wporg_username_' . get_current_user_id();
    173                                 if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), $action ) ) {
    174                                         $user = isset( $_GET['user'] ) ? wp_unslash( $_GET['user'] ) : get_user_option( 'wporg_favorites' );
    175                                         update_user_meta( get_current_user_id(), 'wporg_favorites', $user );
    176                                 } else {
    177                                         $user = get_user_option( 'wporg_favorites' );
    178                                 }
    179                                 if ( $user )
    180                                         $args['user'] = $user;
    181                                 else
    182                                         $args = false;
    183 
    184                                 add_action( 'install_plugins_favorites', 'install_plugins_favorites_form', 9, 0 );
    185                                 break;
    186 
    187                         default:
    188                                 $args = false;
    189                                 break;
    190                 }
    191 
    192                 /**
    193                  * Filters API request arguments for each Plugin Install screen tab.
    194                  *
    195                  * The dynamic portion of the hook name, `$tab`, refers to the plugin install tabs.
    196                  * Default tabs include 'featured', 'popular', 'recommended', 'favorites', and 'upload'.
    197                  *
    198                  * @since 3.7.0
    199                  *
    200                  * @param array|bool $args Plugin Install API arguments.
    201                  */
    202                 $args = apply_filters( "install_plugins_table_api_args_{$tab}", $args );
    203 
    204                 if ( !$args )
    205                         return;
    206 
    207                 $api = plugins_api( 'query_plugins', $args );
    208 
    209                 if ( is_wp_error( $api ) ) {
    210                         $this->error = $api;
    211                         return;
    212                 }
    213 
    214                 $this->items = $api->plugins;
    215 
    216                 if ( $this->orderby ) {
    217                         uasort( $this->items, array( $this, 'order_callback' ) );
    218                 }
    219 
    220                 $this->set_pagination_args( array(
    221                         'total_items' => $api->info['results'],
    222                         'per_page' => $args['per_page'],
    223                 ) );
    224 
    225                 if ( isset( $api->info['groups'] ) ) {
    226                         $this->groups = $api->info['groups'];
    227                 }
    228         }
    229 
    230         /**
    231          * @access public
    232          */
    233         public function no_items() {
    234                 if ( isset( $this->error ) ) {
    235                         $message = $this->error->get_error_message() . '<p class="hide-if-no-js"><a href="#" class="button" onclick="document.location.reload(); return false;">' . __( 'Try again' ) . '</a></p>';
    236                 } else {
    237                         $message = __( 'No plugins match your request.' );
    238                 }
    239                 echo '<div class="no-plugin-results">' . $message . '</div>';
    240         }
    241 
    242         /**
    243          *
    244          * @global array $tabs
    245          * @global string $tab
    246          *
    247          * @return array
    248          */
    249         protected function get_views() {
    250                 global $tabs, $tab;
    251 
    252                 $display_tabs = array();
    253                 foreach ( (array) $tabs as $action => $text ) {
    254                         $class = ( $action === $tab ) ? ' current' : '';
    255                         $href = self_admin_url('plugin-install.php?tab=' . $action);
    256                         $display_tabs['plugin-install-'.$action] = "<a href='$href' class='$class'>$text</a>";
    257                 }
    258                 // No longer a real tab.
    259                 unset( $display_tabs['plugin-install-upload'] );
    260 
    261                 return $display_tabs;
    262         }
    263 
    264         /**
    265          * Override parent views so we can use the filter bar display.
    266          */
    267         public function views() {
    268                 $views = $this->get_views();
    269 
    270                 /** This filter is documented in wp-admin/inclues/class-wp-list-table.php */
    271                 $views = apply_filters( "views_{$this->screen->id}", $views );
    272 
    273                 $this->screen->render_screen_reader_content( 'heading_views' );
     20    public $order = 'ASC';
     21    public $orderby = null;
     22    public $groups = array();
     23
     24    private $error;
     25
     26    /**
     27     *
     28     * @return bool
     29     */
     30    public function ajax_user_can() {
     31        return current_user_can('install_plugins');
     32    }
     33
     34    /**
     35     * Return a list of slugs of installed plugins, if known.
     36     *
     37     * Uses the transient data from the updates API to determine the slugs of
     38     * known installed plugins. This might be better elsewhere, perhaps even
     39     * within get_plugins().
     40     *
     41     * @since 4.0.0
     42     * @access protected
     43     *
     44     * @return array
     45     */
     46    protected function get_installed_plugin_slugs() {
     47        $slugs = array();
     48
     49        $plugin_info = get_site_transient( 'update_plugins' );
     50        if ( isset( $plugin_info->no_update ) ) {
     51            foreach ( $plugin_info->no_update as $plugin ) {
     52                $slugs[] = $plugin->slug;
     53            }
     54        }
     55
     56        if ( isset( $plugin_info->response ) ) {
     57            foreach ( $plugin_info->response as $plugin ) {
     58                $slugs[] = $plugin->slug;
     59            }
     60        }
     61
     62        return $slugs;
     63    }
     64
     65    public function prepare_items() {
     66        include( ABSPATH . 'wp-admin/includes/plugin-install.php' );
     67
     68        $tab = wp_assign_request_var('tab');
     69        $type = wp_assign_request_var('type');
     70        $term = wp_assign_request_var('term');
     71
     72        $paged = $this->get_pagenum();
     73
     74        $per_page = 30;
     75
     76        // These are the tabs which are shown on the page
     77        $tabs = array();
     78
     79        if ( 'search' === $tab ) {
     80            $tabs['search'] = __( 'Search Results' );
     81        }
     82        if ( $tab === 'beta' || false !== strpos( get_bloginfo( 'version' ), '-' ) ) {
     83            $tabs['beta'] = _x( 'Beta Testing', 'Plugin Installer' );
     84        }
     85        $tabs['featured']    = _x( 'Featured', 'Plugin Installer' );
     86        $tabs['popular']     = _x( 'Popular', 'Plugin Installer' );
     87        $tabs['recommended'] = _x( 'Recommended', 'Plugin Installer' );
     88        $tabs['favorites']   = _x( 'Favorites', 'Plugin Installer' );
     89        if ( current_user_can( 'upload_plugins' ) ) {
     90            // No longer a real tab. Here for filter compatibility.
     91            // Gets skipped in get_views().
     92            $tabs['upload'] = __( 'Upload Plugin' );
     93        }
     94
     95        $nonmenu_tabs = array( 'plugin-information' ); // Valid actions to perform which do not have a Menu item.
     96
     97        /**
     98         * Filters the tabs shown on the Plugin Install screen.
     99         *
     100         * @since 2.7.0
     101         *
     102         * @param array $tabs The tabs shown on the Plugin Install screen. Defaults include 'featured', 'popular',
     103         *                    'recommended', 'favorites', and 'upload'.
     104         */
     105        $tabs = apply_filters( 'install_plugins_tabs', $tabs );
     106
     107        /**
     108         * Filters tabs not associated with a menu item on the Plugin Install screen.
     109         *
     110         * @since 2.7.0
     111         *
     112         * @param array $nonmenu_tabs The tabs that don't have a Menu item on the Plugin Install screen.
     113         */
     114        $nonmenu_tabs = apply_filters( 'install_plugins_nonmenu_tabs', $nonmenu_tabs );
     115
     116        // If a non-valid menu tab has been selected, And it's not a non-menu action.
     117        if ( empty( $tab ) || ( !isset( $tabs[ $tab ] ) && !in_array( $tab, (array) $nonmenu_tabs ) ) )
     118            $tab = key( $tabs );
     119
     120        $args = array(
     121            'page' => $paged,
     122            'per_page' => $per_page,
     123            'fields' => array(
     124                'last_updated' => true,
     125                'icons' => true,
     126                'active_installs' => true
     127            ),
     128            // Send the locale and installed plugin slugs to the API so it can provide context-sensitive results.
     129            'locale' => get_user_locale(),
     130            'installed_plugins' => $this->get_installed_plugin_slugs(),
     131        );
     132
     133        switch ( $tab ) {
     134            case 'search':
     135                $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term';
     136                $term = isset( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : '';
     137
     138                switch ( $type ) {
     139                    case 'tag':
     140                        $args['tag'] = sanitize_title_with_dashes( $term );
     141                        break;
     142                    case 'term':
     143                        $args['search'] = $term;
     144                        break;
     145                    case 'author':
     146                        $args['author'] = $term;
     147                        break;
     148                }
     149
     150                break;
     151
     152            case 'featured':
     153                $args['fields']['group'] = true;
     154                $this->orderby = 'group';
     155                // No break!
     156            case 'popular':
     157            case 'new':
     158            case 'beta':
     159            case 'recommended':
     160                $args['browse'] = $tab;
     161                break;
     162
     163            case 'favorites':
     164                $action = 'save_wporg_username_' . get_current_user_id();
     165                if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), $action ) ) {
     166                    $user = isset( $_GET['user'] ) ? wp_unslash( $_GET['user'] ) : get_user_option( 'wporg_favorites' );
     167                    update_user_meta( get_current_user_id(), 'wporg_favorites', $user );
     168                } else {
     169                    $user = get_user_option( 'wporg_favorites' );
     170                }
     171                if ( $user )
     172                    $args['user'] = $user;
     173                else
     174                    $args = false;
     175
     176                add_action( 'install_plugins_favorites', 'install_plugins_favorites_form', 9, 0 );
     177                break;
     178
     179            default:
     180                $args = false;
     181                break;
     182        }
     183
     184        /**
     185         * Filters API request arguments for each Plugin Install screen tab.
     186         *
     187         * The dynamic portion of the hook name, `$tab`, refers to the plugin install tabs.
     188         * Default tabs include 'featured', 'popular', 'recommended', 'favorites', and 'upload'.
     189         *
     190         * @since 3.7.0
     191         *
     192         * @param array|bool $args Plugin Install API arguments.
     193         */
     194        $args = apply_filters( "install_plugins_table_api_args_{$tab}", $args );
     195
     196        if ( !$args )
     197            return;
     198
     199        $api = plugins_api( 'query_plugins', $args );
     200
     201        if ( is_wp_error( $api ) ) {
     202            $this->error = $api;
     203            return;
     204        }
     205
     206        $this->items = $api->plugins;
     207
     208        if ( $this->orderby ) {
     209            uasort( $this->items, array( $this, 'order_callback' ) );
     210        }
     211
     212        $this->set_pagination_args( array(
     213            'total_items' => $api->info['results'],
     214            'per_page' => $args['per_page'],
     215        ) );
     216
     217        if ( isset( $api->info['groups'] ) ) {
     218            $this->groups = $api->info['groups'];
     219        }
     220    }
     221
     222    /**
     223     * @access public
     224     */
     225    public function no_items() {
     226        if ( isset( $this->error ) ) {
     227            $message = $this->error->get_error_message() . '<p class="hide-if-no-js"><a href="#" class="button" onclick="document.location.reload(); return false;">' . __( 'Try again' ) . '</a></p>';
     228        } else {
     229            $message = __( 'No plugins match your request.' );
     230        }
     231        echo '<div class="no-plugin-results">' . $message . '</div>';
     232    }
     233
     234    /**
     235     *
     236     * @global array $tabs
     237     * @global string $tab
     238     *
     239     * @return array
     240     */
     241    protected function get_views() {
     242        global $tabs, $tab;
     243
     244        $display_tabs = array();
     245        foreach ( (array) $tabs as $action => $text ) {
     246            $class = ( $action === $tab ) ? ' current' : '';
     247            $href = self_admin_url('plugin-install.php?tab=' . $action);
     248            $display_tabs['plugin-install-'.$action] = "<a href='$href' class='$class'>$text</a>";
     249        }
     250        // No longer a real tab.
     251        unset( $display_tabs['plugin-install-upload'] );
     252
     253        return $display_tabs;
     254    }
     255
     256    /**
     257     * Override parent views so we can use the filter bar display.
     258     */
     259    public function views() {
     260        $views = $this->get_views();
     261
     262        /** This filter is documented in wp-admin/inclues/class-wp-list-table.php */
     263        $views = apply_filters( "views_{$this->screen->id}", $views );
     264
     265        $this->screen->render_screen_reader_content( 'heading_views' );
    274266?>
    275267<div class="wp-filter">
    276         <ul class="filter-links">
    277                 <?php
    278                 if ( ! empty( $views ) ) {
    279                         foreach ( $views as $class => $view ) {
    280                                 $views[ $class ] = "\t<li class='$class'>$view";
    281                         }
    282                         echo implode( " </li>\n", $views ) . "</li>\n";
    283                 }
    284                 ?>
    285         </ul>
    286 
    287         <?php install_search_form(); ?>
     268    <ul class="filter-links">
     269        <?php
     270        if ( ! empty( $views ) ) {
     271            foreach ( $views as $class => $view ) {
     272                $views[ $class ] = "\t<li class='$class'>$view";
     273            }
     274            echo implode( " </li>\n", $views ) . "</li>\n";
     275        }
     276        ?>
     277    </ul>
     278
     279    <?php install_search_form(); ?>
    288280</div>
    289281<?php
    290         }
     282    }
    291283
    292         /**
    293         * Override the parent display() so we can provide a different container.
    294         */
    295         public function display() {
    296                 $singular = $this->_args['singular'];
     284    /**
     285    * Override the parent display() so we can provide a different container.
     286    */
     287    public function display() {
     288        $singular = $this->_args['singular'];
    297289
    298                 $data_attr = '';
     290        $data_attr = '';
    299291
    300                 if ( $singular ) {
    301                         $data_attr = " data-wp-lists='list:$singular'";
    302                 }
     292        if ( $singular ) {
     293            $data_attr = " data-wp-lists='list:$singular'";
     294        }
    303295
    304                 $this->display_tablenav( 'top' );
     296        $this->display_tablenav( 'top' );
    305297
    306298?>
    307299<div class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
    308300<?php
    309         $this->screen->render_screen_reader_content( 'heading_list' );
     301    $this->screen->render_screen_reader_content( 'heading_list' );
    310302?>
    311         <div id="the-list"<?php echo $data_attr; ?>>
    312                 <?php $this->display_rows_or_placeholder(); ?>
    313         </div>
     303    <div id="the-list"<?php echo $data_attr; ?>>
     304        <?php $this->display_rows_or_placeholder(); ?>
     305    </div>
    314306</div>
    315307<?php
    316                 $this->display_tablenav( 'bottom' );
    317         }
    318 
    319         /**
    320         * @global string $tab
    321         *
    322         * @param string $which
    323         */
    324         protected function display_tablenav( $which ) {
    325                 if ( $GLOBALS['tab'] === 'featured' ) {
    326                         return;
    327                 }
    328 
    329                 if ( 'top' === $which ) {
    330                         wp_referer_field();
    331                 ?>
    332                         <div class="tablenav top">
    333                                 <div class="alignleft actions">
    334                                         <?php
    335                                         /**
    336                                         * Fires before the Plugin Install table header pagination is displayed.
    337                                         *
    338                                         * @since 2.7.0
    339                                         */
    340                                         do_action( 'install_plugins_table_header' ); ?>
    341                                 </div>
    342                                 <?php $this->pagination( $which ); ?>
    343                                 <br class="clear" />
    344                         </div>
    345                 <?php } else { ?>
    346                         <div class="tablenav bottom">
    347                                 <?php $this->pagination( $which ); ?>
    348                                 <br class="clear" />
    349                         </div>
    350                 <?php
    351                 }
    352         }
    353 
    354         /**
    355         * @return array
    356         */
    357         protected function get_table_classes() {
    358                 return array( 'widefat', $this->_args['plural'] );
    359         }
    360 
    361         /**
    362         * @return array
    363         */
    364         public function get_columns() {
    365                 return array();
    366         }
    367 
    368         /**
    369         * @param object $plugin_a
    370         * @param object $plugin_b
    371         * @return int
    372         */
    373         private function order_callback( $plugin_a, $plugin_b ) {
    374                 $orderby = $this->orderby;
    375                 if ( ! isset( $plugin_a->$orderby, $plugin_b->$orderby ) ) {
    376                         return 0;
    377                 }
    378 
    379                 $a = $plugin_a->$orderby;
    380                 $b = $plugin_b->$orderby;
    381 
    382                 if ( $a == $b ) {
    383                         return 0;
    384                 }
    385 
    386                 if ( 'DESC' === $this->order ) {
    387                         return ( $a < $b ) ? 1 : -1;
    388                 } else {
    389                         return ( $a < $b ) ? -1 : 1;
    390                 }
    391         }
    392 
    393         public function display_rows() {
    394                 $plugins_allowedtags = array(
    395                         'a' => array( 'href' => array(),'title' => array(), 'target' => array() ),
    396                         'abbr' => array( 'title' => array() ),'acronym' => array( 'title' => array() ),
    397                         'code' => array(), 'pre' => array(), 'em' => array(),'strong' => array(),
    398                         'ul' => array(), 'ol' => array(), 'li' => array(), 'p' => array(), 'br' => array()
    399                 );
    400 
    401                 $plugins_group_titles = array(
    402                         'Performance' => _x( 'Performance', 'Plugin installer group title' ),
    403                         'Social'      => _x( 'Social',      'Plugin installer group title' ),
    404                         'Tools'       => _x( 'Tools',       'Plugin installer group title' ),
    405                 );
    406 
    407                 $group = null;
    408 
    409                 foreach ( (array) $this->items as $plugin ) {
    410                         if ( is_object( $plugin ) ) {
    411                                 $plugin = (array) $plugin;
    412                         }
    413 
    414                         // Display the group heading if there is one
    415                         if ( isset( $plugin['group'] ) && $plugin['group'] != $group ) {
    416                                 if ( isset( $this->groups[ $plugin['group'] ] ) ) {
    417                                         $group_name = $this->groups[ $plugin['group'] ];
    418                                         if ( isset( $plugins_group_titles[ $group_name ] ) ) {
    419                                                 $group_name = $plugins_group_titles[ $group_name ];
    420                                         }
    421                                 } else {
    422                                         $group_name = $plugin['group'];
    423                                 }
    424 
    425                                 // Starting a new group, close off the divs of the last one
    426                                 if ( ! empty( $group ) ) {
    427                                         echo '</div></div>';
    428                                 }
    429 
    430                                 echo '<div class="plugin-group"><h3>' . esc_html( $group_name ) . '</h3>';
    431                                 // needs an extra wrapping div for nth-child selectors to work
    432                                 echo '<div class="plugin-items">';
    433 
    434                                 $group = $plugin['group'];
    435                         }
    436                         $title = wp_kses( $plugin['name'], $plugins_allowedtags );
    437 
    438                         // Remove any HTML from the description.
    439                         $description = strip_tags( $plugin['short_description'] );
    440                         $version = wp_kses( $plugin['version'], $plugins_allowedtags );
    441 
    442                         $name = strip_tags( $title . ' ' . $version );
    443 
    444                         $author = wp_kses( $plugin['author'], $plugins_allowedtags );
    445                         if ( ! empty( $author ) ) {
    446                                 $author = ' <cite>' . sprintf( __( 'By %s' ), $author ) . '</cite>';
    447                         }
    448 
    449                         $action_links = array();
    450 
    451                         if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) {
    452                                 $status = install_plugin_install_status( $plugin );
    453 
    454                                 switch ( $status['status'] ) {
    455                                         case 'install':
    456                                                 if ( $status['url'] ) {
    457                                                         /* translators: 1: Plugin name and version. */
    458                                                         $action_links[] = '<a class="install-now button" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Install %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Install Now' ) . '</a>';
    459                                                 }
    460                                                 break;
    461 
    462                                         case 'update_available':
    463                                                 if ( $status['url'] ) {
    464                                                         /* translators: 1: Plugin name and version */
    465                                                         $action_links[] = '<a class="update-now button aria-button-if-js" data-plugin="' . esc_attr( $status['file'] ) . '" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Update %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Update Now' ) . '</a>';
    466                                                 }
    467                                                 break;
    468 
    469                                         case 'latest_installed':
    470                                         case 'newer_installed':
    471                                                 if ( is_plugin_active( $status['file'] ) ) {
    472                                                         $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Active', 'plugin' ) . '</button>';
    473                                                 } elseif ( current_user_can( 'activate_plugins' ) ) {
    474                                                         $button_text  = __( 'Activate' );
    475                                                         /* translators: %s: Plugin name */
    476                                                         $button_label = _x( 'Activate %s', 'plugin' );
    477                                                         $activate_url = add_query_arg( array(
    478                                                                 '_wpnonce'    => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
    479                                                                 'action'      => 'activate',
    480                                                                 'plugin'      => $status['file'],
    481                                                         ), network_admin_url( 'plugins.php' ) );
    482 
    483                                                         if ( is_network_admin() ) {
    484                                                                 $button_text  = __( 'Network Activate' );
    485                                                                 /* translators: %s: Plugin name */
    486                                                                 $button_label = _x( 'Network Activate %s', 'plugin' );
    487                                                                 $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url );
    488                                                         }
    489 
    490                                                         $action_links[] = sprintf(
    491                                                                 '<a href="%1$s" class="button activate-now" aria-label="%2$s">%3$s</a>',
    492                                                                 esc_url( $activate_url ),
    493                                                                 esc_attr( sprintf( $button_label, $plugin['name'] ) ),
    494                                                                 $button_text
    495                                                         );
    496                                                 } else {
    497                                                         $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Installed', 'plugin' ) . '</button>';
    498                                                 }
    499                                                 break;
    500                                 }
    501                         }
    502 
    503                         $details_link   = self_admin_url( 'plugin-install.php?tab=plugin-information&amp;plugin=' . $plugin['slug'] .
    504                                                                 '&amp;TB_iframe=true&amp;width=600&amp;height=550' );
    505 
    506                         /* translators: 1: Plugin name and version. */
    507                         $action_links[] = '<a href="' . esc_url( $details_link ) . '" class="thickbox open-plugin-details-modal" aria-label="' . esc_attr( sprintf( __( 'More information about %s' ), $name ) ) . '" data-title="' . esc_attr( $name ) . '">' . __( 'More Details' ) . '</a>';
    508 
    509                         if ( !empty( $plugin['icons']['svg'] ) ) {
    510                                 $plugin_icon_url = $plugin['icons']['svg'];
    511                         } elseif ( !empty( $plugin['icons']['2x'] ) ) {
    512                                 $plugin_icon_url = $plugin['icons']['2x'];
    513                         } elseif ( !empty( $plugin['icons']['1x'] ) ) {
    514                                 $plugin_icon_url = $plugin['icons']['1x'];
    515                         } else {
    516                                 $plugin_icon_url = $plugin['icons']['default'];
    517                         }
    518 
    519                         /**
    520                         * Filters the install action links for a plugin.
    521                         *
    522                         * @since 2.7.0
    523                         *
    524                         * @param array $action_links An array of plugin action hyperlinks. Defaults are links to Details and Install Now.
    525                         * @param array $plugin       The plugin currently being listed.
    526                         */
    527                         $action_links = apply_filters( 'plugin_install_action_links', $action_links, $plugin );
    528 
    529                         $last_updated_timestamp = strtotime( $plugin['last_updated'] );
    530                 ?>
    531                 <div class="plugin-card plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>">
    532                         <div class="plugin-card-top">
    533                                 <div class="name column-name">
    534                                         <h3>
    535                                                 <a href="<?php echo esc_url( $details_link ); ?>" class="thickbox open-plugin-details-modal">
    536                                                 <?php echo $title; ?>
    537                                                 <img src="<?php echo esc_attr( $plugin_icon_url ) ?>" class="plugin-icon" alt="">
    538                                                 </a>
    539                                         </h3>
    540                                 </div>
    541                                 <div class="action-links">
    542                                         <?php
    543                                                 if ( $action_links ) {
    544                                                         echo '<ul class="plugin-action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>';
    545                                                 }
    546                                         ?>
    547                                 </div>
    548                                 <div class="desc column-description">
    549                                         <p><?php echo $description; ?></p>
    550                                         <p class="authors"><?php echo $author; ?></p>
    551                                 </div>
    552                         </div>
    553                         <div class="plugin-card-bottom">
    554                                 <div class="vers column-rating">
    555                                         <?php wp_star_rating( array( 'rating' => $plugin['rating'], 'type' => 'percent', 'number' => $plugin['num_ratings'] ) ); ?>
    556                                         <span class="num-ratings" aria-hidden="true">(<?php echo number_format_i18n( $plugin['num_ratings'] ); ?>)</span>
    557                                 </div>
    558                                 <div class="column-updated">
    559                                         <strong><?php _e( 'Last Updated:' ); ?></strong> <?php printf( __( '%s ago' ), human_time_diff( $last_updated_timestamp ) ); ?>
    560                                 </div>
    561                                 <div class="column-downloaded">
    562                                         <?php
    563                                         if ( $plugin['active_installs'] >= 1000000 ) {
    564                                                 $active_installs_text = _x( '1+ Million', 'Active plugin installs' );
    565                                         } elseif ( 0 == $plugin['active_installs'] ) {
    566                                                 $active_installs_text = _x( 'Less Than 10', 'Active plugin installs' );
    567                                         } else {
    568                                                 $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+';
    569                                         }
    570                                         printf( __( '%s Active Installs' ), $active_installs_text );
    571                                         ?>
    572                                 </div>
    573                                 <div class="column-compatibility">
    574                                         <?php
    575                                         $wp_version = get_bloginfo( 'version' );
    576 
    577                                         if ( ! empty( $plugin['tested'] ) && version_compare( substr( $wp_version, 0, strlen( $plugin['tested'] ) ), $plugin['tested'], '>' ) ) {
    578                                                 echo '<span class="compatibility-untested">' . __( 'Untested with your version of WordPress' ) . '</span>';
    579                                         } elseif ( ! empty( $plugin['requires'] ) && version_compare( substr( $wp_version, 0, strlen( $plugin['requires'] ) ), $plugin['requires'], '<' ) ) {
    580                                                 echo '<span class="compatibility-incompatible">' . __( '<strong>Incompatible</strong> with your version of WordPress' ) . '</span>';
    581                                         } else {
    582                                                 echo '<span class="compatibility-compatible">' . __( '<strong>Compatible</strong> with your version of WordPress' ) . '</span>';
    583                                         }
    584                                         ?>
    585                                 </div>
    586                         </div>
    587                 </div>
    588                 <?php
    589                 }
    590 
    591                 // Close off the group divs of the last one
    592                 if ( ! empty( $group ) ) {
    593                         echo '</div></div>';
    594                 }
    595         }
     308        $this->display_tablenav( 'bottom' );
     309    }
     310
     311    /**
     312    * @global string $tab
     313    *
     314    * @param string $which
     315    */
     316    protected function display_tablenav( $which ) {
     317        if ( $GLOBALS['tab'] === 'featured' ) {
     318            return;
     319        }
     320
     321        if ( 'top' === $which ) {
     322            wp_referer_field();
     323        ?>
     324            <div class="tablenav top">
     325                <div class="alignleft actions">
     326                    <?php
     327                    /**
     328                    * Fires before the Plugin Install table header pagination is displayed.
     329                    *
     330                    * @since 2.7.0
     331                    */
     332                    do_action( 'install_plugins_table_header' ); ?>
     333                </div>
     334                <?php $this->pagination( $which ); ?>
     335                <br class="clear" />
     336            </div>
     337        <?php } else { ?>
     338            <div class="tablenav bottom">
     339                <?php $this->pagination( $which ); ?>
     340                <br class="clear" />
     341            </div>
     342        <?php
     343        }
     344    }
     345
     346    /**
     347    * @return array
     348    */
     349    protected function get_table_classes() {
     350        return array( 'widefat', $this->_args['plural'] );
     351    }
     352
     353    /**
     354    * @return array
     355    */
     356    public function get_columns() {
     357        return array();
     358    }
     359
     360    /**
     361    * @param object $plugin_a
     362    * @param object $plugin_b
     363    * @return int
     364    */
     365    private function order_callback( $plugin_a, $plugin_b ) {
     366        $orderby = $this->orderby;
     367        if ( ! isset( $plugin_a->$orderby, $plugin_b->$orderby ) ) {
     368            return 0;
     369        }
     370
     371        $a = $plugin_a->$orderby;
     372        $b = $plugin_b->$orderby;
     373
     374        if ( $a == $b ) {
     375            return 0;
     376        }
     377
     378        if ( 'DESC' === $this->order ) {
     379            return ( $a < $b ) ? 1 : -1;
     380        } else {
     381            return ( $a < $b ) ? -1 : 1;
     382        }
     383    }
     384
     385    public function display_rows() {
     386        $plugins_allowedtags = array(
     387            'a' => array( 'href' => array(),'title' => array(), 'target' => array() ),
     388            'abbr' => array( 'title' => array() ),'acronym' => array( 'title' => array() ),
     389            'code' => array(), 'pre' => array(), 'em' => array(),'strong' => array(),
     390            'ul' => array(), 'ol' => array(), 'li' => array(), 'p' => array(), 'br' => array()
     391        );
     392
     393        $plugins_group_titles = array(
     394            'Performance' => _x( 'Performance', 'Plugin installer group title' ),
     395            'Social'      => _x( 'Social',      'Plugin installer group title' ),
     396            'Tools'       => _x( 'Tools',       'Plugin installer group title' ),
     397        );
     398
     399        $group = null;
     400
     401        foreach ( (array) $this->items as $plugin ) {
     402            if ( is_object( $plugin ) ) {
     403                $plugin = (array) $plugin;
     404            }
     405
     406            // Display the group heading if there is one
     407            if ( isset( $plugin['group'] ) && $plugin['group'] != $group ) {
     408                if ( isset( $this->groups[ $plugin['group'] ] ) ) {
     409                    $group_name = $this->groups[ $plugin['group'] ];
     410                    if ( isset( $plugins_group_titles[ $group_name ] ) ) {
     411                        $group_name = $plugins_group_titles[ $group_name ];
     412                    }
     413                } else {
     414                    $group_name = $plugin['group'];
     415                }
     416
     417                // Starting a new group, close off the divs of the last one
     418                if ( ! empty( $group ) ) {
     419                    echo '</div></div>';
     420                }
     421
     422                echo '<div class="plugin-group"><h3>' . esc_html( $group_name ) . '</h3>';
     423                // needs an extra wrapping div for nth-child selectors to work
     424                echo '<div class="plugin-items">';
     425
     426                $group = $plugin['group'];
     427            }
     428            $title = wp_kses( $plugin['name'], $plugins_allowedtags );
     429
     430            // Remove any HTML from the description.
     431            $description = strip_tags( $plugin['short_description'] );
     432            $version = wp_kses( $plugin['version'], $plugins_allowedtags );
     433
     434            $name = strip_tags( $title . ' ' . $version );
     435
     436            $author = wp_kses( $plugin['author'], $plugins_allowedtags );
     437            if ( ! empty( $author ) ) {
     438                $author = ' <cite>' . sprintf( __( 'By %s' ), $author ) . '</cite>';
     439            }
     440
     441            $action_links = array();
     442
     443            if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) {
     444                $status = install_plugin_install_status( $plugin );
     445
     446                switch ( $status['status'] ) {
     447                    case 'install':
     448                        if ( $status['url'] ) {
     449                            /* translators: 1: Plugin name and version. */
     450                            $action_links[] = '<a class="install-now button" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Install %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Install Now' ) . '</a>';
     451                        }
     452                        break;
     453
     454                    case 'update_available':
     455                        if ( $status['url'] ) {
     456                            /* translators: 1: Plugin name and version */
     457                            $action_links[] = '<a class="update-now button aria-button-if-js" data-plugin="' . esc_attr( $status['file'] ) . '" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Update %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Update Now' ) . '</a>';
     458                        }
     459                        break;
     460
     461                    case 'latest_installed':
     462                    case 'newer_installed':
     463                        if ( is_plugin_active( $status['file'] ) ) {
     464                            $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Active', 'plugin' ) . '</button>';
     465                        } elseif ( current_user_can( 'activate_plugins' ) ) {
     466                            $button_text  = __( 'Activate' );
     467                            /* translators: %s: Plugin name */
     468                            $button_label = _x( 'Activate %s', 'plugin' );
     469                            $activate_url = add_query_arg( array(
     470                                '_wpnonce'    => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
     471                                'action'      => 'activate',
     472                                'plugin'      => $status['file'],
     473                            ), network_admin_url( 'plugins.php' ) );
     474
     475                            if ( is_network_admin() ) {
     476                                $button_text  = __( 'Network Activate' );
     477                                /* translators: %s: Plugin name */
     478                                $button_label = _x( 'Network Activate %s', 'plugin' );
     479                                $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url );
     480                            }
     481
     482                            $action_links[] = sprintf(
     483                                '<a href="%1$s" class="button activate-now" aria-label="%2$s">%3$s</a>',
     484                                esc_url( $activate_url ),
     485                                esc_attr( sprintf( $button_label, $plugin['name'] ) ),
     486                                $button_text
     487                            );
     488                        } else {
     489                            $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Installed', 'plugin' ) . '</button>';
     490                        }
     491                        break;
     492                }
     493            }
     494
     495            $details_link   = self_admin_url( 'plugin-install.php?tab=plugin-information&amp;plugin=' . $plugin['slug'] .
     496                                '&amp;TB_iframe=true&amp;width=600&amp;height=550' );
     497
     498            /* translators: 1: Plugin name and version. */
     499            $action_links[] = '<a href="' . esc_url( $details_link ) . '" class="thickbox open-plugin-details-modal" aria-label="' . esc_attr( sprintf( __( 'More information about %s' ), $name ) ) . '" data-title="' . esc_attr( $name ) . '">' . __( 'More Details' ) . '</a>';
     500
     501            if ( !empty( $plugin['icons']['svg'] ) ) {
     502                $plugin_icon_url = $plugin['icons']['svg'];
     503            } elseif ( !empty( $plugin['icons']['2x'] ) ) {
     504                $plugin_icon_url = $plugin['icons']['2x'];
     505            } elseif ( !empty( $plugin['icons']['1x'] ) ) {
     506                $plugin_icon_url = $plugin['icons']['1x'];
     507            } else {
     508                $plugin_icon_url = $plugin['icons']['default'];
     509            }
     510
     511            /**
     512            * Filters the install action links for a plugin.
     513            *
     514            * @since 2.7.0
     515            *
     516            * @param array $action_links An array of plugin action hyperlinks. Defaults are links to Details and Install Now.
     517            * @param array $plugin       The plugin currently being listed.
     518            */
     519            $action_links = apply_filters( 'plugin_install_action_links', $action_links, $plugin );
     520
     521            $last_updated_timestamp = strtotime( $plugin['last_updated'] );
     522        ?>
     523        <div class="plugin-card plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>">
     524            <div class="plugin-card-top">
     525                <div class="name column-name">
     526                    <h3>
     527                        <a href="<?php echo esc_url( $details_link ); ?>" class="thickbox open-plugin-details-modal">
     528                        <?php echo $title; ?>
     529                        <img src="<?php echo esc_attr( $plugin_icon_url ) ?>" class="plugin-icon" alt="">
     530                        </a>
     531                    </h3>
     532                </div>
     533                <div class="action-links">
     534                    <?php
     535                        if ( $action_links ) {
     536                            echo '<ul class="plugin-action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>';
     537                        }
     538                    ?>
     539                </div>
     540                <div class="desc column-description">
     541                    <p><?php echo $description; ?></p>
     542                    <p class="authors"><?php echo $author; ?></p>
     543                </div>
     544            </div>
     545            <div class="plugin-card-bottom">
     546                <div class="vers column-rating">
     547                    <?php wp_star_rating( array( 'rating' => $plugin['rating'], 'type' => 'percent', 'number' => $plugin['num_ratings'] ) ); ?>
     548                    <span class="num-ratings" aria-hidden="true">(<?php echo number_format_i18n( $plugin['num_ratings'] ); ?>)</span>
     549                </div>
     550                <div class="column-updated">
     551                    <strong><?php _e( 'Last Updated:' ); ?></strong> <?php printf( __( '%s ago' ), human_time_diff( $last_updated_timestamp ) ); ?>
     552                </div>
     553                <div class="column-downloaded">
     554                    <?php
     555                    if ( $plugin['active_installs'] >= 1000000 ) {
     556                        $active_installs_text = _x( '1+ Million', 'Active plugin installs' );
     557                    } elseif ( 0 == $plugin['active_installs'] ) {
     558                        $active_installs_text = _x( 'Less Than 10', 'Active plugin installs' );
     559                    } else {
     560                        $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+';
     561                    }
     562                    printf( __( '%s Active Installs' ), $active_installs_text );
     563                    ?>
     564                </div>
     565                <div class="column-compatibility">
     566                    <?php
     567                    $wp_version = get_bloginfo( 'version' );
     568
     569                    if ( ! empty( $plugin['tested'] ) && version_compare( substr( $wp_version, 0, strlen( $plugin['tested'] ) ), $plugin['tested'], '>' ) ) {
     570                        echo '<span class="compatibility-untested">' . __( 'Untested with your version of WordPress' ) . '</span>';
     571                    } elseif ( ! empty( $plugin['requires'] ) && version_compare( substr( $wp_version, 0, strlen( $plugin['requires'] ) ), $plugin['requires'], '<' ) ) {
     572                        echo '<span class="compatibility-incompatible">' . __( '<strong>Incompatible</strong> with your version of WordPress' ) . '</span>';
     573                    } else {
     574                        echo '<span class="compatibility-compatible">' . __( '<strong>Compatible</strong> with your version of WordPress' ) . '</span>';
     575                    }
     576                    ?>
     577                </div>
     578            </div>
     579        </div>
     580        <?php
     581        }
     582
     583        // Close off the group divs of the last one
     584        if ( ! empty( $group ) ) {
     585            echo '</div></div>';
     586        }
     587    }
    596588}
  • src/wp-admin/includes/class-wp-plugins-list-table.php

    diff --git a/src/wp-admin/includes/class-wp-plugins-list-table.php b/src/wp-admin/includes/class-wp-plugins-list-table.php
    index 136fbd4c73..8622411681 100644
    <
    a b  
    1717 */
    1818class WP_Plugins_List_Table extends WP_List_Table {
    1919
    20         /**
    21          * Constructor.
    22          *
    23          * @since 3.1.0
    24          * @access public
    25          *
    26          * @see WP_List_Table::__construct() for more information on default arguments.
    27          *
    28          * @global string $status
    29          * @global int    $page
    30          *
    31          * @param array $args An associative array of arguments.
    32          */
    33         public function __construct( $args = array() ) {
    34                 global $status, $page;
    35 
    36                 parent::__construct( array(
    37                         'plural' => 'plugins',
    38                         'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
    39                 ) );
    40 
    41                 $status = 'all';
    42                 if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search' ) ) )
    43                         $status = $_REQUEST['plugin_status'];
    44 
    45                 if ( isset($_REQUEST['s']) )
    46                         $_SERVER['REQUEST_URI'] = add_query_arg('s', wp_unslash($_REQUEST['s']) );
    47 
    48                 $page = $this->get_pagenum();
    49         }
    50 
    51         /**
    52          * @return array
    53          */
    54         protected function get_table_classes() {
    55                 return array( 'widefat', $this->_args['plural'] );
    56         }
    57 
    58         /**
    59          * @return bool
    60          */
    61         public function ajax_user_can() {
    62                 return current_user_can('activate_plugins');
    63         }
    64 
    65         /**
    66          *
    67          * @global string $status
    68          * @global array  $plugins
    69          * @global array  $totals
    70          * @global int    $page
    71          * @global string $orderby
    72          * @global string $order
    73          * @global string $s
    74          */
    75         public function prepare_items() {
    76                 global $status, $plugins, $totals, $page, $orderby, $order, $s;
    77 
    78                 wp_reset_vars( array( 'orderby', 'order' ) );
    79 
    80                 /**
    81                  * Filters the full array of plugins to list in the Plugins list table.
    82                  *
    83                  * @since 3.0.0
    84                  *
    85                  * @see get_plugins()
    86                  *
    87                  * @param array $all_plugins An array of plugins to display in the list table.
    88                  */
    89                 $all_plugins = apply_filters( 'all_plugins', get_plugins() );
    90 
    91                 $plugins = array(
    92                         'all'                => $all_plugins,
    93                         'search'             => array(),
    94                         'active'             => array(),
    95                         'inactive'           => array(),
    96                         'recently_activated' => array(),
    97                         'upgrade'            => array(),
    98                         'mustuse'            => array(),
    99                         'dropins'            => array(),
    100                 );
    101 
    102                 $screen = $this->screen;
    103 
    104                 if ( ! is_multisite() || ( $screen->in_admin( 'network' ) && current_user_can( 'manage_network_plugins' ) ) ) {
    105 
    106                         /**
    107                          * Filters whether to display the advanced plugins list table.
    108                          *
    109                          * There are two types of advanced plugins - must-use and drop-ins -
    110                          * which can be used in a single site or Multisite network.
    111                          *
    112                          * The $type parameter allows you to differentiate between the type of advanced
    113                          * plugins to filter the display of. Contexts include 'mustuse' and 'dropins'.
    114                          *
    115                          * @since 3.0.0
    116                          *
    117                          * @param bool   $show Whether to show the advanced plugins for the specified
    118                          *                     plugin type. Default true.
    119                          * @param string $type The plugin type. Accepts 'mustuse', 'dropins'.
    120                          */
    121                         if ( apply_filters( 'show_advanced_plugins', true, 'mustuse' ) ) {
    122                                 $plugins['mustuse'] = get_mu_plugins();
    123                         }
    124 
    125                         /** This action is documented in wp-admin/includes/class-wp-plugins-list-table.php */
    126                         if ( apply_filters( 'show_advanced_plugins', true, 'dropins' ) )
    127                                 $plugins['dropins'] = get_dropins();
    128 
    129                         if ( current_user_can( 'update_plugins' ) ) {
    130                                 $current = get_site_transient( 'update_plugins' );
    131                                 foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
    132                                         if ( isset( $current->response[ $plugin_file ] ) ) {
    133                                                 $plugins['all'][ $plugin_file ]['update'] = true;
    134                                                 $plugins['upgrade'][ $plugin_file ] = $plugins['all'][ $plugin_file ];
    135                                         }
    136                                 }
    137                         }
    138                 }
    139 
    140                 if ( ! $screen->in_admin( 'network' ) ) {
    141                         $show = current_user_can( 'manage_network_plugins' );
    142                         /**
    143                          * Filters whether to display network-active plugins alongside plugins active for the current site.
    144                          *
    145                          * This also controls the display of inactive network-only plugins (plugins with
    146                          * "Network: true" in the plugin header).
    147                          *
    148                          * Plugins cannot be network-activated or network-deactivated from this screen.
    149                          *
    150                          * @since 4.4.0
    151                          *
    152                          * @param bool $show Whether to show network-active plugins. Default is whether the current
    153                          *                   user can manage network plugins (ie. a Super Admin).
    154                          */
    155                         $show_network_active = apply_filters( 'show_network_active_plugins', $show );
    156                 }
    157 
    158                 set_transient( 'plugin_slugs', array_keys( $plugins['all'] ), DAY_IN_SECONDS );
    159 
    160                 if ( $screen->in_admin( 'network' ) ) {
    161                         $recently_activated = get_site_option( 'recently_activated', array() );
    162                 } else {
    163                         $recently_activated = get_option( 'recently_activated', array() );
    164                 }
    165 
    166                 foreach ( $recently_activated as $key => $time ) {
    167                         if ( $time + WEEK_IN_SECONDS < time() ) {
    168                                 unset( $recently_activated[$key] );
    169                         }
    170                 }
    171 
    172                 if ( $screen->in_admin( 'network' ) ) {
    173                         update_site_option( 'recently_activated', $recently_activated );
    174                 } else {
    175                         update_option( 'recently_activated', $recently_activated );
    176                 }
    177 
    178                 $plugin_info = get_site_transient( 'update_plugins' );
    179 
    180                 foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
    181                         // Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide.
    182                         if ( isset( $plugin_info->response[ $plugin_file ] ) ) {
    183                                 $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data );
    184                                 // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade
    185                                 if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) {
    186                                         $plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data );
    187                                 }
    188 
    189                         } elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) {
    190                                 $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data );
    191                                 // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade
    192                                 if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) {
    193                                         $plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data );
    194                                 }
    195                         }
    196 
    197                         // Filter into individual sections
    198                         if ( is_multisite() && ! $screen->in_admin( 'network' ) && is_network_only_plugin( $plugin_file ) && ! is_plugin_active( $plugin_file ) ) {
    199                                 if ( $show_network_active ) {
    200                                         // On the non-network screen, show inactive network-only plugins if allowed
    201                                         $plugins['inactive'][ $plugin_file ] = $plugin_data;
    202                                 } else {
    203                                         // On the non-network screen, filter out network-only plugins as long as they're not individually active
    204                                         unset( $plugins['all'][ $plugin_file ] );
    205                                 }
    206                         } elseif ( ! $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) {
    207                                 if ( $show_network_active ) {
    208                                         // On the non-network screen, show network-active plugins if allowed
    209                                         $plugins['active'][ $plugin_file ] = $plugin_data;
    210                                 } else {
    211                                         // On the non-network screen, filter out network-active plugins
    212                                         unset( $plugins['all'][ $plugin_file ] );
    213                                 }
    214                         } elseif ( ( ! $screen->in_admin( 'network' ) && is_plugin_active( $plugin_file ) )
    215                                 || ( $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) ) {
    216                                 // On the non-network screen, populate the active list with plugins that are individually activated
    217                                 // On the network-admin screen, populate the active list with plugins that are network activated
    218                                 $plugins['active'][ $plugin_file ] = $plugin_data;
    219                         } else {
    220                                 if ( isset( $recently_activated[ $plugin_file ] ) ) {
    221                                         // Populate the recently activated list with plugins that have been recently activated
    222                                         $plugins['recently_activated'][ $plugin_file ] = $plugin_data;
    223                                 }
    224                                 // Populate the inactive list with plugins that aren't activated
    225                                 $plugins['inactive'][ $plugin_file ] = $plugin_data;
    226                         }
    227                 }
    228 
    229                 if ( strlen( $s ) ) {
    230                         $status = 'search';
    231                         $plugins['search'] = array_filter( $plugins['all'], array( $this, '_search_callback' ) );
    232                 }
    233 
    234                 $totals = array();
    235                 foreach ( $plugins as $type => $list )
    236                         $totals[ $type ] = count( $list );
    237 
    238                 if ( empty( $plugins[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) )
    239                         $status = 'all';
    240 
    241                 $this->items = array();
    242                 foreach ( $plugins[ $status ] as $plugin_file => $plugin_data ) {
    243                         // Translate, Don't Apply Markup, Sanitize HTML
    244                         $this->items[$plugin_file] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true );
    245                 }
    246 
    247                 $total_this_page = $totals[ $status ];
    248 
    249                 $js_plugins = array();
    250                 foreach ( $plugins as $key => $list ) {
    251                         $js_plugins[ $key ] = array_keys( (array) $list );
    252                 }
    253 
    254                 wp_localize_script( 'updates', '_wpUpdatesItemCounts', array(
    255                         'plugins' => $js_plugins,
    256                         'totals'  => wp_get_update_data(),
    257                 ) );
    258 
    259                 if ( ! $orderby ) {
    260                         $orderby = 'Name';
    261                 } else {
    262                         $orderby = ucfirst( $orderby );
    263                 }
    264 
    265                 $order = strtoupper( $order );
    266 
    267                 uasort( $this->items, array( $this, '_order_callback' ) );
    268 
    269                 $plugins_per_page = $this->get_items_per_page( str_replace( '-', '_', $screen->id . '_per_page' ), 999 );
    270 
    271                 $start = ( $page - 1 ) * $plugins_per_page;
    272 
    273                 if ( $total_this_page > $plugins_per_page )
    274                         $this->items = array_slice( $this->items, $start, $plugins_per_page );
    275 
    276                 $this->set_pagination_args( array(
    277                         'total_items' => $total_this_page,
    278                         'per_page' => $plugins_per_page,
    279                 ) );
    280         }
    281 
    282         /**
    283          * @global string $s URL encoded search term.
    284          *
    285          * @param array $plugin
    286          * @return bool
    287          */
    288         public function _search_callback( $plugin ) {
    289                 global $s;
    290 
    291                 foreach ( $plugin as $value ) {
    292                         if ( is_string( $value ) && false !== stripos( strip_tags( $value ), urldecode( $s ) ) ) {
    293                                 return true;
    294                         }
    295                 }
    296 
    297                 return false;
    298         }
    299 
    300         /**
    301          * @global string $orderby
    302          * @global string $order
    303          * @param array $plugin_a
    304          * @param array $plugin_b
    305          * @return int
    306          */
    307         public function _order_callback( $plugin_a, $plugin_b ) {
    308                 global $orderby, $order;
    309 
    310                 $a = $plugin_a[$orderby];
    311                 $b = $plugin_b[$orderby];
    312 
    313                 if ( $a == $b )
    314                         return 0;
    315 
    316                 if ( 'DESC' === $order ) {
    317                         return strcasecmp( $b, $a );
    318                 } else {
    319                         return strcasecmp( $a, $b );
    320                 }
    321         }
    322 
    323         /**
    324          *
    325          * @global array $plugins
    326          */
    327         public function no_items() {
    328                 global $plugins;
    329 
    330                 if ( ! empty( $_REQUEST['s'] ) ) {
    331                         $s = esc_html( wp_unslash( $_REQUEST['s'] ) );
    332 
    333                         printf( __( 'No plugins found for &#8220;%s&#8221;.' ), $s );
    334 
    335                         // We assume that somebody who can install plugins in multisite is experienced enough to not need this helper link.
    336                         if ( ! is_multisite() && current_user_can( 'install_plugins' ) ) {
    337                                 echo ' <a href="' . esc_url( admin_url( 'plugin-install.php?tab=search&s=' . urlencode( $s ) ) ) . '">' . __( 'Search for plugins in the WordPress Plugin Directory.' ) . '</a>';
    338                         }
    339                 } elseif ( ! empty( $plugins['all'] ) )
    340                         _e( 'No plugins found.' );
    341                 else
    342                         _e( 'You do not appear to have any plugins available at this time.' );
    343         }
    344 
    345         /**
    346          * Displays the search box.
    347          *
    348          * @since 4.6.0
    349          * @access public
    350          *
    351          * @param string $text     The 'submit' button label.
    352          * @param string $input_id ID attribute value for the search input field.
    353          */
    354         public function search_box( $text, $input_id ) {
    355                 if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
    356                         return;
    357                 }
    358 
    359                 $input_id = $input_id . '-search-input';
    360 
    361                 if ( ! empty( $_REQUEST['orderby'] ) ) {
    362                         echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
    363                 }
    364                 if ( ! empty( $_REQUEST['order'] ) ) {
    365                         echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
    366                 }
    367                 ?>
    368                 <p class="search-box">
    369                         <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
    370                         <input type="search" id="<?php echo esc_attr( $input_id ); ?>" class="wp-filter-search" name="s" value="<?php _admin_search_query(); ?>" placeholder="<?php esc_attr_e( 'Search installed plugins...' ); ?>"/>
    371                         <?php submit_button( $text, 'hide-if-js', '', false, array( 'id' => 'search-submit' ) ); ?>
    372                 </p>
    373                 <?php
    374         }
    375 
    376         /**
    377          *
    378          * @global string $status
    379          * @return array
    380          */
    381         public function get_columns() {
    382                 global $status;
    383 
    384                 return array(
    385                         'cb'          => !in_array( $status, array( 'mustuse', 'dropins' ) ) ? '<input type="checkbox" />' : '',
    386                         'name'        => __( 'Plugin' ),
    387                         'description' => __( 'Description' ),
    388                 );
    389         }
    390 
    391         /**
    392          * @return array
    393          */
    394         protected function get_sortable_columns() {
    395                 return array();
    396         }
    397 
    398         /**
    399          *
    400          * @global array $totals
    401          * @global string $status
    402          * @return array
    403          */
    404         protected function get_views() {
    405                 global $totals, $status;
    406 
    407                 $status_links = array();
    408                 foreach ( $totals as $type => $count ) {
    409                         if ( !$count )
    410                                 continue;
    411 
    412                         switch ( $type ) {
    413                                 case 'all':
    414                                         $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins' );
    415                                         break;
    416                                 case 'active':
    417                                         $text = _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count );
    418                                         break;
    419                                 case 'recently_activated':
    420                                         $text = _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count );
    421                                         break;
    422                                 case 'inactive':
    423                                         $text = _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count );
    424                                         break;
    425                                 case 'mustuse':
    426                                         $text = _n( 'Must-Use <span class="count">(%s)</span>', 'Must-Use <span class="count">(%s)</span>', $count );
    427                                         break;
    428                                 case 'dropins':
    429                                         $text = _n( 'Drop-ins <span class="count">(%s)</span>', 'Drop-ins <span class="count">(%s)</span>', $count );
    430                                         break;
    431                                 case 'upgrade':
    432                                         $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count );
    433                                         break;
    434                         }
    435 
    436                         if ( 'search' !== $type ) {
    437                                 $status_links[$type] = sprintf( "<a href='%s' %s>%s</a>",
    438                                         add_query_arg('plugin_status', $type, 'plugins.php'),
    439                                         ( $type === $status ) ? ' class="current"' : '',
    440                                         sprintf( $text, number_format_i18n( $count ) )
    441                                         );
    442                         }
    443                 }
    444 
    445                 return $status_links;
    446         }
    447 
    448         /**
    449          *
    450          * @global string $status
    451          * @return array
    452          */
    453         protected function get_bulk_actions() {
    454                 global $status;
    455 
    456                 $actions = array();
    457 
    458                 if ( 'active' != $status )
    459                         $actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Activate' ) : __( 'Activate' );
    460 
    461                 if ( 'inactive' != $status && 'recent' != $status )
    462                         $actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Deactivate' ) : __( 'Deactivate' );
    463 
    464                 if ( !is_multisite() || $this->screen->in_admin( 'network' ) ) {
    465                         if ( current_user_can( 'update_plugins' ) )
    466                                 $actions['update-selected'] = __( 'Update' );
    467                         if ( current_user_can( 'delete_plugins' ) && ( 'active' != $status ) )
    468                                 $actions['delete-selected'] = __( 'Delete' );
    469                 }
    470 
    471                 return $actions;
    472         }
    473 
    474         /**
    475          * @global string $status
    476          * @param string $which
    477          */
    478         public function bulk_actions( $which = '' ) {
    479                 global $status;
    480 
    481                 if ( in_array( $status, array( 'mustuse', 'dropins' ) ) )
    482                         return;
    483 
    484                 parent::bulk_actions( $which );
    485         }
    486 
    487         /**
    488          * @global string $status
    489          * @param string $which
    490          */
    491         protected function extra_tablenav( $which ) {
    492                 global $status;
    493 
    494                 if ( ! in_array($status, array('recently_activated', 'mustuse', 'dropins') ) )
    495                         return;
    496 
    497                 echo '<div class="alignleft actions">';
    498 
    499                 if ( 'recently_activated' == $status ) {
    500                         submit_button( __( 'Clear List' ), '', 'clear-recent-list', false );
    501                 } elseif ( 'top' === $which && 'mustuse' === $status ) {
    502                         /* translators: %s: mu-plugins directory name */
    503                         echo '<p>' . sprintf( __( 'Files in the %s directory are executed automatically.' ),
    504                                 '<code>' . str_replace( ABSPATH, '/', WPMU_PLUGIN_DIR ) . '</code>'
    505                         ) . '</p>';
    506                 } elseif ( 'top' === $which && 'dropins' === $status ) {
    507                         /* translators: %s: wp-content directory name */
    508                         echo '<p>' . sprintf( __( 'Drop-ins are advanced plugins in the %s directory that replace WordPress functionality when present.' ),
    509                                 '<code>' . str_replace( ABSPATH, '', WP_CONTENT_DIR ) . '</code>'
    510                         ) . '</p>';
    511                 }
    512                 echo '</div>';
    513         }
    514 
    515         /**
    516          * @return string
    517          */
    518         public function current_action() {
    519                 if ( isset($_POST['clear-recent-list']) )
    520                         return 'clear-recent-list';
    521 
    522                 return parent::current_action();
    523         }
    524 
    525         /**
    526          *
    527          * @global string $status
    528          */
    529         public function display_rows() {
    530                 global $status;
    531 
    532                 if ( is_multisite() && ! $this->screen->in_admin( 'network' ) && in_array( $status, array( 'mustuse', 'dropins' ) ) )
    533                         return;
    534 
    535                 foreach ( $this->items as $plugin_file => $plugin_data )
    536                         $this->single_row( array( $plugin_file, $plugin_data ) );
    537         }
    538 
    539         /**
    540          * @global string $status
    541          * @global int $page
    542          * @global string $s
    543          * @global array $totals
    544          *
    545          * @param array $item
    546          */
    547         public function single_row( $item ) {
    548                 global $status, $page, $s, $totals;
    549 
    550                 list( $plugin_file, $plugin_data ) = $item;
    551                 $context = $status;
    552                 $screen = $this->screen;
    553 
    554                 // Pre-order.
    555                 $actions = array(
    556                         'deactivate' => '',
    557                         'activate' => '',
    558                         'details' => '',
    559                         'edit' => '',
    560                         'delete' => '',
    561                 );
    562 
    563                 // Do not restrict by default
    564                 $restrict_network_active = false;
    565                 $restrict_network_only = false;
    566 
    567                 if ( 'mustuse' === $context ) {
    568                         $is_active = true;
    569                 } elseif ( 'dropins' === $context ) {
    570                         $dropins = _get_dropins();
    571                         $plugin_name = $plugin_file;
    572                         if ( $plugin_file != $plugin_data['Name'] )
    573                                 $plugin_name .= '<br/>' . $plugin_data['Name'];
    574                         if ( true === ( $dropins[ $plugin_file ][1] ) ) { // Doesn't require a constant
    575                                 $is_active = true;
    576                                 $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
    577                         } elseif ( defined( $dropins[ $plugin_file ][1] ) && constant( $dropins[ $plugin_file ][1] ) ) { // Constant is true
    578                                 $is_active = true;
    579                                 $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
    580                         } else {
    581                                 $is_active = false;
    582                                 $description = '<p><strong>' . $dropins[ $plugin_file ][0] . ' <span class="error-message">' . __( 'Inactive:' ) . '</span></strong> ' .
    583                                         /* translators: 1: drop-in constant name, 2: wp-config.php */
    584                                         sprintf( __( 'Requires %1$s in %2$s file.' ),
    585                                                 "<code>define('" . $dropins[ $plugin_file ][1] . "', true);</code>",
    586                                                 '<code>wp-config.php</code>'
    587                                         ) . '</p>';
    588                         }
    589                         if ( $plugin_data['Description'] )
    590                                 $description .= '<p>' . $plugin_data['Description'] . '</p>';
    591                 } else {
    592                         if ( $screen->in_admin( 'network' ) ) {
    593                                 $is_active = is_plugin_active_for_network( $plugin_file );
    594                         } else {
    595                                 $is_active = is_plugin_active( $plugin_file );
    596                                 $restrict_network_active = ( is_multisite() && is_plugin_active_for_network( $plugin_file ) );
    597                                 $restrict_network_only = ( is_multisite() && is_network_only_plugin( $plugin_file ) && ! $is_active );
    598                         }
    599 
    600                         if ( $screen->in_admin( 'network' ) ) {
    601                                 if ( $is_active ) {
    602                                         if ( current_user_can( 'manage_network_plugins' ) ) {
    603                                                 /* translators: %s: plugin name */
    604                                                 $actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $plugin_file . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Deactivate' ) . '</a>';
    605                                                 }
    606                                 } else {
    607                                         if ( current_user_can( 'manage_network_plugins' ) ) {
    608                                                 /* translators: %s: plugin name */
    609                                                 $actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $plugin_file . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Activate' ) . '</a>';
    610                                         }
    611                                         if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) {
    612                                                 /* translators: %s: plugin name */
    613                                                 $actions['delete'] = '<a href="' . wp_nonce_url( 'plugins.php?action=delete-selected&amp;checked[]=' . $plugin_file . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-plugins' ) . '" class="delete" aria-label="' . esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Delete' ) . '</a>';
    614                                         }
    615                                 }
    616                         } else {
    617                                 if ( $restrict_network_active ) {
    618                                         $actions = array(
    619                                                 'network_active' => __( 'Network Active' ),
    620                                         );
    621                                 } elseif ( $restrict_network_only ) {
    622                                         $actions = array(
    623                                                 'network_only' => __( 'Network Only' ),
    624                                         );
    625                                 } elseif ( $is_active ) {
    626                                         /* translators: %s: plugin name */
    627                                         $actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $plugin_file . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Deactivate' ) . '</a>';
    628                                 } else {
    629                                         /* translators: %s: plugin name */
    630                                         $actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $plugin_file . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Activate' ) . '</a>';
    631 
    632                                         if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) {
    633                                                 /* translators: %s: plugin name */
    634                                                 $actions['delete'] = '<a href="' . wp_nonce_url( 'plugins.php?action=delete-selected&amp;checked[]=' . $plugin_file . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-plugins' ) . '" class="delete" aria-label="' . esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Delete' ) . '</a>';
    635                                         }
    636                                 } // end if $is_active
    637 
    638                          } // end if $screen->in_admin( 'network' )
    639 
    640                         if ( ( ! is_multisite() || $screen->in_admin( 'network' ) ) && current_user_can( 'edit_plugins' ) && is_writable( WP_PLUGIN_DIR . '/' . $plugin_file ) ) {
    641                                 /* translators: %s: plugin name */
    642                                 $actions['edit'] = '<a href="plugin-editor.php?file=' . $plugin_file . '" class="edit" aria-label="' . esc_attr( sprintf( __( 'Edit %s' ), $plugin_data['Name'] ) ) . '">' . __( 'Edit' ) . '</a>';
    643                         }
    644                 } // end if $context
    645 
    646                 $actions = array_filter( $actions );
    647 
    648                 if ( $screen->in_admin( 'network' ) ) {
    649 
    650                         /**
    651                          * Filters the action links displayed for each plugin in the Network Admin Plugins list table.
    652                          *
    653                          * The default action links for the Network plugins list table include
    654                          * 'Network Activate', 'Network Deactivate', 'Edit', and 'Delete'.
    655                          *
    656                          * @since 3.1.0
    657                          *
    658                          * @param array  $actions     An array of plugin action links.
    659                          * @param string $plugin_file Path to the plugin file relative to the plugins directory.
    660                          * @param array  $plugin_data An array of plugin data.
    661                          * @param string $context     The plugin context. Defaults are 'All', 'Active',
    662                          *                            'Inactive', 'Recently Activated', 'Upgrade',
    663                          *                            'Must-Use', 'Drop-ins', 'Search'.
    664                          */
    665                         $actions = apply_filters( 'network_admin_plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
    666 
    667                         /**
    668                          * Filters the list of action links displayed for a specific plugin in the Network Admin Plugins list table.
    669                          *
    670                          * The dynamic portion of the hook name, $plugin_file, refers to the path
    671                          * to the plugin file, relative to the plugins directory.
    672                          *
    673                          * @since 3.1.0
    674                          *
    675                          * @param array  $actions     An array of plugin action links.
    676                          * @param string $plugin_file Path to the plugin file relative to the plugins directory.
    677                          * @param array  $plugin_data An array of plugin data.
    678                          * @param string $context     The plugin context. Defaults are 'All', 'Active',
    679                          *                            'Inactive', 'Recently Activated', 'Upgrade',
    680                          *                            'Must-Use', 'Drop-ins', 'Search'.
    681                          */
    682                         $actions = apply_filters( "network_admin_plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
    683 
    684                 } else {
    685 
    686                         /**
    687                          * Filters the action links displayed for each plugin in the Plugins list table.
    688                          *
    689                          * The default action links for the site plugins list table include
    690                          * 'Activate', 'Deactivate', and 'Edit', for a network site, and
    691                          * 'Activate', 'Deactivate', 'Edit', and 'Delete' for a single site.
    692                          *
    693                          * @since 2.5.0
    694                          * @since 2.6.0 The `$context` parameter was added.
    695                          *
    696                          * @param array  $actions     An array of plugin action links.
    697                          * @param string $plugin_file Path to the plugin file relative to the plugins directory.
    698                          * @param array  $plugin_data An array of plugin data.
    699                          * @param string $context     The plugin context. Defaults are 'All', 'Active',
    700                          *                            'Inactive', 'Recently Activated', 'Upgrade',
    701                          *                            'Must-Use', 'Drop-ins', 'Search'.
    702                          */
    703                         $actions = apply_filters( 'plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
    704 
    705                         /**
    706                          * Filters the list of action links displayed for a specific plugin in the Plugins list table.
    707                          *
    708                          * The dynamic portion of the hook name, $plugin_file, refers to the path
    709                          * to the plugin file, relative to the plugins directory.
    710                          *
    711                          * @since 2.7.0
    712                          *
    713                          * @param array  $actions     An array of plugin action links.
    714                          * @param string $plugin_file Path to the plugin file relative to the plugins directory.
    715                          * @param array  $plugin_data An array of plugin data.
    716                          * @param string $context     The plugin context. Defaults are 'All', 'Active',
    717                          *                            'Inactive', 'Recently Activated', 'Upgrade',
    718                          *                            'Must-Use', 'Drop-ins', 'Search'.
    719                          */
    720                         $actions = apply_filters( "plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
    721 
    722                 }
    723 
    724                 $class = $is_active ? 'active' : 'inactive';
    725                 $checkbox_id =  "checkbox_" . md5($plugin_data['Name']);
    726                 if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ) ) ) {
    727                         $checkbox = '';
    728                 } else {
    729                         $checkbox = "<label class='screen-reader-text' for='" . $checkbox_id . "' >" . sprintf( __( 'Select %s' ), $plugin_data['Name'] ) . "</label>"
    730                                 . "<input type='checkbox' name='checked[]' value='" . esc_attr( $plugin_file ) . "' id='" . $checkbox_id . "' />";
    731                 }
    732                 if ( 'dropins' != $context ) {
    733                         $description = '<p>' . ( $plugin_data['Description'] ? $plugin_data['Description'] : '&nbsp;' ) . '</p>';
    734                         $plugin_name = $plugin_data['Name'];
    735                 }
    736 
    737                 if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) )
    738                         $class .= ' update';
    739 
    740                 $plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_name );
    741                 printf( '<tr class="%s" data-slug="%s" data-plugin="%s">',
    742                         esc_attr( $class ),
    743                         esc_attr( $plugin_slug ),
    744                         esc_attr( $plugin_file )
    745                 );
    746 
    747                 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
    748 
    749                 foreach ( $columns as $column_name => $column_display_name ) {
    750                         $extra_classes = '';
    751                         if ( in_array( $column_name, $hidden ) ) {
    752                                 $extra_classes = ' hidden';
    753                         }
    754 
    755                         switch ( $column_name ) {
    756                                 case 'cb':
    757                                         echo "<th scope='row' class='check-column'>$checkbox</th>";
    758                                         break;
    759                                 case 'name':
    760                                         echo "<td class='plugin-title column-primary'><strong>$plugin_name</strong>";
    761                                         echo $this->row_actions( $actions, true );
    762                                         echo "</td>";
    763                                         break;
    764                                 case 'description':
    765                                         $classes = 'column-description desc';
    766 
    767                                         echo "<td class='$classes{$extra_classes}'>
    768                                                 <div class='plugin-description'>$description</div>
    769                                                 <div class='$class second plugin-version-author-uri'>";
    770 
    771                                         $plugin_meta = array();
    772                                         if ( !empty( $plugin_data['Version'] ) )
    773                                                 $plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
    774                                         if ( !empty( $plugin_data['Author'] ) ) {
    775                                                 $author = $plugin_data['Author'];
    776                                                 if ( !empty( $plugin_data['AuthorURI'] ) )
    777                                                         $author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
    778                                                 $plugin_meta[] = sprintf( __( 'By %s' ), $author );
    779                                         }
    780 
    781                                         // Details link using API info, if available
    782                                         if ( isset( $plugin_data['slug'] ) && current_user_can( 'install_plugins' ) ) {
    783                                                 $plugin_meta[] = sprintf( '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
    784                                                         esc_url( network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_data['slug'] .
    785                                                                 '&TB_iframe=true&width=600&height=550' ) ),
    786                                                         esc_attr( sprintf( __( 'More information about %s' ), $plugin_name ) ),
    787                                                         esc_attr( $plugin_name ),
    788                                                         __( 'View details' )
    789                                                 );
    790                                         } elseif ( ! empty( $plugin_data['PluginURI'] ) ) {
    791                                                 $plugin_meta[] = sprintf( '<a href="%s">%s</a>',
    792                                                         esc_url( $plugin_data['PluginURI'] ),
    793                                                         __( 'Visit plugin site' )
    794                                                 );
    795                                         }
    796 
    797                                         /**
    798                                          * Filters the array of row meta for each plugin in the Plugins list table.
    799                                          *
    800                                          * @since 2.8.0
    801                                          *
    802                                          * @param array  $plugin_meta An array of the plugin's metadata,
    803                                          *                            including the version, author,
    804                                          *                            author URI, and plugin URI.
    805                                          * @param string $plugin_file Path to the plugin file, relative to the plugins directory.
    806                                          * @param array  $plugin_data An array of plugin data.
    807                                          * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
    808                                          *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
    809                                          *                            'Drop-ins', 'Search'.
    810                                          */
    811                                         $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status );
    812                                         echo implode( ' | ', $plugin_meta );
    813 
    814                                         echo "</div></td>";
    815                                         break;
    816                                 default:
    817                                         $classes = "$column_name column-$column_name $class";
    818 
    819                                         echo "<td class='$classes{$extra_classes}'>";
    820 
    821                                         /**
    822                                          * Fires inside each custom column of the Plugins list table.
    823                                          *
    824                                          * @since 3.1.0
    825                                          *
    826                                          * @param string $column_name Name of the column.
    827                                          * @param string $plugin_file Path to the plugin file.
    828                                          * @param array  $plugin_data An array of plugin data.
    829                                          */
    830                                         do_action( 'manage_plugins_custom_column', $column_name, $plugin_file, $plugin_data );
    831 
    832                                         echo "</td>";
    833                         }
    834                 }
    835 
    836                 echo "</tr>";
    837 
    838                 /**
    839                  * Fires after each row in the Plugins list table.
    840                  *
    841                  * @since 2.3.0
    842                  *
    843                  * @param string $plugin_file Path to the plugin file, relative to the plugins directory.
    844                  * @param array  $plugin_data An array of plugin data.
    845                  * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
    846                  *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
    847                  *                            'Drop-ins', 'Search'.
    848                  */
    849                 do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status );
    850 
    851                 /**
    852                  * Fires after each specific row in the Plugins list table.
    853                  *
    854                  * The dynamic portion of the hook name, `$plugin_file`, refers to the path
    855                  * to the plugin file, relative to the plugins directory.
    856                  *
    857                  * @since 2.7.0
    858                  *
    859                  * @param string $plugin_file Path to the plugin file, relative to the plugins directory.
    860                  * @param array  $plugin_data An array of plugin data.
    861                  * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
    862                  *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
    863                  *                            'Drop-ins', 'Search'.
    864                  */
    865                 do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status );
    866         }
    867 
    868         /**
    869          * Gets the name of the primary column for this specific list table.
    870          *
    871          * @since 4.3.0
    872          * @access protected
    873          *
    874          * @return string Unalterable name for the primary column, in this case, 'name'.