: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param array $query_args The arguments passed to WP_Query to produce the list of posts.
$query_args = apply_filters( 'dashboard_recent_posts_query_args', $query_args );
$posts = new WP_Query( $query_args );
if ( $posts->have_posts() ) {
echo '<div id="' . $args['id'] . '" class="activity-block">';
echo '<h3>' . $args['title'] . '</h3>';
$today = current_time( 'Y-m-d' );
$tomorrow = current_datetime()->modify( '+1 day' )->format( 'Y-m-d' );
$year = current_time( 'Y' );
while ( $posts->have_posts() ) {
$time = get_the_time( 'U' );
if ( gmdate( 'Y-m-d', $time ) === $today ) {
$relative = __( 'Today' );
} elseif ( gmdate( 'Y-m-d', $time ) === $tomorrow ) {
$relative = __( 'Tomorrow' );
} elseif ( gmdate( 'Y', $time ) !== $year ) {
/* 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 */
$relative = date_i18n( __( 'M jS Y' ), $time );
/* translators: Date and time format for recent posts on the dashboard, see https://www.php.net/manual/datetime.format.php */
$relative = date_i18n( __( 'M jS' ), $time );
// Use the post edit link for those who can edit, the permalink otherwise.
$recent_post_link = current_user_can( 'edit_post', get_the_ID() ) ? get_edit_post_link() : get_permalink();
$draft_or_post_title = _draft_or_post_title();
'<li><span>%1$s</span> <a href="%2$s" aria-label="%3$s">%4$s</a></li>',
/* translators: 1: Relative date, 2: Time. */
sprintf( _x( '%1$s, %2$s', 'dashboard' ), $relative, get_the_time() ),
/* translators: %s: Post title. */
esc_attr( sprintf( __( 'Edit “%s”' ), $draft_or_post_title ) ),
* @param int $total_items Optional. Number of comments to query. Default 5.
* @return bool False if no comments were found. True otherwise.
function wp_dashboard_recent_comments( $total_items = 5 ) {
// Select all comment types and filter out spam later for better query performance.
'number' => $total_items * 5,
if ( ! current_user_can( 'edit_posts' ) ) {
$comments_query['status'] = 'approve';
while ( count( $comments ) < $total_items && $possible = get_comments( $comments_query ) ) {
if ( ! is_array( $possible ) ) {
foreach ( $possible as $comment ) {
if ( ! current_user_can( 'edit_post', $comment->comment_post_ID )
&& ( post_password_required( $comment->comment_post_ID )
|| ! current_user_can( 'read_post', $comment->comment_post_ID ) )
// The user has no access to the post and thus cannot see the comments.
if ( count( $comments ) === $total_items ) {
$comments_query['offset'] += $comments_query['number'];
$comments_query['number'] = $total_items * 10;
echo '<div id="latest-comments" class="activity-block table-view-list">';
echo '<h3>' . __( 'Recent Comments' ) . '</h3>';
echo '<ul id="the-comment-list" data-wp-lists="list:comment">';
foreach ( $comments as $comment ) {
_wp_dashboard_recent_comments_row( $comment );
if ( current_user_can( 'edit_posts' ) ) {
echo '<h3 class="screen-reader-text">' .
/* translators: Hidden accessibility text. */
__( 'View more comments' ) .
_get_list_table( 'WP_Comments_List_Table' )->views();
wp_comment_reply( -1, false, 'dashboard', false );
wp_comment_trashnotice();
* Display generic dashboard RSS widget feed.
* @param string $widget_id
function wp_dashboard_rss_output( $widget_id ) {
$widgets = get_option( 'dashboard_widget_options' );
echo '<div class="rss-widget">';
wp_widget_rss_output( $widgets[ $widget_id ] );
* Checks to see if all of the feed url in $check_urls are cached.
* If $check_urls is empty, look for the rss feed url found in the dashboard
* widget options of $widget_id. If cached, call $callback, a function that
* echoes out output for this widget. If not cache, echo a "Loading..." stub
* which is later replaced by Ajax call (see top of /wp-admin/index.php)
* @since 5.3.0 Formalized the existing and already documented `...$args` parameter
* by adding it to the function signature.
* @param string $widget_id The widget ID.
* @param callable $callback The callback function used to display each feed.
* @param array $check_urls RSS feeds.
* @param mixed ...$args Optional additional parameters to pass to the callback function.
* @return bool True on success, false on failure.
function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array(), ...$args ) {
$doing_ajax = wp_doing_ajax();
$loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading…' ) . '</p>';
$loading .= wp_get_admin_notice(
__( 'This widget requires JavaScript.' ),
'additional_classes' => array( 'inline', 'hide-if-js' ),
if ( empty( $check_urls ) ) {
$widgets = get_option( 'dashboard_widget_options' );
if ( empty( $widgets[ $widget_id ]['url'] ) && ! $doing_ajax ) {
$check_urls = array( $widgets[ $widget_id ]['url'] );
$locale = get_user_locale();
$cache_key = 'dash_v2_' . md5( $widget_id . '_' . $locale );
$output = get_transient( $cache_key );
if ( false !== $output ) {
if ( $callback && is_callable( $callback ) ) {
array_unshift( $args, $widget_id, $check_urls );
call_user_func_array( $callback, $args );
// Default lifetime in cache of 12 hours (same as the feeds).
set_transient( $cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS );
// Dashboard Widgets Controls.
* Calls widget control callback.
* @global callable[] $wp_dashboard_control_callbacks
* @param int|false $widget_control_id Optional. Registered widget ID. Default false.
function wp_dashboard_trigger_widget_control( $widget_control_id = false ) {
global $wp_dashboard_control_callbacks;
if ( is_scalar( $widget_control_id ) && $widget_control_id
&& isset( $wp_dashboard_control_callbacks[ $widget_control_id ] )
&& is_callable( $wp_dashboard_control_callbacks[ $widget_control_id ] )
$wp_dashboard_control_callbacks[ $widget_control_id ],
'id' => $widget_control_id,
'callback' => $wp_dashboard_control_callbacks[ $widget_control_id ],
* Sets up the RSS dashboard widget control and $args to be used as input to wp_widget_rss_form().
* Handles POST data from RSS-type widgets.
* @param string $widget_id
* @param array $form_inputs
function wp_dashboard_rss_control( $widget_id, $form_inputs = array() ) {
$widget_options = get_option( 'dashboard_widget_options' );
if ( ! $widget_options ) {
$widget_options = array();
if ( ! isset( $widget_options[ $widget_id ] ) ) {
$widget_options[ $widget_id ] = array();
$number = 1; // Hack to use wp_widget_rss_form().
$widget_options[ $widget_id ]['number'] = $number;
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['widget-rss'][ $number ] ) ) {
$_POST['widget-rss'][ $number ] = wp_unslash( $_POST['widget-rss'][ $number ] );
$widget_options[ $widget_id ] = wp_widget_rss_process( $_POST['widget-rss'][ $number ] );
$widget_options[ $widget_id ]['number'] = $number;
// Title is optional. If black, fill it if possible.
if ( ! $widget_options[ $widget_id ]['title'] && isset( $_POST['widget-rss'][ $number ]['title'] ) ) {
$rss = fetch_feed( $widget_options[ $widget_id ]['url'] );
if ( is_wp_error( $rss ) ) {
$widget_options[ $widget_id ]['title'] = htmlentities( __( 'Unknown Feed' ) );
$widget_options[ $widget_id ]['title'] = htmlentities( strip_tags( $rss->get_title() ) );
update_option( 'dashboard_widget_options', $widget_options );
$locale = get_user_locale();
$cache_key = 'dash_v2_' . md5( $widget_id . '_' . $locale );
delete_transient( $cache_key );
wp_widget_rss_form( $widget_options[ $widget_id ], $form_inputs );
* Renders the Events and News dashboard widget.
function wp_dashboard_events_news() {
wp_print_community_events_markup();
<div class="wordpress-news hide-if-no-js">
<?php wp_dashboard_primary(); ?>
<p class="community-events-footer">
'<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>',
'https://make.wordpress.org/community/meetups-landing-page',
/* translators: Hidden accessibility text. */
__( '(opens in a new tab)' )
'<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>',
'https://central.wordcamp.org/schedule/',
/* translators: Hidden accessibility text. */
__( '(opens in a new tab)' )
'<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>',
/* translators: If a Rosetta site exists (e.g. https://es.wordpress.org/news/), then use that. Otherwise, leave untranslated. */
esc_url( _x( 'https://wordpress.org/news/', 'Events and News dashboard widget' ) ),
/* translators: Hidden accessibility text. */
__( '(opens in a new tab)' )
* Prints the markup for the Community Events section of the Events and News Dashboard widget.
function wp_print_community_events_markup() {
$community_events_notice = '<p class="hide-if-js">' . ( 'This widget requires JavaScript.' ) . '</p>';
$community_events_notice .= '<p class="community-events-error-occurred" aria-hidden="true">' . __( 'An error occurred. Please try again.' ) . '</p>';
$community_events_notice .= '<p class="community-events-could-not-locate" aria-hidden="true"></p>';
$community_events_notice,
'additional_classes' => array( 'community-events-errors', 'inline', 'hide-if-js' ),
'paragraph_wrap' => false,
* Hide the main element when the page first loads, because the content
* won't be ready until wp.communityEvents.renderEventsTemplate() has run.
<div id="community-events" class="community-events" aria-hidden="true">
<div class="activity-block">
<span id="community-events-location-message"></span>
<button class="button-link community-events-toggle-location" aria-expanded="false">
<span class="dashicons dashicons-location" aria-hidden="true"></span>
<span class="community-events-location-edit"><?php _e( 'Select location' ); ?></span>
<form class="community-events-form" aria-hidden="true" action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" method="post">
<label for="community-events-location">
/* translators: Replace with a city related to your locale.
* Test that it matches the expected location and has upcoming
* events before including it. If no cities related to your
* locale have events, then use a city related to your locale
* that would be recognizable to most users. Use only the city
* name itself, without any region or country. Use the endonym
* (native locale name) instead of the English name if possible.
<input id="community-events-location" class="regular-text" type="text" name="community-events-location" placeholder="<?php esc_attr_e( 'Cincinnati' ); ?>" />
<?php submit_button( __( 'Submit' ), 'secondary', 'community-events-submit', false ); ?>
<button class="community-events-cancel button-link" type="button" aria-expanded="false">
<span class="spinner"></span>
<ul class="community-events-results activity-block last"></ul>
* Renders the events templates for the Event and News widget.
function wp_print_community_events_templates() {
<script id="tmpl-community-events-attend-event-near" type="text/template">
/* translators: %s: The name of a city. */
__( 'Attend an upcoming event near %s.' ),
'<strong>{{ data.location.description }}</strong>'
<script id="tmpl-community-events-could-not-locate" type="text/template">
/* translators: %s is the name of the city we couldn't locate.
* Replace the examples with cities in your locale, but test
* that they match the expected location before including them.
* Use endonyms (native locale names) whenever possible.
__( '%s could not be located. Please try another nearby city. For example: Kansas City; Springfield; Portland.' ),
'<em>{{data.unknownCity}}</em>'
<script id="tmpl-community-events-event-list" type="text/template">
<# _.each( data.events, function( event ) { #>
<li class="event event-{{ event.type }} wp-clearfix">
<div class="dashicons event-icon" aria-hidden="true"></div>
<div class="event-info-inner">
<a class="event-title" href="{{ event.url }}">{{ event.title }}</a>
const titleCaseEventType = event.type.replace(
function ( type ) { return type.charAt(0).toUpperCase() + type.substr(1).toLowerCase(); }
{{ 'wordcamp' === event.type ? 'WordCamp' : titleCaseEventType }}
<span class="ce-separator"></span>
<span class="event-city">{{ event.location.location }}</span>
<div class="event-date-time">
<span class="event-date">{{ event.user_formatted_date }}</span>
<# if ( 'meetup' === event.type ) { #>
<span class="event-time">
{{ event.user_formatted_time }} {{ event.timeZoneAbbreviation }}
<# if ( data.events.length <= 2 ) { #>
/* translators: %s: Localized meetup organization documentation URL. */
__( 'Want more events? <a href="%s">Help organize the next one</a>!' ),
__( 'https://make.wordpress.org/community/organize-event-landing-page/' )
<script id="tmpl-community-events-no-upcoming-events" type="text/template">