To complement @s_ha_dum’s excellent answer, here is an example showing how to use the built-in jQuery UI date picker on your plugin page.
The result will look like this:
The most important parts:
- Use your option page slug to enqueue the scripts and stylesheets on your page only, not on all admin pages (background).
- Make sure to set
datepicker({ dateFormat: "yy-mm-dd" })
, so you know what to expect in your callback handler.
- There is no built-in style for the date picker in WordPress, so you have to enqueue a separate stylesheet. But there is also a nice demo plugin from @helenhousandi with CSS that fits nicely into the core styles.
I built a base class first to have something I can use in other answers too and to keep the actual code for the date picker script specific and simple.
Base class Wpse_Plugin_Options_Page
/**
*
* We do not use the so called Settings API here, because that is way too
* complicated.
* admin-post.php is used instead: simple, clean markup, works.
*/
class Wpse_Plugin_Options_Page
{
protected static $instance = NULL;
protected $slug = \'\';
protected $menu_slug = \'wpse_demo\';
protected $option = \'wpse_option\';
protected $title = \'WPSE Demo\';
protected $styles = array();
protected $scripts = array();
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
public function wp_loaded()
{
add_action(
"admin_post_update_$this->option",
array ( $this, \'admin_post\' )
);
add_action(
\'admin_menu\',
array ( $this, \'admin_menu\' )
);
}
public function admin_menu()
{
$slug = add_options_page(
$this->title, // page title
$this->title, // menu title
\'manage_options\', // capability
$this->menu_slug, // menu slug
array ( $this, \'render_page_base\' ) // callback function
);
$this->slug = $slug;
add_action( "admin_print_styles-$slug", array ( $this, \'enqueue_style\' ) );
add_action( "admin_print_scripts-$slug", array ( $this, \'enqueue_script\' ) );
add_action( "page_content_$slug", array ( $this, \'render_page_content\' ) );
}
public function render_page_base()
{
$this->print_message();
printf(
\'<div class="wrap"><h2>%1$s</h2><form method="post" action="%2$s">\',
$GLOBALS[\'title\'],
admin_url( \'admin-post.php\' )
);
printf(
\'<input type="hidden" name="action" value="%s"/>\',
"update_$this->option"
);
wp_nonce_field( "update_$this->option" );
do_action( \'page_content_\' . $this->slug );
print \'</form></div>\';
}
protected function print_message()
{
if ( ! isset ( $_GET[\'msg\'] ) )
return;
$text = $this->get_message_text( $_GET[\'msg\'] );
if ( ! $text )
return;
print "<div id=\'message\' class=\'updated fade\'><p>$text</p></div>";
}
protected function get_message_text( $id )
{
$messages = $this->get_messages();
if ( isset ( $messages[ $id ] ) )
return $messages[ $id ];
return FALSE;
}
protected function get_messages()
{
return array();
}
public function render_page_content()
{
echo $this->slug;
}
public function enqueue_style()
{
foreach ( $this->styles as $style )
wp_enqueue_style( $style );
do_action( \'base_styles_loaded_\' . $this->slug );
}
public function enqueue_script()
{
foreach ( $this->scripts as $script )
wp_enqueue_script( $script );
do_action( \'base_scripts_loaded_\' . $this->slug );
}
public function admin_post()
{
if ( ! check_admin_referer( "update_$this->option" ) )
die( \'nope\' );
if ( ! isset ( $_POST[ $this->option ] ) )
die( \'something is missing\' );
$msg = $this->save_option( $_POST[ $this->option ] );
$url = add_query_arg( \'msg\', $msg, $_POST[ \'_wp_http_referer\' ] );
wp_safe_redirect( $url, 303 );
exit;
}
protected function save_option( $data )
{
return (bool) update_option( $this->option, $data );
}
}
Now we have to redefine only the most important pieces. Nice and short.
Special class Wpse_Datepicker_Example
class Wpse_Datepicker_Example extends Wpse_Plugin_Options_Page
{
protected $title = \'jQuery Date Picker\';
protected $menu_slug = \'wpse_datepicker\';
protected $option = \'wpse_datepicker\';
protected $scripts = array ( \'jquery-ui-datepicker\' );
// not inherited
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
public function render_page_content()
{
$value = esc_attr( get_option( $this->option ) );
printf(
\'<p style="margin:100px auto;width:30em"><label for="%1$s">Pick a date
<input type="text" id="%1$s" name="%2$s" value="%3$s" />
</label> %4$s</p>\',
\'datepicker\',
$this->option,
$value,
get_submit_button( \'Save\', \'primary\', \'submit\', FALSE )
);
add_action(
"admin_footer-$this->slug",
array ( $this, \'print_footer_script\' )
);
}
public function enqueue_style()
{
wp_register_style(
\'jquery-ui-datepicker\',
\'http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/themes/pepper-grinder/jquery-ui.min.css\'
);
wp_enqueue_style( \'jquery-ui-datepicker\' );
}
public function print_footer_script()
{
?>
<script>
jQuery( "#datepicker" ).datepicker({ dateFormat: "yy-mm-dd" });
</script>
<?php
}
protected function get_messages()
{
return array (
1 => \'Date saved.\'
);
}
}
There is still much room for improvements, but as a start it should be useful.