Last active
August 29, 2015 14:18
-
-
Save tddewey/0f19de1e7dc52e9f5bfc to your computer and use it in GitHub Desktop.
Feature grid builder for GeekyLibrary
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Handle the CPT and related things for the featured items CPT | |
* | |
* Example of how this UI looks: | |
* https://cloudup.com/c3KSu4DhA-M | |
* https://cloudup.com/cj1lZPzRLcq | |
* | |
* Class GL_Featured_Items | |
*/ | |
class GL_Featured_Items { | |
/** | |
* The only instance of the GL_Featured_Items Object | |
* @var GL_Featured_Items | |
*/ | |
private static $instance; | |
/** | |
* Custom Post Type slug | |
* @var string | |
*/ | |
public $slug = 'gl-feature-grids'; | |
/** | |
* Holds the current Tenup_Featured_Item that is being worked on | |
* @var Tenup_Featured_Item | |
*/ | |
private $features; | |
/** | |
* Returns the main instance. | |
* | |
* @return GL_Featured_Items | |
*/ | |
public static function get_instance() { | |
if ( ! isset( self::$instance ) ) { | |
self::$instance = new GL_Featured_Items; | |
self::$instance->setup_actions(); | |
} | |
return self::$instance; | |
} | |
/** | |
* A dummy constructor. | |
* | |
* @return GL_Featured_Items | |
*/ | |
private function __construct() { | |
} | |
/** | |
* Set up WordPress hooks | |
*/ | |
function setup_actions() { | |
add_action( 'init', array( $this, 'setup_post_type' ) ); | |
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); | |
add_filter( 'wp_insert_post_data', array( $this, 'post_data' ), null, 2 ); | |
add_filter( 'post_updated_messages', array( $this, 'filter_updated_messages' ) ); | |
add_filter( 'manage_edit-' . $this->slug . '_columns', array( $this, 'add_columns' ) ); | |
add_action( 'manage_' . $this->slug . '_posts_custom_column', array( $this, 'render_columns' ), null, 2 ); | |
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); | |
add_action( 'wp_ajax_feature-grid-search', array( $this, 'ajax_feature_grid_search' ) ); | |
add_action( 'wp_ajax_feature_edit_fields', array( $this, 'ajax_feature_edit_fields' ) ); | |
} | |
/** | |
* Set up Post Type | |
*/ | |
function setup_post_type() { | |
register_post_type( $this->slug, array( | |
'labels' => array( | |
'name' => 'Feature Grids', | |
'singular_name' => 'Feature Grid', | |
'menu_name' => 'Feature Grids', | |
'all_items' => 'All Grids', | |
'add_new_item' => 'Add New Grid', | |
'edit_item' => 'Edit Grid', | |
'view_item' => 'View Grid', | |
'search_items' => 'Search Grids', | |
'not_found' => 'No grids found', | |
'not_found_in_trash' => 'No grids found in trash' | |
), | |
'description' => 'A single feature grid is used on the home page to display several items in a grid', | |
'public' => false, | |
'show_ui' => true, | |
'menu_icon' => 'dashicons-slides', | |
'supports' => false | |
) ); | |
} | |
function add_meta_boxes() { | |
$this->features = new Tenup_Featured_Item( get_the_ID() ); | |
foreach ( array( 'Secondary', 'Feature', 'Tertiary' ) as $index ) { | |
add_meta_box( 'featured-' . sanitize_key( $index ), $index, array( $this, 'feature_meta_box' ), $this->slug, 'normal', 'high', array( 'index' => $index ) ); | |
} | |
} | |
/** | |
* Display the meta box for features | |
* | |
* @param $post | |
* @param $metabox | |
*/ | |
function feature_meta_box( $post, $metabox ) { | |
$index = $metabox['args']['index']; | |
$index_key = sanitize_key( $index ); | |
$post_id = $this->features->get_position_field( 'post_id', $index_key ); | |
?> | |
<label for="gl-feature-<?php echo $index_key; ?>-post_id">Linked to this content:</label> | |
<input id="gl-feature-<?php echo $index_key; ?>-post_id" type="hidden" name="gl-feature[<?php echo $index_key; ?>][post_id]" class="select2" style="min-width: 50%" value="<?php echo $post_id; ?>" data-title="<?php echo esc_attr( get_post_field( 'post_title', $post_id, 'raw' ) ); ?>"> | |
<div class="feature-edit-fields" data-index="<?php echo $index_key; ?>" style="<?php echo empty( $post_id ) ? 'display:none' : ''; ?>"> | |
<?php echo $this->get_feature_edit_fields( $index ); ?> | |
</div> | |
<?php | |
wp_nonce_field( 'feature-edit-' . $post->ID, 'gl_feature_editor_nonce' ); | |
} | |
/** | |
* Get the fields that can be edited. | |
* In a separate function so it can be ajax'ed in | |
*/ | |
function get_feature_edit_fields( $position_index, $post_id_override = 0 ) { | |
$index_key = sanitize_key( $position_index ); | |
if ( empty( $post_id_override ) ) { | |
$post_id = $this->features->get_position_field( 'post_id', $index_key ); | |
} else { | |
$post_id = $post_id_override; | |
} | |
$saved_data = $this->features->get_position_data( $index_key ); | |
$data = wp_parse_args( $saved_data, array( | |
'display' => 'image', | |
'quote' => '', | |
'citation' => '', | |
'title' => get_the_title( $post_id ), | |
'img_id' => get_post_thumbnail_id( $post_id ), | |
), $saved_data ); | |
ob_start(); | |
?> | |
<div class="tabbed-area"> | |
<input class="tab" id="gl-feature-<?php echo $index_key; ?>-display-image" type="radio" name="gl-feature[<?php echo $index_key; ?>][display]" value="image" <?php checked( 'image', $data['display'] ); ?> data-tab="image"> | |
<label class="tab" for="gl-feature-<?php echo $index_key; ?>-display-image">Display as Image</label> | |
<input class="tab" id="gl-feature-<?php echo $index_key; ?>-display-quote" type="radio" name="gl-feature[<?php echo $index_key; ?>][display]" value="quote" <?php checked( 'quote', $data['display'] ); ?> data-tab="quote"> | |
<label class="tab" for="gl-feature-<?php echo $index_key; ?>-display-quote">Display as Quote</label> | |
<div class="tab-content image"> | |
<span class="image-wrap"> | |
<?php | |
$img_id = $this->features->get_position_field( 'img_id', $index_key ); | |
if ( ! empty( $img_id ) ) { | |
echo wp_get_attachment_image( $img_id, 'bitty-book' ); | |
} else { | |
echo '<span class="no-image">No featured image</span>'; | |
} | |
?> | |
</span> | |
<input size="5" type="hidden" name="gl-feature[<?php echo $index_key; ?>][img_id]" value="<?php echo esc_attr( $data['img_id'] ); ?>"> | |
<label>Title | |
<input type="text" name="gl-feature[<?php echo $index_key; ?>][title]" value="<?php echo esc_attr( $data['title'] ) ?>"></label> | |
</div> | |
<div class="tab-content quote"> | |
<p> | |
<label for="gl-feature-<?php echo $index_key; ?>-quote">Quote:</label> | |
<textarea id="gl-feature-<?php echo $index_key; ?>-quote" name="gl-feature[<?php echo $index_key; ?>][quote]"><?php echo esc_textarea( $data['quote'] ); ?></textarea> | |
</p> | |
<p> | |
<label>Cite: | |
<input type="text" name="gl-feature[<?php echo $index_key; ?>][citation]" value="<?php echo esc_attr( $data['citation'] ); ?>"></label> | |
</p> | |
</div> | |
</div> | |
<?php | |
return ob_get_clean(); | |
} | |
/** | |
* Enqueue additional scripts needed for this class | |
*/ | |
function admin_enqueue_scripts() { | |
if ( get_post_type() !== $this->slug ) { | |
return; | |
} | |
wp_enqueue_style( 'select2', get_template_directory_uri() . '/assets/js/vendor/select2/select2.css' ); | |
} | |
/** | |
* Hook in just before saving data to the database so we can manipulate the post_content | |
* | |
* @param $data array | |
* @param $postarr array | |
* | |
* @return array | |
*/ | |
function post_data( $data, $postarr ) { | |
if ( get_post_type() !== $this->slug ) { | |
return $data; | |
} | |
if ( ! wp_verify_nonce( $_POST['gl_feature_editor_nonce'], 'feature-edit-' . get_the_ID() ) ) { | |
return $data; | |
}; | |
// set post_content to be some JSON from $_POST | |
if ( isset( $_POST['gl-feature'] ) ) { | |
$escaped_data = $_POST['gl-feature']; | |
$data['post_content'] = wp_slash( json_encode( $escaped_data ) ); // note wp_insert_post does sanitization | |
} | |
return $data; | |
} | |
/** | |
* Filter updated messages | |
* @param $messages | |
* | |
* @return mixed | |
*/ | |
function filter_updated_messages( $messages ) { | |
global $post; | |
if ( $this->slug !== get_post_type( $post ) ) { | |
return $messages; | |
} | |
$preview_url = esc_url( add_query_arg( 'preview', $post->ID, home_url() ) ); | |
$messages['post'] = array( | |
0 => '', // Unused. Messages start at index 1. | |
1 => sprintf( 'Grid updated. <a href="%s">View homepage with this grid</a>', $preview_url ), | |
2 => __( 'Custom field updated.' ), | |
3 => __( 'Custom field deleted.' ), | |
4 => 'Grid updated.', | |
5 => isset( $_GET['revision'] ) ? sprintf( 'Grid restored to revision from %s', wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, | |
6 => sprintf( 'Grid published. <a href="%s">View homepage with this grid</a>', $preview_url ), | |
7 => __( 'Grid saved.' ), | |
8 => sprintf( 'Grid submitted. <a target="_blank" href="%s">Preview homepage with this grid</a>', $preview_url ), | |
9 => sprintf( 'Grid scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview homepage with this grid</a>', | |
date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), $preview_url ), | |
10 => sprintf( 'Post draft updated. <a target="_blank" href="%s">Preview homepage with this grid</a>', $preview_url ), | |
); | |
return $messages; | |
} | |
/** | |
* Filter the columns on the edit post screen | |
* | |
* @param $columns array | |
* | |
* @return array | |
*/ | |
function add_columns( $columns ) { | |
$columns = array( | |
'cb' => '<input type="checkbox">', | |
'gl-preview' => 'Preview', | |
'date' => _x( 'Date', 'column name' ), | |
); | |
return $columns; | |
} | |
/** | |
* Display our custom column's content | |
* | |
* @param $column_name string | |
* @param $post_id integer | |
*/ | |
function render_columns( $column_name, $post_id ) { | |
if ( $column_name !== 'gl-preview' ) { | |
return; | |
} | |
// Get all the images for the given post ID | |
$feature = new Tenup_Featured_Item( $post_id ); | |
if ( $feature->featured_items ) { | |
$image_ids = wp_list_pluck( $feature->featured_items, 'img_id' ); | |
foreach ( $image_ids as $img_id ) { | |
if ( empty( $img_id ) ) { | |
echo '[no preview img]'; | |
} | |
echo wp_get_attachment_image( $img_id, 'bitty-book' ); | |
} | |
} | |
/** | |
* Steal the bit from wp-posts-list-table for post locking | |
*/ | |
$lock_holder = wp_check_post_lock( get_the_ID() ); | |
if ( $lock_holder ) { | |
$lock_holder = get_userdata( $lock_holder ); | |
} | |
if ( current_user_can( 'edit_post', get_the_ID() ) && get_post_status( get_the_ID() ) != 'trash' ) { | |
if ( $lock_holder ) { | |
$locked_avatar = get_avatar( $lock_holder->ID, 18 ); | |
$locked_text = esc_html( sprintf( __( '%s is currently editing' ), $lock_holder->display_name ) ); | |
} else { | |
$locked_avatar = $locked_text = ''; | |
} | |
echo '<div class="locked-info"><span class="locked-avatar">' . $locked_avatar . '</span> <span class="locked-text">' . $locked_text . "</span></div>\n"; | |
} | |
/** | |
* Steal the bit from wp-posts-list-table for post status | |
*/ | |
if ( get_post_status( get_the_ID() ) === 'draft' ) { | |
echo '<strong> — Draft</strong>'; | |
} | |
if ( get_post_status( get_the_ID() ) === 'future' ) { | |
echo '<strong> — Scheduled</strong>'; | |
} | |
if ( get_post_status( get_the_ID() ) === 'pending' ) { | |
echo '<strong> — Pending Review</strong>'; | |
} | |
$this->row_actions( $post_id ); | |
} | |
/** | |
* Echo the row actions for this CPT. | |
* | |
* This code is taken from WP_List_Table. Unfortunately WP only generates the | |
* $actions array when it's dealing with the 'title' column since we removed the | |
* title column, we need to generate our own $actions array, pass it to the | |
* globalized $wp_list_table object's row_actions method. It's ridiculous. | |
* | |
* @param $post_id | |
*/ | |
private function row_actions( $post_id ) { | |
global $wp_list_table; | |
$actions = array(); | |
$post = get_post( $post_id ); | |
$can_edit_post = current_user_can( 'edit_post', $post_id ); | |
$post_type_object = get_post_type_object( $post->post_type ); | |
$preview_url = esc_url( add_query_arg( 'preview', $post_id, home_url() ) ); | |
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>'; | |
$actions['inline hide-if-no-js'] = '<a href="#" class="editinline" title="' . esc_attr( __( 'Edit this item inline' ) ) . '">' . __( 'Quick Edit' ) . '</a>'; | |
} | |
if ( current_user_can( '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->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>"; | |
} | |
if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ) ) ) { | |
if ( $can_edit_post ) | |
$actions['view'] = '<a href="' . $preview_url . '" rel="permalink">' . __( 'Preview' ) . '</a>'; | |
} elseif ( 'trash' != $post->post_status ) { | |
$actions['view'] = '<a href="' . $preview_url . '" rel="permalink">' . __( 'View' ) . '</a>'; | |
} | |
$actions = apply_filters( is_post_type_hierarchical( $post->post_type ) ? 'page_row_actions' : 'post_row_actions', $actions, $post ); | |
echo $wp_list_table->row_actions( $actions ); | |
} | |
public static function ajax_feature_grid_search() { | |
$posts = new WP_Query( array( | |
'posts_per_page' => absint( $_GET['posts_per_page'] ), | |
'post_type' => array( GL_CPT_Library::CPT_SLUG, 'post', 'page' ), | |
'paged' => absint( $_GET['paged'] ), | |
's' => sanitize_text_field( $_GET['s'] ), | |
'update_term_cache' => false, | |
'update_post_meta_cache' => false, | |
) ); | |
$results = array(); | |
if ( $posts->have_posts() ) { | |
while ( $posts->have_posts() ) { | |
$posts->the_post(); | |
$results[] = array( | |
'id' => get_the_ID(), | |
'text' => get_post_field( 'post_title', get_the_ID(), 'display' ) | |
); | |
} | |
} | |
$response = array( | |
'results' => $results, | |
'more' => $posts->max_num_pages > $_GET['paged'] | |
); | |
wp_send_json( $response ); | |
} | |
/** | |
* Get the default field data so it can be ajax loaded | |
*/ | |
public static function ajax_feature_edit_fields() { | |
$index = $_GET['index']; | |
$postid = $_GET['postid']; | |
$default_response = array( | |
'display' => 'image', | |
'title' => '', | |
'img_id' => 0, | |
'img_preview' => '', | |
'quote' => '', | |
'citation' => '' | |
); | |
if ( empty( $index ) || empty( $postid ) ) { | |
wp_send_json_error( $default_response ); | |
} | |
$response = wp_parse_args( array( | |
'title' => utf8_encode( get_post_field( 'post_title', $postid, 'display' ) ), | |
'img_id' => get_post_thumbnail_id( $postid ), | |
'img_preview' => utf8_encode( get_the_post_thumbnail( $postid, 'bitty-book' ) ), | |
), $default_response ); | |
wp_send_json_success( $response ); | |
} | |
} | |
/** | |
* Gets the main GL_Featured_Items Instance | |
* | |
* Calling this function places into motion the main functions of the class, but can also be utilized to get properties | |
* and run methods of the class. | |
* | |
* @return GL_Featured_Items | |
*/ | |
function get_gl_featured_items() { | |
return GL_Featured_Items::get_instance(); | |
} | |
add_action( 'after_setup_theme', 'get_gl_featured_items' ); | |
/** | |
* Handle getting information related to featured items. Designed to handle one | |
* post of the featured items CPT. | |
* | |
* Class TenupFeaturedItem | |
*/ | |
class Tenup_Featured_Item { | |
/** | |
* Post object associated with this featured item | |
* @var WP_Post | |
*/ | |
var $item_post = ''; | |
/** | |
* featured item array | |
* @var array | |
*/ | |
var $featured_items = ''; | |
/** | |
* Constructor | |
* | |
* @param $post WP_Post or Post ID | |
*/ | |
function __construct( $post ) { | |
$this->item_post = get_post( $post ); | |
$this->featured_items = $this->_get_data(); | |
} | |
/** | |
* Get all the featured item data for the post | |
* @return array | |
*/ | |
private function _get_data() { | |
if ( $this->item_post->post_content ) { | |
return stripslashes_deep( json_decode( $this->item_post->post_content, true ) ); | |
} else { | |
return array(); | |
} | |
} | |
/** | |
* Get an array of positions. For easy use in iterators | |
*/ | |
public function get_positions() { | |
return array( 'Feature', 'Secondary', 'Tertiary' ); | |
} | |
/** | |
* Get all the data for a given position | |
* | |
* @param $position_index mixed | |
* @param $placeholder_img_in_admin bool display a placeholder image in admin contexts | |
* | |
* @return array | |
*/ | |
public function get_position_data( $position_index, $placeholder_img_in_admin = true ) { | |
if ( empty( $this->featured_items[sanitize_key( $position_index )] ) ) { | |
$position_data = array(); | |
} else { | |
$position_data = $this->featured_items[sanitize_key( $position_index )]; | |
} | |
return $position_data; | |
} | |
/** | |
* Get the data from a specific field for a specific index | |
* | |
* @param $position_field | |
* @param $position_index | |
* @param $placeholder_img_in_admin bool display a placeholder image in admin contexts | |
* | |
* @return mixed | |
*/ | |
public function get_position_field( $position_field, $position_index, $placeholder_img_in_admin = true ) { | |
$data = $this->get_position_data( $position_index, $placeholder_img_in_admin ); | |
if ( ! empty( $data[sanitize_key( $position_field )] ) ) { | |
return wp_kses_post( wptexturize( stripslashes( $data[sanitize_key( $position_field )] ) ) ); | |
} else { | |
return ''; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* | |
* Does a query for the latest published feature grid, | |
* then pulls each item listed in the grid. | |
* | |
*/ | |
<section class="curated-content"> | |
<div class="inner"> | |
<?php | |
$query_args = array( | |
'post_type' => get_gl_featured_items()->slug, | |
'posts_per_page' => 1, | |
'no_found_rows' => true, | |
'update_term_cache' => false | |
); | |
if ( isset( $_GET['preview'] ) ) { | |
$query_args['post__in'] = (array) $_GET['preview']; | |
$query_args['post_status'] = array('any'); | |
$query_args['perm'] = 'readable'; | |
} | |
$features = new WP_Query( $query_args ); | |
if ( $features->have_posts() ){ | |
$features->the_post(); | |
$feature = new Tenup_Featured_Item( get_the_ID() ); | |
foreach ( array( 'secondary', 'feature', 'tertiary' ) as $position ) { | |
$position = strtolower( $position ); | |
$display = $feature->get_position_field( 'display', $position ); | |
locate_template( array( | |
'template-parts/featured-' . $position . '-' . $display . '.php', | |
'template-parts/featured-' . $position . '.php', | |
'template-parts/featured-tertiary-image.php' | |
), true ); | |
} | |
} | |
wp_reset_postdata(); | |
?> | |
</div> | |
</section> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment