Dashboard Widget Generator
The widget your client actually looks at: gated by capability, filled by a real callback, and — optionally — the calls that clear the core boxes around it.
The widget
Left column, high priority — until a user drags it somewhere else, which their profile then remembers.
What it shows
get_posts() with edit links and dates. Drafts and pending posts are the two lists clients actually want.
Clear core widgets
2 will be removed
Extras
Capability gate
A current_user_can() check before the widget registers at all.
Configure form
The Configure link with a nonced save handler.
Force to the top
Rewrites $wp_meta_boxes so this box comes first for everyone.
Network dashboard
Registers on wp_network_dashboard_setup instead of the site dashboard.
// Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Register the dashboard widget. */ function acme_setup() { if ( ! current_user_can( 'edit_posts' ) ) { return; } wp_add_dashboard_widget( 'acme_overview', __( 'Acme Overview', 'acme' ), 'acme_render', null, array(), 'normal', 'high' ); // Clear the core boxes the client does not use. remove_meta_box( 'dashboard_primary', 'dashboard', 'side' ); remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' ); } add_action( 'wp_dashboard_setup', 'acme_setup' ); /** * Print the widget body. */ function acme_render() { $posts = get_posts( array( 'post_type' => 'post', 'post_status' => 'draft', 'numberposts' => 5, 'orderby' => 'date', 'order' => 'DESC', 'suppress_filters' => false, ) ); if ( ! $posts ) { echo '<p>' . esc_html__( 'Nothing here yet.', 'acme' ) . '</p>'; return; } echo '<ul>'; foreach ( $posts as $post ) { printf( '<li><a href="%1$s">%2$s</a> <span class="post-date">%3$s</span></li>', esc_url( get_edit_post_link( $post->ID ) ), esc_html( get_the_title( $post ) ), esc_html( get_the_date( '', $post ) ) ); } echo '</ul>'; }