Editorial Calendar Screen
Adds a Posts > Calendar screen showing a month grid of published, scheduled and draft posts on their dates, with links to edit each one and arrows to move between months.
Opens WPIDE on
Code
add_action( 'admin_menu', function () {
add_posts_page( 'Editorial Calendar', 'Calendar', 'edit_posts', 'wpide-calendar', function () {
$month = isset( $_GET['month'] ) && preg_match( '/^\d{4}-\d{2}$/', $_GET['month'] ) ? sanitize_text_field( wp_unslash( $_GET['month'] ) ) : wp_date( 'Y-m' );
$first = new DateTimeImmutable( $month . '-01', wp_timezone() );
$last = $first->modify( 'last day of this month' );
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => array( 'publish', 'future', 'draft', 'pending' ),
'posts_per_page' => 300,
'no_found_rows' => true,
'date_query' => array( array( 'after' => $first->format( 'Y-m-d 00:00:00' ), 'before' => $last->format( 'Y-m-d 23:59:59' ), 'inclusive' => true ) ),
) );
$by_day = array();
foreach ( $posts as $post ) {
$by_day[ (int) mysql2date( 'j', $post->post_date ) ][] = $post;
}
$colors = array( 'publish' => '#00a32a', 'future' => '#2271b1', 'draft' => '#8c8f94', 'pending' => '#dba617' );
$nav = function ( $offset, $label ) use ( $first ) {
return sprintf( '<a class="button" href="%s">%s</a>', esc_url( add_query_arg( array( 'page' => 'wpide-calendar', 'month' => $first->modify( $offset . ' month' )->format( 'Y-m' ) ), admin_url( 'edit.php' ) ) ), esc_html( $label ) );
};
echo '<div class="wrap"><h1>Editorial Calendar</h1>';
printf( '<p style="display:flex;gap:8px;align-items:center">%s <strong style="font-size:16px">%s</strong> %s</p>', $nav( -1, '← Previous' ), esc_html( wp_date( 'F Y', $first->getTimestamp() ) ), $nav( 1, 'Next →' ) );
echo '<div style="display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:4px">';
$start_of_week = (int) get_option( 'start_of_week', 1 );
for ( $i = 0; $i < 7; $i++ ) {
printf( '<div style="font-weight:600;padding:4px">%s</div>', esc_html( $GLOBALS['wp_locale']->get_weekday_abbrev( $GLOBALS['wp_locale']->get_weekday( ( $start_of_week + $i ) % 7 ) ) ) );
}
$blank = ( (int) $first->format( 'w' ) - $start_of_week + 7 ) % 7;
echo str_repeat( '<div></div>', $blank );
for ( $day = 1; $day <= (int) $last->format( 'j' ); $day++ ) {
echo '<div style="background:#fff;border:1px solid #dcdcde;min-height:90px;padding:4px;overflow:hidden"><div style="color:#646970">' . (int) $day . '</div>';
foreach ( $by_day[ $day ] ?? array() as $post ) {
printf(
'<a href="%s" style="display:block;font-size:12px;margin-top:3px;padding:2px 4px;border-left:3px solid %s;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="%s">%s</a>',
esc_url( get_edit_post_link( $post ) ),
esc_attr( $colors[ $post->post_status ] ?? '#8c8f94' ),
esc_attr( get_the_title( $post ) . ' (' . $post->post_status . ')' ),
esc_html( get_the_title( $post ) ? get_the_title( $post ) : '(no title)' )
);
}
echo '</div>';
}
echo '</div></div>';
} );
} );
Notes
Drafts appear on their last modified date in grey; scheduled posts in blue.
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.