Skip to content

Instantly share code, notes, and snippets.

@Bot12313
Forked from Kelderic/custom_post_types.php
Created September 2, 2019 15:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bot12313/4d9f6e209fb361388f051ed1e8b0e9b5 to your computer and use it in GitHub Desktop.
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.
<?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>';
}
}
}
}
@Bot12313
Copy link
Author

Bot12313 commented Sep 2, 2019

Added: custom strings , menu_icon
Updated: optional taxonomy (if not set)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment