Ticket #38073: 38073.patch
File 38073.patch, 490.0 KB (added by , 8 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' ); 12 12 $parent_file = 'edit-comments.php'; 13 13 $submenu_file = 'edit-comments.php'; 14 14 15 /** 16 * @global string $action 17 */ 18 global $action; 19 wp_reset_vars( array('action') ); 15 $action = wp_assign_request_var('action'); 20 16 21 17 if ( isset( $_POST['deletecomment'] ) ) 22 18 $action = 'deletecomment'; 23 19 24 20 if ( 'cdc' == $action ) 25 21 $action = 'delete'; 26 22 elseif ( 'mac' == $action ) 27 23 $action = 'approve'; 28 24 29 25 if ( isset( $_GET['dt'] ) ) { 30 31 32 33 26 if ( 'spam' == $_GET['dt'] ) 27 $action = 'spam'; 28 elseif ( 'trash' == $_GET['dt'] ) 29 $action = 'trash'; 34 30 } 35 31 36 32 switch( $action ) { 37 33 38 34 case 'editcomment' : 39 35 $title = __('Edit Comment'); 40 36 41 42 43 44 45 46 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 ) ); 48 44 49 50 51 52 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 ); 54 50 55 56 51 wp_enqueue_script('comment'); 52 require_once( ABSPATH . 'wp-admin/admin-header.php' ); 57 53 58 54 $comment_id = absint( $_GET['c'] ); 59 55 60 61 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)') ); 62 58 63 64 59 if ( !current_user_can( 'edit_comment', $comment_id ) ) 60 comment_footer_die( __('Sorry, you are not allowed to edit this comment.') ); 65 61 66 67 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.') ); 68 64 69 65 $comment = get_comment_to_edit( $comment_id ); 70 66 71 67 include( ABSPATH . 'wp-admin/edit-form-comment.php' ); 72 68 73 69 break; 74 70 75 71 case 'delete' : 76 72 case 'approve' : 77 73 case 'trash' : 78 74 case 'spam' : 79 75 80 76 $title = __('Moderate Comment'); 81 77 82 78 $comment_id = absint( $_GET['c'] ); 83 79 84 85 86 87 80 if ( ! $comment = get_comment( $comment_id ) ) { 81 wp_redirect( admin_url('edit-comments.php?error=1') ); 82 die(); 83 } 88 84 89 90 91 92 85 if ( !current_user_can( 'edit_comment', $comment->comment_ID ) ) { 86 wp_redirect( admin_url('edit-comments.php?error=2') ); 87 die(); 88 } 93 89 94 95 96 97 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 } 99 95 100 96 require_once( ABSPATH . 'wp-admin/admin-header.php' ); 101 97 102 103 104 98 $formaction = $action . 'comment'; 99 $nonce_action = 'approve' == $action ? 'approve-comment_' : 'delete-comment_'; 100 $nonce_action .= $comment_id; 105 101 106 102 ?> 107 103 <div class="wrap"> … … case 'spam' : 110 106 111 107 <?php 112 108 switch ( $action ) { 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 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; 129 125 } 130 126 131 127 if ( $comment->comment_approved != '0' ) { // if not unapproved 132 133 134 135 136 137 138 139 140 141 142 143 144 145 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 } 147 143 } 148 144 ?> 149 145 <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 166 162 </tr> 167 163 <?php } ?> 168 164 <tr> 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 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> 193 189 </tr> 194 190 <tr> 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 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> 211 207 </tr> 212 208 <tr> 213 209 <th scope="row"><?php /* translators: field name in comment form */ _ex('Comment', 'noun'); ?></th> 214 210 <td class="comment-content"> 215 216 211 <?php comment_text( $comment ); ?> 212 <p class="edit-comment"><a href="<?php echo admin_url( "comment.php?action=editcomment&c={$comment->comment_ID}" ); ?>"><?php esc_html_e( 'Edit' ); ?></a></p> 217 213 </td> 218 214 </tr> 219 215 </table> … … if ( $comment->comment_approved != '0' ) { // if not unapproved 221 217 <form action="comment.php" method="get" class="comment-ays-submit"> 222 218 223 219 <p> 224 225 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> 226 222 </p> 227 223 228 224 <?php wp_nonce_field( $nonce_action ); ?> … … if ( $comment->comment_approved != '0' ) { // if not unapproved 233 229 234 230 </div> 235 231 <?php 236 232 break; 237 233 238 234 case 'deletecomment' : 239 235 case 'trashcomment' : … … case 'spamcomment' : 242 238 case 'unspamcomment' : 243 239 case 'approvecomment' : 244 240 case 'unapprovecomment' : 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 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; 303 299 304 300 case 'editedcomment' : 305 301 306 307 302 $comment_id = absint( $_POST['comment_ID'] ); 303 $comment_post_id = absint( $_POST['comment_post_ID'] ); 308 304 309 305 check_admin_referer( 'update-comment_' . $comment_id ); 310 306 311 307 edit_comment(); 312 308 313 309 $location = ( empty( $_POST['referredby'] ) ? "edit-comments.php?p=$comment_post_id" : $_POST['referredby'] ) . '#comment-' . $comment_id; 314 310 315 316 317 318 319 320 321 322 323 324 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 ); 325 321 326 322 exit(); 327 323 328 324 default: 329 325 wp_die( __('Unknown action.') ); 330 326 331 327 } // end switch 332 328 -
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 ); 13 13 require_once( dirname( __FILE__ ) . '/admin.php' ); 14 14 15 15 if ( ! current_user_can( 'customize' ) ) { 16 17 18 19 20 16 wp_die( 17 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' . 18 '<p>' . __( 'Sorry, you are not allowed to customize this site.' ) . '</p>', 19 403 20 ); 21 21 } 22 22 23 23 /** … … if ( ! current_user_can( 'customize' ) ) { 27 27 global $wp_scripts, $wp_customize; 28 28 29 29 if ( $wp_customize->changeset_post_id() ) { 30 31 32 33 34 35 36 37 38 39 40 41 42 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’ 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’ 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 } 45 45 } 46 46 47 48 wp_reset_vars( array( 'url', 'return', 'autofocus' ) ); 47 $url = wp_assign_request_var('url'); 49 48 if ( ! empty( $url ) ) { 50 49 $wp_customize->set_preview_url( wp_unslash( $url ) ); 51 50 } 51 52 $return = wp_assign_request_var('return'); 52 53 if ( ! empty( $return ) ) { 53 54 $wp_customize->set_return_url( wp_unslash( $return ) ); 54 55 } 56 57 $autofocus = wp_assign_request_var('autofocus'); 55 58 if ( ! empty( $autofocus ) && is_array( $autofocus ) ) { 56 59 $wp_customize->set_autofocus( wp_unslash( $autofocus ) ); 57 60 } 58 61 59 62 $registered = $wp_scripts->registered; … … _wp_admin_html_begin(); 90 93 $body_class = 'wp-core-ui wp-customizer js'; 91 94 92 95 if ( wp_is_mobile() ) : 93 96 $body_class .= ' mobile'; 94 97 95 98 ?><meta name="viewport" id="viewport-meta" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=1.2" /><?php 96 99 endif; 97 100 98 101 if ( $wp_customize->is_ios() ) { 99 102 $body_class .= ' ios'; 100 103 } 101 104 102 105 if ( is_rtl() ) { 103 106 $body_class .= ' rtl'; 104 107 } 105 108 $body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) ); 106 109 … … do_action( 'customize_controls_print_scripts' ); 130 133 </head> 131 134 <body class="<?php echo esc_attr( $body_class ); ?>"> 132 135 <div class="wp-full-overlay expanded"> 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 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 & Publish' ) : __( 'Save & 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 ?> 212 215 </div> 213 216 </body> 214 217 </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 8 8 9 9 // don't load directly 10 10 if ( ! defined( 'ABSPATH' ) ) { 11 11 die( '-1' ); 12 12 } 13 13 14 14 // Back compat hooks 15 15 if ( 'category' == $taxonomy ) { 16 17 18 19 20 21 22 23 24 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 ); 25 25 } elseif ( 'link_category' == $taxonomy ) { 26 27 28 29 30 31 32 33 34 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 ); 35 35 } else { 36 37 38 39 40 41 42 43 44 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 ); 45 45 } 46 46 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'); 51 48 52 49 $wp_http_referer = remove_query_arg( array( 'action', 'message', 'tag_ID' ), $wp_http_referer ); 53 50 … … do_action( "{$taxonomy}_pre_edit_form", $tag, $taxonomy ); ?> 72 69 73 70 <?php if ( $message ) : ?> 74 71 <div id="message" class="updated"> 75 76 77 78 79 80 81 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( '← Back to %s', 'admin screen' ), $tax->labels->name ); 77 ?></a></p> 78 <?php } ?> 82 79 </div> 83 80 <?php endif; ?> 84 81 … … wp_nonce_field( 'update-tag_' . $tag_ID ); 115 112 */ 116 113 do_action( "{$taxonomy}_term_edit_form_top", $tag, $taxonomy ); 117 114 ?> 118 119 120 121 122 123 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> 124 121 <?php if ( !global_terms_enabled() ) { ?> 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 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 “slug” is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.'); ?></p></td> 142 </tr> 146 143 <?php } ?> 147 144 <?php if ( is_taxonomy_hierarchical($taxonomy) ) : ?> 148 149 150 151 152 153 154 155 156 157 158 159 160 161 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 ); 163 160 164 165 166 167 168 169 170 171 172 173 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> 174 171 <?php endif; // is_taxonomy_hierarchical() ?> 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 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> 227 224 <?php 228 225 // Back compat hooks 229 226 if ( 'category' == $taxonomy ) { 230 231 227 /** This action is documented in wp-admin/edit-tags.php */ 228 do_action( 'edit_category_form', $tag ); 232 229 } elseif ( 'link_category' == $taxonomy ) { 233 234 230 /** This action is documented in wp-admin/edit-tags.php */ 231 do_action( 'edit_link_category_form', $tag ); 235 232 } else { 236 237 238 239 240 241 242 243 244 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 ); 245 242 } 246 243 /** 247 244 * Fires at the end of the Edit Term form for all taxonomies. … … do_action( "{$taxonomy}_edit_form", $tag, $taxonomy ); 258 255 259 256 <div class="edit-tag-actions"> 260 257 261 258 <?php submit_button( __( 'Update' ), 'primary', null, false ); ?> 262 259 263 264 265 266 267 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; ?> 268 265 269 266 </div> 270 267 -
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 17 17 */ 18 18 class WP_Links_List_Table extends WP_List_Table { 19 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 } 36 36 37 38 39 40 41 42 43 37 /** 38 * 39 * @return bool 40 */ 41 public function ajax_user_can() { 42 return current_user_can( 'manage_links' ); 43 } 44 44 45 /**46 *47 * @global int $cat_id48 * @global string $s49 * @global string $orderby50 * @global string $order51 */52 public function prepare_items() {53 global $cat_id, $s, $orderby, $order;54 45 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'); 56 51 57 $args = array( 'hide_invisible' => 0, 'hide_empty' => 0 );58 52 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 ); 67 54 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 } 70 67 71 /** 72 * @access public 73 */ 74 public function no_items() { 75 _e( 'No links found.' ); 76 } 68 $this->items = get_bookmarks( $args ); 69 } 77 70 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 } 85 77 86 return $actions; 87 } 78 /** 79 * 80 * @return array 81 */ 82 protected function get_bulk_actions() { 83 $actions = array(); 84 $actions['delete'] = __( 'Delete' ); 88 85 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 } 96 88 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; 99 99 ?> 100 100 <div class="alignleft actions"> 101 101 <?php 102 103 104 105 106 107 108 109 110 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 ); 112 112 113 114 115 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' ) ); 116 116 ?> 117 117 </div> 118 118 <?php 119 119 } 120 120 121 122 123 124 125 126 127 128 129 130 131 132 133 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 } 136 136 137 138 139 140 141 142 143 144 145 146 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 } 149 149 150 151 152 153 154 155 156 157 158 159 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 } 161 161 162 163 164 165 166 167 168 169 170 171 172 173 174 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 } 176 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 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 “%s”' ), $link->link_name ) ), 191 $link->link_name 192 ); 193 } 194 194 195 196 197 198 199 200 201 202 203 204 205 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 } 207 207 208 209 210 211 212 213 214 215 216 217 218 219 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; 220 220 221 222 223 224 225 226 227 228 229 230 231 232 233 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 } 235 235 236 237 238 239 240 241 242 243 244 245 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 } 247 247 248 249 250 251 252 253 254 255 256 257 258 259 260 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 } 263 263 264 265 266 267 268 269 270 271 272 273 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 } 275 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 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 } 296 296 297 298 299 300 301 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 ); 302 302 ?> 303 304 305 303 <tr id="link-<?php echo $link->link_id; ?>"> 304 <?php $this->single_row_columns( $link ) ?> 305 </tr> 306 306 <?php 307 308 307 } 308 } 309 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 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 } 325 325 326 326 $edit_link = get_edit_bookmark_link( $link ); 327 327 328 329 330 331 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&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 } 333 333 } -
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 17 17 */ 18 18 class WP_MS_Themes_List_Table extends WP_List_Table { 19 19 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}&"; 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}&"; 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 } 732 726 } -
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 17 17 */ 18 18 class WP_Plugin_Install_List_Table extends WP_List_Table { 19 19 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' ); 274 266 ?> 275 267 <div class="wp-filter"> 276 277 278 279 280 281 282 283 284 285 286 287 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(); ?> 288 280 </div> 289 281 <?php 290 282 } 291 283 292 293 294 295 296 284 /** 285 * Override the parent display() so we can provide a different container. 286 */ 287 public function display() { 288 $singular = $this->_args['singular']; 297 289 298 290 $data_attr = ''; 299 291 300 301 302 292 if ( $singular ) { 293 $data_attr = " data-wp-lists='list:$singular'"; 294 } 303 295 304 296 $this->display_tablenav( 'top' ); 305 297 306 298 ?> 307 299 <div class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>"> 308 300 <?php 309 301 $this->screen->render_screen_reader_content( 'heading_list' ); 310 302 ?> 311 312 313 303 <div id="the-list"<?php echo $data_attr; ?>> 304 <?php $this->display_rows_or_placeholder(); ?> 305 </div> 314 306 </div> 315 307 <?php 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 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&plugin=' . $plugin['slug'] . 496 '&TB_iframe=true&width=600&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 } 596 588 } -
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 17 17 */ 18 18 class WP_Plugins_List_Table extends WP_List_Table { 19 19 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 “%s”.' ), $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&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&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&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&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&checked[]=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&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&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&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&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&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&checked[]=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&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'] : ' ' ) . '</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'. 875 */ 876 protected function get_primary_column_name() { 877 return 'name'; 878 } 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 public function prepare_items() { 66 $status = wp_assign_request_var('status'); 67 $plugin = wp_assign_request_var('plugin'); 68 $totals = wp_assign_request_var('totals'); 69 $page = wp_assign_request_var('page'); 70 $orderby = wp_assign_request_var('orderby'); 71 $order = wp_assign_request_var('order'); 72 $s = wp_assign_request_var('s'); 73 74 /** 75 * Filters the full array of plugins to list in the Plugins list table. 76 * 77 * @since 3.0.0 78 * 79 * @see get_plugins() 80 * 81 * @param array $all_plugins An array of plugins to display in the list table. 82 */ 83 $all_plugins = apply_filters( 'all_plugins', get_plugins() ); 84 85 $plugins = array( 86 'all' => $all_plugins, 87 'search' => array(), 88 'active' => array(), 89 'inactive' => array(), 90 'recently_activated' => array(), 91 'upgrade' => array(), 92 'mustuse' => array(), 93 'dropins' => array(), 94 ); 95 96 $screen = $this->screen; 97 98 if ( ! is_multisite() || ( $screen->in_admin( 'network' ) && current_user_can( 'manage_network_plugins' ) ) ) { 99 100 /** 101 * Filters whether to display the advanced plugins list table. 102 * 103 * There are two types of advanced plugins - must-use and drop-ins - 104 * which can be used in a single site or Multisite network. 105 * 106 * The $type parameter allows you to differentiate between the type of advanced 107 * plugins to filter the display of. Contexts include 'mustuse' and 'dropins'. 108 * 109 * @since 3.0.0 110 * 111 * @param bool $show Whether to show the advanced plugins for the specified 112 * plugin type. Default true. 113 * @param string $type The plugin type. Accepts 'mustuse', 'dropins'. 114 */ 115 if ( apply_filters( 'show_advanced_plugins', true, 'mustuse' ) ) { 116 $plugins['mustuse'] = get_mu_plugins(); 117 } 118 119 /** This action is documented in wp-admin/includes/class-wp-plugins-list-table.php */ 120 if ( apply_filters( 'show_advanced_plugins', true, 'dropins' ) ) 121 $plugins['dropins'] = get_dropins(); 122 123 if ( current_user_can( 'update_plugins' ) ) { 124 $current = get_site_transient( 'update_plugins' ); 125 foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) { 126 if ( isset( $current->response[ $plugin_file ] ) ) { 127 $plugins['all'][ $plugin_file ]['update'] = true; 128 $plugins['upgrade'][ $plugin_file ] = $plugins['all'][ $plugin_file ]; 129 } 130 } 131 } 132 } 133 134 if ( ! $screen->in_admin( 'network' ) ) { 135 $show = current_user_can( 'manage_network_plugins' ); 136 /** 137 * Filters whether to display network-active plugins alongside plugins active for the current site. 138 * 139 * This also controls the display of inactive network-only plugins (plugins with 140 * "Network: true" in the plugin header). 141 * 142 * Plugins cannot be network-activated or network-deactivated from this screen. 143 * 144 * @since 4.4.0 145 * 146 * @param bool $show Whether to show network-active plugins. Default is whether the current 147 * user can manage network plugins (ie. a Super Admin). 148 */ 149 $show_network_active = apply_filters( 'show_network_active_plugins', $show ); 150 } 151 152 set_transient( 'plugin_slugs', array_keys( $plugins['all'] ), DAY_IN_SECONDS ); 153 154 if ( $screen->in_admin( 'network' ) ) { 155 $recently_activated = get_site_option( 'recently_activated', array() ); 156 } else { 157 $recently_activated = get_option( 'recently_activated', array() ); 158 } 159 160 foreach ( $recently_activated as $key => $time ) { 161 if ( $time + WEEK_IN_SECONDS < time() ) { 162 unset( $recently_activated[$key] ); 163 } 164 } 165 166 if ( $screen->in_admin( 'network' ) ) { 167 update_site_option( 'recently_activated', $recently_activated ); 168 } else { 169 update_option( 'recently_activated', $recently_activated ); 170 } 171 172 $plugin_info = get_site_transient( 'update_plugins' ); 173 174 foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) { 175 // Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide. 176 if ( isset( $plugin_info->response[ $plugin_file ] ) ) { 177 $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data ); 178 // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade 179 if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) { 180 $plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data ); 181 } 182 183 } elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) { 184 $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data ); 185 // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade 186 if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) { 187 $plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data ); 188 } 189 } 190 191 // Filter into individual sections 192 if ( is_multisite() && ! $screen->in_admin( 'network' ) && is_network_only_plugin( $plugin_file ) && ! is_plugin_active( $plugin_file ) ) { 193 if ( $show_network_active ) { 194 // On the non-network screen, show inactive network-only plugins if allowed 195 $plugins['inactive'][ $plugin_file ] = $plugin_data; 196 } else { 197 // On the non-network screen, filter out network-only plugins as long as they're not individually active 198 unset( $plugins['all'][ $plugin_file ] ); 199 } 200 } elseif ( ! $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) { 201 if ( $show_network_active ) { 202 // On the non-network screen, show network-active plugins if allowed 203 $plugins['active'][ $plugin_file ] = $plugin_data; 204 } else { 205 // On the non-network screen, filter out network-active plugins 206 unset( $plugins['all'][ $plugin_file ] ); 207 } 208 } elseif ( ( ! $screen->in_admin( 'network' ) && is_plugin_active( $plugin_file ) ) 209 || ( $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) ) { 210 // On the non-network screen, populate the active list with plugins that are individually activated 211 // On the network-admin screen, populate the active list with plugins that are network activated 212 $plugins['active'][ $plugin_file ] = $plugin_data; 213 } else { 214 if ( isset( $recently_activated[ $plugin_file ] ) ) { 215 // Populate the recently activated list with plugins that have been recently activated 216 $plugins['recently_activated'][ $plugin_file ] = $plugin_data; 217 } 218 // Populate the inactive list with plugins that aren't activated 219 $plugins['inactive'][ $plugin_file ] = $plugin_data; 220 } 221 } 222 223 if ( strlen( $s ) ) { 224 $status = 'search'; 225 $plugins['search'] = array_filter( $plugins['all'], array( $this, '_search_callback' ) ); 226 } 227 228 $totals = array(); 229 foreach ( $plugins as $type => $list ) 230 $totals[ $type ] = count( $list ); 231 232 if ( empty( $plugins[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) ) 233 $status = 'all'; 234 235 $this->items = array(); 236 foreach ( $plugins[ $status ] as $plugin_file => $plugin_data ) { 237 // Translate, Don't Apply Markup, Sanitize HTML 238 $this->items[$plugin_file] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true ); 239 } 240 241 $total_this_page = $totals[ $status ]; 242 243 $js_plugins = array(); 244 foreach ( $plugins as $key => $list ) { 245 $js_plugins[ $key ] = array_keys( (array) $list ); 246 } 247 248 wp_localize_script( 'updates', '_wpUpdatesItemCounts', array( 249 'plugins' => $js_plugins, 250 'totals' => wp_get_update_data(), 251 ) ); 252 253 if ( ! $orderby ) { 254 $orderby = 'Name'; 255 } else { 256 $orderby = ucfirst( $orderby ); 257 } 258 259 $order = strtoupper( $order ); 260 261 uasort( $this->items, array( $this, '_order_callback' ) ); 262 263 $plugins_per_page = $this->get_items_per_page( str_replace( '-', '_', $screen->id . '_per_page' ), 999 ); 264 265 $start = ( $page - 1 ) * $plugins_per_page; 266 267 if ( $total_this_page > $plugins_per_page ) 268 $this->items = array_slice( $this->items, $start, $plugins_per_page ); 269 270 $this->set_pagination_args( array( 271 'total_items' => $total_this_page, 272 'per_page' => $plugins_per_page, 273 ) ); 274 } 275 276 /** 277 * @global string $s URL encoded search term. 278 * 279 * @param array $plugin 280 * @return bool 281 */ 282 public function _search_callback( $plugin ) { 283 global $s; 284 285 foreach ( $plugin as $value ) { 286 if ( is_string( $value ) && false !== stripos( strip_tags( $value ), urldecode( $s ) ) ) { 287 return true; 288 } 289 } 290 291 return false; 292 } 293 294 /** 295 * @global string $orderby 296 * @global string $order 297 * @param array $plugin_a 298 * @param array $plugin_b 299 * @return int 300 */ 301 public function _order_callback( $plugin_a, $plugin_b ) { 302 global $orderby, $order; 303 304 $a = $plugin_a[$orderby]; 305 $b = $plugin_b[$orderby]; 306 307 if ( $a == $b ) 308 return 0; 309 310 if ( 'DESC' === $order ) { 311 return strcasecmp( $b, $a ); 312 } else { 313 return strcasecmp( $a, $b ); 314 } 315 } 316 317 /** 318 * 319 * @global array $plugins 320 */ 321 public function no_items() { 322 global $plugins; 323 324 if ( ! empty( $_REQUEST['s'] ) ) { 325 $s = esc_html( wp_unslash( $_REQUEST['s'] ) ); 326 327 printf( __( 'No plugins found for “%s”.' ), $s ); 328 329 // We assume that somebody who can install plugins in multisite is experienced enough to not need this helper link. 330 if ( ! is_multisite() && current_user_can( 'install_plugins' ) ) { 331 echo ' <a href="' . esc_url( admin_url( 'plugin-install.php?tab=search&s=' . urlencode( $s ) ) ) . '">' . __( 'Search for plugins in the WordPress Plugin Directory.' ) . '</a>'; 332 } 333 } elseif ( ! empty( $plugins['all'] ) ) 334 _e( 'No plugins found.' ); 335 else 336 _e( 'You do not appear to have any plugins available at this time.' ); 337 } 338 339 /** 340 * Displays the search box. 341 * 342 * @since 4.6.0 343 * @access public 344 * 345 * @param string $text The 'submit' button label. 346 * @param string $input_id ID attribute value for the search input field. 347 */ 348 public function search_box( $text, $input_id ) { 349 if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) { 350 return; 351 } 352 353 $input_id = $input_id . '-search-input'; 354 355 if ( ! empty( $_REQUEST['orderby'] ) ) { 356 echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />'; 357 } 358 if ( ! empty( $_REQUEST['order'] ) ) { 359 echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />'; 360 } 361 ?> 362 <p class="search-box"> 363 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label> 364 <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...' ); ?>"/> 365 <?php submit_button( $text, 'hide-if-js', '', false, array( 'id' => 'search-submit' ) ); ?> 366 </p> 367 <?php 368 } 369 370 /** 371 * 372 * @global string $status 373 * @return array 374 */ 375 public function get_columns() { 376 global $status; 377 378 return array( 379 'cb' => !in_array( $status, array( 'mustuse', 'dropins' ) ) ? '<input type="checkbox" />' : '', 380 'name' => __( 'Plugin' ), 381 'description' => __( 'Description' ), 382 ); 383 } 384 385 /** 386 * @return array 387 */ 388 protected function get_sortable_columns() { 389 return array(); 390 } 391 392 /** 393 * 394 * @global array $totals 395 * @global string $status 396 * @return array 397 */ 398 protected function get_views() { 399 global $totals, $status; 400 401 $status_links = array(); 402 foreach ( $totals as $type => $count ) { 403 if ( !$count ) 404 continue; 405 406 switch ( $type ) { 407 case 'all': 408 $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins' ); 409 break; 410 case 'active': 411 $text = _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count ); 412 break; 413 case 'recently_activated': 414 $text = _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count ); 415 break; 416 case 'inactive': 417 $text = _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count ); 418 break; 419 case 'mustuse': 420 $text = _n( 'Must-Use <span class="count">(%s)</span>', 'Must-Use <span class="count">(%s)</span>', $count ); 421 break; 422 case 'dropins': 423 $text = _n( 'Drop-ins <span class="count">(%s)</span>', 'Drop-ins <span class="count">(%s)</span>', $count ); 424 break; 425 case 'upgrade': 426 $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count ); 427 break; 428 } 429 430 if ( 'search' !== $type ) { 431 $status_links[$type] = sprintf( "<a href='%s' %s>%s</a>", 432 add_query_arg('plugin_status', $type, 'plugins.php'), 433 ( $type === $status ) ? ' class="current"' : '', 434 sprintf( $text, number_format_i18n( $count ) ) 435 ); 436 } 437 } 438 439 return $status_links; 440 } 441 442 /** 443 * 444 * @global string $status 445 * @return array 446 */ 447 protected function get_bulk_actions() { 448 global $status; 449 450 $actions = array(); 451 452 if ( 'active' != $status ) 453 $actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Activate' ) : __( 'Activate' ); 454 455 if ( 'inactive' != $status && 'recent' != $status ) 456 $actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Deactivate' ) : __( 'Deactivate' ); 457 458 if ( !is_multisite() || $this->screen->in_admin( 'network' ) ) { 459 if ( current_user_can( 'update_plugins' ) ) 460 $actions['update-selected'] = __( 'Update' ); 461 if ( current_user_can( 'delete_plugins' ) && ( 'active' != $status ) ) 462 $actions['delete-selected'] = __( 'Delete' ); 463 } 464 465 return $actions; 466 } 467 468 /** 469 * @global string $status 470 * @param string $which 471 */ 472 public function bulk_actions( $which = '' ) { 473 global $status; 474 475 if ( in_array( $status, array( 'mustuse', 'dropins' ) ) ) 476 return; 477 478 parent::bulk_actions( $which ); 479 } 480 481 /** 482 * @global string $status 483 * @param string $which 484 */ 485 protected function extra_tablenav( $which ) { 486 global $status; 487 488 if ( ! in_array($status, array('recently_activated', 'mustuse', 'dropins') ) ) 489 return; 490 491 echo '<div class="alignleft actions">'; 492 493 if ( 'recently_activated' == $status ) { 494 submit_button( __( 'Clear List' ), '', 'clear-recent-list', false ); 495 } elseif ( 'top' === $which && 'mustuse' === $status ) { 496 /* translators: %s: mu-plugins directory name */ 497 echo '<p>' . sprintf( __( 'Files in the %s directory are executed automatically.' ), 498 '<code>' . str_replace( ABSPATH, '/', WPMU_PLUGIN_DIR ) . '</code>' 499 ) . '</p>'; 500 } elseif ( 'top' === $which && 'dropins' === $status ) { 501 /* translators: %s: wp-content directory name */ 502 echo '<p>' . sprintf( __( 'Drop-ins are advanced plugins in the %s directory that replace WordPress functionality when present.' ), 503 '<code>' . str_replace( ABSPATH, '', WP_CONTENT_DIR ) . '</code>' 504 ) . '</p>'; 505 } 506 echo '</div>'; 507 } 508 509 /** 510 * @return string 511 */ 512 public function current_action() { 513 if ( isset($_POST['clear-recent-list']) ) 514 return 'clear-recent-list'; 515 516 return parent::current_action(); 517 } 518 519 /** 520 * 521 * @global string $status 522 */ 523 public function display_rows() { 524 global $status; 525 526 if ( is_multisite() && ! $this->screen->in_admin( 'network' ) && in_array( $status, array( 'mustuse', 'dropins' ) ) ) 527 return; 528 529 foreach ( $this->items as $plugin_file => $plugin_data ) 530 $this->single_row( array( $plugin_file, $plugin_data ) ); 531 } 532 533 /** 534 * @global string $status 535 * @global int $page 536 * @global string $s 537 * @global array $totals 538 * 539 * @param array $item 540 */ 541 public function single_row( $item ) { 542 global $status, $page, $s, $totals; 543 544 list( $plugin_file, $plugin_data ) = $item; 545 $context = $status; 546 $screen = $this->screen; 547 548 // Pre-order. 549 $actions = array( 550 'deactivate' => '', 551 'activate' => '', 552 'details' => '', 553 'edit' => '', 554 'delete' => '', 555 ); 556 557 // Do not restrict by default 558 $restrict_network_active = false; 559 $restrict_network_only = false; 560 561 if ( 'mustuse' === $context ) { 562 $is_active = true; 563 } elseif ( 'dropins' === $context ) { 564 $dropins = _get_dropins(); 565 $plugin_name = $plugin_file; 566 if ( $plugin_file != $plugin_data['Name'] ) 567 $plugin_name .= '<br/>' . $plugin_data['Name']; 568 if ( true === ( $dropins[ $plugin_file ][1] ) ) { // Doesn't require a constant 569 $is_active = true; 570 $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>'; 571 } elseif ( defined( $dropins[ $plugin_file ][1] ) && constant( $dropins[ $plugin_file ][1] ) ) { // Constant is true 572 $is_active = true; 573 $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>'; 574 } else { 575 $is_active = false; 576 $description = '<p><strong>' . $dropins[ $plugin_file ][0] . ' <span class="error-message">' . __( 'Inactive:' ) . '</span></strong> ' . 577 /* translators: 1: drop-in constant name, 2: wp-config.php */ 578 sprintf( __( 'Requires %1$s in %2$s file.' ), 579 "<code>define('" . $dropins[ $plugin_file ][1] . "', true);</code>", 580 '<code>wp-config.php</code>' 581 ) . '</p>'; 582 } 583 if ( $plugin_data['Description'] ) 584 $description .= '<p>' . $plugin_data['Description'] . '</p>'; 585 } else { 586 if ( $screen->in_admin( 'network' ) ) { 587 $is_active = is_plugin_active_for_network( $plugin_file ); 588 } else { 589 $is_active = is_plugin_active( $plugin_file ); 590 $restrict_network_active = ( is_multisite() && is_plugin_active_for_network( $plugin_file ) ); 591 $restrict_network_only = ( is_multisite() && is_network_only_plugin( $plugin_file ) && ! $is_active ); 592 } 593 594 if ( $screen->in_admin( 'network' ) ) { 595 if ( $is_active ) { 596 if ( current_user_can( 'manage_network_plugins' ) ) { 597 /* translators: %s: plugin name */ 598 $actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Deactivate' ) . '</a>'; 599 } 600 } else { 601 if ( current_user_can( 'manage_network_plugins' ) ) { 602 /* translators: %s: plugin name */ 603 $actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Activate' ) . '</a>'; 604 } 605 if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) { 606 /* translators: %s: plugin name */ 607 $actions['delete'] = '<a href="' . wp_nonce_url( 'plugins.php?action=delete-selected&checked[]=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'bulk-plugins' ) . '" class="delete" aria-label="' . esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Delete' ) . '</a>'; 608 } 609 } 610 } else { 611 if ( $restrict_network_active ) { 612 $actions = array( 613 'network_active' => __( 'Network Active' ), 614 ); 615 } elseif ( $restrict_network_only ) { 616 $actions = array( 617 'network_only' => __( 'Network Only' ), 618 ); 619 } elseif ( $is_active ) { 620 /* translators: %s: plugin name */ 621 $actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Deactivate' ) . '</a>'; 622 } else { 623 /* translators: %s: plugin name */ 624 $actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&plugin=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Activate' ) . '</a>'; 625 626 if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) { 627 /* translators: %s: plugin name */ 628 $actions['delete'] = '<a href="' . wp_nonce_url( 'plugins.php?action=delete-selected&checked[]=' . $plugin_file . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'bulk-plugins' ) . '" class="delete" aria-label="' . esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Delete' ) . '</a>'; 629 } 630 } // end if $is_active 631 632 } // end if $screen->in_admin( 'network' ) 633 634 if ( ( ! is_multisite() || $screen->in_admin( 'network' ) ) && current_user_can( 'edit_plugins' ) && is_writable( WP_PLUGIN_DIR . '/' . $plugin_file ) ) { 635 /* translators: %s: plugin name */ 636 $actions['edit'] = '<a href="plugin-editor.php?file=' . $plugin_file . '" class="edit" aria-label="' . esc_attr( sprintf( __( 'Edit %s' ), $plugin_data['Name'] ) ) . '">' . __( 'Edit' ) . '</a>'; 637 } 638 } // end if $context 639 640 $actions = array_filter( $actions ); 641 642 if ( $screen->in_admin( 'network' ) ) { 643 644 /** 645 * Filters the action links displayed for each plugin in the Network Admin Plugins list table. 646 * 647 * The default action links for the Network plugins list table include 648 * 'Network Activate', 'Network Deactivate', 'Edit', and 'Delete'. 649 * 650 * @since 3.1.0 651 * 652 * @param array $actions An array of plugin action links. 653 * @param string $plugin_file Path to the plugin file relative to the plugins directory. 654 * @param array $plugin_data An array of plugin data. 655 * @param string $context The plugin context. Defaults are 'All', 'Active', 656 * 'Inactive', 'Recently Activated', 'Upgrade', 657 * 'Must-Use', 'Drop-ins', 'Search'. 658 */ 659 $actions = apply_filters( 'network_admin_plugin_action_links', $actions, $plugin_file, $plugin_data, $context ); 660 661 /** 662 * Filters the list of action links displayed for a specific plugin in the Network Admin Plugins list table. 663 * 664 * The dynamic portion of the hook name, $plugin_file, refers to the path 665 * to the plugin file, relative to the plugins directory. 666 * 667 * @since 3.1.0 668 * 669 * @param array $actions An array of plugin action links. 670 * @param string $plugin_file Path to the plugin file relative to the plugins directory. 671 * @param array $plugin_data An array of plugin data. 672 * @param string $context The plugin context. Defaults are 'All', 'Active', 673 * 'Inactive', 'Recently Activated', 'Upgrade', 674 * 'Must-Use', 'Drop-ins', 'Search'. 675 */ 676 $actions = apply_filters( "network_admin_plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context ); 677 678 } else { 679 680 /** 681 * Filters the action links displayed for each plugin in the Plugins list table. 682 * 683 * The default action links for the site plugins list table include 684 * 'Activate', 'Deactivate', and 'Edit', for a network site, and 685 * 'Activate', 'Deactivate', 'Edit', and 'Delete' for a single site. 686 * 687 * @since 2.5.0 688 * @since 2.6.0 The `$context` parameter was added. 689 * 690 * @param array $actions An array of plugin action links. 691 * @param string $plugin_file Path to the plugin file relative to the plugins directory. 692 * @param array $plugin_data An array of plugin data. 693 * @param string $context The plugin context. Defaults are 'All', 'Active', 694 * 'Inactive', 'Recently Activated', 'Upgrade', 695 * 'Must-Use', 'Drop-ins', 'Search'. 696 */ 697 $actions = apply_filters( 'plugin_action_links', $actions, $plugin_file, $plugin_data, $context ); 698 699 /** 700 * Filters the list of action links displayed for a specific plugin in the Plugins list table. 701 * 702 * The dynamic portion of the hook name, $plugin_file, refers to the path 703 * to the plugin file, relative to the plugins directory. 704 * 705 * @since 2.7.0 706 * 707 * @param array $actions An array of plugin action links. 708 * @param string $plugin_file Path to the plugin file relative to the plugins directory. 709 * @param array $plugin_data An array of plugin data. 710 * @param string $context The plugin context. Defaults are 'All', 'Active', 711 * 'Inactive', 'Recently Activated', 'Upgrade', 712 * 'Must-Use', 'Drop-ins', 'Search'. 713 */ 714 $actions = apply_filters( "plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context ); 715 716 } 717 718 $class = $is_active ? 'active' : 'inactive'; 719 $checkbox_id = "checkbox_" . md5($plugin_data['Name']); 720 if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ) ) ) { 721 $checkbox = ''; 722 } else { 723 $checkbox = "<label class='screen-reader-text' for='" . $checkbox_id . "' >" . sprintf( __( 'Select %s' ), $plugin_data['Name'] ) . "</label>" 724 . "<input type='checkbox' name='checked[]' value='" . esc_attr( $plugin_file ) . "' id='" . $checkbox_id . "' />"; 725 } 726 if ( 'dropins' != $context ) { 727 $description = '<p>' . ( $plugin_data['Description'] ? $plugin_data['Description'] : ' ' ) . '</p>'; 728 $plugin_name = $plugin_data['Name']; 729 } 730 731 if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) ) 732 $class .= ' update'; 733 734 $plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_name ); 735 printf( '<tr class="%s" data-slug="%s" data-plugin="%s">', 736 esc_attr( $class ), 737 esc_attr( $plugin_slug ), 738 esc_attr( $plugin_file ) 739 ); 740 741 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); 742 743 foreach ( $columns as $column_name => $column_display_name ) { 744 $extra_classes = ''; 745 if ( in_array( $column_name, $hidden ) ) { 746 $extra_classes = ' hidden'; 747 } 748 749 switch ( $column_name ) { 750 case 'cb': 751 echo "<th scope='row' class='check-column'>$checkbox</th>"; 752 break; 753 case 'name': 754 echo "<td class='plugin-title column-primary'><strong>$plugin_name</strong>"; 755 echo $this->row_actions( $actions, true ); 756 echo "</td>"; 757 break; 758 case 'description': 759 $classes = 'column-description desc'; 760 761 echo "<td class='$classes{$extra_classes}'> 762 <div class='plugin-description'>$description</div> 763 <div class='$class second plugin-version-author-uri'>"; 764 765 $plugin_meta = array(); 766 if ( !empty( $plugin_data['Version'] ) ) 767 $plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); 768 if ( !empty( $plugin_data['Author'] ) ) { 769 $author = $plugin_data['Author']; 770 if ( !empty( $plugin_data['AuthorURI'] ) ) 771 $author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>'; 772 $plugin_meta[] = sprintf( __( 'By %s' ), $author ); 773 } 774 775 // Details link using API info, if available 776 if ( isset( $plugin_data['slug'] ) && current_user_can( 'install_plugins' ) ) { 777 $plugin_meta[] = sprintf( '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>', 778 esc_url( network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_data['slug'] . 779 '&TB_iframe=true&width=600&height=550' ) ), 780 esc_attr( sprintf( __( 'More information about %s' ), $plugin_name ) ), 781 esc_attr( $plugin_name ), 782 __( 'View details' ) 783 ); 784 } elseif ( ! empty( $plugin_data['PluginURI'] ) ) { 785 $plugin_meta[] = sprintf( '<a href="%s">%s</a>', 786 esc_url( $plugin_data['PluginURI'] ), 787 __( 'Visit plugin site' ) 788 ); 789 } 790 791 /** 792 * Filters the array of row meta for each plugin in the Plugins list table. 793 * 794 * @since 2.8.0 795 * 796 * @param array $plugin_meta An array of the plugin's metadata, 797 * including the version, author, 798 * author URI, and plugin URI. 799 * @param string $plugin_file Path to the plugin file, relative to the plugins directory. 800 * @param array $plugin_data An array of plugin data. 801 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 802 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 803 * 'Drop-ins', 'Search'. 804 */ 805 $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status ); 806 echo implode( ' | ', $plugin_meta ); 807 808 echo "</div></td>"; 809 break; 810 default: 811 $classes = "$column_name column-$column_name $class"; 812 813 echo "<td class='$classes{$extra_classes}'>"; 814 815 /** 816 * Fires inside each custom column of the Plugins list table. 817 * 818 * @since 3.1.0 819 * 820 * @param string $column_name Name of the column. 821 * @param string $plugin_file Path to the plugin file. 822 * @param array $plugin_data An array of plugin data. 823 */ 824 do_action( 'manage_plugins_custom_column', $column_name, $plugin_file, $plugin_data ); 825 826 echo "</td>"; 827 } 828 } 829 830 echo "</tr>"; 831 832 /** 833 * Fires after each row in the Plugins list table. 834 * 835 * @since 2.3.0 836 * 837 * @param string $plugin_file Path to the plugin file, relative to the plugins directory. 838 * @param array $plugin_data An array of plugin data. 839 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 840 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 841 * 'Drop-ins', 'Search'. 842 */ 843 do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status ); 844 845 /** 846 * Fires after each specific row in the Plugins list table. 847 * 848 * The dynamic portion of the hook name, `$plugin_file`, refers to the path 849 * to the plugin file, relative to the plugins directory. 850 * 851 * @since 2.7.0 852 * 853 * @param string $plugin_file Path to the plugin file, relative to the plugins directory. 854 * @param array $plugin_data An array of plugin data. 855 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 856 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 857 * 'Drop-ins', 'Search'. 858 */ 859 do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status ); 860 } 861 862 /** 863 * Gets the name of the primary column for this specific list table. 864 * 865 * @since 4.3.0 866 * @access protected 867 * 868 * @return string Unalterable name for the primary column, in this case, 'name'. 869 */ 870 protected function get_primary_column_name() { 871 return 'name'; 872 } 879 873 } -
src/wp-admin/includes/class-wp-theme-install-list-table.php
diff --git a/src/wp-admin/includes/class-wp-theme-install-list-table.php b/src/wp-admin/includes/class-wp-theme-install-list-table.php index f459d67d1c..7532056377 100644
a b 17 17 */ 18 18 class WP_Theme_Install_List_Table extends WP_Themes_List_Table { 19 19 20 public $features = array(); 21 22 /** 23 * 24 * @return bool 25 */ 26 public function ajax_user_can() { 27 return current_user_can( 'install_themes' ); 28 } 29 30 /** 31 * 32 * @global array $tabs 33 * @global string $tab 34 * @global int $paged 35 * @global string $type 36 * @global array $theme_field_defaults 37 */ 38 public function prepare_items() { 39 include( ABSPATH . 'wp-admin/includes/theme-install.php' ); 40 41 global $tabs, $tab, $paged, $type, $theme_field_defaults; 42 wp_reset_vars( array( 'tab' ) ); 43 44 $search_terms = array(); 45 $search_string = ''; 46 if ( ! empty( $_REQUEST['s'] ) ){ 47 $search_string = strtolower( wp_unslash( $_REQUEST['s'] ) ); 48 $search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', $search_string ) ) ) ); 49 } 50 51 if ( ! empty( $_REQUEST['features'] ) ) 52 $this->features = $_REQUEST['features']; 53 54 $paged = $this->get_pagenum(); 55 56 $per_page = 36; 57 58 // These are the tabs which are shown on the page, 59 $tabs = array(); 60 $tabs['dashboard'] = __( 'Search' ); 61 if ( 'search' === $tab ) 62 $tabs['search'] = __( 'Search Results' ); 63 $tabs['upload'] = __( 'Upload' ); 64 $tabs['featured'] = _x( 'Featured', 'themes' ); 65 //$tabs['popular'] = _x( 'Popular', 'themes' ); 66 $tabs['new'] = _x( 'Latest', 'themes' ); 67 $tabs['updated'] = _x( 'Recently Updated', 'themes' ); 68 69 $nonmenu_tabs = array( 'theme-information' ); // Valid actions to perform which do not have a Menu item. 70 71 /** This filter is documented in wp-admin/theme-install.php */ 72 $tabs = apply_filters( 'install_themes_tabs', $tabs ); 73 74 /** 75 * Filters tabs not associated with a menu item on the Install Themes screen. 76 * 77 * @since 2.8.0 78 * 79 * @param array $nonmenu_tabs The tabs that don't have a menu item on 80 * the Install Themes screen. 81 */ 82 $nonmenu_tabs = apply_filters( 'install_themes_nonmenu_tabs', $nonmenu_tabs ); 83 84 // If a non-valid menu tab has been selected, And it's not a non-menu action. 85 if ( empty( $tab ) || ( ! isset( $tabs[ $tab ] ) && ! in_array( $tab, (array) $nonmenu_tabs ) ) ) 86 $tab = key( $tabs ); 87 88 $args = array( 'page' => $paged, 'per_page' => $per_page, 'fields' => $theme_field_defaults ); 89 90 switch ( $tab ) { 91 case 'search': 92 $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term'; 93 switch ( $type ) { 94 case 'tag': 95 $args['tag'] = array_map( 'sanitize_key', $search_terms ); 96 break; 97 case 'term': 98 $args['search'] = $search_string; 99 break; 100 case 'author': 101 $args['author'] = $search_string; 102 break; 103 } 104 105 if ( ! empty( $this->features ) ) { 106 $args['tag'] = $this->features; 107 $_REQUEST['s'] = implode( ',', $this->features ); 108 $_REQUEST['type'] = 'tag'; 109 } 110 111 add_action( 'install_themes_table_header', 'install_theme_search_form', 10, 0 ); 112 break; 113 114 case 'featured': 115 // case 'popular': 116 case 'new': 117 case 'updated': 118 $args['browse'] = $tab; 119 break; 120 121 default: 122 $args = false; 123 break; 124 } 125 126 /** 127 * Filters API request arguments for each Install Themes screen tab. 128 * 129 * The dynamic portion of the hook name, `$tab`, refers to the theme install 130 * tabs. Default tabs are 'dashboard', 'search', 'upload', 'featured', 131 * 'new', and 'updated'. 132 * 133 * @since 3.7.0 134 * 135 * @param array $args An array of themes API arguments. 136 */ 137 $args = apply_filters( "install_themes_table_api_args_{$tab}", $args ); 138 139 if ( ! $args ) 140 return; 141 142 $api = themes_api( 'query_themes', $args ); 143 144 if ( is_wp_error( $api ) ) 145 wp_die( $api->get_error_message() . '</p> <p><a href="#" onclick="document.location.reload(); return false;">' . __( 'Try again' ) . '</a>' ); 146 147 $this->items = $api->themes; 148 149 $this->set_pagination_args( array( 150 'total_items' => $api->info['results'], 151 'per_page' => $args['per_page'], 152 'infinite_scroll' => true, 153 ) ); 154 } 155 156 /** 157 * @access public 158 */ 159 public function no_items() { 160 _e( 'No themes match your request.' ); 161 } 162 163 /** 164 * 165 * @global array $tabs 166 * @global string $tab 167 * @return array 168 */ 169 protected function get_views() { 170 global $tabs, $tab; 171 172 $display_tabs = array(); 173 foreach ( (array) $tabs as $action => $text ) { 174 $class = ( $action === $tab ) ? ' class="current"' : ''; 175 $href = self_admin_url('theme-install.php?tab=' . $action); 176 $display_tabs['theme-install-'.$action] = "<a href='$href'$class>$text</a>"; 177 } 178 179 return $display_tabs; 180 } 181 182 /** 183 * @access public 184 */ 185 public function display() { 186 wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' ); 20 public $features = array(); 21 22 /** 23 * 24 * @return bool 25 */ 26 public function ajax_user_can() { 27 return current_user_can( 'install_themes' ); 28 } 29 30 public function prepare_items() { 31 include( ABSPATH . 'wp-admin/includes/theme-install.php' ); 32 33 $tab = wp_assign_request_var('tab'); 34 $type = wp_assign_request_var('type'); 35 $term = wp_assign_request_var('term'); 36 $theme_field_defaults = wp_assign_request_var('theme_field_defaults'); 37 38 $search_terms = array(); 39 $search_string = ''; 40 if ( ! empty( $_REQUEST['s'] ) ){ 41 $search_string = strtolower( wp_unslash( $_REQUEST['s'] ) ); 42 $search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', $search_string ) ) ) ); 43 } 44 45 if ( ! empty( $_REQUEST['features'] ) ) 46 $this->features = $_REQUEST['features']; 47 48 $paged = $this->get_pagenum(); 49 50 $per_page = 36; 51 52 // These are the tabs which are shown on the page, 53 $tabs = array(); 54 $tabs['dashboard'] = __( 'Search' ); 55 if ( 'search' === $tab ) 56 $tabs['search'] = __( 'Search Results' ); 57 $tabs['upload'] = __( 'Upload' ); 58 $tabs['featured'] = _x( 'Featured', 'themes' ); 59 //$tabs['popular'] = _x( 'Popular', 'themes' ); 60 $tabs['new'] = _x( 'Latest', 'themes' ); 61 $tabs['updated'] = _x( 'Recently Updated', 'themes' ); 62 63 $nonmenu_tabs = array( 'theme-information' ); // Valid actions to perform which do not have a Menu item. 64 65 /** This filter is documented in wp-admin/theme-install.php */ 66 $tabs = apply_filters( 'install_themes_tabs', $tabs ); 67 68 /** 69 * Filters tabs not associated with a menu item on the Install Themes screen. 70 * 71 * @since 2.8.0 72 * 73 * @param array $nonmenu_tabs The tabs that don't have a menu item on 74 * the Install Themes screen. 75 */ 76 $nonmenu_tabs = apply_filters( 'install_themes_nonmenu_tabs', $nonmenu_tabs ); 77 78 // If a non-valid menu tab has been selected, And it's not a non-menu action. 79 if ( empty( $tab ) || ( ! isset( $tabs[ $tab ] ) && ! in_array( $tab, (array) $nonmenu_tabs ) ) ) 80 $tab = key( $tabs ); 81 82 $args = array( 'page' => $paged, 'per_page' => $per_page, 'fields' => $theme_field_defaults ); 83 84 switch ( $tab ) { 85 case 'search': 86 $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term'; 87 switch ( $type ) { 88 case 'tag': 89 $args['tag'] = array_map( 'sanitize_key', $search_terms ); 90 break; 91 case 'term': 92 $args['search'] = $search_string; 93 break; 94 case 'author': 95 $args['author'] = $search_string; 96 break; 97 } 98 99 if ( ! empty( $this->features ) ) { 100 $args['tag'] = $this->features; 101 $_REQUEST['s'] = implode( ',', $this->features ); 102 $_REQUEST['type'] = 'tag'; 103 } 104 105 add_action( 'install_themes_table_header', 'install_theme_search_form', 10, 0 ); 106 break; 107 108 case 'featured': 109 // case 'popular': 110 case 'new': 111 case 'updated': 112 $args['browse'] = $tab; 113 break; 114 115 default: 116 $args = false; 117 break; 118 } 119 120 /** 121 * Filters API request arguments for each Install Themes screen tab. 122 * 123 * The dynamic portion of the hook name, `$tab`, refers to the theme install 124 * tabs. Default tabs are 'dashboard', 'search', 'upload', 'featured', 125 * 'new', and 'updated'. 126 * 127 * @since 3.7.0 128 * 129 * @param array $args An array of themes API arguments. 130 */ 131 $args = apply_filters( "install_themes_table_api_args_{$tab}", $args ); 132 133 if ( ! $args ) 134 return; 135 136 $api = themes_api( 'query_themes', $args ); 137 138 if ( is_wp_error( $api ) ) 139 wp_die( $api->get_error_message() . '</p> <p><a href="#" onclick="document.location.reload(); return false;">' . __( 'Try again' ) . '</a>' ); 140 141 $this->items = $api->themes; 142 143 $this->set_pagination_args( array( 144 'total_items' => $api->info['results'], 145 'per_page' => $args['per_page'], 146 'infinite_scroll' => true, 147 ) ); 148 } 149 150 /** 151 * @access public 152 */ 153 public function no_items() { 154 _e( 'No themes match your request.' ); 155 } 156 157 /** 158 * 159 * @global array $tabs 160 * @global string $tab 161 * @return array 162 */ 163 protected function get_views() { 164 global $tabs, $tab; 165 166 $display_tabs = array(); 167 foreach ( (array) $tabs as $action => $text ) { 168 $class = ( $action === $tab ) ? ' class="current"' : ''; 169 $href = self_admin_url('theme-install.php?tab=' . $action); 170 $display_tabs['theme-install-'.$action] = "<a href='$href'$class>$text</a>"; 171 } 172 173 return $display_tabs; 174 } 175 176 /** 177 * @access public 178 */ 179 public function display() { 180 wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' ); 187 181 ?> 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 182 <div class="tablenav top themes"> 183 <div class="alignleft actions"> 184 <?php 185 /** 186 * Fires in the Install Themes list table header. 187 * 188 * @since 2.8.0 189 */ 190 do_action( 'install_themes_table_header' ); 191 ?> 192 </div> 193 <?php $this->pagination( 'top' ); ?> 194 <br class="clear" /> 195 </div> 196 197 <div id="availablethemes"> 198 <?php $this->display_rows_or_placeholder(); ?> 199 </div> 200 201 <?php 202 $this->tablenav( 'bottom' ); 203 } 204 205 /** 206 * @access public 207 */ 208 public function display_rows() { 209 $themes = $this->items; 210 foreach ( $themes as $theme ) { 211 ?> 212 <div class="available-theme installable-theme"><?php 213 $this->single_row( $theme ); 214 ?></div> 215 <?php } // end foreach $theme_names 216 217 $this->theme_installer(); 218 } 219 220 /** 221 * Prints a theme from the WordPress.org API. 222 * 223 * @since 3.1.0 224 * @access public 225 * 226 * @global array $themes_allowedtags 227 * 228 * @param object $theme { 229 * An object that contains theme data returned by the WordPress.org API. 230 * 231 * @type string $name Theme name, e.g. 'Twenty Seventeen'. 232 * @type string $slug Theme slug, e.g. 'twentyseventeen'. 233 * @type string $version Theme version, e.g. '1.1'. 234 * @type string $author Theme author username, e.g. 'melchoyce'. 235 * @type string $preview_url Preview URL, e.g. 'http://2017.wordpress.net/'. 236 * @type string $screenshot_url Screenshot URL, e.g. 'https://wordpress.org/themes/twentyseventeen/'. 237 * @type float $rating Rating score. 238 * @type int $num_ratings The number of ratings. 239 * @type string $homepage Theme homepage, e.g. 'https://wordpress.org/themes/twentyseventeen/'. 240 * @type string $description Theme description. 241 * @type string $download_link Theme ZIP download URL. 242 * } 243 */ 244 public function single_row( $theme ) { 245 global $themes_allowedtags; 246 247 if ( empty( $theme ) ) 248 return; 249 250 $name = wp_kses( $theme->name, $themes_allowedtags ); 251 $author = wp_kses( $theme->author, $themes_allowedtags ); 252 253 $preview_title = sprintf( __('Preview “%s”'), $name ); 254 $preview_url = add_query_arg( array( 255 'tab' => 'theme-information', 256 'theme' => $theme->slug, 257 ), self_admin_url( 'theme-install.php' ) ); 258 259 $actions = array(); 260 261 $install_url = add_query_arg( array( 262 'action' => 'install-theme', 263 'theme' => $theme->slug, 264 ), self_admin_url( 'update.php' ) ); 265 266 $update_url = add_query_arg( array( 267 'action' => 'upgrade-theme', 268 'theme' => $theme->slug, 269 ), self_admin_url( 'update.php' ) ); 270 271 $status = $this->_get_theme_status( $theme ); 272 273 switch ( $status ) { 274 case 'update_available': 275 $actions[] = '<a class="install-now" href="' . esc_url( wp_nonce_url( $update_url, 'upgrade-theme_' . $theme->slug ) ) . '" title="' . esc_attr( sprintf( __( 'Update to version %s' ), $theme->version ) ) . '">' . __( 'Update' ) . '</a>'; 276 break; 277 case 'newer_installed': 278 case 'latest_installed': 279 $actions[] = '<span class="install-now" title="' . esc_attr__( 'This theme is already installed and is up to date' ) . '">' . _x( 'Installed', 'theme' ) . '</span>'; 280 break; 281 case 'install': 282 default: 283 $actions[] = '<a class="install-now" href="' . esc_url( wp_nonce_url( $install_url, 'install-theme_' . $theme->slug ) ) . '" title="' . esc_attr( sprintf( __( 'Install %s' ), $name ) ) . '">' . __( 'Install Now' ) . '</a>'; 284 break; 285 } 286 287 $actions[] = '<a class="install-theme-preview" href="' . esc_url( $preview_url ) . '" title="' . esc_attr( sprintf( __( 'Preview %s' ), $name ) ) . '">' . __( 'Preview' ) . '</a>'; 288 289 /** 290 * Filters the install action links for a theme in the Install Themes list table. 291 * 292 * @since 3.4.0 293 * 294 * @param array $actions An array of theme action hyperlinks. Defaults are 295 * links to Install Now, Preview, and Details. 296 * @param WP_Theme $theme Theme object. 297 */ 298 $actions = apply_filters( 'theme_install_actions', $actions, $theme ); 299 300 ?> 301 <a class="screenshot install-theme-preview" href="<?php echo esc_url( $preview_url ); ?>" title="<?php echo esc_attr( $preview_title ); ?>"> 302 <img src="<?php echo esc_url( $theme->screenshot_url ); ?>" width="150" alt="" /> 303 </a> 304 305 <h3><?php echo $name; ?></h3> 306 <div class="theme-author"><?php printf( __( 'By %s' ), $author ); ?></div> 307 308 <div class="action-links"> 309 <ul> 310 <?php foreach ( $actions as $action ): ?> 311 <li><?php echo $action; ?></li> 312 <?php endforeach; ?> 313 <li class="hide-if-no-js"><a href="#" class="theme-detail"><?php _e('Details') ?></a></li> 314 </ul> 315 </div> 316 317 <?php 318 $this->install_theme_info( $theme ); 319 } 320 321 /** 322 * Prints the wrapper for the theme installer. 323 */ 324 public function theme_installer() { 325 ?> 326 <div id="theme-installer" class="wp-full-overlay expanded"> 327 <div class="wp-full-overlay-sidebar"> 328 <div class="wp-full-overlay-header"> 329 <a href="#" class="close-full-overlay button"><?php _e( 'Close' ); ?></a> 330 <span class="theme-install"></span> 331 </div> 332 <div class="wp-full-overlay-sidebar-content"> 333 <div class="install-theme-info"></div> 334 </div> 335 <div class="wp-full-overlay-footer"> 336 <button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php esc_attr_e( 'Collapse Sidebar' ); ?>"> 337 <span class="collapse-sidebar-arrow"></span> 338 <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span> 339 </button> 340 </div> 341 </div> 342 <div class="wp-full-overlay-main"></div> 343 </div> 344 <?php 345 } 346 347 /** 348 * Prints the wrapper for the theme installer with a provided theme's data. 349 * Used to make the theme installer work for no-js. 350 * 351 * @param object $theme - A WordPress.org Theme API object. 352 */ 353 public function theme_installer_single( $theme ) { 354 ?> 355 <div id="theme-installer" class="wp-full-overlay single-theme"> 356 <div class="wp-full-overlay-sidebar"> 357 <?php $this->install_theme_info( $theme ); ?> 358 </div> 359 <div class="wp-full-overlay-main"> 360 <iframe src="<?php echo esc_url( $theme->preview_url ); ?>"></iframe> 361 </div> 362 </div> 363 <?php 364 } 365 366 /** 367 * Prints the info for a theme (to be used in the theme installer modal). 368 * 369 * @global array $themes_allowedtags 370 * 371 * @param object $theme - A WordPress.org Theme API object. 372 */ 373 public function install_theme_info( $theme ) { 374 global $themes_allowedtags; 375 376 if ( empty( $theme ) ) 377 return; 378 379 $name = wp_kses( $theme->name, $themes_allowedtags ); 380 $author = wp_kses( $theme->author, $themes_allowedtags ); 381 382 $install_url = add_query_arg( array( 383 'action' => 'install-theme', 384 'theme' => $theme->slug, 385 ), self_admin_url( 'update.php' ) ); 386 387 $update_url = add_query_arg( array( 388 'action' => 'upgrade-theme', 389 'theme' => $theme->slug, 390 ), self_admin_url( 'update.php' ) ); 391 392 $status = $this->_get_theme_status( $theme ); 393 394 ?> 395 <div class="install-theme-info"><?php 396 switch ( $status ) { 397 case 'update_available': 398 echo '<a class="theme-install button button-primary" href="' . esc_url( wp_nonce_url( $update_url, 'upgrade-theme_' . $theme->slug ) ) . '" title="' . esc_attr( sprintf( __( 'Update to version %s' ), $theme->version ) ) . '">' . __( 'Update' ) . '</a>'; 399 break; 400 case 'newer_installed': 401 case 'latest_installed': 402 echo '<span class="theme-install" title="' . esc_attr__( 'This theme is already installed and is up to date' ) . '">' . _x( 'Installed', 'theme' ) . '</span>'; 403 break; 404 case 'install': 405 default: 406 echo '<a class="theme-install button button-primary" href="' . esc_url( wp_nonce_url( $install_url, 'install-theme_' . $theme->slug ) ) . '">' . __( 'Install' ) . '</a>'; 407 break; 408 } ?> 409 <h3 class="theme-name"><?php echo $name; ?></h3> 410 <span class="theme-by"><?php printf( __( 'By %s' ), $author ); ?></span> 411 <?php if ( isset( $theme->screenshot_url ) ): ?> 412 <img class="theme-screenshot" src="<?php echo esc_url( $theme->screenshot_url ); ?>" alt="" /> 413 <?php endif; ?> 414 <div class="theme-details"> 415 <?php wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings ) ); ?> 416 <div class="theme-version"> 417 <strong><?php _e('Version:') ?> </strong> 418 <?php echo wp_kses( $theme->version, $themes_allowedtags ); ?> 419 </div> 420 <div class="theme-description"> 421 <?php echo wp_kses( $theme->description, $themes_allowedtags ); ?> 422 </div> 423 </div> 424 <input class="theme-preview-url" type="hidden" value="<?php echo esc_url( $theme->preview_url ); ?>" /> 425 </div> 426 <?php 427 } 428 429 /** 430 * Send required variables to JavaScript land 431 * 432 * @since 3.4.0 433 * @access public 434 * 435 * @global string $tab Current tab within Themes->Install screen 436 * @global string $type Type of search. 437 * 438 * @param array $extra_args Unused. 439 */ 440 public function _js_vars( $extra_args = array() ) { 441 global $tab, $type; 442 parent::_js_vars( compact( 'tab', 'type' ) ); 443 } 444 445 /** 446 * Check to see if the theme is already installed. 447 * 448 * @since 3.4.0 449 * @access private 450 * 451 * @param object $theme - A WordPress.org Theme API object. 452 * @return string Theme status. 453 */ 454 private function _get_theme_status( $theme ) { 455 $status = 'install'; 456 457 $installed_theme = wp_get_theme( $theme->slug ); 458 if ( $installed_theme->exists() ) { 459 if ( version_compare( $installed_theme->get('Version'), $theme->version, '=' ) ) 460 $status = 'latest_installed'; 461 elseif ( version_compare( $installed_theme->get('Version'), $theme->version, '>' ) ) 462 $status = 'newer_installed'; 463 else 464 $status = 'update_available'; 465 } 466 467 return $status; 468 } 475 469 } -
src/wp-admin/includes/misc.php
diff --git a/src/wp-admin/includes/misc.php b/src/wp-admin/includes/misc.php index 679b9ac5d6..3a782a67ef 100644
a b 14 14 * @return bool 15 15 */ 16 16 function got_mod_rewrite() { 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 17 $got_rewrite = apache_mod_loaded('mod_rewrite', true); 18 19 /** 20 * Filters whether Apache and mod_rewrite are present. 21 * 22 * This filter was previously used to force URL rewriting for other servers, 23 * like nginx. Use the {@see 'got_url_rewrite'} filter in got_url_rewrite() instead. 24 * 25 * @since 2.5.0 26 * 27 * @see got_url_rewrite() 28 * 29 * @param bool $got_rewrite Whether Apache and mod_rewrite are present. 30 */ 31 return apply_filters( 'got_rewrite', $got_rewrite ); 32 32 } 33 33 34 34 /** … … function got_mod_rewrite() { 43 43 * @return bool Whether the server supports URL rewriting. 44 44 */ 45 45 function got_url_rewrite() { 46 47 48 49 50 51 52 53 54 55 46 $got_url_rewrite = ( got_mod_rewrite() || $GLOBALS['is_nginx'] || iis7_supports_permalinks() ); 47 48 /** 49 * Filters whether URL rewriting is available. 50 * 51 * @since 3.7.0 52 * 53 * @param bool $got_url_rewrite Whether URL rewriting is available. 54 */ 55 return apply_filters( 'got_url_rewrite', $got_url_rewrite ); 56 56 } 57 57 58 58 /** … … function got_url_rewrite() { 65 65 * @return array An array of strings from a file (.htaccess ) from between BEGIN and END markers. 66 66 */ 67 67 function extract_from_markers( $filename, $marker ) { 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 68 $result = array (); 69 70 if (!file_exists( $filename ) ) { 71 return $result; 72 } 73 74 if ( $markerdata = explode( "\n", implode( '', file( $filename ) ) )); 75 { 76 $state = false; 77 foreach ( $markerdata as $markerline ) { 78 if (strpos($markerline, '# END ' . $marker) !== false) 79 $state = false; 80 if ( $state ) 81 $result[] = $markerline; 82 if (strpos($markerline, '# BEGIN ' . $marker) !== false) 83 $state = true; 84 } 85 } 86 87 return $result; 88 88 } 89 89 90 90 /** … … function extract_from_markers( $filename, $marker ) { 102 102 * @return bool True on write success, false on failure. 103 103 */ 104 104 function insert_with_markers( $filename, $marker, $insertion ) { 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 105 if ( ! file_exists( $filename ) ) { 106 if ( ! is_writable( dirname( $filename ) ) ) { 107 return false; 108 } 109 if ( ! touch( $filename ) ) { 110 return false; 111 } 112 } elseif ( ! is_writeable( $filename ) ) { 113 return false; 114 } 115 116 if ( ! is_array( $insertion ) ) { 117 $insertion = explode( "\n", $insertion ); 118 } 119 120 $start_marker = "# BEGIN {$marker}"; 121 $end_marker = "# END {$marker}"; 122 123 $fp = fopen( $filename, 'r+' ); 124 if ( ! $fp ) { 125 return false; 126 } 127 128 // Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired. 129 flock( $fp, LOCK_EX ); 130 131 $lines = array(); 132 while ( ! feof( $fp ) ) { 133 $lines[] = rtrim( fgets( $fp ), "\r\n" ); 134 } 135 136 // Split out the existing file into the preceding lines, and those that appear after the marker 137 $pre_lines = $post_lines = $existing_lines = array(); 138 $found_marker = $found_end_marker = false; 139 foreach ( $lines as $line ) { 140 if ( ! $found_marker && false !== strpos( $line, $start_marker ) ) { 141 $found_marker = true; 142 continue; 143 } elseif ( ! $found_end_marker && false !== strpos( $line, $end_marker ) ) { 144 $found_end_marker = true; 145 continue; 146 } 147 if ( ! $found_marker ) { 148 $pre_lines[] = $line; 149 } elseif ( $found_marker && $found_end_marker ) { 150 $post_lines[] = $line; 151 } else { 152 $existing_lines[] = $line; 153 } 154 } 155 156 // Check to see if there was a change 157 if ( $existing_lines === $insertion ) { 158 flock( $fp, LOCK_UN ); 159 fclose( $fp ); 160 161 return true; 162 } 163 164 // Generate the new file data 165 $new_file_data = implode( "\n", array_merge( 166 $pre_lines, 167 array( $start_marker ), 168 $insertion, 169 array( $end_marker ), 170 $post_lines 171 ) ); 172 173 // Write to the start of the file, and truncate it to that length 174 fseek( $fp, 0 ); 175 $bytes = fwrite( $fp, $new_file_data ); 176 if ( $bytes ) { 177 ftruncate( $fp, ftell( $fp ) ); 178 } 179 fflush( $fp ); 180 flock( $fp, LOCK_UN ); 181 fclose( $fp ); 182 183 return (bool) $bytes; 184 184 } 185 185 186 186 /** … … function insert_with_markers( $filename, $marker, $insertion ) { 194 194 * @global WP_Rewrite $wp_rewrite 195 195 */ 196 196 function save_mod_rewrite_rules() { 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 197 if ( is_multisite() ) 198 return; 199 200 global $wp_rewrite; 201 202 $home_path = get_home_path(); 203 $htaccess_file = $home_path.'.htaccess'; 204 205 /* 206 * If the file doesn't already exist check for write access to the directory 207 * and whether we have some rules. Else check for write access to the file. 208 */ 209 if ((!file_exists($htaccess_file) && is_writable($home_path) && $wp_rewrite->using_mod_rewrite_permalinks()) || is_writable($htaccess_file)) { 210 if ( got_mod_rewrite() ) { 211 $rules = explode( "\n", $wp_rewrite->mod_rewrite_rules() ); 212 return insert_with_markers( $htaccess_file, 'WordPress', $rules ); 213 } 214 } 215 216 return false; 217 217 } 218 218 219 219 /** … … function save_mod_rewrite_rules() { 227 227 * @return bool True if web.config was updated successfully 228 228 */ 229 229 function iis7_save_url_rewrite_rules(){ 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 230 if ( is_multisite() ) 231 return; 232 233 global $wp_rewrite; 234 235 $home_path = get_home_path(); 236 $web_config_file = $home_path . 'web.config'; 237 238 // Using win_is_writable() instead of is_writable() because of a bug in Windows PHP 239 if ( iis7_supports_permalinks() && ( ( ! file_exists($web_config_file) && win_is_writable($home_path) && $wp_rewrite->using_mod_rewrite_permalinks() ) || win_is_writable($web_config_file) ) ) { 240 $rule = $wp_rewrite->iis7_url_rewrite_rules(false, '', ''); 241 if ( ! empty($rule) ) { 242 return iis7_add_rewrite_rule($web_config_file, $rule); 243 } else { 244 return iis7_delete_rewrite_rule($web_config_file); 245 } 246 } 247 return false; 248 248 } 249 249 250 250 /** … … function iis7_save_url_rewrite_rules(){ 255 255 * @param string $file 256 256 */ 257 257 function update_recently_edited( $file ) { 258 259 260 261 262 263 264 265 266 267 268 269 258 $oldfiles = (array ) get_option( 'recently_edited' ); 259 if ( $oldfiles ) { 260 $oldfiles = array_reverse( $oldfiles ); 261 $oldfiles[] = $file; 262 $oldfiles = array_reverse( $oldfiles ); 263 $oldfiles = array_unique( $oldfiles ); 264 if ( 5 < count( $oldfiles )) 265 array_pop( $oldfiles ); 266 } else { 267 $oldfiles[] = $file; 268 } 269 update_option( 'recently_edited', $oldfiles ); 270 270 } 271 271 272 272 /** … … function update_recently_edited( $file ) { 278 278 * @param string $value 279 279 */ 280 280 function update_home_siteurl( $old_value, $value ) { 281 282 283 284 285 286 287 288 281 if ( wp_installing() ) 282 return; 283 284 if ( is_multisite() && ms_is_switched() ) { 285 delete_option( 'rewrite_rules' ); 286 } else { 287 flush_rewrite_rules(); 288 } 289 289 } 290 290 291 291 … … function update_home_siteurl( $old_value, $value ) { 301 301 * @param array $vars An array of globals to reset. 302 302 */ 303 303 function wp_reset_vars( $vars ) { 304 foreach ( $vars as $var ) { 305 if ( empty( $_POST[ $var ] ) ) { 306 if ( empty( $_GET[ $var ] ) ) { 307 $GLOBALS[ $var ] = ''; 308 } else { 309 $GLOBALS[ $var ] = $_GET[ $var ]; 310 } 311 } else { 312 $GLOBALS[ $var ] = $_POST[ $var ]; 313 } 314 } 304 foreach ( $vars as $var ) { 305 if ( empty( $_POST[ $var ] ) ) { 306 if ( empty( $_GET[ $var ] ) ) { 307 $GLOBALS[ $var ] = ''; 308 } else { 309 $GLOBALS[ $var ] = $_GET[ $var ]; 310 } 311 } else { 312 $GLOBALS[ $var ] = $_POST[ $var ]; 313 } 314 } 315 } 316 317 /** 318 * Return $_POST[ $var ] or $_GET[ $var ] value. 319 * 320 * This functions returns $_POST[ $var ] or $_GET[ $var ] value in this order 321 * if they are not empty, otherwise it returns an empty string. 322 * 323 * @since 4.8.0 324 * 325 * @param string $var The key of the array for getting value to return. 326 * 327 * @return mixed $_POST[ $var ] or $_GET[ $var ] value or an empty string. 328 */ 329 function wp_assign_request_var( $var ) { 330 if ( empty( $_POST[ $var ] ) ) { 331 if ( empty( $_GET[ $var ] ) ) { 332 return ''; 333 } else { 334 return $_GET[ $var ]; 335 } 336 } else { 337 return $_POST[ $var ]; 338 } 315 339 } 316 340 317 341 /** … … function wp_reset_vars( $vars ) { 322 346 * @param string|WP_Error $message 323 347 */ 324 348 function show_message($message) { 325 326 327 328 329 330 331 332 333 349 if ( is_wp_error($message) ){ 350 if ( $message->get_error_data() && is_string( $message->get_error_data() ) ) 351 $message = $message->get_error_message() . ': ' . $message->get_error_data(); 352 else 353 $message = $message->get_error_message(); 354 } 355 echo "<p>$message</p>\n"; 356 wp_ob_end_flush_all(); 357 flush(); 334 358 } 335 359 336 360 /** … … function show_message($message) { 340 364 * @return array 341 365 */ 342 366 function wp_doc_link_parse( $content ) { 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 367 if ( !is_string( $content ) || empty( $content ) ) 368 return array(); 369 370 if ( !function_exists('token_get_all') ) 371 return array(); 372 373 $tokens = token_get_all( $content ); 374 $count = count( $tokens ); 375 $functions = array(); 376 $ignore_functions = array(); 377 for ( $t = 0; $t < $count - 2; $t++ ) { 378 if ( ! is_array( $tokens[ $t ] ) ) { 379 continue; 380 } 381 382 if ( T_STRING == $tokens[ $t ][0] && ( '(' == $tokens[ $t + 1 ] || '(' == $tokens[ $t + 2 ] ) ) { 383 // If it's a function or class defined locally, there's not going to be any docs available 384 if ( ( isset( $tokens[ $t - 2 ][1] ) && in_array( $tokens[ $t - 2 ][1], array( 'function', 'class' ) ) ) || ( isset( $tokens[ $t - 2 ][0] ) && T_OBJECT_OPERATOR == $tokens[ $t - 1 ][0] ) ) { 385 $ignore_functions[] = $tokens[$t][1]; 386 } 387 // Add this to our stack of unique references 388 $functions[] = $tokens[$t][1]; 389 } 390 } 391 392 $functions = array_unique( $functions ); 393 sort( $functions ); 394 395 /** 396 * Filters the list of functions and classes to be ignored from the documentation lookup. 397 * 398 * @since 2.8.0 399 * 400 * @param array $ignore_functions Functions and classes to be ignored. 401 */ 402 $ignore_functions = apply_filters( 'documentation_ignore_functions', $ignore_functions ); 403 404 $ignore_functions = array_unique( $ignore_functions ); 405 406 $out = array(); 407 foreach ( $functions as $function ) { 408 if ( in_array( $function, $ignore_functions ) ) 409 continue; 410 $out[] = $function; 411 } 412 413 return $out; 390 414 } 391 415 392 416 /** … … function wp_doc_link_parse( $content ) { 396 420 */ 397 421 function set_screen_options() { 398 422 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 423 if ( isset($_POST['wp_screen_options']) && is_array($_POST['wp_screen_options']) ) { 424 check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' ); 425 426 if ( !$user = wp_get_current_user() ) 427 return; 428 $option = $_POST['wp_screen_options']['option']; 429 $value = $_POST['wp_screen_options']['value']; 430 431 if ( $option != sanitize_key( $option ) ) 432 return; 433 434 $map_option = $option; 435 $type = str_replace('edit_', '', $map_option); 436 $type = str_replace('_per_page', '', $type); 437 if ( in_array( $type, get_taxonomies() ) ) 438 $map_option = 'edit_tags_per_page'; 439 elseif ( in_array( $type, get_post_types() ) ) 440 $map_option = 'edit_per_page'; 441 else 442 $option = str_replace('-', '_', $option); 443 444 switch ( $map_option ) { 445 case 'edit_per_page': 446 case 'users_per_page': 447 case 'edit_comments_per_page': 448 case 'upload_per_page': 449 case 'edit_tags_per_page': 450 case 'plugins_per_page': 451 // Network admin 452 case 'sites_network_per_page': 453 case 'users_network_per_page': 454 case 'site_users_network_per_page': 455 case 'plugins_network_per_page': 456 case 'themes_network_per_page': 457 case 'site_themes_network_per_page': 458 $value = (int) $value; 459 if ( $value < 1 || $value > 999 ) 460 return; 461 break; 462 default: 463 464 /** 465 * Filters a screen option value before it is set. 466 * 467 * The filter can also be used to modify non-standard [items]_per_page 468 * settings. See the parent function for a full list of standard options. 469 * 470 * Returning false to the filter will skip saving the current option. 471 * 472 * @since 2.8.0 473 * 474 * @see set_screen_options() 475 * 476 * @param bool|int $value Screen option value. Default false to skip. 477 * @param string $option The option name. 478 * @param int $value The number of rows to use. 479 */ 480 $value = apply_filters( 'set-screen-option', false, $option, $value ); 481 482 if ( false === $value ) 483 return; 484 break; 485 } 486 487 update_user_meta($user->ID, $option, $value); 488 489 $url = remove_query_arg( array( 'pagenum', 'apage', 'paged' ), wp_get_referer() ); 490 if ( isset( $_POST['mode'] ) ) { 491 $url = add_query_arg( array( 'mode' => $_POST['mode'] ), $url ); 492 } 493 494 wp_safe_redirect( $url ); 495 exit; 496 } 473 497 } 474 498 475 499 /** … … function set_screen_options() { 481 505 * @param string $filename The file path to the configuration file 482 506 */ 483 507 function iis7_rewrite_rule_exists($filename) { 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 508 if ( ! file_exists($filename) ) 509 return false; 510 if ( ! class_exists( 'DOMDocument', false ) ) { 511 return false; 512 } 513 514 $doc = new DOMDocument(); 515 if ( $doc->load($filename) === false ) 516 return false; 517 $xpath = new DOMXPath($doc); 518 $rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]'); 519 if ( $rules->length == 0 ) 520 return false; 521 else 522 return true; 499 523 } 500 524 501 525 /** … … function iis7_rewrite_rule_exists($filename) { 507 531 * @return bool 508 532 */ 509 533 function iis7_delete_rewrite_rule($filename) { 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 534 // If configuration file does not exist then rules also do not exist so there is nothing to delete 535 if ( ! file_exists($filename) ) 536 return true; 537 538 if ( ! class_exists( 'DOMDocument', false ) ) { 539 return false; 540 } 541 542 $doc = new DOMDocument(); 543 $doc->preserveWhiteSpace = false; 544 545 if ( $doc -> load($filename) === false ) 546 return false; 547 $xpath = new DOMXPath($doc); 548 $rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]'); 549 if ( $rules->length > 0 ) { 550 $child = $rules->item(0); 551 $parent = $child->parentNode; 552 $parent->removeChild($child); 553 $doc->formatOutput = true; 554 saveDomDocument($doc, $filename); 555 } 556 return true; 533 557 } 534 558 535 559 /** … … function iis7_delete_rewrite_rule($filename) { 542 566 * @return bool 543 567 */ 544 568 function iis7_add_rewrite_rule($filename, $rewrite_rule) { 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 569 if ( ! class_exists( 'DOMDocument', false ) ) { 570 return false; 571 } 572 573 // If configuration file does not exist then we create one. 574 if ( ! file_exists($filename) ) { 575 $fp = fopen( $filename, 'w'); 576 fwrite($fp, '<configuration/>'); 577 fclose($fp); 578 } 579 580 $doc = new DOMDocument(); 581 $doc->preserveWhiteSpace = false; 582 583 if ( $doc->load($filename) === false ) 584 return false; 585 586 $xpath = new DOMXPath($doc); 587 588 // First check if the rule already exists as in that case there is no need to re-add it 589 $wordpress_rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]'); 590 if ( $wordpress_rules->length > 0 ) 591 return true; 592 593 // Check the XPath to the rewrite rule and create XML nodes if they do not exist 594 $xmlnodes = $xpath->query('/configuration/system.webServer/rewrite/rules'); 595 if ( $xmlnodes->length > 0 ) { 596 $rules_node = $xmlnodes->item(0); 597 } else { 598 $rules_node = $doc->createElement('rules'); 599 600 $xmlnodes = $xpath->query('/configuration/system.webServer/rewrite'); 601 if ( $xmlnodes->length > 0 ) { 602 $rewrite_node = $xmlnodes->item(0); 603 $rewrite_node->appendChild($rules_node); 604 } else { 605 $rewrite_node = $doc->createElement('rewrite'); 606 $rewrite_node->appendChild($rules_node); 607 608 $xmlnodes = $xpath->query('/configuration/system.webServer'); 609 if ( $xmlnodes->length > 0 ) { 610 $system_webServer_node = $xmlnodes->item(0); 611 $system_webServer_node->appendChild($rewrite_node); 612 } else { 613 $system_webServer_node = $doc->createElement('system.webServer'); 614 $system_webServer_node->appendChild($rewrite_node); 615 616 $xmlnodes = $xpath->query('/configuration'); 617 if ( $xmlnodes->length > 0 ) { 618 $config_node = $xmlnodes->item(0); 619 $config_node->appendChild($system_webServer_node); 620 } else { 621 $config_node = $doc->createElement('configuration'); 622 $doc->appendChild($config_node); 623 $config_node->appendChild($system_webServer_node); 624 } 625 } 626 } 627 } 628 629 $rule_fragment = $doc->createDocumentFragment(); 630 $rule_fragment->appendXML($rewrite_rule); 631 $rules_node->appendChild($rule_fragment); 632 633 $doc->encoding = "UTF-8"; 634 $doc->formatOutput = true; 635 saveDomDocument($doc, $filename); 636 637 return true; 614 638 } 615 639 616 640 /** … … function iis7_add_rewrite_rule($filename, $rewrite_rule) { 622 646 * @param string $filename 623 647 */ 624 648 function saveDomDocument($doc, $filename) { 625 626 627 628 629 649 $config = $doc->saveXML(); 650 $config = preg_replace("/([^\r])\n/", "$1\r\n", $config); 651 $fp = fopen($filename, 'w'); 652 fwrite($fp, $config); 653 fclose($fp); 630 654 } 631 655 632 656 /** … … function saveDomDocument($doc, $filename) { 639 663 * @param int $user_id User ID. 640 664 */ 641 665 function admin_color_scheme_picker( $user_id ) { 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 666 global $_wp_admin_css_colors; 667 668 ksort( $_wp_admin_css_colors ); 669 670 if ( isset( $_wp_admin_css_colors['fresh'] ) ) { 671 // Set Default ('fresh') and Light should go first. 672 $_wp_admin_css_colors = array_filter( array_merge( array( 'fresh' => '', 'light' => '' ), $_wp_admin_css_colors ) ); 673 } 674 675 $current_color = get_user_option( 'admin_color', $user_id ); 676 677 if ( empty( $current_color ) || ! isset( $_wp_admin_css_colors[ $current_color ] ) ) { 678 $current_color = 'fresh'; 679 } 680 681 ?> 682 <fieldset id="color-picker" class="scheme-list"> 683 <legend class="screen-reader-text"><span><?php _e( 'Admin Color Scheme' ); ?></span></legend> 684 <?php 685 wp_nonce_field( 'save-color-scheme', 'color-nonce', false ); 686 foreach ( $_wp_admin_css_colors as $color => $color_info ) : 687 688 ?> 689 <div class="color-option <?php echo ( $color == $current_color ) ? 'selected' : ''; ?>"> 690 <input name="admin_color" id="admin_color_<?php echo esc_attr( $color ); ?>" type="radio" value="<?php echo esc_attr( $color ); ?>" class="tog" <?php checked( $color, $current_color ); ?> /> 691 <input type="hidden" class="css_url" value="<?php echo esc_url( $color_info->url ); ?>" /> 692 <input type="hidden" class="icon_colors" value="<?php echo esc_attr( wp_json_encode( array( 'icons' => $color_info->icon_colors ) ) ); ?>" /> 693 <label for="admin_color_<?php echo esc_attr( $color ); ?>"><?php echo esc_html( $color_info->name ); ?></label> 694 <table class="color-palette"> 695 <tr> 696 <?php 697 698 foreach ( $color_info->colors as $html_color ) { 699 ?> 700 <td style="background-color: <?php echo esc_attr( $html_color ); ?>"> </td> 701 <?php 702 } 703 704 ?> 705 </tr> 706 </table> 707 </div> 708 <?php 709 710 endforeach; 711 712 ?> 713 </fieldset> 714 <?php 691 715 } 692 716 693 717 /** … … function admin_color_scheme_picker( $user_id ) { 695 719 * @global array $_wp_admin_css_colors 696 720 */ 697 721 function wp_color_scheme_settings() { 698 722 global $_wp_admin_css_colors; 699 723 700 724 $color_scheme = get_user_option( 'admin_color' ); 701 725 702 703 704 705 726 // It's possible to have a color scheme set that is no longer registered. 727 if ( empty( $_wp_admin_css_colors[ $color_scheme ] ) ) { 728 $color_scheme = 'fresh'; 729 } 706 730 707 708 709 710 711 712 713 714 731 if ( ! empty( $_wp_admin_css_colors[ $color_scheme ]->icon_colors ) ) { 732 $icon_colors = $_wp_admin_css_colors[ $color_scheme ]->icon_colors; 733 } elseif ( ! empty( $_wp_admin_css_colors['fresh']->icon_colors ) ) { 734 $icon_colors = $_wp_admin_css_colors['fresh']->icon_colors; 735 } else { 736 // Fall back to the default set of icon colors if the default scheme is missing. 737 $icon_colors = array( 'base' => '#82878c', 'focus' => '#00a0d2', 'current' => '#fff' ); 738 } 715 739 716 740 echo '<script type="text/javascript">var _wpColorScheme = ' . wp_json_encode( array( 'icons' => $icon_colors ) ) . ";</script>\n"; 717 741 } 718 742 719 743 /** 720 744 * @since 3.3.0 721 745 */ 722 746 function _ipad_meta() { 723 724 725 726 727 747 if ( wp_is_mobile() ) { 748 ?> 749 <meta name="viewport" id="viewport-meta" content="width=device-width, initial-scale=1"> 750 <?php 751 } 728 752 } 729 753 730 754 /** … … function _ipad_meta() { 738 762 * @return array The Heartbeat response. 739 763 */ 740 764 function wp_check_locked_posts( $response, $data, $screen_id ) { 741 765 $checked = array(); 742 766 743 744 745 746 767 if ( array_key_exists( 'wp-check-locked-posts', $data ) && is_array( $data['wp-check-locked-posts'] ) ) { 768 foreach ( $data['wp-check-locked-posts'] as $key ) { 769 if ( ! $post_id = absint( substr( $key, 5 ) ) ) 770 continue; 747 771 748 749 772 if ( ( $user_id = wp_check_post_lock( $post_id ) ) && ( $user = get_userdata( $user_id ) ) && current_user_can( 'edit_post', $post_id ) ) { 773 $send = array( 'text' => sprintf( __( '%s is currently editing' ), $user->display_name ) ); 750 774 751 752 775 if ( ( $avatar = get_avatar( $user->ID, 18 ) ) && preg_match( "|src='([^']+)'|", $avatar, $matches ) ) 776 $send['avatar_src'] = $matches[1]; 753 777 754 755 756 757 778 $checked[$key] = $send; 779 } 780 } 781 } 758 782 759 760 783 if ( ! empty( $checked ) ) 784 $response['wp-check-locked-posts'] = $checked; 761 785 762 786 return $response; 763 787 } 764 788 765 789 /** … … function wp_check_locked_posts( $response, $data, $screen_id ) { 773 797 * @return array The Heartbeat response. 774 798 */ 775 799 function wp_refresh_post_lock( $response, $data, $screen_id ) { 776 777 778 800 if ( array_key_exists( 'wp-refresh-post-lock', $data ) ) { 801 $received = $data['wp-refresh-post-lock']; 802 $send = array(); 779 803 780 781 804 if ( ! $post_id = absint( $received['post_id'] ) ) 805 return $response; 782 806 783 784 807 if ( ! current_user_can('edit_post', $post_id) ) 808 return $response; 785 809 786 787 788 789 810 if ( ( $user_id = wp_check_post_lock( $post_id ) ) && ( $user = get_userdata( $user_id ) ) ) { 811 $error = array( 812 'text' => sprintf( __( '%s has taken over and is currently editing.' ), $user->display_name ) 813 ); 790 814 791 792 793 794 815 if ( $avatar = get_avatar( $user->ID, 64 ) ) { 816 if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) 817 $error['avatar_src'] = $matches[1]; 818 } 795 819 796 797 798 799 800 820 $send['lock_error'] = $error; 821 } else { 822 if ( $new_lock = wp_set_post_lock( $post_id ) ) 823 $send['new_lock'] = implode( ':', $new_lock ); 824 } 801 825 802 803 826 $response['wp-refresh-post-lock'] = $send; 827 } 804 828 805 829 return $response; 806 830 } 807 831 808 832 /** … … function wp_refresh_post_lock( $response, $data, $screen_id ) { 816 840 * @return array The Heartbeat response. 817 841 */ 818 842 function wp_refresh_post_nonces( $response, $data, $screen_id ) { 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 843 if ( array_key_exists( 'wp-refresh-post-nonces', $data ) ) { 844 $received = $data['wp-refresh-post-nonces']; 845 $response['wp-refresh-post-nonces'] = array( 'check' => 1 ); 846 847 if ( ! $post_id = absint( $received['post_id'] ) ) { 848 return $response; 849 } 850 851 if ( ! current_user_can( 'edit_post', $post_id ) ) { 852 return $response; 853 } 854 855 $response['wp-refresh-post-nonces'] = array( 856 'replace' => array( 857 'getpermalinknonce' => wp_create_nonce('getpermalink'), 858 'samplepermalinknonce' => wp_create_nonce('samplepermalink'), 859 'closedpostboxesnonce' => wp_create_nonce('closedpostboxes'), 860 '_ajax_linking_nonce' => wp_create_nonce( 'internal-linking' ), 861 '_wpnonce' => wp_create_nonce( 'update-post_' . $post_id ), 862 ), 863 'heartbeatNonce' => wp_create_nonce( 'heartbeat-nonce' ), 864 ); 865 } 866 867 return $response; 844 868 } 845 869 846 870 /** … … function wp_refresh_post_nonces( $response, $data, $screen_id ) { 854 878 * @return array Filtered Heartbeat settings. 855 879 */ 856 880 function wp_heartbeat_set_suspension( $settings ) { 857 881 global $pagenow; 858 882 859 860 861 883 if ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) { 884 $settings['suspension'] = 'disable'; 885 } 862 886 863 887 return $settings; 864 888 } 865 889 866 890 /** … … function wp_heartbeat_set_suspension( $settings ) { 873 897 * @return array The Heartbeat response. 874 898 */ 875 899 function heartbeat_autosave( $response, $data ) { 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 900 if ( ! empty( $data['wp_autosave'] ) ) { 901 $saved = wp_autosave( $data['wp_autosave'] ); 902 903 if ( is_wp_error( $saved ) ) { 904 $response['wp_autosave'] = array( 'success' => false, 'message' => $saved->get_error_message() ); 905 } elseif ( empty( $saved ) ) { 906 $response['wp_autosave'] = array( 'success' => false, 'message' => __( 'Error while saving.' ) ); 907 } else { 908 /* translators: draft saved date format, see https://secure.php.net/date */ 909 $draft_saved_date_format = __( 'g:i:s a' ); 910 /* translators: %s: date and time */ 911 $response['wp_autosave'] = array( 'success' => true, 'message' => sprintf( __( 'Draft saved at %s.' ), date_i18n( $draft_saved_date_format ) ) ); 912 } 913 } 914 915 return $response; 892 916 } 893 917 894 918 /** … … function heartbeat_autosave( $response, $data ) { 900 924 * @since 4.2.0 901 925 */ 902 926 function wp_admin_canonical_url() { 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 927 $removable_query_args = wp_removable_query_args(); 928 929 if ( empty( $removable_query_args ) ) { 930 return; 931 } 932 933 // Ensure we're using an absolute URL. 934 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); 935 $filtered_url = remove_query_arg( $removable_query_args, $current_url ); 936 ?> 937 <link id="wp-admin-canonical" rel="canonical" href="<?php echo esc_url( $filtered_url ); ?>" /> 938 <script> 939 if ( window.history.replaceState ) { 940 window.history.replaceState( null, null, document.getElementById( 'wp-admin-canonical' ).href + window.location.hash ); 941 } 942 </script> 919 943 <?php 920 944 } 921 945 … … function wp_admin_canonical_url() { 928 952 * @since 4.6.0 929 953 */ 930 954 function wp_page_reload_on_back_button_js() { 931 932 933 934 935 936 937 955 ?> 956 <script> 957 if ( typeof performance !== 'undefined' && performance.navigation && performance.navigation.type === 2 ) { 958 document.location.reload( true ); 959 } 960 </script> 961 <?php 938 962 } -
src/wp-admin/link-add.php
diff --git a/src/wp-admin/link-add.php b/src/wp-admin/link-add.php index 423c6680e5..245b62cbbd 100644
a b 10 10 require_once( dirname( __FILE__ ) . '/admin.php' ); 11 11 12 12 if ( ! current_user_can('manage_links') ) 13 13 wp_die(__('Sorry, you are not allowed to add links to this site.')); 14 14 15 15 $title = __('Add New Link'); 16 16 $parent_file = 'link-manager.php'; 17 17 18 wp_reset_vars( array('action', 'cat_id', 'link_id' ) ); 18 $action = wp_assign_request_var('action'); 19 $cat_id = wp_assign_request_var('cat_id'); 20 $link_id = wp_assign_request_var('link_id'); 19 21 20 22 wp_enqueue_script('link'); 21 23 wp_enqueue_script('xfn'); 22 24 23 25 if ( wp_is_mobile() ) 24 26 wp_enqueue_script( 'jquery-touch-punch' ); 25 27 26 28 $link = get_default_link_to_edit(); 27 29 include( ABSPATH . 'wp-admin/edit-link-form.php' ); -
src/wp-admin/link.php
diff --git a/src/wp-admin/link.php b/src/wp-admin/link.php index bcfe578fb0..6100401ec0 100644
a b 12 12 /** Load WordPress Administration Bootstrap */ 13 13 require_once( dirname( __FILE__ ) . '/admin.php' ); 14 14 15 wp_reset_vars( array( 'action', 'cat_id', 'link_id' ) ); 15 $action = wp_assign_request_var('action'); 16 $cat_id = wp_assign_request_var('cat_id'); 17 $link_id = wp_assign_request_var('link_id'); 16 18 17 19 if ( ! current_user_can('manage_links') ) 18 20 wp_link_manager_disabled_message(); 19 21 20 22 if ( !empty($_POST['deletebookmarks']) ) 21 23 $action = 'deletebookmarks'; 22 24 if ( !empty($_POST['move']) ) 23 25 $action = 'move'; 24 26 if ( !empty($_POST['linkcheck']) ) 25 27 $linkcheck = $_POST['linkcheck']; 26 28 27 29 $this_file = admin_url('link-manager.php'); 28 30 29 31 switch ($action) { 30 31 32 case 'deletebookmarks' : 33 check_admin_referer('bulk-bookmarks'); 32 34 33 34 35 36 37 35 // For each link id (in $linkcheck[]) change category to selected value. 36 if (count($linkcheck) == 0) { 37 wp_redirect($this_file); 38 exit; 39 } 38 40 39 40 41 41 $deleted = 0; 42 foreach ($linkcheck as $link_id) { 43 $link_id = (int) $link_id; 42 44 43 44 45 45 if ( wp_delete_link($link_id) ) 46 $deleted++; 47 } 46 48 47 48 49 wp_redirect("$this_file?deleted=$deleted"); 50 exit; 49 51 50 51 52 case 'move' : 53 check_admin_referer('bulk-bookmarks'); 52 54 53 54 55 56 57 58 59 60 61 62 55 // For each link id (in $linkcheck[]) change category to selected value. 56 if (count($linkcheck) == 0) { 57 wp_redirect($this_file); 58 exit; 59 } 60 $all_links = join(',', $linkcheck); 61 /* 62 * Should now have an array of links we can change: 63 * $q = $wpdb->query("update $wpdb->links SET link_category='$category' WHERE link_id IN ($all_links)"); 64 */ 63 65 64 65 66 wp_redirect($this_file); 67 exit; 66 68 67 68 69 case 'add' : 70 check_admin_referer('add-bookmark'); 69 71 70 71 72 72 $redir = wp_get_referer(); 73 if ( add_link() ) 74 $redir = add_query_arg( 'added', 'true', $redir ); 73 75 74 75 76 wp_redirect( $redir ); 77 exit; 76 78 77 78 79 79 case 'save' : 80 $link_id = (int) $_POST['link_id']; 81 check_admin_referer('update-bookmark_' . $link_id); 80 82 81 83 edit_link($link_id); 82 84 83 84 85 wp_redirect($this_file); 86 exit; 85 87 86 87 88 88 case 'delete' : 89 $link_id = (int) $_GET['link_id']; 90 check_admin_referer('delete-bookmark_' . $link_id); 89 91 90 92 wp_delete_link($link_id); 91 93 92 93 94 wp_redirect($this_file); 95 exit; 94 96 95 96 97 97 case 'edit' : 98 wp_enqueue_script('link'); 99 wp_enqueue_script('xfn'); 98 100 99 100 101 if ( wp_is_mobile() ) 102 wp_enqueue_script( 'jquery-touch-punch' ); 101 103 102 103 104 104 $parent_file = 'link-manager.php'; 105 $submenu_file = 'link-manager.php'; 106 $title = __('Edit Link'); 105 107 106 108 $link_id = (int) $_GET['link_id']; 107 109 108 109 110 if (!$link = get_link_to_edit($link_id)) 111 wp_die(__('Link not found.')); 110 112 111 112 113 113 include( ABSPATH . 'wp-admin/edit-link-form.php' ); 114 include( ABSPATH . 'wp-admin/admin-footer.php' ); 115 break; 114 116 115 116 117 default : 118 break; 117 119 } -
src/wp-admin/media.php
diff --git a/src/wp-admin/media.php b/src/wp-admin/media.php index be3cecad98..e89bb03b82 100644
a b require_once( dirname( __FILE__ ) . '/admin.php' ); 12 12 $parent_file = 'upload.php'; 13 13 $submenu_file = 'upload.php'; 14 14 15 wp_reset_vars(array('action'));15 $action = wp_assign_request_var('action'); 16 16 17 17 switch ( $action ) { 18 18 case 'editattachment' : 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 19 $attachment_id = (int) $_POST['attachment_id']; 20 check_admin_referer('media-form'); 21 22 if ( !current_user_can('edit_post', $attachment_id) ) 23 wp_die ( __('Sorry, you are not allowed to edit this attachment.') ); 24 25 $errors = media_upload_form_handler(); 26 27 if ( empty($errors) ) { 28 $location = 'media.php'; 29 if ( $referer = wp_get_original_referer() ) { 30 if ( false !== strpos($referer, 'upload.php') || ( url_to_postid($referer) == $attachment_id ) ) 31 $location = $referer; 32 } 33 if ( false !== strpos($location, 'upload.php') ) { 34 $location = remove_query_arg('message', $location); 35 $location = add_query_arg('posted', $attachment_id, $location); 36 } elseif ( false !== strpos($location, 'media.php') ) { 37 $location = add_query_arg('message', 'updated', $location); 38 } 39 wp_redirect($location); 40 exit; 41 } 42 43 // No break. 44 44 case 'edit' : 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 45 $title = __('Edit Media'); 46 47 if ( empty($errors) ) 48 $errors = null; 49 50 if ( empty( $_GET['attachment_id'] ) ) { 51 wp_redirect( admin_url('upload.php') ); 52 exit(); 53 } 54 $att_id = (int) $_GET['attachment_id']; 55 56 if ( !current_user_can('edit_post', $att_id) ) 57 wp_die ( __('Sorry, you are not allowed to edit this attachment.') ); 58 59 $att = get_post($att_id); 60 61 if ( empty($att->ID) ) wp_die( __('You attempted to edit an attachment that doesn’t exist. Perhaps it was deleted?') ); 62 if ( 'attachment' !== $att->post_type ) wp_die( __('You attempted to edit an item that isn’t an attachment. Please go back and try again.') ); 63 if ( $att->post_status == 'trash' ) wp_die( __('You can’t edit this attachment because it is in the Trash. Please move it out of the Trash and try again.') ); 64 65 add_filter('attachment_fields_to_edit', 'media_single_attachment_fields_to_edit', 10, 2); 66 67 wp_enqueue_script( 'wp-ajax-response' ); 68 wp_enqueue_script('image-edit'); 69 wp_enqueue_style('imgareaselect'); 70 71 get_current_screen()->add_help_tab( array( 72 'id' => 'overview', 73 'title' => __('Overview'), 74 'content' => 75 '<p>' . __('This screen allows you to edit five fields for metadata in a file within the media library.') . '</p>' . 76 '<p>' . __('For images only, you can click on Edit Image under the thumbnail to expand out an inline image editor with icons for cropping, rotating, or flipping the image as well as for undoing and redoing. The boxes on the right give you more options for scaling the image, for cropping it, and for cropping the thumbnail in a different way than you crop the original image. You can click on Help in those boxes to get more information.') . '</p>' . 77 '<p>' . __('Note that you crop the image by clicking on it (the Crop icon is already selected) and dragging the cropping frame to select the desired part. Then click Save to retain the cropping.') . '</p>' . 78 '<p>' . __('Remember to click Update Media to save metadata entered or changed.') . '</p>' 79 ) ); 80 81 get_current_screen()->set_help_sidebar( 82 '<p><strong>' . __('For more information:') . '</strong></p>' . 83 '<p>' . __('<a href="https://codex.wordpress.org/Media_Add_New_Screen#Edit_Media">Documentation on Edit Media</a>') . '</p>' . 84 '<p>' . __('<a href="https://wordpress.org/support/">Support Forums</a>') . '</p>' 85 ); 86 87 require( ABSPATH . 'wp-admin/admin-header.php' ); 88 89 $parent_file = 'upload.php'; 90 $message = ''; 91 $class = ''; 92 if ( isset($_GET['message']) ) { 93 switch ( $_GET['message'] ) { 94 case 'updated' : 95 $message = __('Media file updated.'); 96 $class = 'updated'; 97 break; 98 } 99 } 100 if ( $message ) 101 echo "<div id='message' class='$class'><p>$message</p></div>\n"; 102 102 103 103 ?> 104 104 … … echo esc_html( $title ); 109 109 110 110 <?php 111 111 if ( current_user_can( 'upload_files' ) ) { ?> 112 112 <a href="media-new.php" class="page-title-action"><?php echo esc_html_x('Add New', 'file'); ?></a> 113 113 <?php } ?> 114 114 115 115 <hr class="wp-header-end"> … … if ( current_user_can( 'upload_files' ) ) { ?> 138 138 139 139 <?php 140 140 141 141 require( ABSPATH . 'wp-admin/admin-footer.php' ); 142 142 143 143 exit; 144 144 145 145 default: 146 147 146 wp_redirect( admin_url('upload.php') ); 147 exit; 148 148 149 149 } -
src/wp-admin/options-head.php
diff --git a/src/wp-admin/options-head.php b/src/wp-admin/options-head.php index bee3ae7e67..848733eda1 100644
a b 8 8 * @subpackage Administration 9 9 */ 10 10 11 wp_reset_vars( array( 'action' ));11 $action = wp_assign_request_var('action'); 12 12 13 13 if ( isset( $_GET['updated'] ) && isset( $_GET['page'] ) ) { 14 15 14 // For back-compat with plugins that don't use the Settings API and just set updated=1 in the redirect. 15 add_settings_error('general', 'settings_updated', __('Settings saved.'), 'updated'); 16 16 } 17 17 18 18 settings_errors(); -
src/wp-admin/options.php
diff --git a/src/wp-admin/options.php b/src/wp-admin/options.php index d2e1c0374f..7f2c7a5129 100644
a b $title = __('Settings'); 22 22 $this_file = 'options.php'; 23 23 $parent_file = 'options-general.php'; 24 24 25 wp_reset_vars(array('action', 'option_page')); 25 $action = wp_assign_request_var('action'); 26 $option_page = wp_assign_request_var('option_page'); 26 27 27 28 $capability = 'manage_options'; 28 29 29 30 // This is for back compat and will eventually be removed. 30 31 if ( empty($option_page) ) { 31 32 $option_page = 'options'; 32 33 } else { 33 34 34 35 36 37 38 39 40 41 42 43 44 35 /** 36 * Filters the capability required when using the Settings API. 37 * 38 * By default, the options groups for all registered settings require the manage_options capability. 39 * This filter is required to change the capability required for a certain options page. 40 * 41 * @since 3.2.0 42 * 43 * @param string $capability The capability used for the page, which is manage_options by default. 44 */ 45 $capability = apply_filters( "option_page_capability_{$option_page}", $capability ); 45 46 } 46 47 47 48 if ( ! current_user_can( $capability ) ) { 48 49 50 51 52 49 wp_die( 50 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' . 51 '<p>' . __( 'Sorry, you are not allowed to manage these options.' ) . '</p>', 52 403 53 ); 53 54 } 54 55 55 56 // Handle admin email change requests 56 57 if ( is_multisite() ) { 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 58 if ( ! empty($_GET[ 'adminhash' ] ) ) { 59 $new_admin_details = get_option( 'adminhash' ); 60 $redirect = 'options-general.php?updated=false'; 61 if ( is_array( $new_admin_details ) && hash_equals( $new_admin_details[ 'hash' ], $_GET[ 'adminhash' ] ) && !empty($new_admin_details[ 'newemail' ]) ) { 62 update_option( 'admin_email', $new_admin_details[ 'newemail' ] ); 63 delete_option( 'adminhash' ); 64 delete_option( 'new_admin_email' ); 65 $redirect = 'options-general.php?updated=true'; 66 } 67 wp_redirect( admin_url( $redirect ) ); 68 exit; 69 } elseif ( ! empty( $_GET['dismiss'] ) && 'new_admin_email' == $_GET['dismiss'] ) { 70 check_admin_referer( 'dismiss-' . get_current_blog_id() . '-new_admin_email' ); 71 delete_option( 'adminhash' ); 72 delete_option( 'new_admin_email' ); 73 wp_redirect( admin_url( 'options-general.php?updated=true' ) ); 74 exit; 75 } 75 76 } 76 77 77 78 if ( is_multisite() && ! current_user_can( 'manage_network_options' ) && 'update' != $action ) { 78 79 80 81 82 79 wp_die( 80 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' . 81 '<p>' . __( 'Sorry, you are not allowed to delete these items.' ) . '</p>', 82 403 83 ); 83 84 } 84 85 85 86 $whitelist_options = array( 86 87 88 89 90 87 'general' => array( 'blogname', 'blogdescription', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string', 'WPLANG' ), 88 'discussion' => array( 'default_pingback_flag', 'default_ping_status', 'default_comment_status', 'comments_notify', 'moderation_notify', 'comment_moderation', 'require_name_email', 'comment_whitelist', 'comment_max_links', 'moderation_keys', 'blacklist_keys', 'show_avatars', 'avatar_rating', 'avatar_default', 'close_comments_for_old_posts', 'close_comments_days_old', 'thread_comments', 'thread_comments_depth', 'page_comments', 'comments_per_page', 'default_comments_page', 'comment_order', 'comment_registration' ), 89 'media' => array( 'thumbnail_size_w', 'thumbnail_size_h', 'thumbnail_crop', 'medium_size_w', 'medium_size_h', 'large_size_w', 'large_size_h', 'image_default_size', 'image_default_align', 'image_default_link_type' ), 90 'reading' => array( 'posts_per_page', 'posts_per_rss', 'rss_use_excerpt', 'show_on_front', 'page_on_front', 'page_for_posts', 'blog_public' ), 91 'writing' => array( 'default_category', 'default_email_category', 'default_link_category', 'default_post_format' ) 91 92 ); 92 93 $whitelist_options['misc'] = $whitelist_options['options'] = $whitelist_options['privacy'] = array(); 93 94 94 95 $mail_options = array('mailserver_url', 'mailserver_port', 'mailserver_login', 'mailserver_pass'); 95 96 96 97 if ( ! in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) 97 98 $whitelist_options['reading'][] = 'blog_charset'; 98 99 99 100 if ( get_site_option( 'initial_db_version' ) < 32453 ) { 100 101 101 $whitelist_options['writing'][] = 'use_smilies'; 102 $whitelist_options['writing'][] = 'use_balanceTags'; 102 103 } 103 104 104 105 if ( !is_multisite() ) { 105 106 107 108 106 if ( !defined( 'WP_SITEURL' ) ) 107 $whitelist_options['general'][] = 'siteurl'; 108 if ( !defined( 'WP_HOME' ) ) 109 $whitelist_options['general'][] = 'home'; 109 110 110 111 112 111 $whitelist_options['general'][] = 'admin_email'; 112 $whitelist_options['general'][] = 'users_can_register'; 113 $whitelist_options['general'][] = 'default_role'; 113 114 114 115 115 $whitelist_options['writing'] = array_merge($whitelist_options['writing'], $mail_options); 116 $whitelist_options['writing'][] = 'ping_sites'; 116 117 117 118 $whitelist_options['media'][] = 'uploads_use_yearmonth_folders'; 118 119 119 120 121 122 123 120 // If upload_url_path and upload_path are both default values, they're locked. 121 if ( get_option( 'upload_url_path' ) || ( get_option('upload_path') != 'wp-content/uploads' && get_option('upload_path') ) ) { 122 $whitelist_options['media'][] = 'upload_path'; 123 $whitelist_options['media'][] = 'upload_url_path'; 124 } 124 125 } else { 125 126 127 128 129 130 131 132 133 134 135 126 $whitelist_options['general'][] = 'new_admin_email'; 127 128 /** 129 * Filters whether the post-by-email functionality is enabled. 130 * 131 * @since 3.0.0 132 * 133 * @param bool $enabled Whether post-by-email configuration is enabled. Default true. 134 */ 135 if ( apply_filters( 'enable_post_by_email_configuration', true ) ) 136 $whitelist_options['writing'] = array_merge($whitelist_options['writing'], $mail_options); 136 137 } 137 138 138 139 /** … … $whitelist_options = apply_filters( 'whitelist_options', $whitelist_options ); 148 149 * If $_GET['action'] == 'update' we are saving settings sent from a settings page 149 150 */ 150 151 if ( 'update' == $action ) { 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 152 if ( 'options' == $option_page && !isset( $_POST['option_page'] ) ) { // This is for back compat and will eventually be removed. 153 $unregistered = true; 154 check_admin_referer( 'update-options' ); 155 } else { 156 $unregistered = false; 157 check_admin_referer( $option_page . '-options' ); 158 } 159 160 if ( !isset( $whitelist_options[ $option_page ] ) ) 161 wp_die( __( '<strong>ERROR</strong>: options page not found.' ) ); 162 163 if ( 'options' == $option_page ) { 164 if ( is_multisite() && ! current_user_can( 'manage_network_options' ) ) { 165 wp_die( __( 'Sorry, you are not allowed to modify unregistered settings for this site.' ) ); 166 } 167 $options = explode( ',', wp_unslash( $_POST[ 'page_options' ] ) ); 168 } else { 169 $options = $whitelist_options[ $option_page ]; 170 } 171 172 if ( 'general' == $option_page ) { 173 // Handle custom date/time formats. 174 if ( !empty($_POST['date_format']) && isset($_POST['date_format_custom']) && '\c\u\s\t\o\m' == wp_unslash( $_POST['date_format'] ) ) 175 $_POST['date_format'] = $_POST['date_format_custom']; 176 if ( !empty($_POST['time_format']) && isset($_POST['time_format_custom']) && '\c\u\s\t\o\m' == wp_unslash( $_POST['time_format'] ) ) 177 $_POST['time_format'] = $_POST['time_format_custom']; 178 // Map UTC+- timezones to gmt_offsets and set timezone_string to empty. 179 if ( !empty($_POST['timezone_string']) && preg_match('/^UTC[+-]/', $_POST['timezone_string']) ) { 180 $_POST['gmt_offset'] = $_POST['timezone_string']; 181 $_POST['gmt_offset'] = preg_replace('/UTC\+?/', '', $_POST['gmt_offset']); 182 $_POST['timezone_string'] = ''; 183 } 184 185 // Handle translation install. 186 if ( ! empty( $_POST['WPLANG'] ) && ( ! is_multisite() || is_super_admin() ) ) { // @todo: Skip if already installed 187 require_once( ABSPATH . 'wp-admin/includes/translation-install.php' ); 188 189 if ( wp_can_install_language_pack() ) { 190 $language = wp_download_language_pack( $_POST['WPLANG'] ); 191 if ( $language ) { 192 $_POST['WPLANG'] = $language; 193 } 194 } 195 } 196 } 197 198 if ( $options ) { 199 $user_language_old = get_user_locale(); 200 201 foreach ( $options as $option ) { 202 if ( $unregistered ) { 203 _deprecated_argument( 'options.php', '2.7.0', 204 sprintf( 205 /* translators: %s: the option/setting */ 206 __( 'The %s setting is unregistered. Unregistered settings are deprecated. See https://codex.wordpress.org/Settings_API' ), 207 '<code>' . $option . '</code>' 208 ) 209 ); 210 } 211 212 $option = trim( $option ); 213 $value = null; 214 if ( isset( $_POST[ $option ] ) ) { 215 $value = $_POST[ $option ]; 216 if ( ! is_array( $value ) ) { 217 $value = trim( $value ); 218 } 219 $value = wp_unslash( $value ); 220 } 221 update_option( $option, $value ); 222 } 223 224 /* 225 * Switch translation in case WPLANG was changed. 226 * The global $locale is used in get_locale() which is 227 * used as a fallback in get_user_locale(). 228 */ 229 unset( $GLOBALS['locale'] ); 230 $user_language_new = get_user_locale(); 231 if ( $user_language_old !== $user_language_new ) { 232 load_default_textdomain( $user_language_new ); 233 } 234 } 235 236 /** 237 * Handle settings errors and return to options page 238 */ 239 // If no settings errors were registered add a general 'updated' message. 240 if ( !count( get_settings_errors() ) ) 241 add_settings_error('general', 'settings_updated', __('Settings saved.'), 'updated'); 242 set_transient('settings_errors', get_settings_errors(), 30); 243 244 /** 245 * Redirect back to the settings page that was submitted 246 */ 247 $goback = add_query_arg( 'settings-updated', 'true', wp_get_referer() ); 248 wp_redirect( $goback ); 249 exit; 249 250 } 250 251 251 252 include( ABSPATH . 'wp-admin/admin-header.php' ); ?> … … include( ABSPATH . 'wp-admin/admin-header.php' ); ?> 261 262 $options = $wpdb->get_results( "SELECT * FROM $wpdb->options ORDER BY option_name" ); 262 263 263 264 foreach ( (array) $options as $option ) : 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 265 $disabled = false; 266 if ( $option->option_name == '' ) 267 continue; 268 if ( is_serialized( $option->option_value ) ) { 269 if ( is_serialized_string( $option->option_value ) ) { 270 // This is a serialized string, so we should display it. 271 $value = maybe_unserialize( $option->option_value ); 272 $options_to_update[] = $option->option_name; 273 $class = 'all-options'; 274 } else { 275 $value = 'SERIALIZED DATA'; 276 $disabled = true; 277 $class = 'all-options disabled'; 278 } 279 } else { 280 $value = $option->option_value; 281 $options_to_update[] = $option->option_name; 282 $class = 'all-options'; 283 } 284 $name = esc_attr( $option->option_name ); 285 ?> 285 286 <tr> 286 287 <th scope="row"><label for="<?php echo $name ?>"><?php echo esc_html( $option->option_name ); ?></label></th> 287 288 <td> 288 289 <?php if ( strpos( $value, "\n" ) !== false ) : ?> 289 290 291 292 293 294 290 <textarea class="<?php echo $class ?>" name="<?php echo $name ?>" id="<?php echo $name ?>" cols="30" rows="5"><?php 291 echo esc_textarea( $value ); 292 ?></textarea> 293 <?php else: ?> 294 <input class="regular-text <?php echo $class ?>" type="text" name="<?php echo $name ?>" id="<?php echo $name ?>" value="<?php echo esc_attr( $value ) ?>"<?php disabled( $disabled, true ) ?> /> 295 <?php endif ?></td> 295 296 </tr> 296 297 <?php endforeach; ?> 297 298 </table> -
src/wp-admin/post.php
diff --git a/src/wp-admin/post.php b/src/wp-admin/post.php index cf2bee5790..73d4f7a2fc 100644
a b require_once( dirname( __FILE__ ) . '/admin.php' ); 14 14 $parent_file = 'edit.php'; 15 15 $submenu_file = 'edit.php'; 16 16 17 wp_reset_vars( array( 'action' ));17 $action = wp_assign_request_var('action'); 18 18 19 19 if ( isset( $_GET['post'] ) ) 20 20 $post_id = $post_ID = (int) $_GET['post']; 21 21 elseif ( isset( $_POST['post_ID'] ) ) 22 22 $post_id = $post_ID = (int) $_POST['post_ID']; 23 23 else 24 24 $post_id = $post_ID = 0; 25 25 26 26 /** 27 27 * @global string $post_type … … else 31 31 global $post_type, $post_type_object, $post; 32 32 33 33 if ( $post_id ) 34 34 $post = get_post( $post_id ); 35 35 36 36 if ( $post ) { 37 38 37 $post_type = $post->post_type; 38 $post_type_object = get_post_type_object( $post_type ); 39 39 } 40 40 41 41 if ( isset( $_POST['deletepost'] ) ) 42 42 $action = 'delete'; 43 43 elseif ( isset($_POST['wp-preview']) && 'dopreview' == $_POST['wp-preview'] ) 44 44 $action = 'preview'; 45 45 46 46 $sendback = wp_get_referer(); 47 47 if ( ! $sendback || 48 48 strpos( $sendback, 'post.php' ) !== false || 49 49 strpos( $sendback, 'post-new.php' ) !== false ) { 50 51 52 53 54 55 56 57 50 if ( 'attachment' == $post_type ) { 51 $sendback = admin_url( 'upload.php' ); 52 } else { 53 $sendback = admin_url( 'edit.php' ); 54 if ( ! empty( $post_type ) ) { 55 $sendback = add_query_arg( 'post_type', $post_type, $sendback ); 56 } 57 } 58 58 } else { 59 59 $sendback = remove_query_arg( array('trashed', 'untrashed', 'deleted', 'ids'), $sendback ); 60 60 } 61 61 62 62 switch($action) { 63 63 case 'post-quickdraft-save': 64 65 66 64 // Check nonce and capabilities 65 $nonce = $_REQUEST['_wpnonce']; 66 $error_msg = false; 67 67 68 69 68 // For output of the quickdraft dashboard widget 69 require_once ABSPATH . 'wp-admin/includes/dashboard.php'; 70 70 71 72 71 if ( ! wp_verify_nonce( $nonce, 'add-post' ) ) 72 $error_msg = __( 'Unable to submit this form, please refresh and try again.' ); 73 73 74 75 76 74 if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) { 75 exit; 76 } 77 77 78 79 78 if ( $error_msg ) 79 return wp_dashboard_quick_press( $error_msg ); 80 80 81 82 81 $post = get_post( $_REQUEST['post_ID'] ); 82 check_admin_referer( 'add-' . $post->post_type ); 83 83 84 85 84 $_POST['comment_status'] = get_default_comment_status( $post->post_type ); 85 $_POST['ping_status'] = get_default_comment_status( $post->post_type, 'pingback' ); 86 86 87 88 89 87 edit_post(); 88 wp_dashboard_quick_press(); 89 exit; 90 90 91 91 case 'postajaxpost': 92 92 case 'post': 93 94 95 96 93 check_admin_referer( 'add-' . $post_type ); 94 $post_id = 'postajaxpost' == $action ? edit_post() : write_post(); 95 redirect_post( $post_id ); 96 exit(); 97 97 98 98 case 'edit': 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 99 $editing = true; 100 101 if ( empty( $post_id ) ) { 102 wp_redirect( admin_url('post.php') ); 103 exit(); 104 } 105 106 if ( ! $post ) 107 wp_die( __( 'You attempted to edit an item that doesn’t exist. Perhaps it was deleted?' ) ); 108 109 if ( ! $post_type_object ) 110 wp_die( __( 'Invalid post type.' ) ); 111 112 if ( ! in_array( $typenow, get_post_types( array( 'show_ui' => true ) ) ) ) { 113 wp_die( __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); 114 } 115 116 if ( ! current_user_can( 'edit_post', $post_id ) ) 117 wp_die( __( 'Sorry, you are not allowed to edit this item.' ) ); 118 119 if ( 'trash' == $post->post_status ) 120 wp_die( __( 'You can’t edit this item because it is in the Trash. Please restore it and try again.' ) ); 121 122 if ( ! empty( $_GET['get-post-lock'] ) ) { 123 check_admin_referer( 'lock-post_' . $post_id ); 124 wp_set_post_lock( $post_id ); 125 wp_redirect( get_edit_post_link( $post_id, 'url' ) ); 126 exit(); 127 } 128 129 $post_type = $post->post_type; 130 if ( 'post' == $post_type ) { 131 $parent_file = "edit.php"; 132 $submenu_file = "edit.php"; 133 $post_new_file = "post-new.php"; 134 } elseif ( 'attachment' == $post_type ) { 135 $parent_file = 'upload.php'; 136 $submenu_file = 'upload.php'; 137 $post_new_file = 'media-new.php'; 138 } else { 139 if ( isset( $post_type_object ) && $post_type_object->show_in_menu && $post_type_object->show_in_menu !== true ) 140 $parent_file = $post_type_object->show_in_menu; 141 else 142 $parent_file = "edit.php?post_type=$post_type"; 143 $submenu_file = "edit.php?post_type=$post_type"; 144 $post_new_file = "post-new.php?post_type=$post_type"; 145 } 146 147 if ( ! wp_check_post_lock( $post->ID ) ) { 148 $active_post_lock = wp_set_post_lock( $post->ID ); 149 150 if ( 'attachment' !== $post_type ) 151 wp_enqueue_script('autosave'); 152 } 153 154 if ( is_multisite() ) { 155 add_action( 'admin_footer', '_admin_notice_post_locked' ); 156 } else { 157 $check_users = get_users( array( 'fields' => 'ID', 'number' => 2 ) ); 158 159 if ( count( $check_users ) > 1 ) 160 add_action( 'admin_footer', '_admin_notice_post_locked' ); 161 162 unset( $check_users ); 163 } 164 165 $title = $post_type_object->labels->edit_item; 166 $post = get_post($post_id, OBJECT, 'edit'); 167 168 if ( post_type_supports($post_type, 'comments') ) { 169 wp_enqueue_script('admin-comments'); 170 enqueue_comment_hotkeys_js(); 171 } 172 173 include( ABSPATH . 'wp-admin/edit-form-advanced.php' ); 174 175 break; 176 176 177 177 case 'editattachment': 178 178 check_admin_referer('update-post_' . $post_id); 179 179 180 181 182 180 // Don't let these be changed 181 unset($_POST['guid']); 182 $_POST['post_type'] = 'attachment'; 183 183 184 185 186 184 // Update the thumbnail filename 185 $newmeta = wp_get_attachment_metadata( $post_id, true ); 186 $newmeta['thumb'] = $_POST['thumb']; 187 187 188 188 wp_update_attachment_metadata( $post_id, $newmeta ); 189 189 190 190 case 'editpost': 191 191 check_admin_referer('update-post_' . $post_id); 192 192 193 193 $post_id = edit_post(); 194 194 195 196 197 198 195 // Session cookie flag that the post was saved 196 if ( isset( $_COOKIE['wp-saving-post'] ) && $_COOKIE['wp-saving-post'] === $post_id . '-check' ) { 197 setcookie( 'wp-saving-post', $post_id . '-saved', time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl() ); 198 } 199 199 200 200 redirect_post($post_id); // Send user on their way while we keep working 201 201 202 202 exit(); 203 203 204 204 case 'trash': 205 205 check_admin_referer('trash-post_' . $post_id); 206 206 207 208 207 if ( ! $post ) 208 wp_die( __( 'The item you are trying to move to the Trash no longer exists.' ) ); 209 209 210 211 210 if ( ! $post_type_object ) 211 wp_die( __( 'Invalid post type.' ) ); 212 212 213 214 213 if ( ! current_user_can( 'delete_post', $post_id ) ) 214 wp_die( __( 'Sorry, you are not allowed to move this item to the Trash.' ) ); 215 215 216 217 218 219 216 if ( $user_id = wp_check_post_lock( $post_id ) ) { 217 $user = get_userdata( $user_id ); 218 wp_die( sprintf( __( 'You cannot move this item to the Trash. %s is currently editing.' ), $user->display_name ) ); 219 } 220 220 221 222 221 if ( ! wp_trash_post( $post_id ) ) 222 wp_die( __( 'Error in moving to Trash.' ) ); 223 223 224 225 224 wp_redirect( add_query_arg( array('trashed' => 1, 'ids' => $post_id), $sendback ) ); 225 exit(); 226 226 227 227 case 'untrash': 228 228 check_admin_referer('untrash-post_' . $post_id); 229 229 230 231 230 if ( ! $post ) 231 wp_die( __( 'The item you are trying to restore from the Trash no longer exists.' ) ); 232 232 233 234 233 if ( ! $post_type_object ) 234 wp_die( __( 'Invalid post type.' ) ); 235 235 236 237 236 if ( ! current_user_can( 'delete_post', $post_id ) ) 237 wp_die( __( 'Sorry, you are not allowed to restore this item from the Trash.' ) ); 238 238 239 240 239 if ( ! wp_untrash_post( $post_id ) ) 240 wp_die( __( 'Error in restoring from Trash.' ) ); 241 241 242 243 242 wp_redirect( add_query_arg('untrashed', 1, $sendback) ); 243 exit(); 244 244 245 245 case 'delete': 246 246 check_admin_referer('delete-post_' . $post_id); 247 247 248 249 248 if ( ! $post ) 249 wp_die( __( 'This item has already been deleted.' ) ); 250 250 251 252 251 if ( ! $post_type_object ) 252 wp_die( __( 'Invalid post type.' ) ); 253 253 254 255 254 if ( ! current_user_can( 'delete_post', $post_id ) ) 255 wp_die( __( 'Sorry, you are not allowed to delete this item.' ) ); 256 256 257 258 259 260 261 262 263 264 257 if ( $post->post_type == 'attachment' ) { 258 $force = ( ! MEDIA_TRASH ); 259 if ( ! wp_delete_attachment( $post_id, $force ) ) 260 wp_die( __( 'Error in deleting.' ) ); 261 } else { 262 if ( ! wp_delete_post( $post_id, true ) ) 263 wp_die( __( 'Error in deleting.' ) ); 264 } 265 265 266 267 266 wp_redirect( add_query_arg('deleted', 1, $sendback) ); 267 exit(); 268 268 269 269 case 'preview': 270 270 check_admin_referer( 'update-post_' . $post_id ); 271 271 272 272 $url = post_preview(); 273 273 274 275 274 wp_redirect($url); 275 exit(); 276 276 277 277 default: 278 279 280 281 282 283 284 285 286 287 288 289 290 278 /** 279 * Fires for a given custom post action request. 280 * 281 * The dynamic portion of the hook name, `$action`, refers to the custom post action. 282 * 283 * @since 4.6.0 284 * 285 * @param int $post_id Post ID sent with the request. 286 */ 287 do_action( "post_action_{$action}", $post_id ); 288 289 wp_redirect( admin_url('edit.php') ); 290 exit(); 291 291 } // end switch 292 292 include( ABSPATH . 'wp-admin/admin-footer.php' ); -
src/wp-admin/revision.php
diff --git a/src/wp-admin/revision.php b/src/wp-admin/revision.php index c86f78a451..fae38191a1 100644
a b require_once( dirname( __FILE__ ) . '/admin.php' ); 20 20 21 21 require ABSPATH . 'wp-admin/includes/revision.php'; 22 22 23 wp_reset_vars( array( 'revision', 'action', 'from', 'to' ) ); 23 $revision = wp_assign_request_var('revision'); 24 $action = wp_assign_request_var('action'); 25 $from = wp_assign_request_var('from'); 26 $to = wp_assign_request_var('to'); 24 27 25 28 $revision_id = absint( $revision ); 26 29 27 30 $from = is_numeric( $from ) ? absint( $from ) : null; 28 31 if ( ! $revision_id ) 29 32 $revision_id = absint( $to ); 30 33 $redirect = 'edit.php'; 31 34 32 35 switch ( $action ) { 33 36 case 'restore' : 34 35 37 if ( ! $revision = wp_get_post_revision( $revision_id ) ) 38 break; 36 39 37 38 40 if ( ! current_user_can( 'edit_post', $revision->post_parent ) ) 41 break; 39 42 40 41 43 if ( ! $post = get_post( $revision->post_parent ) ) 44 break; 42 45 43 44 45 46 47 46 // Restore if revisions are enabled or this is an autosave. 47 if ( ! wp_revisions_enabled( $post ) && ! wp_is_post_autosave( $revision ) ) { 48 $redirect = 'edit.php?post_type=' . $post->post_type; 49 break; 50 } 48 51 49 50 51 52 // Don't allow revision restore when post is locked 53 if ( wp_check_post_lock( $post->ID ) ) 54 break; 52 55 53 56 check_admin_referer( "restore-post_{$revision->ID}" ); 54 57 55 56 57 58 wp_restore_post_revision( $revision->ID ); 59 $redirect = add_query_arg( array( 'message' => 5, 'revision' => $revision->ID ), get_edit_post_link( $post->ID, 'url' ) ); 60 break; 58 61 case 'view' : 59 62 case 'edit' : 60 63 default : 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 64 if ( ! $revision = wp_get_post_revision( $revision_id ) ) 65 break; 66 if ( ! $post = get_post( $revision->post_parent ) ) 67 break; 68 69 if ( ! current_user_can( 'read_post', $revision->ID ) || ! current_user_can( 'edit_post', $revision->post_parent ) ) 70 break; 71 72 // Revisions disabled and we're not looking at an autosave 73 if ( ! wp_revisions_enabled( $post ) && ! wp_is_post_autosave( $revision ) ) { 74 $redirect = 'edit.php?post_type=' . $post->post_type; 75 break; 76 } 77 78 $post_edit_link = get_edit_post_link(); 79 $post_title = '<a href="' . $post_edit_link . '">' . _draft_or_post_title() . '</a>'; 80 /* translators: 1: Post title */ 81 $h1 = sprintf( __( 'Compare Revisions of “%1$s”' ), $post_title ); 82 $return_to_post = '<a href="' . $post_edit_link . '">' . __( '← Return to editor' ) . '</a>'; 83 $title = __( 'Revisions' ); 84 85 $redirect = false; 86 break; 84 87 } 85 88 86 89 // Empty post_type means either malformed object found, or no valid parent was found. 87 90 if ( ! $redirect && empty( $post->post_type ) ) 88 91 $redirect = 'edit.php'; 89 92 90 93 if ( ! empty( $redirect ) ) { 91 92 94 wp_redirect( $redirect ); 95 exit; 93 96 } 94 97 95 98 // This is so that the correct "Edit" menu item is selected. 96 99 if ( ! empty( $post->post_type ) && 'post' != $post->post_type ) 97 100 $parent_file = $submenu_file = 'edit.php?post_type=' . $post->post_type; 98 101 else 99 102 $parent_file = $submenu_file = 'edit.php'; 100 103 101 104 wp_enqueue_script( 'revisions' ); 102 105 wp_localize_script( 'revisions', '_wpRevisionsSettings', wp_prepare_revisions_for_js( $post, $revision_id, $from ) ); … … $revisions_overview .= '<li>' . __( 'Compare two different revisions by <strong> 111 114 $revisions_overview .= '<li>' . __( 'To restore a revision, <strong>click Restore This Revision</strong>.' ) . '</li></ul>'; 112 115 113 116 get_current_screen()->add_help_tab( array( 114 115 116 117 'id' => 'revisions-overview', 118 'title' => __( 'Overview' ), 119 'content' => $revisions_overview 117 120 ) ); 118 121 119 122 $revisions_sidebar = '<p><strong>' . __( 'For more information:' ) . '</strong></p>'; … … require_once( ABSPATH . 'wp-admin/admin-header.php' ); 127 130 ?> 128 131 129 132 <div class="wrap"> 130 131 133 <h1 class="long-header"><?php echo $h1; ?></h1> 134 <?php echo $return_to_post; ?> 132 135 </div> 133 136 <?php 134 137 wp_print_revision_templates(); -
src/wp-admin/theme-editor.php
diff --git a/src/wp-admin/theme-editor.php b/src/wp-admin/theme-editor.php index 03b91944ea..3c1e50ffe6 100644
a b 10 10 require_once( dirname( __FILE__ ) . '/admin.php' ); 11 11 12 12 if ( is_multisite() && ! is_network_admin() ) { 13 14 13 wp_redirect( network_admin_url( 'theme-editor.php' ) ); 14 exit(); 15 15 } 16 16 17 17 if ( !current_user_can('edit_themes') ) 18 18 wp_die('<p>'.__('Sorry, you are not allowed to edit templates for this site.').'</p>'); 19 19 20 20 $title = __("Edit Themes"); 21 21 $parent_file = 'themes.php'; … … get_current_screen()->add_help_tab( array( 24 24 'id' => 'overview', 25 25 'title' => __('Overview'), 26 26 'content' => 27 28 29 30 31 32 33 34 27 '<p>' . __('You can use the Theme Editor to edit the individual CSS and PHP files which make up your theme.') . '</p> 28 <p>' . __( 'Begin by choosing a theme to edit from the dropdown menu and clicking the Select button. A list then appears of the theme’s template files. Clicking once on any file name causes the file to appear in the large Editor box.' ) . '</p> 29 <p>' . __('For PHP files, you can use the Documentation dropdown to select from functions recognized in that file. Look Up takes you to a web page with reference material about that particular function.') . '</p> 30 <p id="newcontent-description">' . __( 'In the editing area the Tab key enters a tab character. To move below this area by pressing Tab, press the Esc key followed by the Tab key. In some cases the Esc key will need to be pressed twice before the Tab key will allow you to continue.' ) . '</p> 31 <p>' . __('After typing in your edits, click Update File.') . '</p> 32 <p>' . __('<strong>Advice:</strong> think very carefully about your site crashing if you are live-editing the theme currently in use.') . '</p> 33 <p>' . sprintf( __('Upgrading to a newer version of the same theme will override changes made here. To avoid this, consider creating a <a href="%s">child theme</a> instead.'), __('https://codex.wordpress.org/Child_Themes') ) . '</p>' . 34 ( is_network_admin() ? '<p>' . __('Any edits to files from this screen will be reflected on all sites in the network.') . '</p>' : '' ) 35 35 ) ); 36 36 37 37 get_current_screen()->set_help_sidebar( 38 39 40 41 42 43 38 '<p><strong>' . __('For more information:') . '</strong></p>' . 39 '<p>' . __('<a href="https://codex.wordpress.org/Theme_Development">Documentation on Theme Development</a>') . '</p>' . 40 '<p>' . __('<a href="https://codex.wordpress.org/Using_Themes">Documentation on Using Themes</a>') . '</p>' . 41 '<p>' . __('<a href="https://codex.wordpress.org/Editing_Files">Documentation on Editing Files</a>') . '</p>' . 42 '<p>' . __('<a href="https://codex.wordpress.org/Template_Tags">Documentation on Template Tags</a>') . '</p>' . 43 '<p>' . __('<a href="https://wordpress.org/support/">Support Forums</a>') . '</p>' 44 44 ); 45 45 46 wp_reset_vars( array( 'action', 'error', 'file', 'theme' ) ); 46 $action = wp_assign_request_var('action'); 47 $error = wp_assign_request_var('error'); 48 $file = wp_assign_request_var('file'); 49 $theme = wp_assign_request_var('theme'); 47 50 48 51 if ( $theme ) { 49 52 $stylesheet = $theme; 50 53 } else { 51 54 $stylesheet = get_stylesheet(); 52 55 } 53 56 54 57 $theme = wp_get_theme( $stylesheet ); 55 58 56 59 if ( ! $theme->exists() ) { 57 60 wp_die( __( 'The requested theme does not exist.' ) ); 58 61 } 59 62 60 63 if ( $theme->errors() && 'theme_no_stylesheet' == $theme->errors()->get_error_code() ) { 61 64 wp_die( __( 'The requested theme does not exist.' ) . ' ' . $theme->errors()->get_error_message() ); 62 65 } 63 66 64 67 $allowed_files = $style_files = array(); … … $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme 79 82 $file_types = array_unique( array_merge( $file_types, $default_types ) ); 80 83 81 84 foreach ( $file_types as $type ) { 82 83 84 85 86 87 88 89 90 91 92 93 94 95 85 switch ( $type ) { 86 case 'php': 87 $allowed_files += $theme->get_files( 'php', 1 ); 88 $has_templates = ! empty( $allowed_files ); 89 break; 90 case 'css': 91 $style_files = $theme->get_files( 'css' ); 92 $allowed_files['style.css'] = $style_files['style.css']; 93 $allowed_files += $style_files; 94 break; 95 default: 96 $allowed_files += $theme->get_files( $type ); 97 break; 98 } 96 99 } 97 100 98 101 if ( empty( $file ) ) { 99 100 102 $relative_file = 'style.css'; 103 $file = $allowed_files['style.css']; 101 104 } else { 102 103 105 $relative_file = $file; 106 $file = $theme->get_stylesheet_directory() . '/' . $relative_file; 104 107 } 105 108 106 109 validate_file_to_edit( $file, $allowed_files ); … … $scrollto = isset( $_REQUEST['scrollto'] ) ? (int) $_REQUEST['scrollto'] : 0; 108 111 109 112 switch( $action ) { 110 113 case 'update': 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 114 check_admin_referer( 'edit-theme_' . $file . $stylesheet ); 115 $newcontent = wp_unslash( $_POST['newcontent'] ); 116 $location = 'theme-editor.php?file=' . urlencode( $relative_file ) . '&theme=' . urlencode( $stylesheet ) . '&scrollto=' . $scrollto; 117 if ( is_writeable( $file ) ) { 118 // is_writable() not always reliable, check return value. see comments @ https://secure.php.net/is_writable 119 $f = fopen( $file, 'w+' ); 120 if ( $f !== false ) { 121 fwrite( $f, $newcontent ); 122 fclose( $f ); 123 $location .= '&updated=true'; 124 $theme->cache_delete(); 125 } 126 } 127 wp_redirect( $location ); 128 exit; 126 129 127 130 default: 128 131 129 132 require_once( ABSPATH . 'wp-admin/admin-header.php' ); 130 133 131 134 update_recently_edited( $file ); 132 135 133 134 136 if ( ! is_file( $file ) ) 137 $error = true; 135 138 136 137 138 139 139 $content = ''; 140 if ( ! $error && filesize( $file ) > 0 ) { 141 $f = fopen($file, 'r'); 142 $content = fread($f, filesize($file)); 140 143 141 142 144 if ( '.php' == substr( $file, strrpos( $file, '.' ) ) ) { 145 $functions = wp_doc_link_parse( $content ); 143 146 144 145 146 147 148 149 150 147 $docs_select = '<select name="docs-list" id="docs-list">'; 148 $docs_select .= '<option value="">' . esc_attr__( 'Function Name…' ) . '</option>'; 149 foreach ( $functions as $function ) { 150 $docs_select .= '<option value="' . esc_attr( urlencode( $function ) ) . '">' . htmlspecialchars( $function ) . '()</option>'; 151 } 152 $docs_select .= '</select>'; 153 } 151 154 152 153 155 $content = esc_textarea( $content ); 156 } 154 157 155 158 if ( isset( $_GET['updated'] ) ) : ?> 156 159 <div id="message" class="updated notice is-dismissible"><p><?php _e( 'File edited successfully.' ) ?></p></div> 157 160 <?php endif; 158 161 159 162 $description = get_file_description( $relative_file ); 160 163 $file_show = array_search( $file, array_filter( $allowed_files ) ); 161 164 if ( $description != $file_show ) 162 165 $description .= ' <span>(' . $file_show . ')</span>'; 163 166 ?> 164 167 <div class="wrap"> 165 168 <h1><?php echo esc_html( $title ); ?></h1> … … if ( $description != $file_show ) 169 172 <h2><?php echo $theme->display( 'Name' ); if ( $description ) echo ': ' . $description; ?></h2> 170 173 </div> 171 174 <div class="alignright"> 172 173 174 175 <form action="theme-editor.php" method="post"> 176 <strong><label for="theme"><?php _e('Select theme to edit:'); ?> </label></strong> 177 <select name="theme" id="theme"> 175 178 <?php 176 179 foreach ( wp_get_themes( array( 'errors' => null ) ) as $a_stylesheet => $a_theme ) { 177 178 180 if ( $a_theme->errors() && 'theme_no_stylesheet' == $a_theme->errors()->get_error_code() ) 181 continue; 179 182 180 181 183 $selected = $a_stylesheet == $stylesheet ? ' selected="selected"' : ''; 184 echo "\n\t" . '<option value="' . esc_attr( $a_stylesheet ) . '"' . $selected . '>' . $a_theme->display('Name') . '</option>'; 182 185 } 183 186 ?> 184 185 186 187 </select> 188 <?php submit_button( __( 'Select' ), '', 'Submit', false ); ?> 189 </form> 187 190 </div> 188 191 <br class="clear" /> 189 192 </div> 190 193 <?php 191 194 if ( $theme->errors() ) 192 195 echo '<div class="error"><p><strong>' . __( 'This theme is broken.' ) . '</strong> ' . $theme->errors()->get_error_message() . '</p></div>'; 193 196 ?> 194 197 <div id="templateside"> 195 198 <?php 196 199 if ( $allowed_files ) : 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 200 $previous_file_type = ''; 201 202 foreach ( $allowed_files as $filename => $absolute_filename ) : 203 $file_type = substr( $filename, strrpos( $filename, '.' ) ); 204 205 if ( $file_type !== $previous_file_type ) { 206 if ( '' !== $previous_file_type ) { 207 echo "\t</ul>\n"; 208 } 209 210 switch ( $file_type ) { 211 case '.php': 212 if ( $has_templates || $theme->parent() ) : 213 echo "\t<h2>" . __( 'Templates' ) . "</h2>\n"; 214 if ( $theme->parent() ) { 215 echo '<p class="howto">' . sprintf( __( 'This child theme inherits templates from a parent theme, %s.' ), 216 sprintf( '<a href="%s">%s</a>', 217 self_admin_url( 'theme-editor.php?theme=' . urlencode( $theme->get_template() ) ), 218 $theme->parent()->display( 'Name' ) 219 ) 220 ) . "</p>\n"; 221 } 222 endif; 223 break; 224 case '.css': 225 echo "\t<h2>" . _x( 'Styles', 'Theme stylesheets in theme editor' ) . "</h2>\n"; 226 break; 227 default: 228 /* translators: %s: file extension */ 229 echo "\t<h2>" . sprintf( __( '%s files' ), $file_type ) . "</h2>\n"; 230 break; 231 } 232 233 echo "\t<ul>\n"; 234 } 235 236 $file_description = get_file_description( $filename ); 237 if ( $filename !== basename( $absolute_filename ) || $file_description !== $filename ) { 238 $file_description .= '<br /><span class="nonessential">(' . $filename . ')</span>'; 239 } 240 241 if ( $absolute_filename === $file ) { 242 $file_description = '<span class="highlight">' . $file_description . '</span>'; 243 } 244 245 $previous_file_type = $file_type; 243 246 ?> 244 247 <li><a href="theme-editor.php?file=<?php echo urlencode( $filename ) ?>&theme=<?php echo urlencode( $stylesheet ) ?>"><?php echo $file_description; ?></a></li> 245 248 <?php 246 249 endforeach; 247 250 ?> 248 251 </ul> 249 252 <?php endif; ?> 250 253 </div> 251 254 <?php if ( $error ) : 252 255 echo '<div class="error"><p>' . __('Oops, no such file exists! Double check the name and try again, merci.') . '</p></div>'; 253 256 else : ?> 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 257 <form name="template" id="template" action="theme-editor.php" method="post"> 258 <?php wp_nonce_field( 'edit-theme_' . $file . $stylesheet ); ?> 259 <div><textarea cols="70" rows="30" name="newcontent" id="newcontent" aria-describedby="newcontent-description"><?php echo $content; ?></textarea> 260 <input type="hidden" name="action" value="update" /> 261 <input type="hidden" name="file" value="<?php echo esc_attr( $relative_file ); ?>" /> 262 <input type="hidden" name="theme" value="<?php echo esc_attr( $theme->get_stylesheet() ); ?>" /> 263 <input type="hidden" name="scrollto" id="scrollto" value="<?php echo $scrollto; ?>" /> 264 </div> 265 <?php if ( ! empty( $functions ) ) : ?> 266 <div id="documentation" class="hide-if-no-js"> 267 <label for="docs-list"><?php _e('Documentation:') ?></label> 268 <?php echo $docs_select; ?> 269 <input type="button" class="button" value="<?php esc_attr_e( 'Look Up' ); ?>" onclick="if ( '' != jQuery('#docs-list').val() ) { window.open( 'https://api.wordpress.org/core/handbook/1.0/?function=' + escape( jQuery( '#docs-list' ).val() ) + '&locale=<?php echo urlencode( get_user_locale() ) ?>&version=<?php echo urlencode( get_bloginfo( 'version' ) ) ?>&redirect=true'); }" /> 270 </div> 271 <?php endif; ?> 272 273 <div> 274 <?php if ( is_child_theme() && $theme->get_stylesheet() == get_template() ) : ?> 275 <p><?php if ( is_writeable( $file ) ) { ?><strong><?php _e( 'Caution:' ); ?></strong><?php } ?> 276 <?php _e( 'This is a file in your current parent theme.' ); ?></p> 277 <?php endif; ?> 275 278 <?php 276 277 278 279 if ( is_writeable( $file ) ) : 280 submit_button( __( 'Update File' ), 'primary', 'submit', true ); 281 else : ?> 279 282 <p><em><?php _e('You need to make this file writable before you can save your changes. See <a href="https://codex.wordpress.org/Changing_File_Permissions">the Codex</a> for more information.'); ?></em></p> 280 283 <?php endif; ?> 281 282 284 </div> 285 </form> 283 286 <?php 284 287 endif; // $error 285 288 ?> … … endif; // $error 287 290 </div> 288 291 <script type="text/javascript"> 289 292 jQuery(document).ready(function($){ 290 291 293 $('#template').submit(function(){ $('#scrollto').val( $('#newcontent').scrollTop() ); }); 294 $('#newcontent').scrollTop( $('#scrollto').val() ); 292 295 }); 293 296 </script> 294 297 <?php -
src/wp-admin/theme-install.php
diff --git a/src/wp-admin/theme-install.php b/src/wp-admin/theme-install.php index dd9ff500d1..64054a3de1 100644
a b 10 10 require_once( dirname( __FILE__ ) . '/admin.php' ); 11 11 require( ABSPATH . 'wp-admin/includes/theme-install.php' ); 12 12 13 wp_reset_vars( array( 'tab' ));13 $tab = wp_assign_request_var('tab'); 14 14 15 15 if ( ! current_user_can('install_themes') ) 16 16 wp_die( __( 'Sorry, you are not allowed to install themes on this site.' ) ); 17 17 18 18 if ( is_multisite() && ! is_network_admin() ) { 19 20 19 wp_redirect( network_admin_url( 'theme-install.php' ) ); 20 exit(); 21 21 } 22 22 23 23 $title = __( 'Add Themes' ); 24 24 $parent_file = 'themes.php'; 25 25 26 26 if ( ! is_network_admin() ) { 27 27 $submenu_file = 'themes.php'; 28 28 } 29 29 30 30 $installed_themes = search_theme_directories(); 31 31 32 32 if ( false === $installed_themes ) { 33 33 $installed_themes = array(); 34 34 } 35 35 36 36 foreach ( $installed_themes as $k => $v ) { 37 38 39 37 if ( false !== strpos( $k, '/' ) ) { 38 unset( $installed_themes[ $k ] ); 39 } 40 40 } 41 41 42 42 wp_localize_script( 'theme', '_wpThemeSettings', array( 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 43 'themes' => false, 44 'settings' => array( 45 'isInstall' => true, 46 'canInstall' => current_user_can( 'install_themes' ), 47 'installURI' => current_user_can( 'install_themes' ) ? self_admin_url( 'theme-install.php' ) : null, 48 'adminUrl' => parse_url( self_admin_url(), PHP_URL_PATH ) 49 ), 50 'l10n' => array( 51 'addNew' => __( 'Add New Theme' ), 52 'search' => __( 'Search Themes' ), 53 'searchPlaceholder' => __( 'Search themes...' ), // placeholder (no ellipsis) 54 'upload' => __( 'Upload Theme' ), 55 'back' => __( 'Back' ), 56 'error' => sprintf( 57 /* translators: %s: support forums URL */ 58 __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ), 59 __( 'https://wordpress.org/support/' ) 60 ), 61 'themesFound' => __( 'Number of Themes found: %d' ), 62 'noThemesFound' => __( 'No themes found. Try a different search.' ), 63 'collapseSidebar' => __( 'Collapse Sidebar' ), 64 'expandSidebar' => __( 'Expand Sidebar' ), 65 /* translators: accessibility text */ 66 'selectFeatureFilter' => __( 'Select one or more Theme features to filter by' ), 67 ), 68 'installedThemes' => array_keys( $installed_themes ), 69 69 ) ); 70 70 71 71 wp_enqueue_script( 'theme' ); 72 72 wp_enqueue_script( 'updates' ); 73 73 74 74 if ( $tab ) { 75 76 77 78 79 80 81 82 83 84 75 /** 76 * Fires before each of the tabs are rendered on the Install Themes page. 77 * 78 * The dynamic portion of the hook name, `$tab`, refers to the current 79 * theme install tab. Possible values are 'dashboard', 'search', 'upload', 80 * 'featured', 'new', or 'updated'. 81 * 82 * @since 2.8.0 83 */ 84 do_action( "install_themes_pre_{$tab}" ); 85 85 } 86 86 87 87 $help_overview = 88 89 90 91 92 93 94 95 96 97 98 99 88 '<p>' . sprintf( 89 /* translators: %s: Theme Directory URL */ 90 __( 'You can find additional themes for your site by using the Theme Browser/Installer on this screen, which will display themes from the <a href="%s">WordPress Theme Directory</a>. These themes are designed and developed by third parties, are available free of charge, and are compatible with the license WordPress uses.' ), 91 __( 'https://wordpress.org/themes/' ) 92 ) . '</p>' . 93 '<p>' . __( 'You can Search for themes by keyword, author, or tag, or can get more specific and search by criteria listed in the feature filter.' ) . ' <span id="live-search-desc">' . __( 'The search results will be updated as you type.' ) . '</span></p>' . 94 '<p>' . __( 'Alternately, you can browse the themes that are Featured, Popular, or Latest. When you find a theme you like, you can preview it or install it.' ) . '</p>' . 95 '<p>' . sprintf( 96 /* translators: %s: /wp-content/themes */ 97 __( 'You can Upload a theme manually if you have already downloaded its ZIP archive onto your computer (make sure it is from a trusted and original source). You can also do it the old-fashioned way and copy a downloaded theme’s folder via FTP into your %s directory.' ), 98 '<code>/wp-content/themes</code>' 99 ) . '</p>'; 100 100 101 101 get_current_screen()->add_help_tab( array( 102 103 104 102 'id' => 'overview', 103 'title' => __('Overview'), 104 'content' => $help_overview 105 105 ) ); 106 106 107 107 $help_installing = 108 109 108 '<p>' . __('Once you have generated a list of themes, you can preview and install any of them. Click on the thumbnail of the theme you’re interested in previewing. It will open up in a full-screen Preview page to give you a better idea of how that theme will look.') . '</p>' . 109 '<p>' . __('To install the theme so you can preview it with your site’s content and customize its theme options, click the "Install" button at the top of the left-hand pane. The theme files will be downloaded to your website automatically. When this is complete, the theme is now available for activation, which you can do by clicking the "Activate" link, or by navigating to your Manage Themes screen and clicking the "Live Preview" link under any installed theme’s thumbnail image.') . '</p>'; 110 110 111 111 get_current_screen()->add_help_tab( array( 112 113 114 112 'id' => 'installing', 113 'title' => __('Previewing and Installing'), 114 'content' => $help_installing 115 115 ) ); 116 116 117 117 get_current_screen()->set_help_sidebar( 118 119 120 118 '<p><strong>' . __('For more information:') . '</strong></p>' . 119 '<p>' . __('<a href="https://codex.wordpress.org/Using_Themes#Adding_New_Themes">Documentation on Adding New Themes</a>') . '</p>' . 120 '<p>' . __('<a href="https://wordpress.org/support/">Support Forums</a>') . '</p>' 121 121 ); 122 122 123 123 include(ABSPATH . 'wp-admin/admin-header.php'); 124 124 125 125 ?> 126 126 <div class="wrap"> 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 127 <h1 class="wp-heading-inline"><?php echo esc_html( $title ); ?></h1> 128 129 <?php 130 131 /** 132 * Filters the tabs shown on the Add Themes screen. 133 * 134 * This filter is for backward compatibility only, for the suppression of the upload tab. 135 * 136 * @since 2.8.0 137 * 138 * @param array $tabs The tabs shown on the Add Themes screen. Default is 'upload'. 139 */ 140 $tabs = apply_filters( 'install_themes_tabs', array( 'upload' => __( 'Upload Theme' ) ) ); 141 if ( ! empty( $tabs['upload'] ) && current_user_can( 'upload_themes' ) ) { 142 echo ' <button type="button" class="upload-view-toggle page-title-action hide-if-no-js" aria-expanded="false">' . __( 'Upload Theme' ) . '</button>'; 143 } 144 ?> 145 146 <hr class="wp-header-end"> 147 148 <div class="error hide-if-js"> 149 <p><?php _e( 'The Theme Installer screen requires JavaScript.' ); ?></p> 150 </div> 151 152 <div class="upload-theme"> 153 <?php install_themes_upload(); ?> 154 </div> 155 156 <h2 class="screen-reader-text hide-if-no-js"><?php _e( 'Filter themes list' ); ?></h2> 157 158 <div class="wp-filter hide-if-no-js"> 159 <div class="filter-count"> 160 <span class="count theme-count"></span> 161 </div> 162 163 <ul class="filter-links"> 164 <li><a href="#" data-sort="featured"><?php _ex( 'Featured', 'themes' ); ?></a></li> 165 <li><a href="#" data-sort="popular"><?php _ex( 'Popular', 'themes' ); ?></a></li> 166 <li><a href="#" data-sort="new"><?php _ex( 'Latest', 'themes' ); ?></a></li> 167 <li><a href="#" data-sort="favorites"><?php _ex( 'Favorites', 'themes' ); ?></a></li> 168 </ul> 169 170 <button type="button" class="button drawer-toggle" aria-expanded="false"><?php _e( 'Feature Filter' ); ?></button> 171 172 <form class="search-form"></form> 173 174 <div class="favorites-form"> 175 <?php 176 $action = 'save_wporg_username_' . get_current_user_id(); 177 if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), $action ) ) { 178 $user = isset( $_GET['user'] ) ? wp_unslash( $_GET['user'] ) : get_user_option( 'wporg_favorites' ); 179 update_user_meta( get_current_user_id(), 'wporg_favorites', $user ); 180 } else { 181 $user = get_user_option( 'wporg_favorites' ); 182 } 183 ?> 184 <p class="install-help"><?php _e( 'If you have marked themes as favorites on WordPress.org, you can browse them here.' ); ?></p> 185 186 <p> 187 <label for="wporg-username-input"><?php _e( 'Your WordPress.org username:' ); ?></label> 188 <input type="hidden" id="wporg-username-nonce" name="_wpnonce" value="<?php echo esc_attr( wp_create_nonce( $action ) ); ?>" /> 189 <input type="search" id="wporg-username-input" value="<?php echo esc_attr( $user ); ?>" /> 190 <input type="button" class="button favorites-form-submit" value="<?php esc_attr_e( 'Get Favorites' ); ?>" /> 191 </p> 192 </div> 193 194 <div class="filter-drawer"> 195 <div class="buttons"> 196 <button type="button" class="apply-filters button"><?php _e( 'Apply Filters' ); ?><span></span></button> 197 <button type="button" class="clear-filters button" aria-label="<?php esc_attr_e( 'Clear current filters' ); ?>"><?php _e( 'Clear' ); ?></button> 198 </div> 199 <?php 200 $feature_list = get_theme_feature_list(); 201 foreach ( $feature_list as $feature_name => $features ) { 202 echo '<fieldset class="filter-group">'; 203 $feature_name = esc_html( $feature_name ); 204 echo '<legend>' . $feature_name . '</legend>'; 205 echo '<div class="filter-group-feature">'; 206 foreach ( $features as $feature => $feature_name ) { 207 $feature = esc_attr( $feature ); 208 echo '<input type="checkbox" id="filter-id-' . $feature . '" value="' . $feature . '" /> '; 209 echo '<label for="filter-id-' . $feature . '">' . $feature_name . '</label><br>'; 210 } 211 echo '</div>'; 212 echo '</fieldset>'; 213 } 214 ?> 215 <div class="buttons"> 216 <button type="button" class="apply-filters button"><?php _e( 'Apply Filters' ); ?><span></span></button> 217 <button type="button" class="clear-filters button" aria-label="<?php esc_attr_e( 'Clear current filters' ); ?>"><?php _e( 'Clear' ); ?></button> 218 </div> 219 <div class="filtered-by"> 220 <span><?php _e( 'Filtering by:' ); ?></span> 221 <div class="tags"></div> 222 <button type="button" class="button-link edit-filters"><?php _e( 'Edit Filters' ); ?></button> 223 </div> 224 </div> 225 </div> 226 <h2 class="screen-reader-text hide-if-no-js"><?php _e( 'Themes list' ); ?></h2> 227 <div class="theme-browser content-filterable"></div> 228 <div class="theme-install-overlay wp-full-overlay expanded"></div> 229 230 <p class="no-themes"><?php _e( 'No themes found. Try a different search.' ); ?></p> 231 <span class="spinner"></span> 232 232 233 233 <?php 234 234 if ( $tab ) { 235 236 237 238 239 240 241 242 243 244 245 246 235 /** 236 * Fires at the top of each of the tabs on the Install Themes page. 237 * 238 * The dynamic portion of the hook name, `$tab`, refers to the current 239 * theme install tab. Possible values are 'dashboard', 'search', 'upload', 240 * 'featured', 'new', or 'updated'. 241 * 242 * @since 2.8.0 243 * 244 * @param int $paged Number of the current page of results being viewed. 245 */ 246 do_action( "install_themes_{$tab}", $paged ); 247 247 } 248 248 ?> 249 249 </div> 250 250 251 251 <script id="tmpl-theme" type="text/template"> 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 252 <# if ( data.screenshot_url ) { #> 253 <div class="theme-screenshot"> 254 <img src="{{ data.screenshot_url }}" alt="" /> 255 </div> 256 <# } else { #> 257 <div class="theme-screenshot blank"></div> 258 <# } #> 259 <span class="more-details"><?php _ex( 'Details & Preview', 'theme' ); ?></span> 260 <div class="theme-author"> 261 <?php 262 /* translators: %s: Theme author name */ 263 printf( __( 'By %s' ), '{{ data.author }}' ); 264 ?> 265 </div> 266 <h3 class="theme-name">{{ data.name }}</h3> 267 268 <div class="theme-actions"> 269 <# if ( data.installed ) { #> 270 <?php 271 /* translators: %s: Theme name */ 272 $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); 273 ?> 274 <# if ( data.activate_url ) { #> 275 <a class="button button-primary activate" href="{{ data.activate_url }}" aria-label="<?php echo esc_attr( $aria_label ); ?>"><?php _e( 'Activate' ); ?></a> 276 <# } #> 277 <# if ( data.customize_url ) { #> 278 <a class="button load-customize" href="{{ data.customize_url }}"><?php _e( 'Live Preview' ); ?></a> 279 <# } else { #> 280 <button class="button preview install-theme-preview"><?php _e( 'Preview' ); ?></button> 281 <# } #> 282 <# } else { #> 283 <?php 284 /* translators: %s: Theme name */ 285 $aria_label = sprintf( __( 'Install %s' ), '{{ data.name }}' ); 286 ?> 287 <a class="button button-primary theme-install" data-name="{{ data.name }}" data-slug="{{ data.id }}" href="{{ data.install_url }}" aria-label="<?php echo esc_attr( $aria_label ); ?>"><?php _e( 'Install' ); ?></a> 288 <button class="button preview install-theme-preview"><?php _e( 'Preview' ); ?></button> 289 <# } #> 290 </div> 291 292 <# if ( data.installed ) { #> 293 <div class="notice notice-success notice-alt"><p><?php _ex( 'Installed', 'theme' ); ?></p></div> 294 <# } #> 295 295 </script> 296 296 297 297 <script id="tmpl-theme-preview" type="text/template"> 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 298 <div class="wp-full-overlay-sidebar"> 299 <div class="wp-full-overlay-header"> 300 <button class="close-full-overlay"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button> 301 <button class="previous-theme"><span class="screen-reader-text"><?php _ex( 'Previous', 'Button label for a theme' ); ?></span></button> 302 <button class="next-theme"><span class="screen-reader-text"><?php _ex( 'Next', 'Button label for a theme' ); ?></span></button> 303 <# if ( data.installed ) { #> 304 <a class="button button-primary activate" href="{{ data.activate_url }}"><?php _e( 'Activate' ); ?></a> 305 <# } else { #> 306 <a href="{{ data.install_url }}" class="button button-primary theme-install" data-name="{{ data.name }}" data-slug="{{ data.id }}"><?php _e( 'Install' ); ?></a> 307 <# } #> 308 </div> 309 <div class="wp-full-overlay-sidebar-content"> 310 <div class="install-theme-info"> 311 <h3 class="theme-name">{{ data.name }}</h3> 312 <span class="theme-by"> 313 <?php 314 /* translators: %s: Theme author name */ 315 printf( __( 'By %s' ), '{{ data.author }}' ); 316 ?> 317 </span> 318 319 <img class="theme-screenshot" src="{{ data.screenshot_url }}" alt="" /> 320 321 <div class="theme-details"> 322 <# if ( data.rating ) { #> 323 <div class="theme-rating"> 324 {{{ data.stars }}} 325 <span class="num-ratings">({{ data.num_ratings }})</span> 326 </div> 327 <# } else { #> 328 <span class="no-rating"><?php _e( 'This theme has not been rated yet.' ); ?></span> 329 <# } #> 330 <div class="theme-version"> 331 <?php 332 /* translators: %s: Theme version */ 333 printf( __( 'Version: %s' ), '{{ data.version }}' ); 334 ?> 335 </div> 336 <div class="theme-description">{{{ data.description }}}</div> 337 </div> 338 </div> 339 </div> 340 <div class="wp-full-overlay-footer"> 341 <button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php esc_attr_e( 'Collapse Sidebar' ); ?>"> 342 <span class="collapse-sidebar-arrow"></span> 343 <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span> 344 </button> 345 </div> 346 </div> 347 <div class="wp-full-overlay-main"> 348 <iframe src="{{ data.preview_url }}" title="<?php esc_attr_e( 'Preview' ); ?>"></iframe> 349 </div> 350 350 </script> 351 351 352 352 <?php -
src/wp-admin/themes.php
diff --git a/src/wp-admin/themes.php b/src/wp-admin/themes.php index b73cd166d5..f972e32d5a 100644
a b 10 10 require_once( dirname( __FILE__ ) . '/admin.php' ); 11 11 12 12 if ( ! current_user_can( 'switch_themes' ) && ! current_user_can( 'edit_theme_options' ) ) { 13 14 15 16 17 13 wp_die( 14 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' . 15 '<p>' . __( 'Sorry, you are not allowed to edit theme options on this site.' ) . '</p>', 16 403 17 ); 18 18 } 19 19 20 20 if ( current_user_can( 'switch_themes' ) && isset($_GET['action'] ) ) { 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 21 if ( 'activate' == $_GET['action'] ) { 22 check_admin_referer('switch-theme_' . $_GET['stylesheet']); 23 $theme = wp_get_theme( $_GET['stylesheet'] ); 24 25 if ( ! $theme->exists() || ! $theme->is_allowed() ) { 26 wp_die( 27 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' . 28 '<p>' . __( 'The requested theme does not exist.' ) . '</p>', 29 403 30 ); 31 } 32 33 switch_theme( $theme->get_stylesheet() ); 34 wp_redirect( admin_url('themes.php?activated=true') ); 35 exit; 36 } elseif ( 'delete' == $_GET['action'] ) { 37 check_admin_referer('delete-theme_' . $_GET['stylesheet']); 38 $theme = wp_get_theme( $_GET['stylesheet'] ); 39 40 if ( ! current_user_can( 'delete_themes' ) ) { 41 wp_die( 42 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' . 43 '<p>' . __( 'Sorry, you are not allowed to delete this item.' ) . '</p>', 44 403 45 ); 46 } 47 48 if ( ! $theme->exists() ) { 49 wp_die( 50 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' . 51 '<p>' . __( 'The requested theme does not exist.' ) . '</p>', 52 403 53 ); 54 } 55 56 $active = wp_get_theme(); 57 if ( $active->get( 'Template' ) == $_GET['stylesheet'] ) { 58 wp_redirect( admin_url( 'themes.php?delete-active-child=true' ) ); 59 } else { 60 delete_theme( $_GET['stylesheet'] ); 61 wp_redirect( admin_url( 'themes.php?deleted=true' ) ); 62 } 63 exit; 64 } 65 65 } 66 66 67 67 $title = __('Manage Themes'); … … $parent_file = 'themes.php'; 69 69 70 70 // Help tab: Overview 71 71 if ( current_user_can( 'switch_themes' ) ) { 72 73 74 75 76 77 78 79 80 81 82 83 84 72 $help_overview = '<p>' . __( 'This screen is used for managing your installed themes. Aside from the default theme(s) included with your WordPress installation, themes are designed and developed by third parties.' ) . '</p>' . 73 '<p>' . __( 'From this screen you can:' ) . '</p>' . 74 '<ul><li>' . __( 'Hover or tap to see Activate and Live Preview buttons' ) . '</li>' . 75 '<li>' . __( 'Click on the theme to see the theme name, version, author, description, tags, and the Delete link' ) . '</li>' . 76 '<li>' . __( 'Click Customize for the current theme or Live Preview for any other theme to see a live preview' ) . '</li></ul>' . 77 '<p>' . __( 'The current theme is displayed highlighted as the first theme.' ) . '</p>' . 78 '<p>' . __( 'The search for installed themes will search for terms in their name, description, author, or tag.' ) . ' <span id="live-search-desc">' . __( 'The search results will be updated as you type.' ) . '</span></p>'; 79 80 get_current_screen()->add_help_tab( array( 81 'id' => 'overview', 82 'title' => __( 'Overview' ), 83 'content' => $help_overview 84 ) ); 85 85 } // switch_themes 86 86 87 87 // Help tab: Adding Themes 88 88 if ( current_user_can( 'install_themes' ) ) { 89 90 91 92 93 94 95 96 97 98 99 89 if ( is_multisite() ) { 90 $help_install = '<p>' . __('Installing themes on Multisite can only be done from the Network Admin section.') . '</p>'; 91 } else { 92 $help_install = '<p>' . sprintf( __('If you would like to see more themes to choose from, click on the “Add New” button and you will be able to browse or search for additional themes from the <a href="%s">WordPress Theme Directory</a>. Themes in the WordPress Theme Directory are designed and developed by third parties, and are compatible with the license WordPress uses. Oh, and they’re free!'), __( 'https://wordpress.org/themes/' ) ) . '</p>'; 93 } 94 95 get_current_screen()->add_help_tab( array( 96 'id' => 'adding-themes', 97 'title' => __('Adding Themes'), 98 'content' => $help_install 99 ) ); 100 100 } // install_themes 101 101 102 102 // Help tab: Previewing and Customizing 103 103 if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { 104 105 106 107 108 109 110 111 112 113 104 $help_customize = 105 '<p>' . __( 'Tap or hover on any theme then click the Live Preview button to see a live preview of that theme and change theme options in a separate, full-screen view. You can also find a Live Preview button at the bottom of the theme details screen. Any installed theme can be previewed and customized in this way.' ) . '</p>'. 106 '<p>' . __( 'The theme being previewed is fully interactive — navigate to different pages to see how the theme handles posts, archives, and other page templates. The settings may differ depending on what theme features the theme being previewed supports. To accept the new settings and activate the theme all in one step, click the Save & Activate button above the menu.' ) . '</p>' . 107 '<p>' . __( 'When previewing on smaller monitors, you can use the collapse icon at the bottom of the left-hand pane. This will hide the pane, giving you more room to preview your site in the new theme. To bring the pane back, click on the collapse icon again.' ) . '</p>'; 108 109 get_current_screen()->add_help_tab( array( 110 'id' => 'customize-preview-themes', 111 'title' => __( 'Previewing and Customizing' ), 112 'content' => $help_customize 113 ) ); 114 114 } // edit_theme_options && customize 115 115 116 116 get_current_screen()->set_help_sidebar( 117 118 119 117 '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . 118 '<p>' . __( '<a href="https://codex.wordpress.org/Using_Themes">Documentation on Using Themes</a>' ) . '</p>' . 119 '<p>' . __( '<a href="https://wordpress.org/support/">Support Forums</a>' ) . '</p>' 120 120 ); 121 121 122 122 if ( current_user_can( 'switch_themes' ) ) { 123 123 $themes = wp_prepare_themes_for_js(); 124 124 } else { 125 125 $themes = wp_prepare_themes_for_js( array( wp_get_theme() ) ); 126 126 } 127 wp_reset_vars( array( 'theme', 'search' ) ); 127 128 $theme = wp_assign_request_var('theme'); 129 $search = wp_assign_request_var('search'); 128 130 129 131 wp_localize_script( 'theme', '_wpThemeSettings', array( 130 131 132 133 134 135 136 137 138 139 140 141 142 143 132 'themes' => $themes, 133 'settings' => array( 134 'canInstall' => ( ! is_multisite() && current_user_can( 'install_themes' ) ), 135 'installURI' => ( ! is_multisite() && current_user_can( 'install_themes' ) ) ? admin_url( 'theme-install.php' ) : null, 136 'confirmDelete' => __( "Are you sure you want to delete this theme?\n\nClick 'Cancel' to go back, 'OK' to confirm the delete." ), 137 'adminUrl' => parse_url( admin_url(), PHP_URL_PATH ), 138 ), 139 'l10n' => array( 140 'addNew' => __( 'Add New Theme' ), 141 'search' => __( 'Search Installed Themes' ), 142 'searchPlaceholder' => __( 'Search installed themes...' ), // placeholder (no ellipsis) 143 'themesFound' => __( 'Number of Themes found: %d' ), 144 'noThemesFound' => __( 'No themes found. Try a different search.' ), 145 ), 144 146 ) ); 145 147 146 148 add_thickbox(); … … require_once( ABSPATH . 'wp-admin/admin-header.php' ); 152 154 ?> 153 155 154 156 <div class="wrap"> 155 156 157 157 <h1 class="wp-heading-inline"><?php esc_html_e( 'Themes' ); ?> 158 <span class="title-count theme-count"><?php echo count( $themes ); ?></span> 159 </h1> 158 160 159 160 161 161 <?php if ( ! is_multisite() && current_user_can( 'install_themes' ) ) : ?> 162 <a href="<?php echo admin_url( 'theme-install.php' ); ?>" class="hide-if-no-js page-title-action"><?php echo esc_html_x( 'Add New', 'Add new theme' ); ?></a> 163 <?php endif; ?> 162 164 163 165 <form class="search-form"></form> 164 166 165 167 <hr class="wp-header-end"> 166 168 <?php 167 169 if ( ! validate_current_theme() || isset( $_GET['broken'] ) ) : ?> 168 170 <div id="message1" class="updated notice is-dismissible"><p><?php _e('The active theme is broken. Reverting to the default theme.'); ?></p></div> 169 171 <?php elseif ( isset($_GET['activated']) ) : 170 171 172 172 if ( isset( $_GET['previewed'] ) ) { ?> 173 <div id="message2" class="updated notice is-dismissible"><p><?php _e( 'Settings saved and theme activated.' ); ?> <a href="<?php echo home_url( '/' ); ?>"><?php _e( 'Visit site' ); ?></a></p></div> 174 <?php } else { ?> 173 175 <div id="message2" class="updated notice is-dismissible"><p><?php _e( 'New theme activated.' ); ?> <a href="<?php echo home_url( '/' ); ?>"><?php _e( 'Visit site' ); ?></a></p></div><?php 174 175 176 } 177 elseif ( isset($_GET['deleted']) ) : ?> 176 178 <div id="message3" class="updated notice is-dismissible"><p><?php _e('Theme deleted.') ?></p></div> 177 179 <?php elseif ( isset( $_GET['delete-active-child'] ) ) : ?> 178 180 <div id="message4" class="error"><p><?php _e( 'You cannot delete a theme while it has an active child theme.' ); ?></p></div> 179 181 <?php 180 182 endif; 181 183 182 184 $ct = wp_get_theme(); 183 185 184 186 if ( $ct->errors() && ( ! is_multisite() || current_user_can( 'manage_network_themes' ) ) ) { 185 187 echo '<div class="error"><p>' . __( 'ERROR:' ) . ' ' . $ct->errors()->get_error_message() . '</p></div>'; 186 188 } 187 189 188 190 /* 189 191 // Certain error codes are less fatal than others. We can still display theme information in most cases. 190 192 if ( ! $ct->errors() || ( 1 == count( $ct->errors()->get_error_codes() ) 191 193 && in_array( $ct->errors()->get_error_code(), array( 'theme_no_parent', 'theme_parent_invalid', 'theme_no_index' ) ) ) ) : ?> 192 194 */ 193 195 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 196 // Pretend you didn't see this. 197 $current_theme_actions = array(); 198 if ( is_array( $submenu ) && isset( $submenu['themes.php'] ) ) { 199 foreach ( (array) $submenu['themes.php'] as $item) { 200 $class = ''; 201 if ( 'themes.php' == $item[2] || 'theme-editor.php' == $item[2] || 0 === strpos( $item[2], 'customize.php' ) ) 202 continue; 203 // 0 = name, 1 = capability, 2 = file 204 if ( ( strcmp($self, $item[2]) == 0 && empty($parent_file)) || ($parent_file && ($item[2] == $parent_file)) ) 205 $class = ' current'; 206 if ( !empty($submenu[$item[2]]) ) { 207 $submenu[$item[2]] = array_values($submenu[$item[2]]); // Re-index. 208 $menu_hook = get_plugin_page_hook($submenu[$item[2]][0][2], $item[2]); 209 if ( file_exists(WP_PLUGIN_DIR . "/{$submenu[$item[2]][0][2]}") || !empty($menu_hook)) 210 $current_theme_actions[] = "<a class='button$class' href='admin.php?page={$submenu[$item[2]][0][2]}'>{$item[0]}</a>"; 211 else 212 $current_theme_actions[] = "<a class='button$class' href='{$submenu[$item[2]][0][2]}'>{$item[0]}</a>"; 213 } elseif ( ! empty( $item[2] ) && current_user_can( $item[1] ) ) { 214 $menu_file = $item[2]; 215 216 if ( current_user_can( 'customize' ) ) { 217 if ( 'custom-header' === $menu_file ) { 218 $current_theme_actions[] = "<a class='button hide-if-no-customize$class' href='customize.php?autofocus[control]=header_image'>{$item[0]}</a>"; 219 } elseif ( 'custom-background' === $menu_file ) { 220 $current_theme_actions[] = "<a class='button hide-if-no-customize$class' href='customize.php?autofocus[control]=background_image'>{$item[0]}</a>"; 221 } 222 } 223 224 if ( false !== ( $pos = strpos( $menu_file, '?' ) ) ) { 225 $menu_file = substr( $menu_file, 0, $pos ); 226 } 227 228 if ( file_exists( ABSPATH . "wp-admin/$menu_file" ) ) { 229 $current_theme_actions[] = "<a class='button$class' href='{$item[2]}'>{$item[0]}</a>"; 230 } else { 231 $current_theme_actions[] = "<a class='button$class' href='themes.php?page={$item[2]}'>{$item[0]}</a>"; 232 } 233 } 234 } 235 } 234 236 235 237 ?> 236 238 237 239 <div class="theme-browser"> 238 240 <div class="themes wp-clearfix"> 239 241 240 242 <?php 241 243 /* … … if ( ! $ct->errors() || ( 1 == count( $ct->errors()->get_error_codes() ) 243 245 */ 244 246 245 247 foreach ( $themes as $theme ) : 246 247 248 248 $aria_action = esc_attr( $theme['id'] . '-action' ); 249 $aria_name = esc_attr( $theme['id'] . '-name' ); 250 ?> 249 251 <div class="theme<?php if ( $theme['active'] ) echo ' active'; ?>" tabindex="0" aria-describedby="<?php echo $aria_action . ' ' . $aria_name; ?>"> 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 252 <?php if ( ! empty( $theme['screenshot'][0] ) ) { ?> 253 <div class="theme-screenshot"> 254 <img src="<?php echo $theme['screenshot'][0]; ?>" alt="" /> 255 </div> 256 <?php } else { ?> 257 <div class="theme-screenshot blank"></div> 258 <?php } ?> 259 260 <?php if ( $theme['hasUpdate'] ) : ?> 261 <div class="update-message notice inline notice-warning notice-alt"> 262 <?php if ( $theme['hasPackage'] ) : ?> 263 <p><?php _e( 'New version available. <button class="button-link" type="button">Update now</button>' ); ?></p> 264 <?php else : ?> 265 <p><?php _e( 'New version available.' ); ?></p> 266 <?php endif; ?> 267 </div> 268 <?php endif; ?> 269 270 <span class="more-details" id="<?php echo $aria_action; ?>"><?php _e( 'Theme Details' ); ?></span> 271 <div class="theme-author"><?php printf( __( 'By %s' ), $theme['author'] ); ?></div> 272 273 <?php if ( $theme['active'] ) { ?> 274 <h2 class="theme-name" id="<?php echo $aria_name; ?>"> 275 <?php 276 /* translators: %s: theme name */ 277 printf( __( '<span>Active:</span> %s' ), $theme['name'] ); 278 ?> 279 </h2> 280 <?php } else { ?> 281 <h2 class="theme-name" id="<?php echo $aria_name; ?>"><?php echo $theme['name']; ?></h2> 282 <?php } ?> 283 284 <div class="theme-actions"> 285 286 <?php if ( $theme['active'] ) { ?> 287 <?php if ( $theme['actions']['customize'] && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { ?> 288 <a class="button button-primary customize load-customize hide-if-no-customize" href="<?php echo $theme['actions']['customize']; ?>"><?php _e( 'Customize' ); ?></a> 289 <?php } ?> 290 <?php } else { ?> 291 <?php 292 /* translators: %s: Theme name */ 293 $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); 294 ?> 295 <a class="button activate" href="<?php echo $theme['actions']['activate']; ?>" aria-label="<?php echo esc_attr( $aria_label ); ?>"><?php _e( 'Activate' ); ?></a> 296 <?php if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { ?> 297 <a class="button button-primary load-customize hide-if-no-customize" href="<?php echo $theme['actions']['customize']; ?>"><?php _e( 'Live Preview' ); ?></a> 298 <?php } ?> 299 <?php } ?> 300 301 </div> 300 302 </div> 301 303 <?php endforeach; ?> 302 304 </div> 303 305 </div> 304 306 <div class="theme-overlay"></div> 305 307 … … $can_delete = current_user_can( 'delete_themes' ); 319 321 $can_install = current_user_can( 'install_themes' ); 320 322 ?> 321 323 <table> 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 324 <tr> 325 <th><?php _ex('Name', 'theme name'); ?></th> 326 <th><?php _e('Description'); ?></th> 327 <?php if ( $can_delete ) { ?> 328 <td></td> 329 <?php } ?> 330 <?php if ( $can_install ) { ?> 331 <td></td> 332 <?php } ?> 333 </tr> 334 <?php foreach ( $broken_themes as $broken_theme ) : ?> 335 <tr> 336 <td><?php echo $broken_theme->get( 'Name' ) ? $broken_theme->display( 'Name' ) : $broken_theme->get_stylesheet(); ?></td> 337 <td><?php echo $broken_theme->errors()->get_error_message(); ?></td> 338 <?php 339 if ( $can_delete ) { 340 $stylesheet = $broken_theme->get_stylesheet(); 341 $delete_url = add_query_arg( array( 342 'action' => 'delete', 343 'stylesheet' => urlencode( $stylesheet ), 344 ), admin_url( 'themes.php' ) ); 345 $delete_url = wp_nonce_url( $delete_url, 'delete-theme_' . $stylesheet ); 346 ?> 347 <td><a href="<?php echo esc_url( $delete_url ); ?>" class="button delete-theme"><?php _e( 'Delete' ); ?></a></td> 348 <?php 349 } 350 351 if ( $can_install && 'theme_no_parent' === $broken_theme->errors()->get_error_code() ) { 352 $parent_theme_name = $broken_theme->get( 'Template' ); 353 $parent_theme = themes_api( 'theme_information', array( 'slug' => urlencode( $parent_theme_name ) ) ); 354 355 if ( ! is_wp_error( $parent_theme ) ) { 356 $install_url = add_query_arg( array( 357 'action' => 'install-theme', 358 'theme' => urlencode( $parent_theme_name ), 359 ), admin_url( 'update.php' ) ); 360 $install_url = wp_nonce_url( $install_url, 'install-theme_' . $parent_theme_name ); 361 ?> 362 <td><a href="<?php echo esc_url( $install_url ); ?>" class="button install-theme"><?php _e( 'Install Parent Theme' ); ?></a></td> 363 <?php 364 } 365 } 366 ?> 367 </tr> 368 <?php endforeach; ?> 367 369 </table> 368 370 </div> 369 371 … … $can_install = current_user_can( 'install_themes' ); 378 380 */ 379 381 ?> 380 382 <script id="tmpl-theme" type="text/template"> 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 383 <# if ( data.screenshot[0] ) { #> 384 <div class="theme-screenshot"> 385 <img src="{{ data.screenshot[0] }}" alt="" /> 386 </div> 387 <# } else { #> 388 <div class="theme-screenshot blank"></div> 389 <# } #> 390 391 <# if ( data.hasUpdate ) { #> 392 <# if ( data.hasPackage ) { #> 393 <div class="update-message notice inline notice-warning notice-alt"><p><?php _e( 'New version available. <button class="button-link" type="button">Update now</button>' ); ?></p></div> 394 <# } else { #> 395 <div class="update-message notice inline notice-warning notice-alt"><p><?php _e( 'New version available.' ); ?></p></div> 396 <# } #> 397 <# } #> 398 399 <span class="more-details" id="{{ data.id }}-action"><?php _e( 'Theme Details' ); ?></span> 400 <div class="theme-author"> 401 <?php 402 /* translators: %s: Theme author name */ 403 printf( __( 'By %s' ), '{{{ data.author }}}' ); 404 ?> 405 </div> 406 407 <# if ( data.active ) { #> 408 <h2 class="theme-name" id="{{ data.id }}-name"> 409 <?php 410 /* translators: %s: Theme name */ 411 printf( __( '<span>Active:</span> %s' ), '{{{ data.name }}}' ); 412 ?> 413 </h2> 414 <# } else { #> 415 <h2 class="theme-name" id="{{ data.id }}-name">{{{ data.name }}}</h2> 416 <# } #> 417 418 <div class="theme-actions"> 419 <# if ( data.active ) { #> 420 <# if ( data.actions.customize ) { #> 421 <a class="button button-primary customize load-customize hide-if-no-customize" href="{{{ data.actions.customize }}}"><?php _e( 'Customize' ); ?></a> 422 <# } #> 423 <# } else { #> 424 <?php 425 /* translators: %s: Theme name */ 426 $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); 427 ?> 428 <a class="button activate" href="{{{ data.actions.activate }}}" aria-label="<?php echo $aria_label; ?>"><?php _e( 'Activate' ); ?></a> 429 <a class="button button-primary load-customize hide-if-no-customize" href="{{{ data.actions.customize }}}"><?php _e( 'Live Preview' ); ?></a> 430 <# } #> 431 </div> 430 432 </script> 431 433 432 434 <script id="tmpl-theme-single" type="text/template"> 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 435 <div class="theme-backdrop"></div> 436 <div class="theme-wrap wp-clearfix"> 437 <div class="theme-header"> 438 <button class="left dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Show previous theme' ); ?></span></button> 439 <button class="right dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Show next theme' ); ?></span></button> 440 <button class="close dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Close details dialog' ); ?></span></button> 441 </div> 442 <div class="theme-about wp-clearfix"> 443 <div class="theme-screenshots"> 444 <# if ( data.screenshot[0] ) { #> 445 <div class="screenshot"><img src="{{ data.screenshot[0] }}" alt="" /></div> 446 <# } else { #> 447 <div class="screenshot blank"></div> 448 <# } #> 449 </div> 450 451 <div class="theme-info"> 452 <# if ( data.active ) { #> 453 <span class="current-label"><?php _e( 'Current Theme' ); ?></span> 454 <# } #> 455 <h2 class="theme-name">{{{ data.name }}}<span class="theme-version"><?php printf( __( 'Version: %s' ), '{{ data.version }}' ); ?></span></h2> 456 <p class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.authorAndUri }}}' ); ?></p> 457 458 <# if ( data.hasUpdate ) { #> 459 <div class="notice notice-warning notice-alt notice-large"> 460 <h3 class="notice-title"><?php _e( 'Update Available' ); ?></h3> 461 {{{ data.update }}} 462 </div> 463 <# } #> 464 <p class="theme-description">{{{ data.description }}}</p> 465 466 <# if ( data.parent ) { #> 467 <p class="parent-theme"><?php printf( __( 'This is a child theme of %s.' ), '<strong>{{{ data.parent }}}</strong>' ); ?></p> 468 <# } #> 469 470 <# if ( data.tags ) { #> 471 <p class="theme-tags"><span><?php _e( 'Tags:' ); ?></span> {{{ data.tags }}}</p> 472 <# } #> 473 </div> 474 </div> 475 476 <div class="theme-actions"> 477 <div class="active-theme"> 478 <a href="{{{ data.actions.customize }}}" class="button button-primary customize load-customize hide-if-no-customize"><?php _e( 'Customize' ); ?></a> 479 <?php echo implode( ' ', $current_theme_actions ); ?> 480 </div> 481 <div class="inactive-theme"> 482 <?php 483 /* translators: %s: Theme name */ 484 $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); 485 ?> 486 <# if ( data.actions.activate ) { #> 487 <a href="{{{ data.actions.activate }}}" class="button activate" aria-label="<?php echo $aria_label; ?>"><?php _e( 'Activate' ); ?></a> 488 <# } #> 489 <a href="{{{ data.actions.customize }}}" class="button button-primary load-customize hide-if-no-customize"><?php _e( 'Live Preview' ); ?></a> 490 </div> 491 492 <# if ( ! data.active && data.actions['delete'] ) { #> 493 <a href="{{{ data.actions['delete'] }}}" class="button delete-theme"><?php _e( 'Delete' ); ?></a> 494 <# } #> 495 </div> 496 </div> 495 497 </script> 496 498 497 499 <?php … … wp_print_admin_notice_templates(); 500 502 wp_print_update_row_templates(); 501 503 502 504 wp_localize_script( 'updates', '_wpUpdatesItemCounts', array( 503 505 'totals' => wp_get_update_data(), 504 506 ) ); 505 507 506 508 require( ABSPATH . 'wp-admin/admin-footer.php' ); -
src/wp-admin/user-edit.php
diff --git a/src/wp-admin/user-edit.php b/src/wp-admin/user-edit.php index 80cef618e5..d0f4b37840 100644
a b 9 9 /** WordPress Administration Bootstrap */ 10 10 require_once( dirname( __FILE__ ) . '/admin.php' ); 11 11 12 wp_reset_vars( array( 'action', 'user_id', 'wp_http_referer' ) ); 12 $action = wp_assign_request_var('action'); 13 $user_id = wp_assign_request_var('user_id'); 14 $wp_http_referer = wp_assign_request_var('wp_http_referer'); 13 15 14 16 $user_id = (int) $user_id; 15 17 $current_user = wp_get_current_user(); 16 18 if ( ! defined( 'IS_PROFILE_PAGE' ) ) 17 19 define( 'IS_PROFILE_PAGE', ( $user_id == $current_user->ID ) ); 18 20 19 21 if ( ! $user_id && IS_PROFILE_PAGE ) 20 22 $user_id = $current_user->ID; 21 23 elseif ( ! $user_id && ! IS_PROFILE_PAGE ) 22 24 wp_die(__( 'Invalid user ID.' ) ); 23 25 elseif ( ! get_userdata( $user_id ) ) 24 26 wp_die( __('Invalid user ID.') ); 25 27 26 28 wp_enqueue_script('user-profile'); 27 29 28 30 if ( IS_PROFILE_PAGE ) { 29 31 $title = __( 'Profile' ); 30 32 } else { 31 32 33 /* translators: %s: user's display name */ 34 $title = __( 'Edit User %s' ); 33 35 } 34 36 35 37 if ( current_user_can('edit_users') && !IS_PROFILE_PAGE ) 36 38 $submenu_file = 'users.php'; 37 39 else 38 40 $submenu_file = 'profile.php'; 39 41 40 42 if ( current_user_can('edit_users') && !is_user_admin() ) 41 43 $parent_file = 'users.php'; 42 44 else 43 45 $parent_file = 'profile.php'; 44 46 45 47 $profile_help = '<p>' . __('Your profile contains information about you (your “account”) as well as some personal options related to using WordPress.') . '</p>' . 46 47 48 49 50 51 48 '<p>' . __('You can change your password, turn on keyboard shortcuts, change the color scheme of your WordPress administration screens, and turn off the WYSIWYG (Visual) editor, among other things. You can hide the Toolbar (formerly called the Admin Bar) from the front end of your site, however it cannot be disabled on the admin screens.') . '</p>' . 49 '<p>' . __( 'You can select the language you wish to use while using the WordPress administration screen without affecting the language site visitors see.' ) . '</p>' . 50 '<p>' . __('Your username cannot be changed, but you can use other fields to enter your real name or a nickname, and change which name to display on your posts.') . '</p>' . 51 '<p>' . __( 'You can log out of other devices, such as your phone or a public computer, by clicking the Log Out Everywhere Else button.' ) . '</p>' . 52 '<p>' . __('Required fields are indicated; the rest are optional. Profile information will only be displayed if your theme is set up to do so.') . '</p>' . 53 '<p>' . __('Remember to click the Update Profile button when you are finished.') . '</p>'; 52 54 53 55 get_current_screen()->add_help_tab( array( 54 55 56 56 'id' => 'overview', 57 'title' => __('Overview'), 58 'content' => $profile_help, 57 59 ) ); 58 60 59 61 get_current_screen()->set_help_sidebar( … … $user_can_edit = current_user_can( 'edit_posts' ) || current_user_can( 'edit_pag 80 82 * @param bool $allow Whether to allow editing of any user. Default true. 81 83 */ 82 84 if ( is_multisite() 83 84 85 85 && ! current_user_can( 'manage_network_users' ) 86 && $user_id != $current_user->ID 87 && ! apply_filters( 'enable_edit_any_user_configuration', true ) 86 88 ) { 87 89 wp_die( __( 'Sorry, you are not allowed to edit this user.' ) ); 88 90 } 89 91 90 92 // Execute confirmed email change. See send_confirmation_on_profile_email(). 91 93 if ( is_multisite() && IS_PROFILE_PAGE && isset( $_GET[ 'newuseremail' ] ) && $current_user->ID ) { 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 94 $new_email = get_user_meta( $current_user->ID, '_new_email', true ); 95 if ( $new_email && hash_equals( $new_email[ 'hash' ], $_GET[ 'newuseremail' ] ) ) { 96 $user = new stdClass; 97 $user->ID = $current_user->ID; 98 $user->user_email = esc_html( trim( $new_email[ 'newemail' ] ) ); 99 if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_login FROM {$wpdb->signups} WHERE user_login = %s", $current_user->user_login ) ) ) { 100 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->signups} SET user_email = %s WHERE user_login = %s", $user->user_email, $current_user->user_login ) ); 101 } 102 wp_update_user( $user ); 103 delete_user_meta( $current_user->ID, '_new_email' ); 104 wp_redirect( add_query_arg( array( 'updated' => 'true' ), self_admin_url( 'profile.php' ) ) ); 105 die(); 106 } else { 107 wp_redirect( add_query_arg( array( 'error' => 'new-email' ), self_admin_url( 'profile.php' ) ) ); 108 } 107 109 } elseif ( is_multisite() && IS_PROFILE_PAGE && !empty( $_GET['dismiss'] ) && $current_user->ID . '_new_email' === $_GET['dismiss'] ) { 108 109 110 111 110 check_admin_referer( 'dismiss-' . $current_user->ID . '_new_email' ); 111 delete_user_meta( $current_user->ID, '_new_email' ); 112 wp_redirect( add_query_arg( array('updated' => 'true'), self_admin_url( 'profile.php' ) ) ); 113 die(); 112 114 } 113 115 114 116 switch ($action) { … … case 'update': 117 119 check_admin_referer('update-user_' . $user_id); 118 120 119 121 if ( !current_user_can('edit_user', $user_id) ) 120 122 wp_die(__('Sorry, you are not allowed to edit this user.')); 121 123 122 124 if ( IS_PROFILE_PAGE ) { 123 124 125 126 127 128 129 130 131 132 125 /** 126 * Fires before the page loads on the 'Your Profile' editing screen. 127 * 128 * The action only fires if the current user is editing their own profile. 129 * 130 * @since 2.0.0 131 * 132 * @param int $user_id The user ID. 133 */ 134 do_action( 'personal_options_update', $user_id ); 133 135 } else { 134 135 136 137 138 139 140 141 136 /** 137 * Fires before the page loads on the 'Edit User' screen. 138 * 139 * @since 2.7.0 140 * 141 * @param int $user_id The user ID. 142 */ 143 do_action( 'edit_user_profile_update', $user_id ); 142 144 } 143 145 144 146 // Update the email address in signups, if present. 145 147 if ( is_multisite() ) { 146 148 $user = get_userdata( $user_id ); 147 149 148 149 150 150 if ( $user->user_login && isset( $_POST[ 'email' ] ) && is_email( $_POST[ 'email' ] ) && $wpdb->get_var( $wpdb->prepare( "SELECT user_login FROM {$wpdb->signups} WHERE user_login = %s", $user->user_login ) ) ) { 151 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->signups} SET user_email = %s WHERE user_login = %s", $_POST[ 'email' ], $user_login ) ); 152 } 151 153 } 152 154 153 155 // Update the user. … … $errors = edit_user( $user_id ); 155 157 156 158 // Grant or revoke super admin status if requested. 157 159 if ( is_multisite() && is_network_admin() && !IS_PROFILE_PAGE && current_user_can( 'manage_network_options' ) && !isset($super_admins) && empty( $_POST['super_admin'] ) == is_super_admin( $user_id ) ) { 158 160 empty( $_POST['super_admin'] ) ? revoke_super_admin( $user_id ) : grant_super_admin( $user_id ); 159 161 } 160 162 161 163 if ( !is_wp_error( $errors ) ) { 162 163 164 165 166 164 $redirect = add_query_arg( 'updated', true, get_edit_user_link( $user_id ) ); 165 if ( $wp_http_referer ) 166 $redirect = add_query_arg('wp_http_referer', urlencode($wp_http_referer), $redirect); 167 wp_redirect($redirect); 168 exit; 167 169 } 168 170 169 171 default: 170 172 $profileuser = get_user_to_edit($user_id); 171 173 172 174 if ( !current_user_can('edit_user', $user_id) ) 173 175 wp_die(__('Sorry, you are not allowed to edit this user.')); 174 176 175 177 $title = sprintf( $title, $profileuser->display_name ); 176 178 $sessions = WP_Session_Tokens::get_instance( $profileuser->ID ); … … include(ABSPATH . 'wp-admin/admin-header.php'); 179 181 ?> 180 182 181 183 <?php if ( !IS_PROFILE_PAGE && is_super_admin( $profileuser->ID ) && current_user_can( 'manage_network_options' ) ) { ?> 182 184 <div class="notice notice-info"><p><strong><?php _e('Important:'); ?></strong> <?php _e('This user has super admin privileges.'); ?></p></div> 183 185 <?php } ?> 184 186 <?php if ( isset($_GET['updated']) ) : ?> 185 187 <div id="message" class="updated notice is-dismissible"> 186 187 188 189 190 191 192 193 188 <?php if ( IS_PROFILE_PAGE ) : ?> 189 <p><strong><?php _e('Profile updated.') ?></strong></p> 190 <?php else: ?> 191 <p><strong><?php _e('User updated.') ?></strong></p> 192 <?php endif; ?> 193 <?php if ( $wp_http_referer && false === strpos( $wp_http_referer, 'user-new.php' ) && ! IS_PROFILE_PAGE ) : ?> 194 <p><a href="<?php echo esc_url( $wp_http_referer ); ?>"><?php _e('← Back to Users'); ?></a></p> 195 <?php endif; ?> 194 196 </div> 195 197 <?php endif; ?> 196 198 <?php if ( isset( $_GET['error'] ) ) : ?> 197 199 <div class="notice notice-error"> 198 199 200 200 <?php if ( 'new-email' == $_GET['error'] ) : ?> 201 <p><?php _e( 'Error while saving the new email address. Please try again.' ); ?></p> 202 <?php endif; ?> 201 203 </div> 202 204 <?php endif; ?> 203 205 <?php if ( isset( $errors ) && is_wp_error( $errors ) ) : ?> … … echo esc_html( $title ); 211 213 212 214 <?php 213 215 if ( ! IS_PROFILE_PAGE ) { 214 215 216 217 218 216 if ( current_user_can( 'create_users' ) ) { ?> 217 <a href="user-new.php" class="page-title-action"><?php echo esc_html_x( 'Add New', 'user' ); ?></a> 218 <?php } elseif ( is_multisite() && current_user_can( 'promote_users' ) ) { ?> 219 <a href="user-new.php" class="page-title-action"><?php echo esc_html_x( 'Add Existing', 'user' ); ?></a> 220 <?php } 219 221 } 220 222 ?> 221 223 222 224 <hr class="wp-header-end"> 223 225 224 226 <form id="your-profile" action="<?php echo esc_url( self_admin_url( IS_PROFILE_PAGE ? 'profile.php' : 'user-edit.php' ) ); ?>" method="post" novalidate="novalidate"<?php 225 226 227 228 229 230 227 /** 228 * Fires inside the your-profile form tag on the user editing screen. 229 * 230 * @since 3.0.0 231 */ 232 do_action( 'user_edit_form_tag' ); 231 233 ?>> 232 234 <?php wp_nonce_field('update-user_' . $user_id) ?> 233 235 <?php if ( $wp_http_referer ) : ?> 234 236 <input type="hidden" name="wp_http_referer" value="<?php echo esc_url($wp_http_referer); ?>" /> 235 237 <?php endif; ?> 236 238 <p> 237 239 <input type="hidden" name="from" value="profile" /> … … if ( ! IS_PROFILE_PAGE ) { 242 244 243 245 <table class="form-table"> 244 246 <?php if ( ! ( IS_PROFILE_PAGE && ! $user_can_edit ) ) : ?> 245 246 247 248 247 <tr class="user-rich-editing-wrap"> 248 <th scope="row"><?php _e( 'Visual Editor' ); ?></th> 249 <td><label for="rich_editing"><input name="rich_editing" type="checkbox" id="rich_editing" value="false" <?php if ( ! empty( $profileuser->rich_editing ) ) checked( 'false', $profileuser->rich_editing ); ?> /> <?php _e( 'Disable the visual editor when writing' ); ?></label></td> 250 </tr> 249 251 <?php endif; ?> 250 252 <?php if ( count($_wp_admin_css_colors) > 1 && has_action('admin_color_scheme_picker') ) : ?> 251 253 <tr class="user-admin-color-wrap"> 252 254 <th scope="row"><?php _e('Admin Color Scheme')?></th> 253 255 <td><?php 254 255 256 257 258 259 260 261 262 263 264 265 256 /** 257 * Fires in the 'Admin Color Scheme' section of the user editing screen. 258 * 259 * The section is only enabled if a callback is hooked to the action, 260 * and if there is more than one defined color scheme for the admin. 261 * 262 * @since 3.0.0 263 * @since 3.8.1 Added `$user_id` parameter. 264 * 265 * @param int $user_id The user ID. 266 */ 267 do_action( 'admin_color_scheme_picker', $user_id ); 266 268 ?></td> 267 269 </tr> 268 270 <?php … … if ( !( IS_PROFILE_PAGE && !$user_can_edit ) ) : ?> 287 289 $languages = get_available_languages(); 288 290 if ( $languages ) : ?> 289 291 <tr class="user-language-wrap"> 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 292 <th scope="row"> 293 <?php /* translators: The user language selection field label */ ?> 294 <label for="locale"><?php _e( 'Language' ); ?></label> 295 </th> 296 <td> 297 <?php 298 $user_locale = $profileuser->locale; 299 300 if ( 'en_US' === $user_locale ) { 301 $user_locale = ''; 302 } elseif ( '' === $user_locale || ! in_array( $user_locale, $languages, true ) ) { 303 $user_locale = 'site-default'; 304 } 305 306 wp_dropdown_languages( array( 307 'name' => 'locale', 308 'id' => 'locale', 309 'selected' => $user_locale, 310 'languages' => $languages, 311 'show_available_translations' => false, 312 'show_option_site_default' => true 313 ) ); 314 ?> 315 </td> 314 316 </tr> 315 317 <?php 316 318 endif; … … do_action( 'personal_options', $profileuser ); 329 331 330 332 </table> 331 333 <?php 332 333 334 335 336 337 338 339 340 341 342 343 334 if ( IS_PROFILE_PAGE ) { 335 /** 336 * Fires after the 'Personal Options' settings table on the 'Your Profile' editing screen. 337 * 338 * The action only fires if the current user is editing their own profile. 339 * 340 * @since 2.0.0 341 * 342 * @param WP_User $profileuser The current WP_User object. 343 */ 344 do_action( 'profile_personal_options', $profileuser ); 345 } 344 346 ?> 345 347 346 348 <h2><?php _e( 'Name' ); ?></h2> 347 349 348 350 <table class="form-table"> 349 350 351 352 351 <tr class="user-user-login-wrap"> 352 <th><label for="user_login"><?php _e('Username'); ?></label></th> 353 <td><input type="text" name="user_login" id="user_login" value="<?php echo esc_attr($profileuser->user_login); ?>" disabled="disabled" class="regular-text" /> <span class="description"><?php _e('Usernames cannot be changed.'); ?></span></td> 354 </tr> 353 355 354 356 <?php if ( !IS_PROFILE_PAGE && !is_network_admin() ) : ?> 355 357 <tr class="user-role-wrap"><th><label for="role"><?php _e('Role') ?></label></th> … … wp_dropdown_roles($user_role); 364 366 365 367 // print the 'no role' option. Make it selected if the user has no role yet. 366 368 if ( $user_role ) 367 369 echo '<option value="">' . __('— No role for this site —') . '</option>'; 368 370 else 369 371 echo '<option value="" selected="selected">' . __('— No role for this site —') . '</option>'; 370 372 ?> 371 373 </select></td></tr> 372 374 <?php endif; //!IS_PROFILE_PAGE … … if ( is_multisite() && is_network_admin() && ! IS_PROFILE_PAGE && current_user_c 383 385 <?php } ?> 384 386 385 387 <tr class="user-first-name-wrap"> 386 387 388 <th><label for="first_name"><?php _e('First Name') ?></label></th> 389 <td><input type="text" name="first_name" id="first_name" value="<?php echo esc_attr($profileuser->first_name) ?>" class="regular-text" /></td> 388 390 </tr> 389 391 390 392 <tr class="user-last-name-wrap"> 391 392 393 <th><label for="last_name"><?php _e('Last Name') ?></label></th> 394 <td><input type="text" name="last_name" id="last_name" value="<?php echo esc_attr($profileuser->last_name) ?>" class="regular-text" /></td> 393 395 </tr> 394 396 395 397 <tr class="user-nickname-wrap"> 396 397 398 <th><label for="nickname"><?php _e('Nickname'); ?> <span class="description"><?php _e('(required)'); ?></span></label></th> 399 <td><input type="text" name="nickname" id="nickname" value="<?php echo esc_attr($profileuser->nickname) ?>" class="regular-text" /></td> 398 400 </tr> 399 401 400 402 <tr class="user-display-name-wrap"> 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 403 <th><label for="display_name"><?php _e('Display name publicly as') ?></label></th> 404 <td> 405 <select name="display_name" id="display_name"> 406 <?php 407 $public_display = array(); 408 $public_display['display_nickname'] = $profileuser->nickname; 409 $public_display['display_username'] = $profileuser->user_login; 410 411 if ( !empty($profileuser->first_name) ) 412 $public_display['display_firstname'] = $profileuser->first_name; 413 414 if ( !empty($profileuser->last_name) ) 415 $public_display['display_lastname'] = $profileuser->last_name; 416 417 if ( !empty($profileuser->first_name) && !empty($profileuser->last_name) ) { 418 $public_display['display_firstlast'] = $profileuser->first_name . ' ' . $profileuser->last_name; 419 $public_display['display_lastfirst'] = $profileuser->last_name . ' ' . $profileuser->first_name; 420 } 421 422 if ( !in_array( $profileuser->display_name, $public_display ) ) // Only add this if it isn't duplicated elsewhere 423 $public_display = array( 'display_displayname' => $profileuser->display_name ) + $public_display; 424 425 $public_display = array_map( 'trim', $public_display ); 426 $public_display = array_unique( $public_display ); 427 428 foreach ( $public_display as $id => $item ) { 429 ?> 430 <option <?php selected( $profileuser->display_name, $item ); ?>><?php echo $item; ?></option> 431 <?php 432 } 433 ?> 434 </select> 435 </td> 434 436 </tr> 435 437 </table> 436 438 … … if ( is_multisite() && is_network_admin() && ! IS_PROFILE_PAGE && current_user_c 438 440 439 441 <table class="form-table"> 440 442 <tr class="user-email-wrap"> 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 443 <th><label for="email"><?php _e('Email'); ?> <span class="description"><?php _e('(required)'); ?></span></label></th> 444 <td><input type="email" name="email" id="email" value="<?php echo esc_attr( $profileuser->user_email ) ?>" class="regular-text ltr" /> 445 <?php 446 $new_email = get_user_meta( $current_user->ID, '_new_email', true ); 447 if ( $new_email && $new_email['newemail'] != $current_user->user_email && $profileuser->ID == $current_user->ID ) : ?> 448 <div class="updated inline"> 449 <p><?php 450 printf( 451 /* translators: %s: new email */ 452 __( 'There is a pending change of your email to %s.' ), 453 '<code>' . esc_html( $new_email['newemail'] ) . '</code>' 454 ); 455 printf( 456 ' <a href="%1$s">%2$s</a>', 457 esc_url( wp_nonce_url( self_admin_url( 'profile.php?dismiss=' . $current_user->ID . '_new_email' ), 'dismiss-' . $current_user->ID . '_new_email' ) ), 458 __( 'Cancel' ) 459 ); 460 ?></p> 461 </div> 462 <?php endif; ?> 463 </td> 462 464 </tr> 463 465 464 466 <tr class="user-url-wrap"> 465 466 467 <th><label for="url"><?php _e('Website') ?></label></th> 468 <td><input type="url" name="url" id="url" value="<?php echo esc_attr( $profileuser->user_url ) ?>" class="regular-text code" /></td> 467 469 </tr> 468 470 469 471 <?php 470 472 foreach ( wp_get_user_contact_methods( $profileuser ) as $name => $desc ) { 471 473 ?> 472 474 <tr class="user-<?php echo $name; ?>-wrap"> 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 475 <th><label for="<?php echo $name; ?>"> 476 <?php 477 /** 478 * Filters a user contactmethod label. 479 * 480 * The dynamic portion of the filter hook, `$name`, refers to 481 * each of the keys in the contactmethods array. 482 * 483 * @since 2.9.0 484 * 485 * @param string $desc The translatable label for the contactmethod. 486 */ 487 echo apply_filters( "user_{$name}_label", $desc ); 488 ?> 489 </label></th> 490 <td><input type="text" name="<?php echo $name; ?>" id="<?php echo $name; ?>" value="<?php echo esc_attr($profileuser->$name) ?>" class="regular-text" /></td> 489 491 </tr> 490 492 <?php 491 493 } 492 494 ?> 493 495 </table> 494 496 … … if ( is_multisite() && is_network_admin() && ! IS_PROFILE_PAGE && current_user_c 496 498 497 499 <table class="form-table"> 498 500 <tr class="user-description-wrap"> 499 500 501 501 <th><label for="description"><?php _e('Biographical Info'); ?></label></th> 502 <td><textarea name="description" id="description" rows="5" cols="30"><?php echo $profileuser->description; // textarea_escaped ?></textarea> 503 <p class="description"><?php _e('Share a little biographical information to fill out your profile. This may be shown publicly.'); ?></p></td> 502 504 </tr> 503 505 504 506 <?php if ( get_option( 'show_avatars' ) ) : ?> 505 507 <tr class="user-profile-picture"> 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 508 <th><?php _e( 'Profile Picture' ); ?></th> 509 <td> 510 <?php echo get_avatar( $user_id ); ?> 511 <p class="description"><?php 512 if ( IS_PROFILE_PAGE ) { 513 /* translators: %s: Gravatar URL */ 514 $description = sprintf( __( 'You can change your profile picture on <a href="%s">Gravatar</a>.' ), 515 __( 'https://en.gravatar.com/' ) 516 ); 517 } else { 518 $description = ''; 519 } 520 521 /** 522 * Filters the user profile picture description displayed under the Gravatar. 523 * 524 * @since 4.4.0 525 * @since 4.7.0 Added the `$profileuser` parameter. 526 * 527 * @param string $description The description that will be printed. 528 * @param WP_User $profileuser The current WP_User object. 529 */ 530 echo apply_filters( 'user_profile_picture_description', $description, $profileuser ); 531 ?></p> 532 </td> 531 533 </tr> 532 534 <?php endif; ?> 533 535 … … if ( $show_password_fields = apply_filters( 'show_password_fields', true, $profi 549 551 <h2><?php _e( 'Account Management' ); ?></h2> 550 552 <table class="form-table"> 551 553 <tr id="password" class="user-pass1-wrap"> 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 554 <th><label for="pass1"><?php _e( 'New Password' ); ?></label></th> 555 <td> 556 <input class="hidden" value=" " /><!-- #24364 workaround --> 557 <button type="button" class="button wp-generate-pw hide-if-no-js"><?php _e( 'Generate Password' ); ?></button> 558 <div class="wp-pwd hide-if-js"> 559 <span class="password-input-wrapper"> 560 <input type="password" name="pass1" id="pass1" class="regular-text" value="" autocomplete="off" data-pw="<?php echo esc_attr( wp_generate_password( 24 ) ); ?>" aria-describedby="pass-strength-result" /> 561 </span> 562 <button type="button" class="button wp-hide-pw hide-if-no-js" data-toggle="0" aria-label="<?php esc_attr_e( 'Hide password' ); ?>"> 563 <span class="dashicons dashicons-hidden"></span> 564 <span class="text"><?php _e( 'Hide' ); ?></span> 565 </button> 566 <button type="button" class="button wp-cancel-pw hide-if-no-js" data-toggle="0" aria-label="<?php esc_attr_e( 'Cancel password change' ); ?>"> 567 <span class="text"><?php _e( 'Cancel' ); ?></span> 568 </button> 569 <div style="display:none" id="pass-strength-result" aria-live="polite"></div> 570 </div> 571 </td> 570 572 </tr> 571 573 <tr class="user-pass2-wrap hide-if-js"> 572 573 574 575 576 574 <th scope="row"><label for="pass2"><?php _e( 'Repeat New Password' ); ?></label></th> 575 <td> 576 <input name="pass2" type="password" id="pass2" class="regular-text" value="" autocomplete="off" /> 577 <p class="description"><?php _e( 'Type your new password again.' ); ?></p> 578 </td> 577 579 </tr> 578 580 <tr class="pw-weak"> 579 580 581 582 583 584 585 581 <th><?php _e( 'Confirm Password' ); ?></th> 582 <td> 583 <label> 584 <input type="checkbox" name="pw_weak" class="pw-checkbox" /> 585 <span id="pw-weak-text-label"><?php _e( 'Confirm use of potentially weak password' ); ?></span> 586 </label> 587 </td> 586 588 </tr> 587 589 <?php endif; ?> 588 590 589 591 <?php 590 592 if ( IS_PROFILE_PAGE && count( $sessions->get_all() ) === 1 ) : ?> 591 592 593 594 595 596 597 598 599 593 <tr class="user-sessions-wrap hide-if-no-js"> 594 <th><?php _e( 'Sessions' ); ?></th> 595 <td aria-live="assertive"> 596 <div class="destroy-sessions"><button type="button" disabled class="button"><?php _e( 'Log Out Everywhere Else' ); ?></button></div> 597 <p class="description"> 598 <?php _e( 'You are only logged in at this location.' ); ?> 599 </p> 600 </td> 601 </tr> 600 602 <?php elseif ( IS_PROFILE_PAGE && count( $sessions->get_all() ) > 1 ) : ?> 601 602 603 604 605 606 607 608 609 603 <tr class="user-sessions-wrap hide-if-no-js"> 604 <th><?php _e( 'Sessions' ); ?></th> 605 <td aria-live="assertive"> 606 <div class="destroy-sessions"><button type="button" class="button" id="destroy-sessions"><?php _e( 'Log Out Everywhere Else' ); ?></button></div> 607 <p class="description"> 608 <?php _e( 'Did you lose your phone or leave your account logged in at a public computer? You can log out everywhere else, and stay logged in here.' ); ?> 609 </p> 610 </td> 611 </tr> 610 612 <?php elseif ( ! IS_PROFILE_PAGE && $sessions->get_all() ) : ?> 611 612 613 614 615 616 617 618 619 620 621 622 613 <tr class="user-sessions-wrap hide-if-no-js"> 614 <th><?php _e( 'Sessions' ); ?></th> 615 <td> 616 <p><button type="button" class="button" id="destroy-sessions"><?php _e( 'Log Out Everywhere' ); ?></button></p> 617 <p class="description"> 618 <?php 619 /* translators: 1: User's display name. */ 620 printf( __( 'Log %s out of all locations.' ), $profileuser->display_name ); 621 ?> 622 </p> 623 </td> 624 </tr> 623 625 <?php endif; ?> 624 626 625 627 </table> 626 628 627 629 <?php 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 630 if ( IS_PROFILE_PAGE ) { 631 /** 632 * Fires after the 'About Yourself' settings table on the 'Your Profile' editing screen. 633 * 634 * The action only fires if the current user is editing their own profile. 635 * 636 * @since 2.0.0 637 * 638 * @param WP_User $profileuser The current WP_User object. 639 */ 640 do_action( 'show_user_profile', $profileuser ); 641 } else { 642 /** 643 * Fires after the 'About the User' settings table on the 'Edit User' screen. 644 * 645 * @since 2.0.0 646 * 647 * @param WP_User $profileuser The current WP_User object. 648 */ 649 do_action( 'edit_user_profile', $profileuser ); 650 } 649 651 ?> 650 652 651 653 <?php … … if ( IS_PROFILE_PAGE && count( $sessions->get_all() ) === 1 ) : ?> 662 664 * @param WP_User $profileuser The current WP_User object. 663 665 */ 664 666 if ( count( $profileuser->caps ) > count( $profileuser->roles ) 665 667 && apply_filters( 'additional_capabilities_display', true, $profileuser ) 666 668 ) : ?> 667 669 <h2><?php _e( 'Additional Capabilities' ); ?></h2> 668 670 <table class="form-table"> 669 671 <tr class="user-capabilities-wrap"> 670 671 672 <th scope="row"><?php _e( 'Capabilities' ); ?></th> 673 <td> 672 674 <?php 673 674 675 676 677 678 679 680 681 675 $output = ''; 676 foreach ( $profileuser->caps as $cap => $value ) { 677 if ( ! $wp_roles->is_role( $cap ) ) { 678 if ( '' != $output ) 679 $output .= ', '; 680 $output .= $value ? $cap : sprintf( __( 'Denied: %s' ), $cap ); 681 } 682 } 683 echo $output; 682 684 ?> 683 685 </td> 684 686 </tr> 685 687 </table> 686 688 <?php endif; ?> … … break; 697 699 } 698 700 ?> 699 701 <script type="text/javascript"> 700 701 702 702 if (window.location.hash == '#password') { 703 document.getElementById('pass1').focus(); 704 } 703 705 </script> 704 706 <?php 705 707 include( ABSPATH . 'wp-admin/admin-footer.php');