* WordPress Dashboard Widget Administration Screen API
* @subpackage Administration
* Registers dashboard widgets.
* Handles POST data, sets up filters.
* @global array $wp_registered_widgets
* @global array $wp_registered_widget_controls
* @global callable[] $wp_dashboard_control_callbacks
function wp_dashboard_setup() {
global $wp_registered_widgets, $wp_registered_widget_controls, $wp_dashboard_control_callbacks;
$wp_dashboard_control_callbacks = array();
$screen = get_current_screen();
/* Register Widgets and Controls */
$response = wp_check_browser_version();
if ( $response && $response['upgrade'] ) {
add_filter( 'postbox_classes_dashboard_dashboard_browser_nag', 'dashboard_browser_nag_class' );
if ( $response['insecure'] ) {
wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'You are using an insecure browser!' ), 'wp_dashboard_browser_nag' );
wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'Your browser is out of date!' ), 'wp_dashboard_browser_nag' );
$response = wp_check_php_version();
if ( $response && isset( $response['is_acceptable'] ) && ! $response['is_acceptable'] && current_user_can( 'update_php' ) ) {
add_filter( 'postbox_classes_dashboard_dashboard_php_nag', 'dashboard_php_nag_class' );
wp_add_dashboard_widget( 'dashboard_php_nag', __( 'PHP Update Recommended' ), 'wp_dashboard_php_nag' );
if ( current_user_can( 'view_site_health_checks' ) && ! is_network_admin() ) {
if ( ! class_exists( 'WP_Site_Health' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
WP_Site_Health::get_instance();
wp_enqueue_style( 'site-health' );
wp_enqueue_script( 'site-health' );
wp_add_dashboard_widget( 'dashboard_site_health', __( 'Site Health Status' ), 'wp_dashboard_site_health' );
if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) {
wp_add_dashboard_widget( 'dashboard_right_now', __( 'At a Glance' ), 'wp_dashboard_right_now' );
if ( is_network_admin() ) {
wp_add_dashboard_widget( 'network_dashboard_right_now', __( 'Right Now' ), 'wp_network_dashboard_right_now' );
wp_add_dashboard_widget( 'dashboard_activity', __( 'Activity' ), 'wp_dashboard_site_activity' );
if ( is_blog_admin() && current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) {
$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' ) );
wp_add_dashboard_widget( 'dashboard_quick_press', $quick_draft_title, 'wp_dashboard_quick_press' );
// WordPress Events and News.
wp_add_dashboard_widget( 'dashboard_primary', __( 'WordPress Events and News' ), 'wp_dashboard_events_news' );
if ( is_network_admin() ) {
* Fires after core widgets for the Network Admin dashboard have been registered.
do_action( 'wp_network_dashboard_setup' );
* Filters the list of widgets to load for the Network Admin dashboard.
* @param string[] $dashboard_widgets An array of dashboard widget IDs.
$dashboard_widgets = apply_filters( 'wp_network_dashboard_widgets', array() );
} elseif ( is_user_admin() ) {
* Fires after core widgets for the User Admin dashboard have been registered.
do_action( 'wp_user_dashboard_setup' );
* Filters the list of widgets to load for the User Admin dashboard.
* @param string[] $dashboard_widgets An array of dashboard widget IDs.
$dashboard_widgets = apply_filters( 'wp_user_dashboard_widgets', array() );
* Fires after core widgets for the admin dashboard have been registered.
do_action( 'wp_dashboard_setup' );
* Filters the list of widgets to load for the admin dashboard.
* @param string[] $dashboard_widgets An array of dashboard widget IDs.
$dashboard_widgets = apply_filters( 'wp_dashboard_widgets', array() );
foreach ( $dashboard_widgets as $widget_id ) {
$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>';
wp_add_dashboard_widget( $widget_id, $name, $wp_registered_widgets[ $widget_id ]['callback'], $wp_registered_widget_controls[ $widget_id ]['callback'] );
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['widget_id'] ) ) {
check_admin_referer( 'edit-dashboard-widget_' . $_POST['widget_id'], 'dashboard-widget-nonce' );
ob_start(); // Hack - but the same hack wp-admin/widgets.php uses.
wp_dashboard_trigger_widget_control( $_POST['widget_id'] );
wp_redirect( remove_query_arg( 'edit' ) );
/** This action is documented in wp-admin/includes/meta-boxes.php */
do_action( 'do_meta_boxes', $screen->id, 'normal', '' );
/** This action is documented in wp-admin/includes/meta-boxes.php */
do_action( 'do_meta_boxes', $screen->id, 'side', '' );
* Adds a new dashboard widget.
* @since 5.6.0 The `$context` and `$priority` parameters were added.
* @global callable[] $wp_dashboard_control_callbacks
* @param string $widget_id Widget ID (used in the 'id' attribute for the widget).
* @param string $widget_name Title of the widget.
* @param callable $callback Function that fills the widget with the desired content.
* The function should echo its output.
* @param callable $control_callback Optional. Function that outputs controls for the widget. Default null.
* @param array $callback_args Optional. Data that should be set as the $args property of the widget array
* (which is the second parameter passed to your callback). Default null.
* @param string $context Optional. The context within the screen where the box should display.
* Accepts 'normal', 'side', 'column3', or 'column4'. Default 'normal'.
* @param string $priority Optional. The priority within the context where the box should show.
* Accepts 'high', 'core', 'default', or 'low'. Default 'core'.
function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_callback = null, $callback_args = null, $context = 'normal', $priority = 'core' ) {
$screen = get_current_screen();
global $wp_dashboard_control_callbacks;
$private_callback_args = array( '__widget_basename' => $widget_name );
if ( is_null( $callback_args ) ) {
$callback_args = $private_callback_args;
} elseif ( is_array( $callback_args ) ) {
$callback_args = array_merge( $callback_args, $private_callback_args );
if ( $control_callback && current_user_can( 'edit_dashboard' ) && is_callable( $control_callback ) ) {
$wp_dashboard_control_callbacks[ $widget_id ] = $control_callback;
if ( isset( $_GET['edit'] ) && $widget_id == $_GET['edit'] ) {
list($url) = explode( '#', add_query_arg( 'edit', false ), 2 );
$widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( $url ) . '">' . __( 'Cancel' ) . '</a></span>';
$callback = '_wp_dashboard_control_callback';
list($url) = explode( '#', add_query_arg( 'edit', $widget_id ), 2 );
$widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( "$url#$widget_id" ) . '" class="edit-box open-box">' . __( 'Configure' ) . '</a></span>';
$side_widgets = array( 'dashboard_quick_press', 'dashboard_primary' );
if ( in_array( $widget_id, $side_widgets, true ) ) {
$high_priority_widgets = array( 'dashboard_browser_nag', 'dashboard_php_nag' );
if ( in_array( $widget_id, $high_priority_widgets, true ) ) {
if ( empty( $context ) ) {
if ( empty( $priority ) ) {
add_meta_box( $widget_id, $widget_name, $callback, $screen, $context, $priority, $callback_args );
* Outputs controls for the current dashboard widget.
* @param mixed $dashboard
function _wp_dashboard_control_callback( $dashboard, $meta_box ) {
echo '<form method="post" class="dashboard-widget-control-form wp-clearfix">';
wp_dashboard_trigger_widget_control( $meta_box['id'] );
wp_nonce_field( 'edit-dashboard-widget_' . $meta_box['id'], 'dashboard-widget-nonce' );
echo '<input type="hidden" name="widget_id" value="' . esc_attr( $meta_box['id'] ) . '" />';
submit_button( __( 'Submit' ) );
* Displays the dashboard.
function wp_dashboard() {
$screen = get_current_screen();
$columns = absint( $screen->get_columns() );
$columns_css = " columns-$columns";
<div id="dashboard-widgets" class="metabox-holder<?php echo $columns_css; ?>">
<div id="postbox-container-1" class="postbox-container">
<?php do_meta_boxes( $screen->id, 'normal', '' ); ?>
<div id="postbox-container-2" class="postbox-container">
<?php do_meta_boxes( $screen->id, 'side', '' ); ?>
<div id="postbox-container-3" class="postbox-container">
<?php do_meta_boxes( $screen->id, 'column3', '' ); ?>
<div id="postbox-container-4" class="postbox-container">
<?php do_meta_boxes( $screen->id, 'column4', '' ); ?>
wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
* Dashboard widget that displays some basic stats about the site.
* Formerly 'Right Now'. A streamlined 'At a Glance' as of 3.8.
function wp_dashboard_right_now() {
foreach ( array( 'post', 'page' ) as $post_type ) {
$num_posts = wp_count_posts( $post_type );
if ( $num_posts && $num_posts->publish ) {
if ( 'post' === $post_type ) {
/* translators: %s: Number of posts. */
$text = _n( '%s Post', '%s Posts', $num_posts->publish );
/* translators: %s: Number of pages. */
$text = _n( '%s Page', '%s Pages', $num_posts->publish );
$text = sprintf( $text, number_format_i18n( $num_posts->publish ) );
$post_type_object = get_post_type_object( $post_type );
if ( $post_type_object && current_user_can( $post_type_object->cap->edit_posts ) ) {
printf( '<li class="%1$s-count"><a href="edit.php?post_type=%1$s">%2$s</a></li>', $post_type, $text );
printf( '<li class="%1$s-count"><span>%2$s</span></li>', $post_type, $text );
$num_comm = wp_count_comments();
if ( $num_comm && ( $num_comm->approved || $num_comm->moderated ) ) {
/* translators: %s: Number of comments. */
$text = sprintf( _n( '%s Comment', '%s Comments', $num_comm->approved ), number_format_i18n( $num_comm->approved ) );
<li class="comment-count"><a href="edit-comments.php"><?php echo $text; ?></a></li>
$moderated_comments_count_i18n = number_format_i18n( $num_comm->moderated );
/* translators: %s: Number of comments. */
$text = sprintf( _n( '%s Comment in moderation', '%s Comments in moderation', $num_comm->moderated ), $moderated_comments_count_i18n );
<li class="comment-mod-count
if ( ! $num_comm->moderated ) {
"><a href="edit-comments.php?comment_status=moderated" class="comments-in-moderation-text"><?php echo $text; ?></a></li>
* Filters the array of extra elements to list in the 'At a Glance'
* Prior to 3.8.0, the widget was named 'Right Now'. Each element
* is wrapped in list-item tags on output.
* @param string[] $items Array of extra 'At a Glance' widget items.
$elements = apply_filters( 'dashboard_glance_items', array() );
echo '<li>' . implode( "</li>\n<li>", $elements ) . "</li>\n";
update_right_now_message();
// Check if search engines are asked not to index this site.
if ( ! is_network_admin() && ! is_user_admin() && current_user_can( 'manage_options' ) && '0' == get_option( 'blog_public' ) ) {
* Filters the link title attribute for the 'Search engines discouraged'
* message displayed in the 'At a Glance' dashboard widget.
* Prior to 3.8.0, the widget was named 'Right Now'.
* @since 4.5.0 The default for `$title` was updated to an empty string.
* @param string $title Default attribute text.
$title = apply_filters( 'privacy_on_link_title', '' );
* Filters the link label for the 'Search engines discouraged' message
* displayed in the 'At a Glance' dashboard widget.
* Prior to 3.8.0, the widget was named 'Right Now'.
* @param string $content Default text.
$content = apply_filters( 'privacy_on_link_text', __( 'Search engines discouraged' ) );
$title_attr = '' === $title ? '' : " title='$title'";
echo "<p class='search-engines-info'><a href='options-reading.php'$title_attr>$content</a></p>";
* activity_box_end has a core action, but only prints content when multisite.
* Using an output buffer is the only way to really check if anything's displayed here.
* Fires at the end of the 'At a Glance' dashboard widget.
* Prior to 3.8.0, the widget was named 'Right Now'.
do_action( 'rightnow_end' );
* Fires at the end of the 'At a Glance' dashboard widget.
* Prior to 3.8.0, the widget was named 'Right Now'.
do_action( 'activity_box_end' );
$actions = ob_get_clean();
if ( ! empty( $actions ) ) :
function wp_network_dashboard_right_now() {
if ( current_user_can( 'create_sites' ) ) {
$actions['create-site'] = '<a href="' . network_admin_url( 'site-new.php' ) . '">' . __( 'Create a New Site' ) . '</a>';
if ( current_user_can( 'create_users' ) ) {
$actions['create-user'] = '<a href="' . network_admin_url( 'user-new.php' ) . '">' . __( 'Create a New User' ) . '</a>';
$c_users = get_user_count();
$c_blogs = get_blog_count();
/* translators: %s: Number of users on the network. */
$user_text = sprintf( _n( '%s user', '%s users', $c_users ), number_format_i18n( $c_users ) );
/* translators: %s: Number of sites on the network. */
$blog_text = sprintf( _n( '%s site', '%s sites', $c_blogs ), number_format_i18n( $c_blogs ) );
/* translators: 1: Text indicating the number of sites on the network, 2: Text indicating the number of users on the network. */
$sentence = sprintf( __( 'You have %1$s and %2$s.' ), $blog_text, $user_text );
echo '<ul class="subsubsub">';
foreach ( $actions as $class => $action ) {
$actions[ $class ] = "\t<li class='$class'>$action";
echo implode( " |</li>\n", $actions ) . "</li>\n";
<p class="youhave"><?php echo $sentence; ?></p>
* Fires in the Network Admin 'Right Now' dashboard widget
* just before the user and site search form fields.
do_action( 'wpmuadminresult' );
<form action="<?php echo network_admin_url( 'users.php' ); ?>" method="get">
<label class="screen-reader-text" for="search-users"><?php _e( 'Search Users' ); ?></label>
<input type="search" name="s" value="" size="30" autocomplete="off" id="search-users"/>
<?php submit_button( __( 'Search Users' ), '', false, false, array( 'id' => 'submit_users' ) ); ?>
<form action="<?php echo network_admin_url( 'sites.php' ); ?>" method="get">
<label class="screen-reader-text" for="search-sites"><?php _e( 'Search Sites' ); ?></label>
<input type="search" name="s" value="" size="30" autocomplete="off" id="search-sites"/>
<?php submit_button( __( 'Search Sites' ), '', false, false, array( 'id' => 'submit_sites' ) ); ?>
* Fires at the end of the 'Right Now' widget in the Network Admin dashboard.
do_action( 'mu_rightnow_end' );
* Fires at the end of the 'Right Now' widget in the Network Admin dashboard.
do_action( 'mu_activity_box_end' );