| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Dashboard Widget Administration Screen API 4 * 5 * @package WordPress 6 * @subpackage Administration 7 */ 8 9 /** 10 * Registers dashboard widgets. 11 * 12 * Handles POST data, sets up filters. 13 * 14 * @since 2.5.0 15 * 16 * @global array $wp_registered_widgets 17 * @global array $wp_registered_widget_controls 18 * @global callable[] $wp_dashboard_control_callbacks 19 */ 20 function wp_dashboard_setup() { 21 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_dashboard_control_callbacks; 22 23 $screen = get_current_screen(); 24 25 /* Register Widgets and Controls */ 26 $wp_dashboard_control_callbacks = array(); 27 28 // Browser version 29 $check_browser = wp_check_browser_version(); 30 31 if ( $check_browser && $check_browser['upgrade'] ) { 32 add_filter( 'postbox_classes_dashboard_dashboard_browser_nag', 'dashboard_browser_nag_class' ); 33 34 if ( $check_browser['insecure'] ) { 35 wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'You are using an insecure browser!' ), 'wp_dashboard_browser_nag' ); 36 } else { 37 wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'Your browser is out of date!' ), 'wp_dashboard_browser_nag' ); 38 } 39 } 40 41 // PHP Version. 42 $check_php = wp_check_php_version(); 43 44 if ( $check_php && current_user_can( 'update_php' ) ) { 45 // If "not acceptable" the widget will be shown. 46 if ( isset( $check_php['is_acceptable'] ) && ! $check_php['is_acceptable'] ) { 47 add_filter( 'postbox_classes_dashboard_dashboard_php_nag', 'dashboard_php_nag_class' ); 48 49 if ( $check_php['is_lower_than_future_minimum'] ) { 50 wp_add_dashboard_widget( 'dashboard_php_nag', __( 'PHP Update Required' ), 'wp_dashboard_php_nag' ); 51 } else { 52 wp_add_dashboard_widget( 'dashboard_php_nag', __( 'PHP Update Recommended' ), 'wp_dashboard_php_nag' ); 53 } 54 } 55 } 56 57 // Site Health. 58 if ( current_user_can( 'view_site_health_checks' ) && ! is_network_admin() ) { 59 if ( ! class_exists( 'WP_Site_Health' ) ) { 60 require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php'; 61 } 62 63 WP_Site_Health::get_instance(); 64 65 wp_enqueue_style( 'site-health' ); 66 wp_enqueue_script( 'site-health' ); 67 68 wp_add_dashboard_widget( 'dashboard_site_health', __( 'Site Health Status' ), 'wp_dashboard_site_health' ); 69 } 70 71 // Right Now. 72 if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) { 73 wp_add_dashboard_widget( 'dashboard_right_now', __( 'At a Glance' ), 'wp_dashboard_right_now' ); 74 } 75 76 if ( is_network_admin() ) { 77 wp_add_dashboard_widget( 'network_dashboard_right_now', __( 'Right Now' ), 'wp_network_dashboard_right_now' ); 78 } 79 80 // Activity Widget. 81 if ( is_blog_admin() ) { 82 wp_add_dashboard_widget( 'dashboard_activity', __( 'Activity' ), 'wp_dashboard_site_activity' ); 83 } 84 85 // QuickPress Widget. 86 if ( is_blog_admin() && current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) { 87 $quick_draft_title = sprintf( '<span class="hide-if-no-js">%1$s</span> <span class="hide-if-js">%2$s</span>', __( 'Quick Draft' ), __( 'Your Recent Drafts' ) ); 88 wp_add_dashboard_widget( 'dashboard_quick_press', $quick_draft_title, 'wp_dashboard_quick_press' ); 89 } 90 91 // On This Day. 92 if ( ! function_exists( 'wp_dashboard_on_this_day_setup' ) ) { 93 require_once ABSPATH . 'wp-admin/includes/dashboard-on-this-day.php'; 94 } 95 96 wp_dashboard_on_this_day_setup(); 97 98 // WordPress Events and News. 99 wp_add_dashboard_widget( 'dashboard_primary', __( 'WordPress Events and News' ), 'wp_dashboard_events_news' ); 100 101 if ( is_network_admin() ) { 102 103 /** 104 * Fires after core widgets for the Network Admin dashboard have been registered. 105 * 106 * @since 3.1.0 107 */ 108 do_action( 'wp_network_dashboard_setup' ); 109 110 /** 111 * Filters the list of widgets to load for the Network Admin dashboard. 112 * 113 * @since 3.1.0 114 * 115 * @param string[] $dashboard_widgets An array of dashboard widget IDs. 116 */ 117 $dashboard_widgets = apply_filters( 'wp_network_dashboard_widgets', array() ); 118 } elseif ( is_user_admin() ) { 119 120 /** 121 * Fires after core widgets for the User Admin dashboard have been registered. 122 * 123 * @since 3.1.0 124 */ 125 do_action( 'wp_user_dashboard_setup' ); 126 127 /** 128 * Filters the list of widgets to load for the User Admin dashboard. 129 * 130 * @since 3.1.0 131 * 132 * @param string[] $dashboard_widgets An array of dashboard widget IDs. 133 */ 134 $dashboard_widgets = apply_filters( 'wp_user_dashboard_widgets', array() ); 135 } else { 136 137 /** 138 * Fires after core widgets for the admin dashboard have been registered. 139 * 140 * @since 2.5.0 141 */ 142 do_action( 'wp_dashboard_setup' ); 143 144 /** 145 * Filters the list of widgets to load for the admin dashboard. 146 * 147 * @since 2.5.0 148 * 149 * @param string[] $dashboard_widgets An array of dashboard widget IDs. 150 */ 151 $dashboard_widgets = apply_filters( 'wp_dashboard_widgets', array() ); 152 } 153 154 foreach ( $dashboard_widgets as $widget_id ) { 155 $name = empty( $wp_registered_widgets[ $widget_id ]['all_link'] ) ? $wp_registered_widgets[ $widget_id ]['name'] : $wp_registered_widgets[ $widget_id ]['name'] . " <a href='{$wp_registered_widgets[$widget_id]['all_link']}' class='edit-box open-box'>" . __( 'View all' ) . '</a>'; 156 wp_add_dashboard_widget( $widget_id, $name, $wp_registered_widgets[ $widget_id ]['callback'], $wp_registered_widget_controls[ $widget_id ]['callback'] ); 157 } 158 159 if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['widget_id'] ) ) { 160 check_admin_referer( 'edit-dashboard-widget_' . $_POST['widget_id'], 'dashboard-widget-nonce' ); 161 ob_start(); // Hack - but the same hack wp-admin/widgets.php uses. 162 wp_dashboard_trigger_widget_control( $_POST['widget_id'] ); 163 ob_end_clean(); 164 wp_redirect( remove_query_arg( 'edit' ) ); 165 exit; 166 } 167 168 /** This action is documented in wp-admin/includes/meta-boxes.php */ 169 do_action( 'do_meta_boxes', $screen->id, 'normal', '' ); 170 171 /** This action is documented in wp-admin/includes/meta-boxes.php */ 172 do_action( 'do_meta_boxes', $screen->id, 'side', '' ); 173 } 174 175 /** 176 * Adds a new dashboard widget. 177 * 178 * @since 2.7.0 179 * @since 5.6.0 The `$context` and `$priority` parameters were added. 180 * 181 * @global callable[] $wp_dashboard_control_callbacks 182 * 183 * @param string $widget_id Widget ID (used in the 'id' attribute for the widget). 184 * @param string $widget_name Title of the widget. 185 * @param callable $callback Function that fills the widget with the desired content. 186 * The function should echo its output. 187 * @param callable $control_callback Optional. Function that outputs controls for the widget. Default null. 188 * @param array $callback_args Optional. Data that should be set as the $args property of the widget array 189 * (which is the second parameter passed to your callback). Default null. 190 * @param string $context Optional. The context within the screen where the box should display. 191 * Accepts 'normal', 'side', 'column3', or 'column4'. Default 'normal'. 192 * @param string $priority Optional. The priority within the context where the box should show. 193 * Accepts 'high', 'core', 'default', or 'low'. Default 'core'. 194 */ 195 function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_callback = null, $callback_args = null, $context = 'normal', $priority = 'core' ) { 196 global $wp_dashboard_control_callbacks; 197 198 $screen = get_current_screen(); 199 200 $private_callback_args = array( '__widget_basename' => $widget_name ); 201 202 if ( is_null( $callback_args ) ) { 203 $callback_args = $private_callback_args; 204 } elseif ( is_array( $callback_args ) ) { 205 $callback_args = array_merge( $callback_args, $private_callback_args ); 206 } 207 208 if ( $control_callback && is_callable( $control_callback ) && current_user_can( 'edit_dashboard' ) ) { 209 $wp_dashboard_control_callbacks[ $widget_id ] = $control_callback; 210 211 if ( isset( $_GET['edit'] ) && $widget_id === $_GET['edit'] ) { 212 list($url) = explode( '#', add_query_arg( 'edit', false ), 2 ); 213 $widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( $url ) . '">' . __( 'Cancel' ) . '</a></span>'; 214 $callback = '_wp_dashboard_control_callback'; 215 } else { 216 list($url) = explode( '#', add_query_arg( 'edit', $widget_id ), 2 ); 217 $widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( "$url#$widget_id" ) . '" class="edit-box open-box">' . __( 'Configure' ) . '</a></span>'; 218 } 219 } 220 221 $side_widgets = array( 'dashboard_quick_press', 'dashboard_primary' ); 222 223 if ( in_array( $widget_id, $side_widgets, true ) ) { 224 $context = 'side'; 225 } 226 227 $high_priority_widgets = array( 'dashboard_browser_nag', 'dashboard_php_nag' ); 228 229 if ( in_array( $widget_id, $high_priority_widgets, true ) ) { 230 $priority = 'high'; 231 } 232 233 if ( empty( $context ) ) { 234 $context = 'normal'; 235 } 236 237 if ( empty( $priority ) ) { 238 $priority = 'core'; 239 } 240 241 add_meta_box( $widget_id, $widget_name, $callback, $screen, $context, $priority, $callback_args ); 242 } 243 244 /** 245 * Outputs controls for the current dashboard widget. 246 * 247 * @access private 248 * @since 2.7.0 249 * 250 * @param mixed $dashboard 251 * @param array $meta_box 252 */ 253 function _wp_dashboard_control_callback( $dashboard, $meta_box ) { 254 echo '<form method="post" class="dashboard-widget-control-form wp-clearfix">'; 255 wp_dashboard_trigger_widget_control( $meta_box['id'] ); 256 wp_nonce_field( 'edit-dashboard-widget_' . $meta_box['id'], 'dashboard-widget-nonce' ); 257 echo '<input type="hidden" name="widget_id" value="' . esc_attr( $meta_box['id'] ) . '" />'; 258 submit_button( __( 'Save Changes' ) ); 259 echo '</form>'; 260 } 261 262 /** 263 * Displays the dashboard. 264 * 265 * @since 2.5.0 266 */ 267 function wp_dashboard() { 268 $screen = get_current_screen(); 269 $columns = absint( $screen->get_columns() ); 270 $columns_css = ''; 271 272 if ( $columns ) { 273 $columns_css = " columns-$columns"; 274 } 275 ?> 276 <div id="dashboard-widgets" class="metabox-holder<?php echo $columns_css; ?>"> 277 <div id="postbox-container-1" class="postbox-container"> 278 <?php do_meta_boxes( $screen->id, 'normal', '' ); ?> 279 </div> 280 <div id="postbox-container-2" class="postbox-container"> 281 <?php do_meta_boxes( $screen->id, 'side', '' ); ?> 282 </div> 283 <div id="postbox-container-3" class="postbox-container"> 284 <?php do_meta_boxes( $screen->id, 'column3', '' ); ?> 285 </div> 286 <div id="postbox-container-4" class="postbox-container"> 287 <?php do_meta_boxes( $screen->id, 'column4', '' ); ?> 288 </div> 289 </div> 290 291 <?php 292 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); 293 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); 294 } 295 296 // 297 // Dashboard Widgets. 298 // 299 300 /** 301 * Dashboard widget that displays some basic stats about the site. 302 * 303 * Formerly 'Right Now'. A streamlined 'At a Glance' as of 3.8. 304 * 305 * @since 2.7.0 306 */ 307 function wp_dashboard_right_now() { 308 ?> 309 <div class="main"> 310 <ul> 311 <?php 312 // Posts and Pages. 313 foreach ( array( 'post', 'page' ) as $post_type ) { 314 $num_posts = wp_count_posts( $post_type ); 315 316 if ( $num_posts && $num_posts->publish ) { 317 if ( 'post' === $post_type ) { 318 /* translators: %s: Number of posts. */ 319 $text = _n( '%s Published post', '%s Published posts', $num_posts->publish ); 320 } else { 321 /* translators: %s: Number of pages. */ 322 $text = _n( '%s Published page', '%s Published pages', $num_posts->publish ); 323 } 324 325 $text = sprintf( $text, number_format_i18n( $num_posts->publish ) ); 326 $post_type_object = get_post_type_object( $post_type ); 327 328 if ( $post_type_object && current_user_can( $post_type_object->cap->edit_posts ) ) { 329 $url = add_query_arg( 330 array( 331 'post_status' => 'publish', 332 'post_type' => $post_type, 333 ), 334 admin_url( 'edit.php' ) 335 ); 336 printf( '<li class="%1$s-count"><a href="%2$s">%3$s</a></li>', $post_type, esc_url( $url ), esc_html( $text ) ); 337 } else { 338 printf( '<li class="%1$s-count"><span>%2$s</span></li>', $post_type, $text ); 339 } 340 } 341 } 342 343 // Comments. 344 $num_comm = wp_count_comments(); 345 346 if ( $num_comm && ( $num_comm->approved || $num_comm->moderated ) ) { 347 /* translators: %s: Number of comments. */ 348 $text = sprintf( _n( '%s Comment', '%s Comments', $num_comm->approved ), number_format_i18n( $num_comm->approved ) ); 349 ?> 350 <li class="comment-count"> 351 <a href="edit-comments.php"><?php echo $text; ?></a> 352 </li> 353 <?php 354 $moderated_comments_count_i18n = number_format_i18n( $num_comm->moderated ); 355 /* translators: %s: Number of comments. */ 356 $text = sprintf( _n( '%s Comment in moderation', '%s Comments in moderation', $num_comm->moderated ), $moderated_comments_count_i18n ); 357 ?> 358 <li class="comment-mod-count<?php echo ! $num_comm->moderated ? ' hidden' : ''; ?>"> 359 <a href="edit-comments.php?comment_status=moderated" class="comments-in-moderation-text"><?php echo $text; ?></a> 360 </li> 361 <?php 362 } 363 364 /** 365 * Filters the array of extra elements to list in the 'At a Glance' 366 * dashboard widget. 367 * 368 * Prior to 3.8.0, the widget was named 'Right Now'. Each element 369 * is wrapped in list-item tags on output. 370 * 371 * @since 3.8.0 372 * 373 * @param string[] $items Array of extra 'At a Glance' widget items. 374 */ 375 $elements = apply_filters( 'dashboard_glance_items', array() ); 376 377 if ( $elements ) { 378 echo '<li>' . implode( "</li>\n<li>", $elements ) . "</li>\n"; 379 } 380 381 ?> 382 </ul> 383 <?php 384 update_right_now_message(); 385 386 // Check if search engines are asked not to index this site. 387 if ( ! is_network_admin() && ! is_user_admin() 388 && current_user_can( 'manage_options' ) && ! get_option( 'blog_public' ) 389 ) { 390 391 /** 392 * Filters the link title attribute for the 'Search engines discouraged' 393 * message displayed in the 'At a Glance' dashboard widget. 394 * 395 * Prior to 3.8.0, the widget was named 'Right Now'. 396 * 397 * @since 3.0.0 398 * @since 4.5.0 The default for `$title` was updated to an empty string. 399 * 400 * @param string $title Default attribute text. 401 */ 402 $title = apply_filters( 'privacy_on_link_title', '' ); 403 404 /** 405 * Filters the link label for the 'Search engines discouraged' message 406 * displayed in the 'At a Glance' dashboard widget. 407 * 408 * Prior to 3.8.0, the widget was named 'Right Now'. 409 * 410 * @since 3.0.0 411 * 412 * @param string $content Default text. 413 */ 414 $content = apply_filters( 'privacy_on_link_text', __( 'Search engines discouraged' ) ); 415 416 $title_attr = '' === $title ? '' : " title='$title'"; 417 418 echo "<p class='search-engines-info'><a href='options-reading.php'$title_attr>$content</a></p>"; 419 } 420 ?> 421 </div> 422 <?php 423 /* 424 * activity_box_end has a core action, but only prints content when multisite. 425 * Using an output buffer is the only way to really check if anything's displayed here. 426 */ 427 ob_start(); 428 429 /** 430 * Fires at the end of the 'At a Glance' dashboard widget. 431 * 432 * Prior to 3.8.0, the widget was named 'Right Now'. 433 * 434 * @since 2.5.0 435 */ 436 do_action( 'rightnow_end' ); 437 438 /** 439 * Fires at the end of the 'At a Glance' dashboard widget. 440 * 441 * Prior to 3.8.0, the widget was named 'Right Now'. 442 * 443 * @since 2.0.0 444 */ 445 do_action( 'activity_box_end' ); 446 447 $actions = ob_get_clean(); 448 449 if ( ! empty( $actions ) ) : 450 ?> 451 <div class="sub"> 452 <?php echo $actions; ?> 453 </div> 454 <?php 455 endif; 456 } 457 458 /** 459 * @since 3.1.0 460 */ 461 function wp_network_dashboard_right_now() { 462 $actions = array(); 463 464 if ( current_user_can( 'create_sites' ) ) { 465 $actions['create-site'] = '<a href="' . network_admin_url( 'site-new.php' ) . '">' . __( 'Create a New Site' ) . '</a>'; 466 } 467 if ( current_user_can( 'create_users' ) ) { 468 $actions['create-user'] = '<a href="' . network_admin_url( 'user-new.php' ) . '">' . __( 'Create a New User' ) . '</a>'; 469 } 470 471 $c_users = get_user_count(); 472 $c_blogs = get_blog_count(); 473 474 /* translators: %s: Number of users on the network. */ 475 $user_text = sprintf( _n( '%s user', '%s users', $c_users ), number_format_i18n( $c_users ) ); 476 /* translators: %s: Number of sites on the network. */ 477 $blog_text = sprintf( _n( '%s site', '%s sites', $c_blogs ), number_format_i18n( $c_blogs ) ); 478 479 /* translators: 1: Text indicating the number of sites on the network, 2: Text indicating the number of users on the network. */ 480 $sentence = sprintf( __( 'You have %1$s and %2$s.' ), $blog_text, $user_text ); 481 482 if ( $actions ) { 483 echo '<ul class="subsubsub">'; 484 foreach ( $actions as $class => $action ) { 485 $actions[ $class ] = "\t<li class='$class'>$action"; 486 } 487 echo implode( " |</li>\n", $actions ) . "</li>\n"; 488 echo '</ul>'; 489 } 490 ?> 491 <br class="clear" /> 492 493 <p class="youhave"><?php echo $sentence; ?></p> 494 495 496 <?php 497 /** 498 * Fires in the Network Admin 'Right Now' dashboard widget 499 * just before the user and site search form fields. 500 * 501 * @since MU (3.0.0) 502 */ 503 do_action( 'wpmuadminresult' ); 504 ?> 505 506 <form action="<?php echo esc_url( network_admin_url( 'users.php' ) ); ?>" method="get"> 507 <p> 508 <label class="screen-reader-text" for="search-users"> 509 <?php 510 /* translators: Hidden accessibility text. */ 511 _e( 'Search Users' ); 512 ?> 513 </label> 514 <input type="search" name="s" value="" size="30" autocomplete="off" id="search-users" /> 515 <?php submit_button( __( 'Search Users' ), '', false, false, array( 'id' => 'submit_users' ) ); ?> 516 </p> 517 </form> 518 519 <form action="<?php echo esc_url( network_admin_url( 'sites.php' ) ); ?>" method="get"> 520 <p> 521 <label class="screen-reader-text" for="search-sites"> 522 <?php 523 /* translators: Hidden accessibility text. */ 524 _e( 'Search Sites' ); 525 ?> 526 </label> 527 <input type="search" name="s" value="" size="30" autocomplete="off" id="search-sites" /> 528 <?php submit_button( __( 'Search Sites' ), '', false, false, array( 'id' => 'submit_sites' ) ); ?> 529 </p> 530 </form> 531 <?php 532 /** 533 * Fires at the end of the 'Right Now' widget in the Network Admin dashboard. 534 * 535 * @since MU (3.0.0) 536 */ 537 do_action( 'mu_rightnow_end' ); 538 539 /** 540 * Fires at the end of the 'Right Now' widget in the Network Admin dashboard. 541 * 542 * @since MU (3.0.0) 543 */ 544 do_action( 'mu_activity_box_end' ); 545 } 546 547 /** 548 * Displays the Quick Draft widget. 549 * 550 * @since 3.8.0 551 * @since 7.1.0 Added $notice_type parameter. 552 * 553 * @global int $post_ID 554 * 555 * @param string|false $message Optional. Error or success message. Default false. 556 * @param string $notice_type Optional. Admin notice type. Default 'error'. 557 */ 558 function wp_dashboard_quick_press( $message = false, $notice_type = 'error' ) { 559 global $post_ID; 560 561 if ( ! current_user_can( 'edit_posts' ) ) { 562 return; 563 } 564 565 // Check if a new auto-draft (= no new post_ID) is needed or if the old can be used. 566 $last_post_id = (int) get_user_option( 'dashboard_quick_press_last_post_id' ); // Get the last post_ID. 567 568 if ( $last_post_id ) { 569 $post = get_post( $last_post_id ); 570 571 if ( empty( $post ) || 'auto-draft' !== $post->post_status ) { // auto-draft doesn't exist anymore. 572 $post = get_default_post_to_edit( 'post', true ); 573 update_user_option( get_current_user_id(), 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID. 574 } else { 575 $post->post_title = ''; // Remove the auto draft title. 576 } 577 } else { 578 $post = get_default_post_to_edit( 'post', true ); 579 $user_id = get_current_user_id(); 580 581 // Don't create an option if this is a super admin who does not belong to this site. 582 if ( in_array( get_current_blog_id(), array_keys( get_blogs_of_user( $user_id ) ), true ) ) { 583 update_user_option( $user_id, 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID. 584 } 585 } 586 587 $post_ID = (int) $post->ID; 588 ?> 589 590 <form name="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>" method="post" id="quick-press" class="hide-if-no-js"> 591 592 <?php 593 if ( $message ) { 594 wp_admin_notice( 595 $message, 596 array( 597 'type' => $notice_type, 598 'attributes' => array( 599 'role' => 'alert', 600 ), 601 ) 602 ); 603 } 604 ?> 605 606 <div class="input-text-wrap" id="title-wrap"> 607 <label for="title"> 608 <?php 609 /** This filter is documented in wp-admin/edit-form-advanced.php */ 610 echo apply_filters( 'enter_title_here', __( 'Title' ), $post ); 611 ?> 612 </label> 613 <input type="text" name="post_title" id="title" autocomplete="off" /> 614 </div> 615 616 <div class="textarea-wrap" id="description-wrap"> 617 <label for="content"><?php _e( 'Content' ); ?></label> 618 <textarea name="content" id="content" placeholder="<?php esc_attr_e( 'What’s on your mind?' ); ?>" class="mceEditor" rows="3" cols="15" autocomplete="off"></textarea> 619 </div> 620 621 <p class="submit"> 622 <input type="hidden" name="action" id="quickpost-action" value="post-quickdraft-save" /> 623 <input type="hidden" name="post_ID" value="<?php echo $post_ID; ?>" /> 624 <input type="hidden" name="post_type" value="post" /> 625 <?php wp_nonce_field( 'add-post' ); ?> 626 <?php submit_button( __( 'Save Draft' ), 'primary', 'save', false, array( 'id' => 'save-post' ) ); ?> 627 <br class="clear" /> 628 </p> 629 630 </form> 631 <?php 632 wp_dashboard_recent_drafts(); 633 } 634 635 /** 636 * Show recent drafts of the user on the dashboard. 637 * 638 * @since 2.7.0 639 * 640 * @param WP_Post[]|false $drafts Optional. Array of posts to display. Default false. 641 */ 642 function wp_dashboard_recent_drafts( $drafts = false ) { 643 if ( ! $drafts ) { 644 $query_args = array( 645 'post_type' => 'post', 646 'post_status' => 'draft', 647 'author' => get_current_user_id(), 648 'posts_per_page' => 4, 649 'orderby' => 'modified', 650 'order' => 'DESC', 651 ); 652 653 /** 654 * Filters the post query arguments for the 'Recent Drafts' dashboard widget. 655 * 656 * @since 4.4.0 657 * 658 * @param array $query_args The query arguments for the 'Recent Drafts' dashboard widget. 659 */ 660 $query_args = apply_filters( 'dashboard_recent_drafts_query_args', $query_args ); 661 662 $drafts = get_posts( $query_args ); 663 if ( ! $drafts ) { 664 return; 665 } 666 } 667 668 echo '<div class="drafts">'; 669 670 if ( count( $drafts ) > 3 ) { 671 printf( 672 '<p class="view-all"><a href="%s">%s</a></p>' . "\n", 673 esc_url( admin_url( 'edit.php?post_status=draft' ) ), 674 __( 'View all drafts' ) 675 ); 676 } 677 678 echo '<h2 class="hide-if-no-js">' . __( 'Your Recent Drafts' ) . "</h2>\n"; 679 echo '<ul>'; 680 681 /* translators: Maximum number of words used in a preview of a draft on the dashboard. */ 682 $draft_length = (int) _x( '10', 'draft_length' ); 683 684 $drafts = array_slice( $drafts, 0, 3 ); 685 foreach ( $drafts as $draft ) { 686 $url = get_edit_post_link( $draft->ID ); 687 $title = _draft_or_post_title( $draft->ID ); 688 689 echo "<li>\n"; 690 printf( 691 '<div class="draft-title"><a href="%s" aria-label="%s">%s</a><time datetime="%s">%s</time></div>', 692 esc_url( $url ), 693 /* translators: %s: Post title. */ 694 esc_attr( sprintf( __( 'Edit “%s”' ), $title ) ), 695 esc_html( $title ), 696 get_the_time( 'c', $draft ), 697 get_the_time( __( 'F j, Y' ), $draft ) 698 ); 699 700 $the_content = wp_trim_words( $draft->post_content, $draft_length ); 701 702 if ( $the_content ) { 703 echo '<p class="draft-content">' . $the_content . '</p>'; 704 } 705 echo "</li>\n"; 706 } 707 708 echo "</ul>\n"; 709 echo '</div>'; 710 } 711 712 /** 713 * Outputs a row for the Recent Comments widget. 714 * 715 * @access private 716 * @since 2.7.0 717 * 718 * @global WP_Comment $comment Global comment object. 719 * 720 * @param WP_Comment $comment The current comment. 721 * @param bool $show_date Optional. Whether to display the date. 722 */ 723 function _wp_dashboard_recent_comments_row( &$comment, $show_date = true ) { 724 $GLOBALS['comment'] = clone $comment; 725 726 if ( $comment->comment_post_ID > 0 ) { 727 $comment_post_title = _draft_or_post_title( $comment->comment_post_ID ); 728 $comment_post_url = get_the_permalink( $comment->comment_post_ID ); 729 $comment_post_link = '<a href="' . esc_url( $comment_post_url ) . '">' . $comment_post_title . '</a>'; 730 } else { 731 $comment_post_link = ''; 732 } 733 734 $actions_string = ''; 735 if ( current_user_can( 'edit_comment', $comment->comment_ID ) ) { 736 // Pre-order it: Approve | Reply | Edit | Spam | Trash. 737 $actions = array( 738 'approve' => '', 739 'unapprove' => '', 740 'reply' => '', 741 'edit' => '', 742 'spam' => '', 743 'trash' => '', 744 'delete' => '', 745 'view' => '', 746 ); 747 748 $approve_nonce = esc_html( '_wpnonce=' . wp_create_nonce( 'approve-comment_' . $comment->comment_ID ) ); 749 $del_nonce = esc_html( '_wpnonce=' . wp_create_nonce( 'delete-comment_' . $comment->comment_ID ) ); 750 751 $action_string = 'comment.php?action=%s&p=' . $comment->comment_post_ID . '&c=' . $comment->comment_ID . '&%s'; 752 753 $approve_url = sprintf( $action_string, 'approvecomment', $approve_nonce ); 754 $unapprove_url = sprintf( $action_string, 'unapprovecomment', $approve_nonce ); 755 $spam_url = sprintf( $action_string, 'spamcomment', $del_nonce ); 756 $trash_url = sprintf( $action_string, 'trashcomment', $del_nonce ); 757 $delete_url = sprintf( $action_string, 'deletecomment', $del_nonce ); 758 759 $actions['approve'] = sprintf( 760 '<a href="%s" data-wp-lists="%s" class="vim-a aria-button-if-js" aria-label="%s">%s</a>', 761 esc_url( $approve_url ), 762 "dim:the-comment-list:comment-{$comment->comment_ID}:unapproved:e7e7d3:e7e7d3:new=approved", 763 esc_attr__( 'Approve this comment' ), 764 __( 'Approve' ) 765 ); 766 767 $actions['unapprove'] = sprintf( 768 '<a href="%s" data-wp-lists="%s" class="vim-u aria-button-if-js" aria-label="%s">%s</a>', 769 esc_url( $unapprove_url ), 770 "dim:the-comment-list:comment-{$comment->comment_ID}:unapproved:e7e7d3:e7e7d3:new=unapproved", 771 esc_attr__( 'Unapprove this comment' ), 772 __( 'Unapprove' ) 773 ); 774 775 $actions['edit'] = sprintf( 776 '<a href="%s" aria-label="%s">%s</a>', 777 "comment.php?action=editcomment&c={$comment->comment_ID}", 778 esc_attr__( 'Edit this comment' ), 779 __( 'Edit' ) 780 ); 781 782 $actions['reply'] = sprintf( 783 '<button type="button" onclick="window.commentReply && commentReply.open(\'%s\',\'%s\');" class="vim-r button-link hide-if-no-js" aria-label="%s">%s</button>', 784 $comment->comment_ID, 785 $comment->comment_post_ID, 786 esc_attr__( 'Reply to this comment' ), 787 /* translators: Comment reply button text. */ 788 _x( 'Reply', 'verb' ) 789 ); 790 791 $actions['spam'] = sprintf( 792 '<a href="%s" data-wp-lists="%s" class="vim-s vim-destructive aria-button-if-js" aria-label="%s">%s</a>', 793 esc_url( $spam_url ), 794 "delete:the-comment-list:comment-{$comment->comment_ID}::spam=1", 795 esc_attr__( 'Mark this comment as spam' ), 796 /* translators: "Mark as spam" link. */ 797 _x( 'Spam', 'verb' ) 798 ); 799 800 if ( ! EMPTY_TRASH_DAYS ) { 801 $actions['delete'] = sprintf( 802 '<a href="%s" data-wp-lists="%s" class="delete vim-d vim-destructive aria-button-if-js" aria-label="%s">%s</a>', 803 esc_url( $delete_url ), 804 "delete:the-comment-list:comment-{$comment->comment_ID}::trash=1", 805 esc_attr__( 'Delete this comment permanently' ), 806 __( 'Delete Permanently' ) 807 ); 808 } else { 809 $actions['trash'] = sprintf( 810 '<a href="%s" data-wp-lists="%s" class="delete vim-d vim-destructive aria-button-if-js" aria-label="%s">%s</a>', 811 esc_url( $trash_url ), 812 "delete:the-comment-list:comment-{$comment->comment_ID}::trash=1", 813 esc_attr__( 'Move this comment to the Trash' ), 814 _x( 'Trash', 'verb' ) 815 ); 816 } 817 818 $actions['view'] = sprintf( 819 '<a class="comment-link" href="%s" aria-label="%s">%s</a>', 820 esc_url( get_comment_link( $comment ) ), 821 esc_attr__( 'View this comment' ), 822 __( 'View' ) 823 ); 824 825 /** This filter is documented in wp-admin/includes/class-wp-comments-list-table.php */ 826 $actions = apply_filters( 'comment_row_actions', array_filter( $actions ), $comment ); 827 828 $i = 0; 829 830 foreach ( $actions as $action => $link ) { 831 ++$i; 832 833 if ( ( ( 'approve' === $action || 'unapprove' === $action ) && 2 === $i ) 834 || 1 === $i 835 ) { 836 $separator = ''; 837 } else { 838 $separator = ' | '; 839 } 840 841 // Reply and quickedit need a hide-if-no-js span. 842 if ( 'reply' === $action || 'quickedit' === $action ) { 843 $action .= ' hide-if-no-js'; 844 } 845 846 if ( 'view' === $action && '1' !== $comment->comment_approved ) { 847 $action .= ' hidden'; 848 } 849 850 $actions_string .= "<span class='$action'>{$separator}{$link}</span>"; 851 } 852 } 853 ?> 854 855 <li id="comment-<?php echo $comment->comment_ID; ?>" <?php comment_class( array( 'comment-item', wp_get_comment_status( $comment ) ), $comment ); ?>> 856 857 <?php 858 $comment_row_class = ''; 859 860 if ( get_option( 'show_avatars' ) ) { 861 echo get_avatar( $comment, 50, 'mystery' ); 862 $comment_row_class .= ' has-avatar'; 863 } 864 ?> 865 866 <?php if ( ! $comment->comment_type || 'comment' === $comment->comment_type ) : ?> 867 868 <div class="dashboard-comment-wrap has-row-actions <?php echo $comment_row_class; ?>"> 869 <p class="comment-meta"> 870 <?php 871 // Comments might not have a post they relate to, e.g. programmatically created ones. 872 if ( $comment_post_link ) { 873 printf( 874 /* translators: 1: Comment author, 2: Post link, 3: Notification if the comment is pending. */ 875 __( 'From %1$s on %2$s %3$s' ), 876 '<cite class="comment-author">' . get_comment_author_link( $comment ) . '</cite>', 877 $comment_post_link, 878 '<span class="approve">' . __( '[Pending]' ) . '</span>' 879 ); 880 } else { 881 printf( 882 /* translators: 1: Comment author, 2: Notification if the comment is pending. */ 883 __( 'From %1$s %2$s' ), 884 '<cite class="comment-author">' . get_comment_author_link( $comment ) . '</cite>', 885 '<span class="approve">' . __( '[Pending]' ) . '</span>' 886 ); 887 } 888 ?> 889 </p> 890 891 <?php 892 else : 893 switch ( $comment->comment_type ) { 894 case 'pingback': 895 $type = __( 'Pingback' ); 896 break; 897 case 'trackback': 898 $type = __( 'Trackback' ); 899 break; 900 default: 901 $type = ucwords( $comment->comment_type ); 902 } 903 $type = esc_html( $type ); 904 ?> 905 <div class="dashboard-comment-wrap has-row-actions"> 906 <p class="comment-meta"> 907 <?php 908 // Pingbacks, Trackbacks or custom comment types might not have a post they relate to, e.g. programmatically created ones. 909 if ( $comment_post_link ) { 910 printf( 911 /* translators: 1: Type of comment, 2: Post link, 3: Notification if the comment is pending. */ 912 _x( '%1$s on %2$s %3$s', 'dashboard' ), 913 "<strong>$type</strong>", 914 $comment_post_link, 915 '<span class="approve">' . __( '[Pending]' ) . '</span>' 916 ); 917 } else { 918 printf( 919 /* translators: 1: Type of comment, 2: Notification if the comment is pending. */ 920 _x( '%1$s %2$s', 'dashboard' ), 921 "<strong>$type</strong>", 922 '<span class="approve">' . __( '[Pending]' ) . '</span>' 923 ); 924 } 925 ?> 926 </p> 927 <p class="comment-author"><?php comment_author_link( $comment ); ?></p> 928 929 <?php endif; // comment_type ?> 930 <blockquote><p><?php comment_excerpt( $comment ); ?></p></blockquote> 931 <?php if ( $actions_string ) : ?> 932 <p class="row-actions"><?php echo $actions_string; ?></p> 933 <?php endif; ?> 934 </div> 935 </li> 936 <?php 937 $GLOBALS['comment'] = null; 938 } 939 940 /** 941 * Outputs the Activity widget. 942 * 943 * Callback function for {@see 'dashboard_activity'}. 944 * 945 * @since 3.8.0 946 */ 947 function wp_dashboard_site_activity() { 948 949 echo '<div id="activity-widget">'; 950 951 $future_posts = wp_dashboard_recent_posts( 952 array( 953 'max' => 5, 954 'status' => 'future', 955 'order' => 'ASC', 956 'title' => __( 'Publishing Soon' ), 957 'id' => 'future-posts', 958 ) 959 ); 960 $recent_posts = wp_dashboard_recent_posts( 961 array( 962 'max' => 5, 963 'status' => 'publish', 964 'order' => 'DESC', 965 'title' => __( 'Recently Published' ), 966 'id' => 'published-posts', 967 ) 968 ); 969 970 $recent_comments = wp_dashboard_recent_comments(); 971 972 if ( ! $future_posts && ! $recent_posts && ! $recent_comments ) { 973 echo '<div class="no-activity">'; 974 echo '<p>' . __( 'No activity yet!' ) . '</p>'; 975 echo '</div>'; 976 } 977 978 echo '</div>'; 979 } 980 981 /** 982 * Generates Publishing Soon and Recently Published sections. 983 * 984 * @since 3.8.0 985 * 986 * @param array $args { 987 * An array of query and display arguments. 988 * 989 * @type int $max Number of posts to display. 990 * @type string $status Post status. 991 * @type string $order Designates ascending ('ASC') or descending ('DESC') order. 992 * @type string $title Section title. 993 * @type string $id The container id. 994 * } 995 * @return bool False if no posts were found. True otherwise. 996 */ 997 function wp_dashboard_recent_posts( $args ) { 998 $query_args = array( 999 'post_type' => 'post', 1000 'post_status' => $args['status'], 1001 'orderby' => 'date', 1002 'order' => $args['order'], 1003 'posts_per_page' => (int) $args['max'], 1004 'no_found_rows' => true, 1005 'cache_results' => true, 1006 'perm' => ( 'future' === $args['status'] ) ? 'editable' : 'readable', 1007 ); 1008 1009 /** 1010 * Filters the query arguments used for the Recent Posts widget. 1011 * 1012 * @since 4.2.0 1013 * 1014 * @param array $query_args The arguments passed to WP_Query to produce the list of posts. 1015 */ 1016 $query_args = apply_filters( 'dashboard_recent_posts_query_args', $query_args ); 1017 1018 $posts = new WP_Query( $query_args ); 1019 1020 if ( $posts->have_posts() ) { 1021 1022 echo '<div id="' . $args['id'] . '" class="activity-block">'; 1023 1024 echo '<h3>' . $args['title'] . '</h3>'; 1025 1026 echo '<ul>'; 1027 1028 $today = current_time( 'Y-m-d' ); 1029 $tomorrow = current_datetime()->modify( '+1 day' )->format( 'Y-m-d' ); 1030 $year = current_time( 'Y' ); 1031 1032 while ( $posts->have_posts() ) { 1033 $posts->the_post(); 1034 1035 $time = get_the_time( 'U' ); 1036 1037 if ( ! is_int( $time ) ) { 1038 /* translators: Date and time format for recent posts on the dashboard, from a different calendar year, see https://www.php.net/manual/datetime.format.php */ 1039 $date = get_the_date( __( 'M jS Y' ) ); 1040 } elseif ( gmdate( 'Y-m-d', $time ) === $today ) { 1041 $date = __( 'Today' ); 1042 } elseif ( gmdate( 'Y-m-d', $time ) === $tomorrow ) { 1043 $date = __( 'Tomorrow' ); 1044 } elseif ( gmdate( 'Y', $time ) !== $year ) { 1045 /* translators: Date and time format for recent posts on the dashboard, from a different calendar year, see https://www.php.net/manual/datetime.format.php */ 1046 $date = date_i18n( __( 'M jS Y' ), $time ); 1047 } else { 1048 /* translators: Date and time format for recent posts on the dashboard, see https://www.php.net/manual/datetime.format.php */ 1049 $date = date_i18n( __( 'M jS' ), $time ); 1050 } 1051 1052 // Use the post edit link for those who can edit, the permalink otherwise. 1053 $recent_post_link = current_user_can( 'edit_post', get_the_ID() ) ? get_edit_post_link() : get_permalink(); 1054 1055 $draft_or_post_title = _draft_or_post_title(); 1056 printf( 1057 '<li><span>%1$s</span> <a href="%2$s" aria-label="%3$s">%4$s</a></li>', 1058 /* translators: 1: Relative date, 2: Time. */ 1059 sprintf( _x( '%1$s, %2$s', 'dashboard' ), $date, get_the_time() ), 1060 $recent_post_link, 1061 /* translators: %s: Post title. */ 1062 esc_attr( sprintf( __( 'Edit “%s”' ), $draft_or_post_title ) ), 1063 $draft_or_post_title 1064 ); 1065 } 1066 1067 echo '</ul>'; 1068 echo '</div>'; 1069 1070 } else { 1071 return false; 1072 } 1073 1074 wp_reset_postdata(); 1075 1076 return true; 1077 } 1078 1079 /** 1080 * Show Comments section. 1081 * 1082 * @since 3.8.0 1083 * 1084 * @param int $total_items Optional. Number of comments to query. Default 5. 1085 * @return bool False if no comments were found. True otherwise. 1086 */ 1087 function wp_dashboard_recent_comments( $total_items = 5 ) { 1088 // Select all comment types and filter out spam later for better query performance. 1089 $comments = array(); 1090 1091 $comments_query = array( 1092 'number' => $total_items * 5, 1093 'offset' => 0, 1094 ); 1095 1096 if ( ! current_user_can( 'edit_posts' ) ) { 1097 $comments_query['status'] = 'approve'; 1098 } 1099 1100 $comments_count = 0; 1101 do { 1102 $possible = get_comments( $comments_query ); 1103 1104 if ( empty( $possible ) || ! is_array( $possible ) ) { 1105 break; 1106 } 1107 1108 foreach ( $possible as $comment ) { 1109 if ( ! current_user_can( 'edit_post', $comment->comment_post_ID ) 1110 && ( post_password_required( $comment->comment_post_ID ) 1111 || ! current_user_can( 'read_post', $comment->comment_post_ID ) ) 1112 ) { 1113 // The user has no access to the post and thus cannot see the comments. 1114 continue; 1115 } 1116 1117 $comments[] = $comment; 1118 $comments_count = count( $comments ); 1119 1120 if ( $comments_count === $total_items ) { 1121 break 2; 1122 } 1123 } 1124 1125 $comments_query['offset'] += $comments_query['number']; 1126 $comments_query['number'] = $total_items * 10; 1127 } while ( $comments_count < $total_items ); 1128 1129 if ( $comments ) { 1130 echo '<div id="latest-comments" class="activity-block table-view-list">'; 1131 echo '<h3>' . __( 'Recent Comments' ) . '</h3>'; 1132 1133 echo '<ul id="the-comment-list" data-wp-lists="list:comment">'; 1134 foreach ( $comments as $comment ) { 1135 _wp_dashboard_recent_comments_row( $comment ); 1136 } 1137 echo '</ul>'; 1138 1139 if ( current_user_can( 'edit_posts' ) ) { 1140 echo '<h3 class="screen-reader-text">' . 1141 /* translators: Hidden accessibility text. */ 1142 __( 'View more comments' ) . 1143 '</h3>'; 1144 _get_list_table( 'WP_Comments_List_Table' )->views(); 1145 } 1146 1147 wp_comment_reply( -1, false, 'dashboard', false ); 1148 wp_comment_trashnotice(); 1149 1150 echo '</div>'; 1151 } else { 1152 return false; 1153 } 1154 return true; 1155 } 1156 1157 /** 1158 * Display generic dashboard RSS widget feed. 1159 * 1160 * @since 2.5.0 1161 * 1162 * @param string $widget_id 1163 */ 1164 function wp_dashboard_rss_output( $widget_id ) { 1165 $widgets = get_option( 'dashboard_widget_options' ); 1166 echo '<div class="rss-widget">'; 1167 wp_widget_rss_output( $widgets[ $widget_id ] ); 1168 echo '</div>'; 1169 } 1170 1171 /** 1172 * Checks to see if all of the feed url in $check_urls are cached. 1173 * 1174 * If $check_urls is empty, look for the rss feed url found in the dashboard 1175 * widget options of $widget_id. If cached, call $callback, a function that 1176 * echoes out output for this widget. If not cache, echo a "Loading..." stub 1177 * which is later replaced by Ajax call (see top of /wp-admin/index.php) 1178 * 1179 * @since 2.5.0 1180 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter 1181 * by adding it to the function signature. 1182 * 1183 * @param string $widget_id The widget ID. 1184 * @param callable $callback The callback function used to display each feed. 1185 * @param array $check_urls RSS feeds. 1186 * @param mixed ...$args Optional additional parameters to pass to the callback function. 1187 * @return bool True on success, false on failure. 1188 */ 1189 function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array(), ...$args ) { 1190 $doing_ajax = wp_doing_ajax(); 1191 $loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading…' ) . '</p>'; 1192 $loading .= wp_get_admin_notice( 1193 __( 'This widget requires JavaScript.' ), 1194 array( 1195 'type' => 'error', 1196 'additional_classes' => array( 'inline', 'hide-if-js' ), 1197 ) 1198 ); 1199 1200 if ( empty( $check_urls ) ) { 1201 $widgets = get_option( 'dashboard_widget_options' ); 1202 1203 if ( empty( $widgets[ $widget_id ]['url'] ) && ! $doing_ajax ) { 1204 echo $loading; 1205 return false; 1206 } 1207 1208 $check_urls = array( $widgets[ $widget_id ]['url'] ); 1209 } 1210 1211 $locale = get_user_locale(); 1212 $cache_key = 'dash_v2_' . md5( $widget_id . '_' . $locale ); 1213 $output = get_transient( $cache_key ); 1214 1215 if ( false !== $output ) { 1216 echo $output; 1217 return true; 1218 } 1219 1220 if ( ! $doing_ajax ) { 1221 echo $loading; 1222 return false; 1223 } 1224 1225 if ( $callback && is_callable( $callback ) ) { 1226 array_unshift( $args, $widget_id, $check_urls ); 1227 ob_start(); 1228 call_user_func_array( $callback, $args ); 1229 // Default lifetime in cache of 12 hours (same as the feeds). 1230 set_transient( $cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS ); 1231 } 1232 1233 return true; 1234 } 1235 1236 // 1237 // Dashboard Widgets Controls. 1238 // 1239 1240 /** 1241 * Calls widget control callback. 1242 * 1243 * @since 2.5.0 1244 * 1245 * @global callable[] $wp_dashboard_control_callbacks 1246 * 1247 * @param int|false $widget_control_id Optional. Registered widget ID. Default false. 1248 */ 1249 function wp_dashboard_trigger_widget_control( $widget_control_id = false ) { 1250 global $wp_dashboard_control_callbacks; 1251 1252 if ( is_scalar( $widget_control_id ) && $widget_control_id 1253 && isset( $wp_dashboard_control_callbacks[ $widget_control_id ] ) 1254 && is_callable( $wp_dashboard_control_callbacks[ $widget_control_id ] ) 1255 ) { 1256 call_user_func( 1257 $wp_dashboard_control_callbacks[ $widget_control_id ], 1258 '', 1259 array( 1260 'id' => $widget_control_id, 1261 'callback' => $wp_dashboard_control_callbacks[ $widget_control_id ], 1262 ) 1263 ); 1264 } 1265 } 1266 1267 /** 1268 * Sets up the RSS dashboard widget control and $args to be used as input to wp_widget_rss_form(). 1269 * 1270 * Handles POST data from RSS-type widgets. 1271 * 1272 * @since 2.5.0 1273 * 1274 * @param string $widget_id 1275 * @param array $form_inputs 1276 */ 1277 function wp_dashboard_rss_control( $widget_id, $form_inputs = array() ) { 1278 $widget_options = get_option( 'dashboard_widget_options' ); 1279 1280 if ( ! $widget_options ) { 1281 $widget_options = array(); 1282 } 1283 1284 if ( ! isset( $widget_options[ $widget_id ] ) ) { 1285 $widget_options[ $widget_id ] = array(); 1286 } 1287 1288 $number = 1; // Hack to use wp_widget_rss_form(). 1289 1290 $widget_options[ $widget_id ]['number'] = $number; 1291 1292 if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['widget-rss'][ $number ] ) ) { 1293 $_POST['widget-rss'][ $number ] = wp_unslash( $_POST['widget-rss'][ $number ] ); 1294 $widget_options[ $widget_id ] = wp_widget_rss_process( $_POST['widget-rss'][ $number ] ); 1295 $widget_options[ $widget_id ]['number'] = $number; 1296 1297 // Title is optional. If blank, fill it if possible. 1298 if ( ! $widget_options[ $widget_id ]['title'] && isset( $_POST['widget-rss'][ $number ]['title'] ) ) { 1299 $rss = fetch_feed( $widget_options[ $widget_id ]['url'] ); 1300 if ( is_wp_error( $rss ) ) { 1301 $widget_options[ $widget_id ]['title'] = htmlentities( __( 'Unknown Feed' ) ); 1302 } else { 1303 $widget_options[ $widget_id ]['title'] = htmlentities( strip_tags( $rss->get_title() ) ); 1304 $rss->__destruct(); 1305 unset( $rss ); 1306 } 1307 } 1308 1309 update_option( 'dashboard_widget_options', $widget_options, false ); 1310 1311 $locale = get_user_locale(); 1312 $cache_key = 'dash_v2_' . md5( $widget_id . '_' . $locale ); 1313 delete_transient( $cache_key ); 1314 } 1315 1316 wp_widget_rss_form( $widget_options[ $widget_id ], $form_inputs ); 1317 } 1318 1319 1320 /** 1321 * Renders the Events and News dashboard widget. 1322 * 1323 * @since 4.8.0 1324 */ 1325 function wp_dashboard_events_news() { 1326 wp_print_community_events_markup(); 1327 1328 ?> 1329 1330 <div class="wordpress-news hide-if-no-js"> 1331 <?php wp_dashboard_primary(); ?> 1332 </div> 1333 1334 <p class="community-events-footer"> 1335 <?php 1336 printf( 1337 '<a href="%1$s" target="_blank">%2$s <span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', 1338 'https://make.wordpress.org/community/meetups-landing-page', 1339 __( 'Meetups' ), 1340 /* translators: Hidden accessibility text. */ 1341 __( '(opens in a new tab)' ) 1342 ); 1343 ?> 1344 1345 | 1346 1347 <?php 1348 printf( 1349 '<a href="%1$s" target="_blank">%2$s <span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', 1350 'https://central.wordcamp.org/schedule/', 1351 __( 'WordCamps' ), 1352 /* translators: Hidden accessibility text. */ 1353 __( '(opens in a new tab)' ) 1354 ); 1355 ?> 1356 1357 | 1358 1359 <?php 1360 printf( 1361 '<a href="%1$s" target="_blank">%2$s <span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', 1362 /* translators: If a Rosetta site exists (e.g. https://es.wordpress.org/news/), then use that. Otherwise, leave untranslated. */ 1363 esc_url( _x( 'https://wordpress.org/news/', 'Events and News dashboard widget' ) ), 1364 __( 'News' ), 1365 /* translators: Hidden accessibility text. */ 1366 __( '(opens in a new tab)' ) 1367 ); 1368 ?> 1369 </p> 1370 1371 <?php 1372 } 1373 1374 /** 1375 * Prints the markup for the Community Events section of the Events and News Dashboard widget. 1376 * 1377 * @since 4.8.0 1378 */ 1379 function wp_print_community_events_markup() { 1380 $community_events_notice = '<p class="hide-if-js">' . __( 'This widget requires JavaScript.' ) . '</p>'; 1381 $community_events_notice .= '<p class="community-events-error-occurred" aria-hidden="true">' . __( 'An error occurred. Please try again.' ) . '</p>'; 1382 $community_events_notice .= '<p class="community-events-could-not-locate" aria-hidden="true"></p>'; 1383 1384 wp_admin_notice( 1385 $community_events_notice, 1386 array( 1387 'type' => 'error', 1388 'additional_classes' => array( 'community-events-errors', 'inline', 'hide-if-js' ), 1389 'paragraph_wrap' => false, 1390 ) 1391 ); 1392 1393 /* 1394 * Hide the main element when the page first loads, because the content 1395 * won't be ready until wp.communityEvents.renderEventsTemplate() has run. 1396 */ 1397 ?> 1398 <div id="community-events" class="community-events" aria-hidden="true"> 1399 <div class="activity-block"> 1400 <p> 1401 <span id="community-events-location-message"></span> 1402 1403 <button class="button-link community-events-toggle-location" aria-expanded="false"> 1404 <span class="dashicons dashicons-location" aria-hidden="true"></span> 1405 <span class="community-events-location-edit"><?php _e( 'Select location' ); ?></span> 1406 </button> 1407 </p> 1408 1409 <form class="community-events-form" aria-hidden="true" action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" method="post"> 1410 <label for="community-events-location"> 1411 <?php _e( 'City:' ); ?> 1412 </label> 1413 <?php 1414 /* translators: Replace with a city related to your locale. 1415 * Test that it matches the expected location and has upcoming 1416 * events before including it. If no cities related to your 1417 * locale have events, then use a city related to your locale 1418 * that would be recognizable to most users. Use only the city 1419 * name itself, without any region or country. Use the endonym 1420 * (native locale name) instead of the English name if possible. 1421 */ 1422 ?> 1423 <input id="community-events-location" class="regular-text" type="text" name="community-events-location" placeholder="<?php esc_attr_e( 'Cincinnati' ); ?>" /> 1424 1425 <?php submit_button( __( 'Submit' ), 'secondary', 'community-events-submit', false ); ?> 1426 1427 <button class="community-events-cancel button-link" type="button" aria-expanded="false"> 1428 <?php _e( 'Cancel' ); ?> 1429 </button> 1430 1431 <span class="spinner"></span> 1432 </form> 1433 </div> 1434 1435 <ul class="community-events-results activity-block last"></ul> 1436 </div> 1437 1438 <?php 1439 } 1440 1441 /** 1442 * Renders the events templates for the Event and News widget. 1443 * 1444 * @since 4.8.0 1445 */ 1446 function wp_print_community_events_templates() { 1447 ?> 1448 1449 <script id="tmpl-community-events-attend-event-near" type="text/template"> 1450 <?php 1451 printf( 1452 /* translators: %s: The name of a city. */ 1453 __( 'Attend an upcoming event near %s.' ), 1454 '<strong>{{ data.location.description }}</strong>' 1455 ); 1456 ?> 1457 </script> 1458 1459 <script id="tmpl-community-events-could-not-locate" type="text/template"> 1460 <?php 1461 printf( 1462 /* translators: %s is the name of the city we couldn't locate. 1463 * Replace the examples with cities in your locale, but test 1464 * that they match the expected location before including them. 1465 * Use endonyms (native locale names) whenever possible. 1466 */ 1467 __( '%s could not be located. Please try another nearby city. For example: Kansas City; Springfield; Portland.' ), 1468 '<em>{{data.unknownCity}}</em>' 1469 ); 1470 ?> 1471 </script> 1472 1473 <script id="tmpl-community-events-event-list" type="text/template"> 1474 <# _.each( data.events, function( event ) { #> 1475 <li class="event event-{{ event.type }} wp-clearfix"> 1476 <div class="event-info"> 1477 <div class="dashicons event-icon" aria-hidden="true"></div> 1478 <div class="event-info-inner"> 1479 <a class="event-title" href="{{ event.url }}">{{ event.title }}</a> 1480 <# if ( event.type ) { 1481 const titleCaseEventType = event.type.replace( 1482 /\w\S*/g, 1483 function ( type ) { return type.charAt(0).toUpperCase() + type.substr(1).toLowerCase(); } 1484 ); 1485 #> 1486 {{ 'wordcamp' === event.type ? 'WordCamp' : titleCaseEventType }} 1487 <span class="ce-separator" aria-hidden="true"></span> 1488 <# } #> 1489 <span class="event-city">{{ event.location.location }}</span> 1490 </div> 1491 </div> 1492 1493 <div class="event-date-time"> 1494 <span class="event-date">{{ event.user_formatted_date }}</span> 1495 <# if ( 'meetup' === event.type ) { #> 1496 <span class="event-time"> 1497 {{ event.user_formatted_time }} {{ event.timeZoneAbbreviation }} 1498 </span> 1499 <# } #> 1500 </div> 1501 </li> 1502 <# } ) #> 1503 1504 <# if ( data.events.length <= 2 ) { #> 1505 <li class="event-none"> 1506 <?php 1507 printf( 1508 /* translators: %s: Localized meetup organization documentation URL. */ 1509 __( 'Want more events? <a href="%s">Help organize the next one</a>!' ), 1510 __( 'https://make.wordpress.org/community/organize-event-landing-page/' ) 1511 ); 1512 ?> 1513 </li> 1514 <# } #> 1515 1516 </script> 1517 1518 <script id="tmpl-community-events-no-upcoming-events" type="text/template"> 1519 <li class="event-none"> 1520 <# if ( data.location.description ) { #> 1521 <?php 1522 printf( 1523 /* translators: 1: The city the user searched for, 2: Meetup organization documentation URL. */ 1524 __( 'There are no events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize a WordPress event</a>?' ), 1525 '{{ data.location.description }}', 1526 __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' ) 1527 ); 1528 ?> 1529 1530 <# } else { #> 1531 <?php 1532 printf( 1533 /* translators: %s: Meetup organization documentation URL. */ 1534 __( 'There are no events scheduled near you at the moment. Would you like to <a href="%s">organize a WordPress event</a>?' ), 1535 __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' ) 1536 ); 1537 ?> 1538 <# } #> 1539 </li> 1540 </script> 1541 <?php 1542 } 1543 1544 /** 1545 * 'WordPress Events and News' dashboard widget. 1546 * 1547 * @since 2.7.0 1548 * @since 4.8.0 Removed popular plugins feed. 1549 */ 1550 function wp_dashboard_primary() { 1551 $feeds = array( 1552 'news' => array( 1553 1554 /** 1555 * Filters the primary link URL for the 'WordPress Events and News' dashboard widget. 1556 * 1557 * @since 2.5.0 1558 * 1559 * @param string $link The widget's primary link URL. 1560 */ 1561 'link' => apply_filters( 'dashboard_primary_link', __( 'https://wordpress.org/news/' ) ), 1562 1563 /** 1564 * Filters the primary feed URL for the 'WordPress Events and News' dashboard widget. 1565 * 1566 * @since 2.3.0 1567 * 1568 * @param string $url The widget's primary feed URL. 1569 */ 1570 'url' => apply_filters( 'dashboard_primary_feed', __( 'https://wordpress.org/news/feed/' ) ), 1571 1572 /** 1573 * Filters the primary link title for the 'WordPress Events and News' dashboard widget. 1574 * 1575 * @since 2.3.0 1576 * 1577 * @param string $title Title attribute for the widget's primary link. 1578 */ 1579 'title' => apply_filters( 'dashboard_primary_title', __( 'WordPress Blog' ) ), 1580 'items' => 2, 1581 'show_summary' => 0, 1582 'show_author' => 0, 1583 'show_date' => 0, 1584 ), 1585 'planet' => array( 1586 1587 /** 1588 * Filters the secondary link URL for the 'WordPress Events and News' dashboard widget. 1589 * 1590 * @since 2.3.0 1591 * 1592 * @param string $link The widget's secondary link URL. 1593 */ 1594 'link' => apply_filters( 1595 'dashboard_secondary_link', 1596 /* translators: Link to the Planet website of the locale. */ 1597 __( 'https://planet.wordpress.org/' ) 1598 ), 1599 1600 /** 1601 * Filters the secondary feed URL for the 'WordPress Events and News' dashboard widget. 1602 * 1603 * @since 2.3.0 1604 * 1605 * @param string $url The widget's secondary feed URL. 1606 */ 1607 'url' => apply_filters( 1608 'dashboard_secondary_feed', 1609 /* translators: Link to the Planet feed of the locale. */ 1610 __( 'https://planet.wordpress.org/feed/' ) 1611 ), 1612 1613 /** 1614 * Filters the secondary link title for the 'WordPress Events and News' dashboard widget. 1615 * 1616 * @since 2.3.0 1617 * 1618 * @param string $title Title attribute for the widget's secondary link. 1619 */ 1620 'title' => apply_filters( 'dashboard_secondary_title', __( 'Other WordPress News' ) ), 1621 1622 /** 1623 * Filters the number of secondary link items for the 'WordPress Events and News' dashboard widget. 1624 * 1625 * @since 4.4.0 1626 * 1627 * @param string $items How many items to show in the secondary feed. 1628 */ 1629 'items' => apply_filters( 'dashboard_secondary_items', 3 ), 1630 'show_summary' => 0, 1631 'show_author' => 0, 1632 'show_date' => 0, 1633 ), 1634 ); 1635 1636 wp_dashboard_cached_rss_widget( 'dashboard_primary', 'wp_dashboard_primary_output', $feeds ); 1637 } 1638 1639 /** 1640 * Displays the WordPress events and news feeds. 1641 * 1642 * @since 3.8.0 1643 * @since 4.8.0 Removed popular plugins feed. 1644 * 1645 * @param string $widget_id Widget ID. 1646 * @param array $feeds Array of RSS feeds. 1647 */ 1648 function wp_dashboard_primary_output( $widget_id, $feeds ) { 1649 foreach ( $feeds as $type => $args ) { 1650 $args['type'] = $type; 1651 echo '<div class="rss-widget">'; 1652 wp_widget_rss_output( $args['url'], $args ); 1653 echo '</div>'; 1654 } 1655 } 1656 1657 /** 1658 * Displays file upload quota on dashboard. 1659 * 1660 * Runs on the {@see 'activity_box_end'} hook in wp_dashboard_right_now(). 1661 * 1662 * @since 3.0.0 1663 * 1664 * @return true|null True if not multisite, user can't upload files, or the space check option is disabled. 1665 */ 1666 function wp_dashboard_quota() { 1667 if ( ! is_multisite() || ! current_user_can( 'upload_files' ) 1668 || get_site_option( 'upload_space_check_disabled' ) 1669 ) { 1670 return true; 1671 } 1672 1673 $quota = get_space_allowed(); 1674 $used = get_space_used(); 1675 1676 if ( $used > $quota ) { 1677 $percentused = '100'; 1678 } else { 1679 $percentused = ( $used / $quota ) * 100; 1680 } 1681 1682 $used_class = ( $percentused >= 70 ) ? ' warning' : ''; 1683 $used = round( $used, 2 ); 1684 $percentused = number_format( $percentused ); 1685 1686 ?> 1687 <h3 class="mu-storage"><?php _e( 'Storage Space' ); ?></h3> 1688 <div class="mu-storage"> 1689 <ul> 1690 <li class="storage-count"> 1691 <?php 1692 $text = sprintf( 1693 /* translators: %s: Number of megabytes. */ 1694 __( '%s MB Space Allowed' ), 1695 number_format_i18n( $quota ) 1696 ); 1697 printf( 1698 '<a href="%1$s">%2$s<span class="screen-reader-text"> (%3$s)</span></a>', 1699 esc_url( admin_url( 'upload.php' ) ), 1700 $text, 1701 /* translators: Hidden accessibility text. */ 1702 __( 'Manage Uploads' ) 1703 ); 1704 ?> 1705 </li><li class="storage-count <?php echo $used_class; ?>"> 1706 <?php 1707 $text = sprintf( 1708 /* translators: 1: Number of megabytes, 2: Percentage. */ 1709 __( '%1$s MB (%2$s%%) Space Used' ), 1710 number_format_i18n( $used, 2 ), 1711 $percentused 1712 ); 1713 printf( 1714 '<a href="%1$s" class="musublink">%2$s<span class="screen-reader-text"> (%3$s)</span></a>', 1715 esc_url( admin_url( 'upload.php' ) ), 1716 $text, 1717 /* translators: Hidden accessibility text. */ 1718 __( 'Manage Uploads' ) 1719 ); 1720 ?> 1721 </li> 1722 </ul> 1723 </div> 1724 <?php 1725 return null; 1726 } 1727 1728 /** 1729 * Displays the browser update nag. 1730 * 1731 * @since 3.2.0 1732 * @since 5.8.0 Added a special message for Internet Explorer users. 1733 * 1734 * @global bool $is_IE 1735 */ 1736 function wp_dashboard_browser_nag() { 1737 global $is_IE; 1738 1739 $notice = ''; 1740 $response = wp_check_browser_version(); 1741 1742 if ( $response ) { 1743 if ( $is_IE ) { 1744 $msg = __( 'Internet Explorer does not give you the best WordPress experience. Switch to Microsoft Edge, or another more modern browser to get the most from your site.' ); 1745 } elseif ( $response['insecure'] ) { 1746 $msg = sprintf( 1747 /* translators: %s: Browser name and link. */ 1748 __( "It looks like you're using an insecure version of %s. Using an outdated browser makes your computer unsafe. For the best WordPress experience, please update your browser." ), 1749 sprintf( '<a href="%s">%s</a>', esc_url( $response['update_url'] ), esc_html( $response['name'] ) ) 1750 ); 1751 } else { 1752 $msg = sprintf( 1753 /* translators: %s: Browser name and link. */ 1754 __( "It looks like you're using an old version of %s. For the best WordPress experience, please update your browser." ), 1755 sprintf( '<a href="%s">%s</a>', esc_url( $response['update_url'] ), esc_html( $response['name'] ) ) 1756 ); 1757 } 1758 1759 $browser_nag_class = ''; 1760 if ( ! empty( $response['img_src'] ) ) { 1761 $img_src = ( is_ssl() && ! empty( $response['img_src_ssl'] ) ) ? $response['img_src_ssl'] : $response['img_src']; 1762 1763 $notice .= '<div class="alignright browser-icon"><img src="' . esc_url( $img_src ) . '" alt="" /></div>'; 1764 $browser_nag_class = ' has-browser-icon'; 1765 } 1766 $notice .= "<p class='browser-update-nag{$browser_nag_class}'>{$msg}</p>"; 1767 1768 $browsehappy = 'https://browsehappy.com/'; 1769 $locale = get_user_locale(); 1770 if ( 'en_US' !== $locale ) { 1771 $browsehappy = add_query_arg( 'locale', $locale, $browsehappy ); 1772 } 1773 1774 if ( $is_IE ) { 1775 $msg_browsehappy = sprintf( 1776 /* translators: %s: Browse Happy URL. */ 1777 __( 'Learn how to <a href="%s" class="update-browser-link">browse happy</a>' ), 1778 esc_url( $browsehappy ) 1779 ); 1780 } else { 1781 $msg_browsehappy = sprintf( 1782 /* translators: 1: Browser update URL, 2: Browser name, 3: Browse Happy URL. */ 1783 __( '<a href="%1$s" class="update-browser-link">Update %2$s</a> or learn how to <a href="%3$s" class="browse-happy-link">browse happy</a>' ), 1784 esc_attr( $response['update_url'] ), 1785 esc_html( $response['name'] ), 1786 esc_url( $browsehappy ) 1787 ); 1788 } 1789 1790 $notice .= '<p>' . $msg_browsehappy . '</p>'; 1791 $notice .= '<p class="hide-if-no-js"><a href="" class="dismiss" aria-label="' . esc_attr__( 'Dismiss the browser warning panel' ) . '">' . __( 'Dismiss' ) . '</a></p>'; 1792 $notice .= '<div class="clear"></div>'; 1793 } 1794 1795 /** 1796 * Filters the notice output for the 'Browse Happy' nag meta box. 1797 * 1798 * @since 3.2.0 1799 * 1800 * @param string $notice The notice content. 1801 * @param array|false $response An array containing web browser information, or 1802 * false on failure. See wp_check_browser_version(). 1803 */ 1804 echo apply_filters( 'browse-happy-notice', $notice, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores 1805 } 1806 1807 /** 1808 * Adds an additional class to the browser nag if the current version is insecure. 1809 * 1810 * @since 3.2.0 1811 * 1812 * @param string[] $classes Array of meta box classes. 1813 * @return string[] Modified array of meta box classes. 1814 */ 1815 function dashboard_browser_nag_class( $classes ) { 1816 $response = wp_check_browser_version(); 1817 1818 if ( $response && $response['insecure'] ) { 1819 $classes[] = 'browser-insecure'; 1820 } 1821 1822 return $classes; 1823 } 1824 1825 /** 1826 * Checks if the user needs a browser update. 1827 * 1828 * @since 3.2.0 1829 * 1830 * @return array|false Array of browser data on success, false on failure. 1831 */ 1832 function wp_check_browser_version() { 1833 if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) { 1834 return false; 1835 } 1836 1837 $key = md5( $_SERVER['HTTP_USER_AGENT'] ); 1838 1839 $response = get_site_transient( 'browser_' . $key ); 1840 1841 if ( false === $response ) { 1842 $url = 'http://api.wordpress.org/core/browse-happy/1.1/'; 1843 $options = array( 1844 'body' => array( 'useragent' => $_SERVER['HTTP_USER_AGENT'] ), 1845 'user-agent' => 'WordPress/' . wp_get_wp_version() . '; ' . home_url( '/' ), 1846 ); 1847 1848 if ( wp_http_supports( array( 'ssl' ) ) ) { 1849 $url = set_url_scheme( $url, 'https' ); 1850 } 1851 1852 $response = wp_remote_post( $url, $options ); 1853 1854 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { 1855 return false; 1856 } 1857 1858 /** 1859 * Response should be an array with: 1860 * 'platform' - string - A user-friendly platform name, if it can be determined 1861 * 'name' - string - A user-friendly browser name 1862 * 'version' - string - The version of the browser the user is using 1863 * 'current_version' - string - The most recent version of the browser 1864 * 'upgrade' - boolean - Whether the browser needs an upgrade 1865 * 'insecure' - boolean - Whether the browser is deemed insecure 1866 * 'update_url' - string - The url to visit to upgrade 1867 * 'img_src' - string - An image representing the browser 1868 * 'img_src_ssl' - string - An image (over SSL) representing the browser 1869 */ 1870 $response = json_decode( wp_remote_retrieve_body( $response ), true ); 1871 1872 if ( ! is_array( $response ) ) { 1873 return false; 1874 } 1875 1876 set_site_transient( 'browser_' . $key, $response, WEEK_IN_SECONDS ); 1877 } 1878 1879 return $response; 1880 } 1881 1882 /** 1883 * Displays the PHP update nag. 1884 * 1885 * @since 5.1.0 1886 */ 1887 function wp_dashboard_php_nag() { 1888 $response = wp_check_php_version(); 1889 1890 if ( ! $response ) { 1891 return; 1892 } 1893 1894 if ( isset( $response['is_secure'] ) && ! $response['is_secure'] ) { 1895 // The `is_secure` array key name doesn't actually imply this is a secure version of PHP. It only means it receives security updates. 1896 1897 if ( $response['is_lower_than_future_minimum'] ) { 1898 $message = sprintf( 1899 /* translators: %s: The server PHP version. */ 1900 __( 'Your site is running on an outdated version of PHP (%s), which does not receive security updates and soon will not be supported by WordPress. Ensure that PHP is updated on your server as soon as possible. Otherwise you will not be able to upgrade WordPress.' ), 1901 PHP_VERSION 1902 ); 1903 } else { 1904 $message = sprintf( 1905 /* translators: %s: The server PHP version. */ 1906 __( 'Your site is running on an outdated version of PHP (%s), which does not receive security updates. It should be updated.' ), 1907 PHP_VERSION 1908 ); 1909 } 1910 } elseif ( $response['is_lower_than_future_minimum'] ) { 1911 $message = sprintf( 1912 /* translators: %s: The server PHP version. */ 1913 __( 'Your site is running on an outdated version of PHP (%s), which soon will not be supported by WordPress. Ensure that PHP is updated on your server as soon as possible. Otherwise you will not be able to upgrade WordPress.' ), 1914 PHP_VERSION 1915 ); 1916 } else { 1917 $message = sprintf( 1918 /* translators: %s: The server PHP version. */ 1919 __( 'Your site is running on an outdated version of PHP (%s), which should be updated.' ), 1920 PHP_VERSION 1921 ); 1922 } 1923 ?> 1924 <p class="bigger-bolder-text"><?php echo $message; ?></p> 1925 1926 <p><?php _e( 'What is PHP and how does it affect my site?' ); ?></p> 1927 <p> 1928 <?php _e( 'PHP is one of the programming languages used to build WordPress. Newer versions of PHP receive regular security updates and may increase your site’s performance.' ); ?> 1929 <?php 1930 if ( ! empty( $response['recommended_version'] ) ) { 1931 printf( 1932 /* translators: %s: The minimum recommended PHP version. */ 1933 __( 'The minimum recommended version of PHP is %s.' ), 1934 $response['recommended_version'] 1935 ); 1936 } 1937 ?> 1938 </p> 1939 1940 <p class="button-container"> 1941 <?php 1942 printf( 1943 '<a class="button button-primary" href="%1$s" target="_blank">%2$s<span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', 1944 esc_url( wp_get_update_php_url() ), 1945 __( 'Learn more about updating PHP' ), 1946 /* translators: Hidden accessibility text. */ 1947 __( '(opens in a new tab)' ) 1948 ); 1949 ?> 1950 </p> 1951 <?php 1952 1953 wp_update_php_annotation(); 1954 wp_direct_php_update_button(); 1955 } 1956 1957 /** 1958 * Adds an additional class to the PHP nag if the current version is insecure. 1959 * 1960 * @since 5.1.0 1961 * 1962 * @param string[] $classes Array of meta box classes. 1963 * @return string[] Modified array of meta box classes. 1964 */ 1965 function dashboard_php_nag_class( $classes ) { 1966 $response = wp_check_php_version(); 1967 1968 if ( ! $response ) { 1969 return $classes; 1970 } 1971 1972 if ( isset( $response['is_secure'] ) && ! $response['is_secure'] ) { 1973 $classes[] = 'php-no-security-updates'; 1974 } elseif ( $response['is_lower_than_future_minimum'] ) { 1975 $classes[] = 'php-version-lower-than-future-minimum'; 1976 } 1977 1978 return $classes; 1979 } 1980 1981 /** 1982 * Displays the Site Health Status widget. 1983 * 1984 * @since 5.4.0 1985 */ 1986 function wp_dashboard_site_health() { 1987 $get_issues = get_transient( 'health-check-site-status-result' ); 1988 1989 $issue_counts = array(); 1990 1991 if ( false !== $get_issues ) { 1992 $issue_counts = json_decode( $get_issues, true ); 1993 } 1994 1995 if ( ! is_array( $issue_counts ) || ! $issue_counts ) { 1996 $issue_counts = array( 1997 'good' => 0, 1998 'recommended' => 0, 1999 'critical' => 0, 2000 ); 2001 } 2002 2003 $issues_total = $issue_counts['recommended'] + $issue_counts['critical']; 2004 ?> 2005 <div class="health-check-widget"> 2006 <div class="health-check-widget-title-section site-health-progress-wrapper loading hide-if-no-js"> 2007 <div class="site-health-progress"> 2008 <svg aria-hidden="true" focusable="false" width="100%" height="100%" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg"> 2009 <circle r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle> 2010 <circle id="bar" r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle> 2011 </svg> 2012 </div> 2013 <div class="site-health-progress-label"> 2014 <?php if ( false === $get_issues ) : ?> 2015 <?php _e( 'No information yet…' ); ?> 2016 <?php else : ?> 2017 <?php _e( 'Results are still loading…' ); ?> 2018 <?php endif; ?> 2019 </div> 2020 </div> 2021 2022 <div class="site-health-details"> 2023 <?php if ( false === $get_issues ) : ?> 2024 <p> 2025 <?php 2026 printf( 2027 /* translators: %s: URL to Site Health screen. */ 2028 __( 'Site health checks will automatically run periodically to gather information about your site. You can also <a href="%s">visit the Site Health screen</a> to gather information about your site now.' ), 2029 esc_url( admin_url( 'site-health.php' ) ) 2030 ); 2031 ?> 2032 </p> 2033 <?php else : ?> 2034 <p> 2035 <?php if ( $issues_total <= 0 ) : ?> 2036 <?php _e( 'Great job! Your site currently passes all site health checks.' ); ?> 2037 <?php elseif ( 1 === (int) $issue_counts['critical'] ) : ?> 2038 <?php _e( 'Your site has a critical issue that should be addressed as soon as possible to improve its performance and security.' ); ?> 2039 <?php elseif ( $issue_counts['critical'] > 1 ) : ?> 2040 <?php _e( 'Your site has critical issues that should be addressed as soon as possible to improve its performance and security.' ); ?> 2041 <?php elseif ( 1 === (int) $issue_counts['recommended'] ) : ?> 2042 <?php _e( 'Your site’s health is looking good, but there is still one thing you can do to improve its performance and security.' ); ?> 2043 <?php else : ?> 2044 <?php _e( 'Your site’s health is looking good, but there are still some things you can do to improve its performance and security.' ); ?> 2045 <?php endif; ?> 2046 </p> 2047 <?php endif; ?> 2048 2049 <?php if ( $issues_total > 0 && false !== $get_issues ) : ?> 2050 <p> 2051 <?php 2052 printf( 2053 /* translators: 1: Number of issues. 2: URL to Site Health screen. */ 2054 _n( 2055 'Take a look at the <strong>%1$d item</strong> on the <a href="%2$s">Site Health screen</a>.', 2056 'Take a look at the <strong>%1$d items</strong> on the <a href="%2$s">Site Health screen</a>.', 2057 $issues_total 2058 ), 2059 $issues_total, 2060 esc_url( admin_url( 'site-health.php' ) ) 2061 ); 2062 ?> 2063 </p> 2064 <?php endif; ?> 2065 </div> 2066 </div> 2067 2068 <?php 2069 } 2070 2071 /** 2072 * Outputs empty dashboard widget to be populated by JS later. 2073 * 2074 * Usable by plugins. 2075 * 2076 * @since 2.5.0 2077 */ 2078 function wp_dashboard_empty() {} 2079 2080 /** 2081 * Displays a welcome panel to introduce users to WordPress. 2082 * 2083 * @since 3.3.0 2084 * @since 5.9.0 Send users to the Site Editor if the active theme is block-based. 2085 */ 2086 function wp_welcome_panel() { 2087 list( $display_version ) = explode( '-', wp_get_wp_version() ); 2088 $can_customize = current_user_can( 'customize' ); 2089 $is_block_theme = wp_is_block_theme(); 2090 ?> 2091 <div class="welcome-panel-content"> 2092 <div class="welcome-panel-header-wrap"> 2093 <div class="welcome-panel-header"> 2094 <div class="welcome-panel-header-image"> 2095 <?php echo file_get_contents( dirname( __DIR__ ) . '/images/dashboard-background.svg' ); ?> 2096 </div> 2097 <h2><?php _e( 'Welcome to WordPress!' ); ?></h2> 2098 <p> 2099 <a href="<?php echo esc_url( admin_url( 'about.php' ) ); ?>"> 2100 <?php 2101 /* translators: %s: Current WordPress version. */ 2102 printf( __( 'Learn more about the %s version.' ), esc_html( $display_version ) ); 2103 ?> 2104 </a> 2105 </p> 2106 </div> 2107 </div> 2108 <div class="welcome-panel-column-container"> 2109 <div class="welcome-panel-column"> 2110 <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> 2111 <rect width="48" height="48" rx="4" fill="#1E1E1E"/> 2112 <path fill-rule="evenodd" clip-rule="evenodd" d="M32.0668 17.0854L28.8221 13.9454L18.2008 24.671L16.8983 29.0827L21.4257 27.8309L32.0668 17.0854ZM16 32.75H24V31.25H16V32.75Z" fill="white"/> 2113 </svg> 2114 <div class="welcome-panel-column-content"> 2115 <h3><?php _e( 'Author rich content with blocks and patterns' ); ?></h3> 2116 <p><?php _e( 'Block patterns are pre-configured block layouts. Use them to get inspired or create new pages in a flash.' ); ?></p> 2117 <a href="<?php echo esc_url( admin_url( 'post-new.php?post_type=page' ) ); ?>"><?php _e( 'Add a new page' ); ?></a> 2118 </div> 2119 </div> 2120 <div class="welcome-panel-column"> 2121 <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> 2122 <rect width="48" height="48" rx="4" fill="#1E1E1E"/> 2123 <path fill-rule="evenodd" clip-rule="evenodd" d="M18 16h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H18a2 2 0 0 1-2-2V18a2 2 0 0 1 2-2zm12 1.5H18a.5.5 0 0 0-.5.5v3h13v-3a.5.5 0 0 0-.5-.5zm.5 5H22v8h8a.5.5 0 0 0 .5-.5v-7.5zm-10 0h-3V30a.5.5 0 0 0 .5.5h2.5v-8z" fill="#fff"/> 2124 </svg> 2125 <div class="welcome-panel-column-content"> 2126 <?php if ( $is_block_theme ) : ?> 2127 <h3><?php _e( 'Customize your entire site with block themes' ); ?></h3> 2128 <p><?php _e( 'Design everything on your site — from the header down to the footer, all using blocks and patterns.' ); ?></p> 2129 <a href="<?php echo esc_url( admin_url( 'site-editor.php' ) ); ?>"><?php _e( 'Open site editor' ); ?></a> 2130 <?php else : ?> 2131 <h3><?php _e( 'Start Customizing' ); ?></h3> 2132 <p><?php _e( 'Configure your site’s logo, header, menus, and more in the Customizer.' ); ?></p> 2133 <?php if ( $can_customize ) : ?> 2134 <a class="load-customize hide-if-no-customize" href="<?php echo wp_customize_url(); ?>"><?php _e( 'Open the Customizer' ); ?></a> 2135 <?php endif; ?> 2136 <?php endif; ?> 2137 </div> 2138 </div> 2139 <div class="welcome-panel-column"> 2140 <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> 2141 <rect width="48" height="48" rx="4" fill="#1E1E1E"/> 2142 <path fill-rule="evenodd" clip-rule="evenodd" d="M31 24a7 7 0 0 1-7 7V17a7 7 0 0 1 7 7zm-7-8a8 8 0 1 1 0 16 8 8 0 0 1 0-16z" fill="#fff"/> 2143 </svg> 2144 <div class="welcome-panel-column-content"> 2145 <?php if ( $is_block_theme ) : ?> 2146 <h3><?php _e( 'Switch up your site’s look & feel with Styles' ); ?></h3> 2147 <p><?php _e( 'Tweak your site, or give it a whole new look! Get creative — how about a new color palette or font?' ); ?></p> 2148 <a href="<?php echo esc_url( add_query_arg( 'p', rawurlencode( '/styles' ), admin_url( 'site-editor.php' ) ) ); ?>"><?php _e( 'Edit styles' ); ?></a> 2149 <?php else : ?> 2150 <h3><?php _e( 'Discover a new way to build your site.' ); ?></h3> 2151 <p><?php _e( 'There is a new kind of WordPress theme, called a block theme, that lets you build the site you’ve always wanted — with blocks and styles.' ); ?></p> 2152 <a href="<?php echo esc_url( __( 'https://wordpress.org/documentation/article/block-themes/' ) ); ?>"><?php _e( 'Learn about block themes' ); ?></a> 2153 <?php endif; ?> 2154 </div> 2155 </div> 2156 </div> 2157 </div> 2158 <?php 2159 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Mon Jul 27 08:20:18 2026 | Cross-referenced by PHPXref |