Making Custom Content
Management Disappear
into the WordPress Admin
Helen Hou-Sandí
WordCamp NYC 2012
Helen Hou-Sandí
WordCamp NYC 2012
User Experience Engineer with 10up, LLC
@helenhousandi on Twitter
WordPress Core Contributor *
This dashed border tells us … that I am a developer, not a designer.mdawaffe, WCSF 2011
// Admin menu icon register_post_type( 'event', array( ... 'menu_icon' => get_template_directory_uri() . '/images/icon-16.png'; )); // Screen icon add_action( 'admin_head', 'hhs_event_screen_icon' ); function hhs_event_screen_icon() { $post_type = get_current_screen()->post_type; if ( 'event' != $post_type ) return; ?> <style type="text/css"> .icon32.icon32-posts-event { background: url(<?php echo get_template_directory_uri(); ?>/images/icon-32.png) !important; } </style> <?php }
// Don't set anything for the menu_icon arg in register_post_type() add_action( 'admin_head', 'hhs_event_admin_menu_icon' ); function hhs_event_admin_menu_icon() { ?> <style type="text/css"> #adminmenu #menu-posts-event div.wp-menu-image{ background: transparent url(<?php echo get_template_directory_uri(); ?>/images/icon-16.png) no-repeat 6px -17px; } #adminmenu #menu-posts-event:hover div.wp-menu-image, #adminmenu #menu-posts-event.wp-has-current-submenu div.wp-menu-image { background-position: 6px 7px; </style> <?php }
register_post_type( 'slide', array( ... 'show_in_menu' => 'themes.php', 'show_in_nav_menus' => false, 'supports' => array( 'thumbnail' ) ) );
add_action( 'add_meta_boxes_slide', 'hhs_slide_add_meta_boxes' ); add_filter( 'admin_post_thumbnail_html', 'hhs_slide_post_thumbnail_html' ); function hhs_slide_add_meta_boxes() { remove_meta_box( 'postimagediv', 'slide', 'side' ); add_meta_box( 'postimagediv', 'Slide Image', 'post_thumbnail_meta_box', 'slide', 'normal', 'high' ); } function hhs_slide_post_thumbnail_html( $output ) { global $post_type; // beware of translated admin if ( ! empty ( $post_type ) && 'slide' == $post_type ) { $output = str_replace( 'Set featured image', 'Select / Upload a slide image', $output ); $output = str_replace( 'Remove featured image', 'Remove slide image', $output ); } return $output; }
Note: does not work via Ajax. See Trac ticket #20891
add_filter( 'manage_edit-slide_columns', 'hhs_slide_edit_columns' ); add_action( 'manage_slide_posts_custom_column', 'hhs_slide_custom_columns' ); function hhs_slide_edit_columns( $columns ) { $columns = array( 'cb' => '<input type="checkbox" />', 'thumbnail' => 'Slide', ); return $columns; } function hhs_slide_custom_columns( $column ) { global $post; switch ($column) { case 'thumbnail' : if ( has_post_thumbnail( $post->ID ) ) { // the current post has a thumbnail the_post_thumbnail( $post->ID ); } else { // the current post lacks a thumbnail ?> No image <?php } // add row_action links for Edit and Trash because there's no title column $post_type_object = get_post_type_object( $post->post_type ); $can_edit_post = current_user_can( $post_type_object->cap->edit_post, $post->ID ); $always_visible = false; // change to true to make it always show instead of on hover $actions = array(); if ( $can_edit_post && 'trash' != $post->post_status ) { $actions['edit'] = '<a href="' . get_edit_post_link( $post->ID, true ) . '" title="' . esc_attr( __( 'Edit this item' ) ) . '">' . __( 'Edit' ) . '</a>'; } if ( current_user_can( $post_type_object->cap->delete_post, $post->ID ) ) { if ( 'trash' == $post->post_status ) $actions['untrash'] = "<a title='" . esc_attr( __( 'Restore this item from the Trash' ) ) . "' href='" . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&action=untrash', $post->ID ) ), 'untrash-' . $post->post_type . '_' . $post->ID ) . "'>" . __( 'Restore' ) . "</a>"; elseif ( EMPTY_TRASH_DAYS ) $actions['trash'] = "<a class='submitdelete' title='" . esc_attr( __( 'Move this item to the Trash' ) ) . "' href='" . get_delete_post_link( $post->ID ) . "'>" . __( 'Trash' ) . "</a>"; if ( 'trash' == $post->post_status || !EMPTY_TRASH_DAYS ) $actions['delete'] = "<a class='submitdelete' title='" . esc_attr( __( 'Delete this item permanently' ) ) . "' href='" . get_delete_post_link( $post->ID, '', true ) . "'>" . __( 'Delete Permanently' ) . "</a>"; } $action_count = count( $actions ); $i = 0; $out = '<div class="' . ( $always_visible ? 'row-actions-visible' : 'row-actions' ) . '">'; foreach ( $actions as $action => $link ) { ++$i; ( $i == $action_count ) ? $sep = '' : $sep = ' | '; $out .= "<span class='$action'>$link$sep</span>"; } $out .= '</div>'; echo $out; break; } }
// can also use callback in register_post_type() instead register_post_type( 'event', array( ... 'register_meta_box_cb' => 'hhs_event_add_meta_boxes' ), ) ); function hhs_event_add_meta_boxes( $post ) { add_meta_box( 'hhs-event-date', 'Event Information', 'hhs_event_datepicker_meta_box', 'event', 'side', 'default' ); } function hhs_event_datepicker_meta_box( $post ) { $event_date = get_post_meta( $post->ID, 'hhs_event_date', true ); // mid-page enqueueing came in 3.3 wp_enqueue_script( 'jquery-ui-datepicker' ); wp_nonce_field( plugin_basename(__FILE__), 'hhs_eventmeta_nonce' ); ?> <script type="text/javascript"> jQuery(document).ready(function($) { $('.datepicker').datepicker({ dateFormat : 'yy-mm-dd' }); }); </script> <p><label for="hhs_event_date">Event Date</label><br /> <input type="text" class="datepicker" name="hhs_event_date" id="hhs_event_date"<?php if( $event_date ) echo ' value="' . esc_attr( $event_date) . '"'; ?> /></p> <?php }
add_filter( 'manage_edit-events_columns', 'hhs_event_edit_columns' ); add_action( 'manage_event_posts_custom_column', 'hhs_event_custom_columns' ); function hhs_event_edit_columns( $columns ) { // relabel title column $columns['title'] = 'Event Title'; // remove published date column unset( $columns['date'] ); // append new columns $columns['event-date'] = 'Event Date'; return $columns; } function hhs_event_custom_columns( $column ) { global $post; $event_date = get_post_meta( $post->ID, 'hhs_event_date', true ); switch ( $column ) { case 'event-date' : // escape as appropriate // Idea: Use get_option( 'date_format' ) to format the date instead :) echo esc_html( $event_date ); break; } }
add_filter( 'manage_edit-event_sortable_columns', 'hhs_event_sortable_columns' ); add_action( 'load-edit.php', 'hhs_event_edit_load' ); function hhs_event_sortable_columns( $columns ) { $columns['event-date'] = 'event-date'; return $columns; } function hhs_event_edit_load() { add_filter( 'request', 'hhs_event_sort' ); } function hhs_event_sort( $vars ) { // Check post type and for the orderby request if ( isset( $vars['post_type'] ) && 'event' == $vars['post_type'] && isset( $vars['orderby'] ) && 'event-date' == $vars['orderby'] ) { // Merge in with the rest of the query vars $vars = array_merge( $vars, array( 'meta_key' => 'hhs_event_date', 'orderby' => 'meta_value' ) ); } return $vars; }
Using regular post thumbnails and the Multiple Post Thumbnails plugin to add two more sizes/orientations
function hhs_bmpt_metabox( $post ) { wp_nonce_field( basename(__FILE__), 'hhs_bmpt_nonce' ); $id = get_post_meta( $post->ID, '_thumbnail_id', true ); $img = false; if ( $id ) $img = wp_get_attachment_image_src( $id, 'post-thumbnail' ); ?> <div class="item" data-size="post-thumbnail"> <input type="hidden" name="_thumbnail_id" value="<?php if ( $id ) echo esc_attr( $id ); ?>" /> <h4>Post Thumbnail</h4> <div class="image"> <?php if ( $img ) :?> <img src="<?php echo esc_url( $img[0] ); ?>" /> <?php else : ?> <img src="" class="screen-reader-text" /> <?php endif; ?> </div> <p> <a href="#" class="upload-image">Upload / select image</a><br /> <a href="#" class="remove-image<?php if ( ! $img ) echo ' screen-reader-text'; ?>">Remove image</a> </p> <p class="desc">This is the regular post thumbnail.</p> </div> <?php }
jQuery(document).ready(function($){ $('.upload-image').on('click', function(e){ e.preventDefault(); var item = $(this).closest('.item'), post_id = $('input#post_ID').val(), preview_size = item.data('size'); window.image_item = item; tb_show('Select image', 'media-upload.php?post_id=' + post_id + '&type=image&meta_type=image&meta_preview_size=' + preview_size + 'TB_iframe=1'); }); $('.remove-image').on('click', function(e){ e.preventDefault(); var $this = $(this), div = $this.closest('.item'); div.find('input').attr('value', ''); div.find('img').attr('src', '').addClass('screen-reader-text'); $this.addClass('screen-reader-text'); }); });
<?php // Insert some CSS and JS in the head of the Thickbox to simplify add_action( 'admin_head-media-upload-popup', 'hhs_bmpt_thickbox_head' ); function hhs_bmpt_thickbox_head() { // generally hide the post thumbnail selection link if ( ! isset( $_GET['meta_type'] ) || 'image' !== $_GET['meta_type'] ) : ?> <style type="text/css"> tr.submit .wp-post-thumbnail { display: none; } </style> <?php // hide those links and a bunch of other things else : $preview_size = isset( $arr_postinfo['meta_preview_size'] ) ? sanitize_key( $arr_postinfo['meta_preview_size'] ) : 'post-thumbnail'; ?> <style type="text/css"> #media-upload-header #sidemenu li#tab-type_url, #gallery-settings, #gallery-form table.widefat thead, #gallery-form .menu_order, #sort-buttons, .ml-submit, tr.url, tr.align, tr.image_alt, tr.image-size, tr.post_title, tr.post_excerpt, tr.post_content, tr.image_alt p, table thead input.button, table thead img.imgedit-wait-spin, tr.submit a.wp-post-thumbnail { display: none !important; } </style> <script type="text/javascript"> jQuery(document).ready(function($){ $('#media-items').bind('DOMNodeInserted',function(){ $('input[value="Insert into Post"]').each(function(){ $(this).attr('value','Select Image'); }); }).trigger('DOMNodeInserted'); $('form#filter').each(function(){ $(this).append('<input type="hidden" name="meta_preview_size" value="<?php echo esc_js( $preview_size ); ?>" />'); $(this).append('<input type="hidden" name="meta_type" value="image" />'); }); }); </script> <?php endif; }
// Inject JS to show the image thumbnail and set the hidden input value add_filter( 'media_send_to_editor', 'hhs_bmpt_media_send_to_editor', 15, 2 ); function hhs_bmpt_media_send_to_editor( $html, $id ) { if( ! isset( $arr_postinfo['meta_type'] ) || $arr_postinfo['meta_type'] !== 'image' ) return $html; else { $preview_size = ( ! empty( $arr_postinfo['meta_preview_size'] ) ) ? sanitize_key( $arr_postinfo['meta_preview_size'] ) : 'post-thumbnail'; $file_src = wp_get_attachment_image_src( $id, $preview_size ); $file_src = $file_src[0]; ?> <script type="text/javascript"> self.parent.image_item.find('input').val('<?php echo esc_js( $id ); ?>'); self.parent.image_item.find('img').attr('src','<?php echo esc_js( $file_src ); ?>'); self.parent.image_item.find('img').removeClass('screen-reader-text'); self.parent.image_item.find('.remove-image').removeClass('screen-reader-text'); self.parent.image_item = null; self.parent.tb_remove(); </script> <?php exit; } }
add_action( 'add_meta_boxes', 'hhs_add_meta_boxes', 10, 2 ); function hhs_add_meta_boxes( $post_type, $post ) { $front_page = get_option( 'page_on_front' ); if ( $post->ID === $front_page ) { add_meta_box( 'hhs_front_page_meta', 'Home Page Content', 'hhs_front_page_meta', 'page', 'normal', 'high' ); // remove the editor remove_post_type_support( 'page', 'editor' ); } }
<?php add_action( 'post_submitbox_misc_actions', 'hhs_post_submitbox_misc_actions' ); function hhs_post_submitbox_misc_actions() { // restrict post type if needed if ( 'post' != get_post_type() ) return; wp_nonce_field( 'hhs_post_submitbox_nonce', plugin_basename(__FILE__) ); // Inline styles needed pre-3.4 - see WCPHX 2012 slides // But you're using the latest, right? ?> <div class="misc-pub-section"> <input type="checkbox" name="remove_claim_link" id="remove_claim_link" value="1" <?php checked( get_post_meta( get_the_ID(), 'remove_claim_link', true ) ); ?> /> <label for="remove_claim_link">Claimed</label> </div> <div class="misc-pub-section"> <input type="checkbox" name="is_featured" id="is_featured" value="1" <?php checked( get_post_meta( get_the_ID(), 'is_featured', true ) ); ?> /> <label for="is_featured">Featured</label> </div> <div class="misc-pub-section"> <input type="checkbox" name="is_premium" id="is_premium" value="1" <?php checked( get_post_meta( get_the_ID(), 'is_premium', true ) ); ?> /> <label for="is_premium">Premium</label> </div> <?php }
// These are actual text, not placeholders - will save if not removed // Also: default_excerpt add_filter( 'default_title', 'hhs_event_default_title', 10, 2 ); function hhs_event_default_title( $title, $post ) { if ( 'event' == $post->post_type ) $title = 'Event Title'; return $title; } add_filter( 'default_content', 'hhs_event_default_content', 10, 2 ); function hhs_event_default_content( $content, $post ) { if ( 'event' == $post->post_type ) $content = 'Event description'; return $content; } // This is a placeholder add_filter( 'enter_title_here', 'hhs_event_enter_title_here', 10, 2 ); function hhs_event_enter_title_here( $placeholder, $post ) { if ( 'event' == $post->post_type ) $placeholder = 'Event Title'; return $placeholder; }
Slides online at
http://slides.helenhousandi.com/wcnyc2012.html