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