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