balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

dashboard.php (67306B)


      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( __( 'Submit' ) );
    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&#8217;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 &#8220;%s&#8221;' ), $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='$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&amp;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 &#8220;%s&#8221;' ), $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&hellip;' ) . '</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&hellip;' ); ?>
   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-label="<?php esc_attr_e( 'Edit city' ); ?>" aria-expanded="false">
   1354 					<span class="dashicons dashicons-edit"></span>
   1355 				</button>
   1356 			</p>
   1357 
   1358 			<form class="community-events-form" aria-hidden="true" action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" method="post">
   1359 				<label for="community-events-location">
   1360 					<?php _e( 'City:' ); ?>
   1361 				</label>
   1362 				<?php
   1363 				/* translators: Replace with a city related to your locale.
   1364 				 * Test that it matches the expected location and has upcoming
   1365 				 * events before including it. If no cities related to your
   1366 				 * locale have events, then use a city related to your locale
   1367 				 * that would be recognizable to most users. Use only the city
   1368 				 * name itself, without any region or country. Use the endonym
   1369 				 * (native locale name) instead of the English name if possible.
   1370 				 */
   1371 				?>
   1372 				<input id="community-events-location" class="regular-text" type="text" name="community-events-location" placeholder="<?php esc_attr_e( 'Cincinnati' ); ?>" />
   1373 
   1374 				<?php submit_button( __( 'Submit' ), 'secondary', 'community-events-submit', false ); ?>
   1375 
   1376 				<button class="community-events-cancel button-link" type="button" aria-expanded="false">
   1377 					<?php _e( 'Cancel' ); ?>
   1378 				</button>
   1379 
   1380 				<span class="spinner"></span>
   1381 			</form>
   1382 		</div>
   1383 
   1384 		<ul class="community-events-results activity-block last"></ul>
   1385 	</div>
   1386 
   1387 	<?php
   1388 }
   1389 
   1390 /**
   1391  * Renders the events templates for the Event and News widget.
   1392  *
   1393  * @since 4.8.0
   1394  */
   1395 function wp_print_community_events_templates() {
   1396 	?>
   1397 
   1398 	<script id="tmpl-community-events-attend-event-near" type="text/template">
   1399 		<?php
   1400 		printf(
   1401 			/* translators: %s: The name of a city. */
   1402 			__( 'Attend an upcoming event near %s.' ),
   1403 			'<strong>{{ data.location.description }}</strong>'
   1404 		);
   1405 		?>
   1406 	</script>
   1407 
   1408 	<script id="tmpl-community-events-could-not-locate" type="text/template">
   1409 		<?php
   1410 		printf(
   1411 			/* translators: %s is the name of the city we couldn't locate.
   1412 			 * Replace the examples with cities in your locale, but test
   1413 			 * that they match the expected location before including them.
   1414 			 * Use endonyms (native locale names) whenever possible.
   1415 			 */
   1416 			__( 'We couldn&#8217;t locate %s. Please try another nearby city. For example: Kansas City; Springfield; Portland.' ),
   1417 			'<em>{{data.unknownCity}}</em>'
   1418 		);
   1419 		?>
   1420 	</script>
   1421 
   1422 	<script id="tmpl-community-events-event-list" type="text/template">
   1423 		<# _.each( data.events, function( event ) { #>
   1424 			<li class="event event-{{ event.type }} wp-clearfix">
   1425 				<div class="event-info">
   1426 					<div class="dashicons event-icon" aria-hidden="true"></div>
   1427 					<div class="event-info-inner">
   1428 						<a class="event-title" href="{{ event.url }}">{{ event.title }}</a>
   1429 						<span class="event-city">{{ event.location.location }}</span>
   1430 					</div>
   1431 				</div>
   1432 
   1433 				<div class="event-date-time">
   1434 					<span class="event-date">{{ event.user_formatted_date }}</span>
   1435 					<# if ( 'meetup' === event.type ) { #>
   1436 						<span class="event-time">
   1437 							{{ event.user_formatted_time }} {{ event.timeZoneAbbreviation }}
   1438 						</span>
   1439 					<# } #>
   1440 				</div>
   1441 			</li>
   1442 		<# } ) #>
   1443 
   1444 		<# if ( data.events.length <= 2 ) { #>
   1445 			<li class="event-none">
   1446 				<?php
   1447 				printf(
   1448 					/* translators: %s: Localized meetup organization documentation URL. */
   1449 					__( 'Want more events? <a href="%s">Help organize the next one</a>!' ),
   1450 					__( 'https://make.wordpress.org/community/organize-event-landing-page/' )
   1451 				);
   1452 				?>
   1453 			</li>
   1454 		<# } #>
   1455 
   1456 	</script>
   1457 
   1458 	<script id="tmpl-community-events-no-upcoming-events" type="text/template">
   1459 		<li class="event-none">
   1460 			<# if ( data.location.description ) { #>
   1461 				<?php
   1462 				printf(
   1463 					/* translators: 1: The city the user searched for, 2: Meetup organization documentation URL. */
   1464 					__( 'There aren&#8217;t any events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize a WordPress event</a>?' ),
   1465 					'{{ data.location.description }}',
   1466 					__( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' )
   1467 				);
   1468 				?>
   1469 
   1470 			<# } else { #>
   1471 				<?php
   1472 				printf(
   1473 					/* translators: %s: Meetup organization documentation URL. */
   1474 					__( 'There aren&#8217;t any events scheduled near you at the moment. Would you like to <a href="%s">organize a WordPress event</a>?' ),
   1475 					__( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' )
   1476 				);
   1477 				?>
   1478 			<# } #>
   1479 		</li>
   1480 	</script>
   1481 	<?php
   1482 }
   1483 
   1484 /**
   1485  * 'WordPress Events and News' dashboard widget.
   1486  *
   1487  * @since 2.7.0
   1488  * @since 4.8.0 Removed popular plugins feed.
   1489  */
   1490 function wp_dashboard_primary() {
   1491 	$feeds = array(
   1492 		'news'   => array(
   1493 
   1494 			/**
   1495 			 * Filters the primary link URL for the 'WordPress Events and News' dashboard widget.
   1496 			 *
   1497 			 * @since 2.5.0
   1498 			 *
   1499 			 * @param string $link The widget's primary link URL.
   1500 			 */
   1501 			'link'         => apply_filters( 'dashboard_primary_link', __( 'https://wordpress.org/news/' ) ),
   1502 
   1503 			/**
   1504 			 * Filters the primary feed URL for the 'WordPress Events and News' dashboard widget.
   1505 			 *
   1506 			 * @since 2.3.0
   1507 			 *
   1508 			 * @param string $url The widget's primary feed URL.
   1509 			 */
   1510 			'url'          => apply_filters( 'dashboard_primary_feed', __( 'https://wordpress.org/news/feed/' ) ),
   1511 
   1512 			/**
   1513 			 * Filters the primary link title for the 'WordPress Events and News' dashboard widget.
   1514 			 *
   1515 			 * @since 2.3.0
   1516 			 *
   1517 			 * @param string $title Title attribute for the widget's primary link.
   1518 			 */
   1519 			'title'        => apply_filters( 'dashboard_primary_title', __( 'WordPress Blog' ) ),
   1520 			'items'        => 2,
   1521 			'show_summary' => 0,
   1522 			'show_author'  => 0,
   1523 			'show_date'    => 0,
   1524 		),
   1525 		'planet' => array(
   1526 
   1527 			/**
   1528 			 * Filters the secondary link URL for the 'WordPress Events and News' dashboard widget.
   1529 			 *
   1530 			 * @since 2.3.0
   1531 			 *
   1532 			 * @param string $link The widget's secondary link URL.
   1533 			 */
   1534 			'link'         => apply_filters( 'dashboard_secondary_link', __( 'https://planet.wordpress.org/' ) ),
   1535 
   1536 			/**
   1537 			 * Filters the secondary feed URL for the 'WordPress Events and News' dashboard widget.
   1538 			 *
   1539 			 * @since 2.3.0
   1540 			 *
   1541 			 * @param string $url The widget's secondary feed URL.
   1542 			 */
   1543 			'url'          => apply_filters( 'dashboard_secondary_feed', __( 'https://planet.wordpress.org/feed/' ) ),
   1544 
   1545 			/**
   1546 			 * Filters the secondary link title for the 'WordPress Events and News' dashboard widget.
   1547 			 *
   1548 			 * @since 2.3.0
   1549 			 *
   1550 			 * @param string $title Title attribute for the widget's secondary link.
   1551 			 */
   1552 			'title'        => apply_filters( 'dashboard_secondary_title', __( 'Other WordPress News' ) ),
   1553 
   1554 			/**
   1555 			 * Filters the number of secondary link items for the 'WordPress Events and News' dashboard widget.
   1556 			 *
   1557 			 * @since 4.4.0
   1558 			 *
   1559 			 * @param string $items How many items to show in the secondary feed.
   1560 			 */
   1561 			'items'        => apply_filters( 'dashboard_secondary_items', 3 ),
   1562 			'show_summary' => 0,
   1563 			'show_author'  => 0,
   1564 			'show_date'    => 0,
   1565 		),
   1566 	);
   1567 
   1568 	wp_dashboard_cached_rss_widget( 'dashboard_primary', 'wp_dashboard_primary_output', $feeds );
   1569 }
   1570 
   1571 /**
   1572  * Displays the WordPress events and news feeds.
   1573  *
   1574  * @since 3.8.0
   1575  * @since 4.8.0 Removed popular plugins feed.
   1576  *
   1577  * @param string $widget_id Widget ID.
   1578  * @param array  $feeds     Array of RSS feeds.
   1579  */
   1580 function wp_dashboard_primary_output( $widget_id, $feeds ) {
   1581 	foreach ( $feeds as $type => $args ) {
   1582 		$args['type'] = $type;
   1583 		echo '<div class="rss-widget">';
   1584 			wp_widget_rss_output( $args['url'], $args );
   1585 		echo '</div>';
   1586 	}
   1587 }
   1588 
   1589 /**
   1590  * Displays file upload quota on dashboard.
   1591  *
   1592  * Runs on the {@see 'activity_box_end'} hook in wp_dashboard_right_now().
   1593  *
   1594  * @since 3.0.0
   1595  *
   1596  * @return true|void True if not multisite, user can't upload files, or the space check option is disabled.
   1597  */
   1598 function wp_dashboard_quota() {
   1599 	if ( ! is_multisite() || ! current_user_can( 'upload_files' )
   1600 		|| get_site_option( 'upload_space_check_disabled' )
   1601 	) {
   1602 		return true;
   1603 	}
   1604 
   1605 	$quota = get_space_allowed();
   1606 	$used  = get_space_used();
   1607 
   1608 	if ( $used > $quota ) {
   1609 		$percentused = '100';
   1610 	} else {
   1611 		$percentused = ( $used / $quota ) * 100;
   1612 	}
   1613 
   1614 	$used_class  = ( $percentused >= 70 ) ? ' warning' : '';
   1615 	$used        = round( $used, 2 );
   1616 	$percentused = number_format( $percentused );
   1617 
   1618 	?>
   1619 	<h3 class="mu-storage"><?php _e( 'Storage Space' ); ?></h3>
   1620 	<div class="mu-storage">
   1621 	<ul>
   1622 		<li class="storage-count">
   1623 			<?php
   1624 			$text = sprintf(
   1625 				/* translators: %s: Number of megabytes. */
   1626 				__( '%s MB Space Allowed' ),
   1627 				number_format_i18n( $quota )
   1628 			);
   1629 			printf(
   1630 				'<a href="%1$s">%2$s <span class="screen-reader-text">(%3$s)</span></a>',
   1631 				esc_url( admin_url( 'upload.php' ) ),
   1632 				$text,
   1633 				__( 'Manage Uploads' )
   1634 			);
   1635 			?>
   1636 		</li><li class="storage-count <?php echo $used_class; ?>">
   1637 			<?php
   1638 			$text = sprintf(
   1639 				/* translators: 1: Number of megabytes, 2: Percentage. */
   1640 				__( '%1$s MB (%2$s%%) Space Used' ),
   1641 				number_format_i18n( $used, 2 ),
   1642 				$percentused
   1643 			);
   1644 			printf(
   1645 				'<a href="%1$s" class="musublink">%2$s <span class="screen-reader-text">(%3$s)</span></a>',
   1646 				esc_url( admin_url( 'upload.php' ) ),
   1647 				$text,
   1648 				__( 'Manage Uploads' )
   1649 			);
   1650 			?>
   1651 		</li>
   1652 	</ul>
   1653 	</div>
   1654 	<?php
   1655 }
   1656 
   1657 /**
   1658  * Displays the browser update nag.
   1659  *
   1660  * @since 3.2.0
   1661  * @since 5.8.0 Added a special message for Internet Explorer users.
   1662  *
   1663  * @global bool $is_IE
   1664  */
   1665 function wp_dashboard_browser_nag() {
   1666 	global $is_IE;
   1667 
   1668 	$notice   = '';
   1669 	$response = wp_check_browser_version();
   1670 
   1671 	if ( $response ) {
   1672 		if ( $is_IE ) {
   1673 			$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.' );
   1674 		} elseif ( $response['insecure'] ) {
   1675 			$msg = sprintf(
   1676 				/* translators: %s: Browser name and link. */
   1677 				__( "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." ),
   1678 				sprintf( '<a href="%s">%s</a>', esc_url( $response['update_url'] ), esc_html( $response['name'] ) )
   1679 			);
   1680 		} else {
   1681 			$msg = sprintf(
   1682 				/* translators: %s: Browser name and link. */
   1683 				__( "It looks like you're using an old version of %s. For the best WordPress experience, please update your browser." ),
   1684 				sprintf( '<a href="%s">%s</a>', esc_url( $response['update_url'] ), esc_html( $response['name'] ) )
   1685 			);
   1686 		}
   1687 
   1688 		$browser_nag_class = '';
   1689 		if ( ! empty( $response['img_src'] ) ) {
   1690 			$img_src = ( is_ssl() && ! empty( $response['img_src_ssl'] ) ) ? $response['img_src_ssl'] : $response['img_src'];
   1691 
   1692 			$notice           .= '<div class="alignright browser-icon"><img src="' . esc_attr( $img_src ) . '" alt="" /></div>';
   1693 			$browser_nag_class = ' has-browser-icon';
   1694 		}
   1695 		$notice .= "<p class='browser-update-nag{$browser_nag_class}'>{$msg}</p>";
   1696 
   1697 		$browsehappy = 'https://browsehappy.com/';
   1698 		$locale      = get_user_locale();
   1699 		if ( 'en_US' !== $locale ) {
   1700 			$browsehappy = add_query_arg( 'locale', $locale, $browsehappy );
   1701 		}
   1702 
   1703 		if ( $is_IE ) {
   1704 			$msg_browsehappy = sprintf(
   1705 				/* translators: %s: Browse Happy URL. */
   1706 				__( 'Learn how to <a href="%s" class="update-browser-link">browse happy</a>' ),
   1707 				esc_url( $browsehappy )
   1708 			);
   1709 		} else {
   1710 			$msg_browsehappy = sprintf(
   1711 				/* translators: 1: Browser update URL, 2: Browser name, 3: Browse Happy URL. */
   1712 				__( '<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>' ),
   1713 				esc_attr( $response['update_url'] ),
   1714 				esc_html( $response['name'] ),
   1715 				esc_url( $browsehappy )
   1716 			);
   1717 		}
   1718 
   1719 		$notice .= '<p>' . $msg_browsehappy . '</p>';
   1720 		$notice .= '<p class="hide-if-no-js"><a href="" class="dismiss" aria-label="' . esc_attr__( 'Dismiss the browser warning panel' ) . '">' . __( 'Dismiss' ) . '</a></p>';
   1721 		$notice .= '<div class="clear"></div>';
   1722 	}
   1723 
   1724 	/**
   1725 	 * Filters the notice output for the 'Browse Happy' nag meta box.
   1726 	 *
   1727 	 * @since 3.2.0
   1728 	 *
   1729 	 * @param string $notice   The notice content.
   1730 	 * @param array  $response An array containing web browser information. See `wp_check_browser_version()`.
   1731 	 */
   1732 	echo apply_filters( 'browse-happy-notice', $notice, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   1733 }
   1734 
   1735 /**
   1736  * Adds an additional class to the browser nag if the current version is insecure.
   1737  *
   1738  * @since 3.2.0
   1739  *
   1740  * @param string[] $classes Array of meta box classes.
   1741  * @return string[] Modified array of meta box classes.
   1742  */
   1743 function dashboard_browser_nag_class( $classes ) {
   1744 	$response = wp_check_browser_version();
   1745 
   1746 	if ( $response && $response['insecure'] ) {
   1747 		$classes[] = 'browser-insecure';
   1748 	}
   1749 
   1750 	return $classes;
   1751 }
   1752 
   1753 /**
   1754  * Checks if the user needs a browser update.
   1755  *
   1756  * @since 3.2.0
   1757  *
   1758  * @return array|false Array of browser data on success, false on failure.
   1759  */
   1760 function wp_check_browser_version() {
   1761 	if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
   1762 		return false;
   1763 	}
   1764 
   1765 	$key = md5( $_SERVER['HTTP_USER_AGENT'] );
   1766 
   1767 	$response = get_site_transient( 'browser_' . $key );
   1768 
   1769 	if ( false === $response ) {
   1770 		// Include an unmodified $wp_version.
   1771 		require ABSPATH . WPINC . '/version.php';
   1772 
   1773 		$url     = 'http://api.wordpress.org/core/browse-happy/1.1/';
   1774 		$options = array(
   1775 			'body'       => array( 'useragent' => $_SERVER['HTTP_USER_AGENT'] ),
   1776 			'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
   1777 		);
   1778 
   1779 		if ( wp_http_supports( array( 'ssl' ) ) ) {
   1780 			$url = set_url_scheme( $url, 'https' );
   1781 		}
   1782 
   1783 		$response = wp_remote_post( $url, $options );
   1784 
   1785 		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
   1786 			return false;
   1787 		}
   1788 
   1789 		/**
   1790 		 * Response should be an array with:
   1791 		 *  'platform' - string - A user-friendly platform name, if it can be determined
   1792 		 *  'name' - string - A user-friendly browser name
   1793 		 *  'version' - string - The version of the browser the user is using
   1794 		 *  'current_version' - string - The most recent version of the browser
   1795 		 *  'upgrade' - boolean - Whether the browser needs an upgrade
   1796 		 *  'insecure' - boolean - Whether the browser is deemed insecure
   1797 		 *  'update_url' - string - The url to visit to upgrade
   1798 		 *  'img_src' - string - An image representing the browser
   1799 		 *  'img_src_ssl' - string - An image (over SSL) representing the browser
   1800 		 */
   1801 		$response = json_decode( wp_remote_retrieve_body( $response ), true );
   1802 
   1803 		if ( ! is_array( $response ) ) {
   1804 			return false;
   1805 		}
   1806 
   1807 		set_site_transient( 'browser_' . $key, $response, WEEK_IN_SECONDS );
   1808 	}
   1809 
   1810 	return $response;
   1811 }
   1812 
   1813 /**
   1814  * Displays the PHP update nag.
   1815  *
   1816  * @since 5.1.0
   1817  */
   1818 function wp_dashboard_php_nag() {
   1819 	$response = wp_check_php_version();
   1820 
   1821 	if ( ! $response ) {
   1822 		return;
   1823 	}
   1824 
   1825 	if ( isset( $response['is_secure'] ) && ! $response['is_secure'] ) {
   1826 		$msg = sprintf(
   1827 			/* translators: %s: The server PHP version. */
   1828 			__( 'Your site is running an insecure version of PHP (%s), which should be updated.' ),
   1829 			PHP_VERSION
   1830 		);
   1831 	} else {
   1832 		$msg = sprintf(
   1833 			/* translators: %s: The server PHP version. */
   1834 			__( 'Your site is running an outdated version of PHP (%s), which should be updated.' ),
   1835 			PHP_VERSION
   1836 		);
   1837 	}
   1838 	?>
   1839 	<p><?php echo $msg; ?></p>
   1840 
   1841 	<h3><?php _e( 'What is PHP and how does it affect my site?' ); ?></h3>
   1842 	<p>
   1843 		<?php
   1844 		printf(
   1845 			/* translators: %s: The minimum recommended PHP version. */
   1846 			__( '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&#8217;s performance. The minimum recommended version of PHP is %s.' ),
   1847 			$response ? $response['recommended_version'] : ''
   1848 		);
   1849 		?>
   1850 	</p>
   1851 
   1852 	<p class="button-container">
   1853 		<?php
   1854 		printf(
   1855 			'<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>',
   1856 			esc_url( wp_get_update_php_url() ),
   1857 			__( 'Learn more about updating PHP' ),
   1858 			/* translators: Accessibility text. */
   1859 			__( '(opens in a new tab)' )
   1860 		);
   1861 		?>
   1862 	</p>
   1863 	<?php
   1864 
   1865 	wp_update_php_annotation();
   1866 	wp_direct_php_update_button();
   1867 }
   1868 
   1869 /**
   1870  * Adds an additional class to the PHP nag if the current version is insecure.
   1871  *
   1872  * @since 5.1.0
   1873  *
   1874  * @param string[] $classes Array of meta box classes.
   1875  * @return string[] Modified array of meta box classes.
   1876  */
   1877 function dashboard_php_nag_class( $classes ) {
   1878 	$response = wp_check_php_version();
   1879 
   1880 	if ( $response && isset( $response['is_secure'] ) && ! $response['is_secure'] ) {
   1881 		$classes[] = 'php-insecure';
   1882 	}
   1883 
   1884 	return $classes;
   1885 }
   1886 
   1887 /**
   1888  * Displays the Site Health Status widget.
   1889  *
   1890  * @since 5.4.0
   1891  */
   1892 function wp_dashboard_site_health() {
   1893 	$get_issues = get_transient( 'health-check-site-status-result' );
   1894 
   1895 	$issue_counts = array();
   1896 
   1897 	if ( false !== $get_issues ) {
   1898 		$issue_counts = json_decode( $get_issues, true );
   1899 	}
   1900 
   1901 	if ( ! is_array( $issue_counts ) || ! $issue_counts ) {
   1902 		$issue_counts = array(
   1903 			'good'        => 0,
   1904 			'recommended' => 0,
   1905 			'critical'    => 0,
   1906 		);
   1907 	}
   1908 
   1909 	$issues_total = $issue_counts['recommended'] + $issue_counts['critical'];
   1910 	?>
   1911 	<div class="health-check-widget">
   1912 		<div class="health-check-widget-title-section site-health-progress-wrapper loading hide-if-no-js">
   1913 			<div class="site-health-progress">
   1914 				<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">
   1915 					<circle r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
   1916 					<circle id="bar" r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
   1917 				</svg>
   1918 			</div>
   1919 			<div class="site-health-progress-label">
   1920 				<?php if ( false === $get_issues ) : ?>
   1921 					<?php _e( 'No information yet&hellip;' ); ?>
   1922 				<?php else : ?>
   1923 					<?php _e( 'Results are still loading&hellip;' ); ?>
   1924 				<?php endif; ?>
   1925 			</div>
   1926 		</div>
   1927 
   1928 		<div class="site-health-details">
   1929 			<?php if ( false === $get_issues ) : ?>
   1930 				<p>
   1931 					<?php
   1932 					printf(
   1933 						/* translators: %s: URL to Site Health screen. */
   1934 						__( '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.' ),
   1935 						esc_url( admin_url( 'site-health.php' ) )
   1936 					);
   1937 					?>
   1938 				</p>
   1939 			<?php else : ?>
   1940 				<p>
   1941 					<?php if ( $issues_total <= 0 ) : ?>
   1942 						<?php _e( 'Great job! Your site currently passes all site health checks.' ); ?>
   1943 					<?php elseif ( 1 === (int) $issue_counts['critical'] ) : ?>
   1944 						<?php _e( 'Your site has a critical issue that should be addressed as soon as possible to improve its performance and security.' ); ?>
   1945 					<?php elseif ( $issue_counts['critical'] > 1 ) : ?>
   1946 						<?php _e( 'Your site has critical issues that should be addressed as soon as possible to improve its performance and security.' ); ?>
   1947 					<?php elseif ( 1 === (int) $issue_counts['recommended'] ) : ?>
   1948 						<?php _e( 'Your site&#8217;s health is looking good, but there is still one thing you can do to improve its performance and security.' ); ?>
   1949 					<?php else : ?>
   1950 						<?php _e( 'Your site&#8217;s health is looking good, but there are still some things you can do to improve its performance and security.' ); ?>
   1951 					<?php endif; ?>
   1952 				</p>
   1953 			<?php endif; ?>
   1954 
   1955 			<?php if ( $issues_total > 0 && false !== $get_issues ) : ?>
   1956 				<p>
   1957 					<?php
   1958 					printf(
   1959 						/* translators: 1: Number of issues. 2: URL to Site Health screen. */
   1960 						_n(
   1961 							'Take a look at the <strong>%1$d item</strong> on the <a href="%2$s">Site Health screen</a>.',
   1962 							'Take a look at the <strong>%1$d items</strong> on the <a href="%2$s">Site Health screen</a>.',
   1963 							$issues_total
   1964 						),
   1965 						$issues_total,
   1966 						esc_url( admin_url( 'site-health.php' ) )
   1967 					);
   1968 					?>
   1969 				</p>
   1970 			<?php endif; ?>
   1971 		</div>
   1972 	</div>
   1973 
   1974 	<?php
   1975 }
   1976 
   1977 /**
   1978  * Empty function usable by plugins to output empty dashboard widget (to be populated later by JS).
   1979  *
   1980  * @since 2.5.0
   1981  */
   1982 function wp_dashboard_empty() {}
   1983 
   1984 /**
   1985  * Displays a welcome panel to introduce users to WordPress.
   1986  *
   1987  * @since 3.3.0
   1988  */
   1989 function wp_welcome_panel() {
   1990 	?>
   1991 	<div class="welcome-panel-content">
   1992 	<h2><?php _e( 'Welcome to WordPress!' ); ?></h2>
   1993 	<p class="about-description"><?php _e( 'We&#8217;ve assembled some links to get you started:' ); ?></p>
   1994 	<div class="welcome-panel-column-container">
   1995 	<div class="welcome-panel-column">
   1996 		<?php if ( current_user_can( 'customize' ) ) : ?>
   1997 			<h3><?php _e( 'Get Started' ); ?></h3>
   1998 			<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>
   1999 		<?php endif; ?>
   2000 		<a class="button button-primary button-hero hide-if-customize" href="<?php echo esc_url( admin_url( 'themes.php' ) ); ?>"><?php _e( 'Customize Your Site' ); ?></a>
   2001 		<?php if ( current_user_can( 'install_themes' ) || ( current_user_can( 'switch_themes' ) && count( wp_get_themes( array( 'allowed' => true ) ) ) > 1 ) ) : ?>
   2002 			<?php $themes_link = current_user_can( 'customize' ) ? add_query_arg( 'autofocus[panel]', 'themes', admin_url( 'customize.php' ) ) : admin_url( 'themes.php' ); ?>
   2003 			<p class="hide-if-no-customize">
   2004 				<?php
   2005 					/* translators: %s: URL to Themes panel in Customizer or Themes screen. */
   2006 					printf( __( 'or, <a href="%s">change your theme completely</a>' ), $themes_link );
   2007 				?>
   2008 			</p>
   2009 		<?php endif; ?>
   2010 	</div>
   2011 	<div class="welcome-panel-column">
   2012 		<h3><?php _e( 'Next Steps' ); ?></h3>
   2013 		<ul>
   2014 		<?php if ( 'page' === get_option( 'show_on_front' ) && ! get_option( 'page_for_posts' ) ) : ?>
   2015 			<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>
   2016 			<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>
   2017 		<?php elseif ( 'page' === get_option( 'show_on_front' ) ) : ?>
   2018 			<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>
   2019 			<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>
   2020 			<li><?php printf( '<a href="%s" class="welcome-icon welcome-write-blog">' . __( 'Add a blog post' ) . '</a>', admin_url( 'post-new.php' ) ); ?></li>
   2021 		<?php else : ?>
   2022 			<li><?php printf( '<a href="%s" class="welcome-icon welcome-write-blog">' . __( 'Write your first blog post' ) . '</a>', admin_url( 'post-new.php' ) ); ?></li>
   2023 			<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>
   2024 			<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>
   2025 		<?php endif; ?>
   2026 			<li><?php printf( '<a href="%s" class="welcome-icon welcome-view-site">' . __( 'View your site' ) . '</a>', home_url( '/' ) ); ?></li>
   2027 		</ul>
   2028 	</div>
   2029 	<div class="welcome-panel-column welcome-panel-last">
   2030 		<h3><?php _e( 'More Actions' ); ?></h3>
   2031 		<ul>
   2032 		<?php if ( current_theme_supports( 'widgets' ) ) : ?>
   2033 			<li><?php printf( '<a href="%s" class="welcome-icon welcome-widgets">' . __( 'Manage widgets' ) . '</a>', admin_url( 'widgets.php' ) ); ?></li>
   2034 		<?php endif; ?>
   2035 		<?php if ( current_theme_supports( 'menus' ) ) : ?>
   2036 			<li><?php printf( '<a href="%s" class="welcome-icon welcome-menus">' . __( 'Manage menus' ) . '</a>', admin_url( 'nav-menus.php' ) ); ?></li>
   2037 		<?php endif; ?>
   2038 		<?php if ( current_user_can( 'manage_options' ) ) : ?>
   2039 			<li><?php printf( '<a href="%s" class="welcome-icon welcome-comments">' . __( 'Turn comments on or off' ) . '</a>', admin_url( 'options-discussion.php' ) ); ?></li>
   2040 		<?php endif; ?>
   2041 			<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>
   2042 		</ul>
   2043 	</div>
   2044 	</div>
   2045 	</div>
   2046 	<?php
   2047 }