Almost every part of the WordPress core can be modified using hooks. They can be added to your plugin or a theme and can be used to change how WordPress displays data. If you are new to hooks, here is a whole article about them explaining how they interact with WordPress. We’ll use hooks as well to modify the posts list in the admin area. We suggest adding this functionality to a plugin.

Let’s get started.

Hooks we need

  • restrict_manage_posts
  • pre_get_posts
  • admin_enqueue_scripts

We’ll use the restrict_manage_posts to create the field for filtering. The second hook we’ll be used for filtering the posts, and the third one we’ll be used to include the jquery-ui-datepicker script because it is not automatically included in the admin area.

If successful, the posts list should look something like this:

The code

In your main plugin or a theme file add these hooks (it is up to you how you would like to structure your plugin):

<?php

add_action( 'restrict_manage_posts', 'form_select' );
add_action( 'pre_get_posts', 'query_filter' );
add_action( 'admin_enqueue_scripts', 'jqueryui' );

Create a function (or a method if you are using a class) for each callback:

<?php

function form_select() {
    $post_type = (isset($_GET['post_type'])) ? $_GET['post_type'] : 'post';

    if ($post_type == 'post') {
	// please use unique name
	$from = ( isset( $_GET['pe_date'] ) && $_GET['pe_date'] ) ? $_GET['pe_date'] : '';

	echo '<input type="text" name="pe_date" placeholder="Date From" value="' . esc_attr( $from ) . '" />
		
		   <script>
		      jQuery( function($) {
			  let from = $(\'input[name="pe_date"]\');

			  // the dates look like this "April 3, 2017" by default
			  // you can schoose something different, for example
			  from.datepicker( {dateFormat : "yy-mm-dd"} );
			
		     });
		   </script>';
     } // end if
}

You can include the filter in any post type you want.

function query_filter( $admin_query ) {
	global $pagenow;
	$post_type = (isset($_GET['post_type'])) ? $_GET['post_type'] : 'post';

	if ( $post_type == 'post' && $pagenow == 'edit.php' && isset( $_GET['pe_date'] ) && !empty( $_GET['pe_date'] ) ) {
		$admin_query->set(
			'date_query', // date_query appeared in WordPress 3.7!
			array(
				'after' => sanitize_text_field( $_GET['pe_date'] ),
				'before' => sanitize_text_field( $_GET['pe_date'] ),
				'inclusive' => true, // include the selected days as well
				'column' => 'post_date' // 'post_modified', 'post_date_gmt', 'post_modified_gmt'
			)
		);
	}

	return $admin_query;
}

Do the filtering by date only if the custom post type is post, if we are on the edit page, and if the pe_date field is not empty.

<?php

function jqueryui () {
	wp_enqueue_style( 'pe-jquery-ui', '//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.min.css' );
	wp_enqueue_script( 'jquery-ui-datepicker' );
}

Include the script and the styling for the datepicker.

This is it. Now, let’s make things more interesting and add another filter that will display the posts by status.

Tweak the form_select function like this:

function form_select() {
	$post_type = ( isset($_GET['post_type']) ) ? $_GET['post_type'] : 'post';

	if ($post_type == 'post') {
		// please use unique name
		$from = ( isset( $_GET['pe_date'] ) && $_GET['pe_date'] ) ? $_GET['pe_date'] : '';
		$current_status = $_GET['pe_status'] ?? '';
		$statuses = get_post_statuses();

		echo '<input type="text" name="pe_date" placeholder="Date From" value="' . esc_attr( $from ) . '" />';

		?>

		<select name="pe_status">
			<option value=""><?= __('All statuses', 'projectsengine'); ?></option>

			<?php

			foreach ($statuses as $key => $status) {
				printf(
					'<option value="%s"%s>%s</option>',
					$key,
					$key == $current_status ? ' selected="selected"' : '',
					$status
				);
			}
			?>
		</select>

		<?php


		echo '<script>
		jQuery( function($) {
			let from = $(\'input[name="pe_date"]\');
	
			// the dates look like this "April 3, 2017" by default
			// you can schoose something different, for example
			from.datepicker( {dateFormat : "yy/mm/dd"} );
			
		});
		</script>';
	}
}

get_post_statuses() will return all of the statuses that are currently registered on your website. Add them as options to a select field.

Tweak the query_filter function like this:

function query_filter( $admin_query ) {
	global $pagenow;
	$post_type = (isset($_GET['post_type'])) ? $_GET['post_type'] : 'post';

	if ( $post_type == 'post' && $pagenow == 'edit.php' ) {
		if ( isset( $_GET['pe_status'] ) && ! empty( $_GET['pe_status'] ) ) {
			$admin_query->set('post_status', $_GET['pe_status'] );
		}

		if ( isset( $_GET['pe_date'] ) && ! empty( $_GET['pe_date'] ) ) {
			$admin_query->set(
				'date_query', // date_query appeared in WordPress 3.7!
				array(
					'after'     => sanitize_text_field( $_GET['pe_date'] ),
					'before'    => sanitize_text_field( $_GET['pe_date'] ),
					'inclusive' => true, // include the selected days as well
					'column'    => 'post_date' // 'post_modified', 'post_date_gmt', 'post_modified_gmt'
				)
			);
		}
	}

	return $admin_query;
}

We’ll modify the posts query only if the fields/filters are not empty.