-
-
Save Bot12313/4d9f6e209fb361388f051ed1e8b0e9b5 to your computer and use it in GitHub Desktop.
This is set of helper classes to create and manage custom post types for WordPress. It gets added to a theme's logic folder, or libs or includes folders, and included in functions.php. Then the primary tracker class is initiated once, and used to create new CTPs.
This file contains 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 | |
/***********************************************************************/ | |
/*************************** TRACKER CLASS ***************************/ | |
/***********************************************************************/ | |
if ( ! class_exists( 'Custom_Post_Types_Manager' ) ) { | |
class Custom_Post_Types_Manager { | |
public $ctp_slugs; | |
public $prefix; | |
function __construct($init_data) { | |
$this->prefix = $init_data['prefix']; | |
$this->ctp_slugs = array(); | |
} | |
public function setup_ctp($ctp_options) { | |
$ctp_options['prefix'] = $this->prefix; | |
$ctp = new Custom_Post_Type($ctp_options); | |
array_push( $this->ctp_slugs, $ctp->full_slug() ); | |
} | |
public function get_slugs() { | |
return $this->ctp_slugs; | |
} | |
} | |
} | |
/***********************************************************************/ | |
/*********************** INDIVIDUAL CTP CLASS ************************/ | |
/***********************************************************************/ | |
if ( ! class_exists( 'Custom_Post_Type' ) ) { | |
class Custom_Post_Type { | |
public $prefix; | |
public $slug; | |
public $custom_strings; | |
public $supports; | |
public $menu_icon; | |
public $icon; | |
public $metaboxes; | |
public $metadata; | |
function __construct($init_data) { | |
$this->prefix = array_key_exists('prefix', $init_data) ? $init_data['prefix'] : null; | |
$this->slug = array_key_exists('slug', $init_data) ? $init_data['slug'] : null; | |
$this->custom_strings = array_key_exists('custom_strings', $init_data) ? $init_data['custom_strings'] : null; | |
$this->taxonomy_slug = array_key_exists('taxonomy_slug', $init_data) ? $init_data['taxonomy_slug'] : null; | |
$this->supports = array_key_exists('supports', $init_data) ? $init_data['supports'] : array( 'title', 'editor' ); | |
$this->menu_icon = array_key_exists('menu_icon', $init_data) ? $init_data['menu_icon'] : null; | |
$this->icon = array_key_exists('icon', $init_data) ? $init_data['icon'] : null; | |
$this->metaboxes = array_key_exists('metaboxes', $init_data) ? $init_data['metaboxes'] : null; | |
$this->metadata = array_key_exists('metadata', $init_data) ? $init_data['metadata'] : null; | |
$this->display_page = array_key_exists('display_page', $init_data) ? $init_data['display_page'] : null; | |
$this->hierarchical = array_key_exists('hierarchical', $init_data) ? $init_data['hierarchical'] : false; | |
$this->min_role = array_key_exists('min_role', $init_data) ? $init_data['min_role'] : null; | |
$this->capabilities = $this->build_capabilities( $this->min_role ); | |
add_action( 'init', array( &$this, 'create_ctp' ) ); | |
add_action( 'admin_head', array( &$this, 'create_ctp_icons' ) ); | |
add_action( 'save_post', array( &$this, 'save_ctp_custom_metadata' ), 10, 3 ); | |
add_action( 'rest_api_init', array( &$this, 'expose_metadata_via_restapi' ) ); | |
} | |
private function build_capabilities() { | |
// WORDPRESS DOESN'T GIVE THE ABILITY DIRECTLY TO SPECIFIC MINIMUM USER ROLES TO SEE | |
// AND INTERACT WITH A CTP. BUT YOU CAN MAP CAPABILITIIES. KINDA HARD TO EXPLAIN, BUT | |
// BASICALLY, YOU CAN SPECIFY A min_role IN THE CTP CREATION OPTIONS NOW, AND THIS | |
// FUNCTION WILL GRAB A CAPABILITY OF THE ROLE THAT YOU HAVE SPECIFIED. IT THEN MAPS | |
// THE CAPABILITIES THAT HANDLE INTERACTING WITH THE CTP, TO THE CAPABILITY DECIDED HERE | |
$capability_to_match = null; | |
switch ( $this->min_role ) { | |
case 'administrator' : | |
$capability_to_match = 'update_core'; | |
break; | |
case 'editor' : | |
$capability_to_match = 'edit_pages'; | |
break; | |
case 'author' : | |
$capability_to_match = 'publish_posts'; | |
break; | |
case 'contributor' : | |
$capability_to_match = 'edit_posts'; | |
break; | |
} | |
if ( $capability_to_match ) { | |
return array( | |
'publish_posts' => $capability_to_match, | |
'edit_posts' => $capability_to_match, | |
'edit_others_posts' => $capability_to_match, | |
'delete_posts' => $capability_to_match, | |
'delete_others_posts' => $capability_to_match, | |
'read_private_posts' => $capability_to_match, | |
'edit_post' => $capability_to_match, | |
'delete_post' => $capability_to_match, | |
'read_post' => $capability_to_match | |
); | |
} else { | |
return null; | |
} | |
} | |
public function full_slug() { | |
return ($this->prefix)?strtolower($this->prefix.'_'.$this->slug):$this->slug; | |
} | |
public function create_ctp() { | |
$post_type_slug = $this->full_slug(); | |
$post_label = ucfirst( $this->slug ); | |
// SPECIFY THE ARGUMENTS FOR THE CTP, THEN REGISTER IT | |
$args = array( | |
'labels' => array( | |
'name' => $post_label.'s', | |
'singular_name' => $post_label, | |
'all_items' => $post_label.' '.__( 'List' ), | |
'add_new' => 'Add New'.' '.$post_label, | |
'add_new_item' => 'Add New'.' '.$post_label, | |
'edit_item' => 'Edit'.' '.$post_label, | |
'new_item' => 'New'.' '.$post_label, | |
'view_item' => 'View'.' '.$post_label, | |
'search_items' => 'Search', | |
'not_found' => 'Not found', | |
'not_found_in_trash' => 'Trash is empty' | |
), | |
'hierarchical' => $this->hierarchical, | |
'public' => true, | |
'show_in_rest' => true, | |
'has_archive' => true, | |
'supports' => $this->supports, | |
'menu_icon' => $this->menu_icon, | |
'register_meta_box_cb' => array( $this, 'create_ctp_custom_metaboxes' ), | |
); | |
/** | |
* Rewrite strings for the custom ones | |
*/ | |
array_walk($args["labels"],function (&$v, $k) { | |
if(array_key_exists($k, $this->custom_strings)) | |
$v = $this->custom_strings[$k]; | |
}); | |
if ( $this->taxonomy_slug ) { | |
$taxonomy_slug = $post_type_slug.'_'.$this->taxonomy_slug; | |
$taxonomy_label = ucwords( str_replace( '_', ' ', $this->taxonomy_slug ) ); | |
// SPECIFY THE ARGUMENTS FOR THE CTP'S TAXONOMY, THEN REGISTER IT | |
$tax_args = array( | |
'labels' => array( | |
'name' => $taxonomy_label, | |
'add_new_item' => 'Add New '.$taxonomy_label | |
), | |
'public' => true, | |
'show_ui' => true, | |
'show_tagcloud' => true, | |
'hierarchical' => true, | |
'show_in_nav_menus' => true, | |
'show_admin_column' => true | |
); | |
register_taxonomy($taxonomy_slug, $post_type_slug, $tax_args); | |
$args['taxonomies'] = array( $taxonomy_slug ); | |
} /*else { | |
$taxonomy_slug = $post_type_slug.'_type'; | |
$taxonomy_label = $post_label.' Type'; | |
}*/ | |
// OPTIONAL ARGUMENTS | |
if ( $this->display_page ) { | |
$args['rewrite'] = array( 'slug' => $this->display_page,'with_front' => false ); | |
} | |
if ( $this->capabilities ) { | |
$args['capabilities'] = $this->capabilities; | |
} | |
register_post_type($post_type_slug, $args); | |
} | |
public function create_ctp_custom_metaboxes( $post ) { | |
$post_type_slug = get_post_type($post); | |
if ( $this->metaboxes != null ) { | |
foreach ($this->metaboxes as $metabox) { | |
$metabox_id = $post_type_slug.'_metabox_'.$metabox['slug']; | |
$metabox_label = $metabox['label']; | |
$metabox_callback = array( $this, 'create_ctp_custom_metadata' ); | |
$metabox_screen = $post_type_slug; | |
$metabox_content = $metabox['position']; | |
$metabox_priority = 'default'; | |
$metabox_callback_args = array( $metabox['metadata'], $post_type_slug ); | |
add_meta_box($metabox_id, $metabox_label, $metabox_callback, $metabox_screen, $metabox_content, $metabox_priority, $metabox_callback_args ); | |
} | |
} | |
} | |
public function create_ctp_custom_metadata($post, $data) { | |
global $admin_colors; | |
$metadata = $data['args'][0]; | |
$post_type_slug = $data['args'][1]; | |
$html = ''; | |
foreach ( $metadata as $metadatum ) { | |
$html .= '<div class="metadata-wrap">'; | |
$metadatum_type = array_key_exists('type', $metadatum) ? $metadatum['type'] : 'text'; | |
$metadatum_label = array_key_exists('label', $metadatum) ? $metadatum['label'] : ''; | |
$metadatum_desc = array_key_exists('desc', $metadatum) ? $metadatum['desc'] : ''; | |
$metadatum_slug = array_key_exists('slug', $metadatum) ? $metadatum['slug'] : ''; | |
$metadatum_default = array_key_exists('default', $metadatum) ? $metadatum['default'] : ''; | |
$metadatum_options = array_key_exists('options', $metadatum) ? $metadatum['options'] : ''; | |
$metadatum_id = $post_type_slug . '_metadata_' . $metadatum_slug; | |
$metadatum_value = get_post_meta($post->ID, $metadatum_id, true); | |
$metadatum_value = $metadatum_value ? $metadatum_value : $metadatum_default; | |
register_meta( $post_type_slug, $metadatum_id, array( | |
'single' => true, | |
'show_in_rest' => true | |
)); | |
switch ( $metadatum_type ) { | |
case 'hidden' : | |
$html .= '<input type="hidden" name="' . $metadatum_id .'" id="' . $metadatum_id .'" value="' . $metadatum_value . '" class="widefat" />'; | |
break; | |
case 'select' : | |
$html .= '<p class="post-attributes-label-wrapper"><label class="post-attributes-label" for="' . $metadatum_id . '">' . $metadatum_label .'</label></p>'; | |
$html .= '<div class="metadata-desc">' . $metadatum_desc .'</div>'; | |
$html .= '<select name="' . $metadatum_id .'" id="' . $metadatum_id .'" class="widefat">'; | |
foreach ( $metadatum_options as $metadatum_option_label => $metadatum_option_value ) { | |
$html .= '<option' . ( $metadatum_option_value == $metadatum_value ? ' selected="selected"' : '' ) . ' value="' . $metadatum_option_value . '">' . $metadatum_option_label . '</option>'; | |
} | |
$html .= '</select>'; | |
break; | |
case 'textarea' : | |
$html .= '<p class="post-attributes-label-wrapper"><label class="post-attributes-label" for="' . $metadatum_id . '">' . $metadatum_label .'</label></p>'; | |
$html .= '<div class="metadata-desc">' . $metadatum_desc .'</div>'; | |
$html .= '<textarea name="' . $metadatum_id .'" id="' . $metadatum_id .'" class="widefat">' . $metadatum_value . '</textarea>'; | |
break; | |
case 'textarea_googlemappolygon' : | |
$html .= '<p class="post-attributes-label-wrapper"><label class="post-attributes-label" for="' . $metadatum_id . '">' . $metadatum_label .'</label></p>'; | |
$html .= '<div class="metadata-desc">' . $metadatum_desc .'</div>'; | |
$html .= '<div id="map-wrap" style="max-height:400px;height:80vw;margin-bottom:10px"></div>'; | |
$html .= '<p class="post-attributes-label-wrapper"><label class="post-attributes-label" for="' . $metadatum_id . '">Encoded Polygon (Creates the Above Preview)</label></p>'; | |
$html .= '<textarea style="min-height:200px;" name="' . $metadatum_id . '" id="' . $metadatum_id . '" class="widefat">' . $metadatum_value . '</textarea>'; | |
$html .= ' | |
<script> | |
(function(window, Mapstractor) { | |
window.setTimeout(function(){ | |
// Grab Input Elements that Control the Map | |
var encodedCoordinatesElement = document.getElementById(\'' . $metadatum_id . '\'); | |
var colorElement = document.getElementById(\'' . str_replace('shape', 'color', $metadatum_id) . '\'); | |
var polygon = null; | |
// Setup Map | |
var map = new Mapstractor({ | |
mapWrapID: \'map-wrap\', | |
mapOptions: { | |
center: { | |
lat: 40, | |
lng: -95 | |
}, | |
zoom: 4, | |
minZoom: 3, | |
maxZoom: 9, | |
scrollwheel: true, | |
draggable: true, | |
mapTypeControl:false, | |
streetViewControl:false, | |
zoomControl:false, | |
mapTypeId: google.maps.MapTypeId.ROADMAP, | |
} | |
}); | |
// Add Polygon to Map | |
if ( encodedCoordinatesElement.value ) { | |
polygon = map.addPolygon({ | |
encodedCoordinates: encodedCoordinatesElement.value, | |
color: colorElement.value | |
}); | |
map.gMap.panTo(polygon.getCenter()); | |
} | |
// Add Event Listener to Preview Changes | |
encodedCoordinatesElement.addEventListener(\'change\', function() { | |
if ( encodedCoordinatesElement.value ) { | |
if ( polygon == null ) { | |
polygon = map.addPolygon({ | |
encodedCoordinates: encodedCoordinatesElement.value, | |
color: colorElement.value | |
}); | |
map.gMap.panTo(polygon.getCenter()); | |
} else { | |
polygon.setOptions({paths: google.maps.geometry.encoding.decodePath(encodedCoordinatesElement.value)}); | |
map.gMap.panTo(polygon.getCenter()); | |
} | |
} else { | |
polygon.setMap(null); | |
polygon = null; | |
} | |
}, false); | |
colorElement.addEventListener(\'change\', function() { | |
polygon.setOptions({fillColor: colorElement.value}); | |
}, false); | |
}, 100); | |
}(window, window.Mapstractor || (window.Mapstractor == {}))); | |
</script> | |
'; | |
break; | |
case 'text_googleaddress' : | |
$metadatum_value_latlng = get_post_meta($post->ID, $metadatum_id.'_latlng', true); | |
$html .= '<p class="post-attributes-label-wrapper"><label class="post-attributes-label" for="' . $metadatum_id . '">' . $metadatum_label .'</label></p>'; | |
$html .= '<div class="metadata-desc">' . $metadatum_desc .'</div>'; | |
$html .= '<textarea name="' . $metadatum_id .'" id="' . $metadatum_id .'" class="widefat">' . $metadatum_value . '</textarea>'; | |
$html .= '<input type="hidden" name="' . $metadatum_id .'_latlng" id="' . $metadatum_id .'_latlng" value="' . $metadatum_value_latlng . '" class="widefat">'; | |
$html .= ' | |
<script> | |
(function(window, google) { | |
var addressBoxElement = document.getElementById(\'' . $metadatum_id . '\'); | |
var latlngBoxElement = document.getElementById(\'' . $metadatum_id . '_latlng\'); | |
var addressBox = new google.maps.places.Autocomplete(addressBoxElement, {types: [\'address\'] }); | |
addressBox.addListener(\'place_changed\', function() { | |
var selectedPlace = addressBox.getPlace(); | |
addressBoxElement.value = selectedPlace.formatted_address.replace(\', USA\',\'\'); | |
latlngBoxElement.value = selectedPlace.geometry.location.lat() + \',\' + selectedPlace.geometry.location.lng(); | |
}); | |
}(window, google)); | |
</script> | |
'; | |
break; | |
case 'toggle' : | |
$html .= ' | |
<style> | |
toggle::after { | |
display:block; | |
clear:both; | |
content:""; | |
} | |
toggle input { | |
position:absolute; | |
left:-99999999px; | |
} | |
toggle svg { | |
height:20px; | |
width:20px; | |
display:block; | |
} | |
toggle label div { | |
display:block; | |
float:left; | |
padding:6px 12px; | |
border:solid 1px rgb(160,160,160); | |
fill:gray; | |
position: relative; | |
} | |
toggle label:first-child div { | |
border-radius:5px 0 0 5px; | |
left: 1px; | |
} | |
toggle label:last-child div { | |
border-radius:0 5px 5px 0; | |
right: 1px; | |
} | |
toggle input:checked ~ div { | |
color:white; | |
fill:white; | |
background-color: ' . $admin_colors . '; | |
border-color: ' . $admin_colors . '; | |
z-index:1; | |
} | |
</style>'; | |
$html .= '<p class="post-attributes-label-wrapper"><label class="post-attributes-label">' . $metadatum_label .'</div></p>'; | |
$html .= '<div class="metadata-desc">' . $metadatum_desc .'</div>'; | |
$html .= '<toggle>'; | |
foreach ( $metadatum_options as $metadatum_option ) { | |
$html .= '<label><input type="radio" name="' . $metadatum_id .'"' . ( $metadatum_option['value'] == $metadatum_value ? ' checked="checked"' : '' ) . ' value="' . $metadatum_option['value'] . '"><div>' . $metadatum_option['label'] . '</div></label>'; | |
} | |
$html .= '</toggle>'; | |
break; | |
default : | |
$html .= '<p class="post-attributes-label-wrapper"><label class="post-attributes-label" for="' . $metadatum_id . '">' . $metadatum_label .'</label></p>'; | |
$html .= '<div class="metadata-desc">' . $metadatum_desc .'</div>'; | |
$html .= '<input type="' . $metadatum_type . '" name="' . $metadatum_id .'" id="' . $metadatum_id .'" value="' . $metadatum_value . '" class="widefat" />'; | |
break; | |
} | |
$html .= '</div>'; | |
} | |
echo $html . '<input type="hidden" name="custommeta_noncename" id="custommeta_noncename" value="' . wp_create_nonce( basename(__FILE__) ) . '" />'; | |
} | |
public function expose_metadata_via_restapi() { | |
if ( $this->metaboxes != null ) { | |
foreach ($this->metaboxes as $metabox) { | |
foreach ( $metabox['metadata'] as $metadatum ) { | |
register_rest_field($this->full_slug(), $this->full_slug(). '_metadata_' . $metadatum['slug'], | |
array( | |
'get_callback' => array( $this, 'slug_get_post_meta_cb' ), | |
'update_callback' => array( $this, 'slug_update_post_meta_cb' ), | |
'schema' => null, | |
) | |
); | |
} | |
} | |
} | |
} | |
public function slug_get_post_meta_cb( $object, $field_name, $request ) { | |
return get_post_meta( $object['id'], $field_name ); | |
} | |
public function slug_update_post_meta_cb( $value, $object, $field_name ) { | |
return update_post_meta( $object['id'], $field_name, $value ); | |
} | |
public function save_ctp_custom_metadata( $post_id, $post, $update ) { | |
if (empty($_POST["custommeta_noncename"])) { | |
return; | |
} | |
if ( ! wp_verify_nonce( $_POST['custommeta_noncename'], basename(__FILE__) )) { | |
return; | |
} | |
if ( ! current_user_can( 'edit_post', $post->ID )) { | |
return; | |
} | |
if ( $post->post_type == 'revision' ) { | |
return; | |
} | |
$post_type_slug = get_post_type($post); | |
$metadata_id = ''; | |
$metadata_object = array(); | |
if ( $this->metaboxes != null ) { | |
foreach ( $this->metaboxes as $metabox ) { | |
foreach ( $metabox['metadata'] as $metadatum ) { | |
$metadata_id = $post_type_slug . '_metadata_' . $metadatum['slug']; | |
$metadata_object[$metadata_id] = $_POST[$metadata_id]; | |
if ( $metadatum['type'] == 'text_googleaddress' ) { | |
$metadata_id = $post_type_slug . '_metadata_' . $metadatum['slug'].'_latlng'; | |
$metadata_object[$metadata_id] = $_POST[$metadata_id]; | |
} | |
} | |
} | |
} | |
// Add values of $metadata_saving as custom fields | |
foreach ($metadata_object as $key => $value) { | |
$value = implode(',', (array)$value); | |
if (get_post_meta($post->ID, $key, FALSE)) { | |
update_post_meta($post->ID, $key, $value); | |
} else { | |
add_post_meta($post->ID, $key, $value); | |
} if (!$value) { | |
delete_post_meta($post->ID, $key); | |
} | |
} | |
} | |
public function create_ctp_icons(){ | |
if ( $this->icon !== null ) { | |
echo '<style>#adminmenu #menu-posts-' . $this->full_slug() . ' div.wp-menu-image:before {content: "' . $this->icon . '";}</style>'; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Added: custom strings , menu_icon
Updated: optional taxonomy (if not set)