Skip to content

Instantly share code, notes, and snippets.

@Kelderic
Last active August 8, 2023 13:39
Show Gist options
  • Save Kelderic/dc641aced67f0c0cb0a4a1ded17fa0d4 to your computer and use it in GitHub Desktop.
Save Kelderic/dc641aced67f0c0cb0a4a1ded17fa0d4 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 $supports;
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->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->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, 'add_onetime_assets' ) );
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 strtolower($this->prefix.'_'.$this->slug);
}
public function create_ctp() {
$post_type_slug = strtolower($this->prefix.'_'.$this->slug);
$post_label = ucfirst( $this->slug );
if ( $this->taxonomy_slug ) {
$taxonomy_slug = $post_type_slug.'_'.$this->taxonomy_slug;
$taxonomy_label = ucwords( str_replace( '_', ' ', $this->taxonomy_slug ) );
} else {
$taxonomy_slug = $post_type_slug.'_type';
$taxonomy_label = $post_label.' Type';
}
// SPECIFY THE ARGUMENTS FOR THE CTP'S TAXONOMY, THEN REGISTER IT
$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, $args);
// 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',
),
'hierarchical' => $this->hierarchical,
'public' => true,
'show_in_rest' => true,
'has_archive' => true,
'supports' => $this->supports,
'register_meta_box_cb' => array( $this, 'create_ctp_custom_metaboxes' ),
'taxonomies' => array( $taxonomy_slug )
);
// 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 .= '
<tab-set>
<input id="tab-1" type="radio" name="radio-set" class="tab-1" checked="checked">
<label for="tab-1">Encoded Polygon</label>
<input id="tab-2" type="radio" name="radio-set" class="tab-2">
<label for="tab-2">Raw Coordinates</label>
<input id="tab-3" type="radio" name="radio-set" class="tab-3">
<label for="tab-3">Map Preview</label>
<content>
<section class="content-1">
<textarea style="min-height:200px;" name="' . $metadatum_id . '" id="' . $metadatum_id . '" class="widefat">' . $metadatum_value . '</textarea>
</section>
<section class="content-2">
<textarea style="min-height:200px;" id="' . $metadatum_id . '-decoded" class="widefat"></textarea>
</section>
<section class="content-3">
<div id="map-wrap" style="max-height:400px;height:80vw;"></div>
</section>
</content>
</tab-set>
<div style="clear:both;"></div>
';
$html .= '<div class="metadata-desc">' . $metadatum_desc .'</div>';
$html .= '
<script>
(function(window, Mapstractor) {
window.setTimeout(function(){
// LOAD ELEMENT REFERENCES
var inputs = {
encodedPolygon: document.getElementById(\'' . $metadatum_id . '\'),
rawLatLng: document.getElementById(\'' . $metadatum_id . '-decoded\')
};
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,
}
});
// UPDATE THE MAP PREVIEW AND RAW LATLNGS INPUT
inputs.rawLatLng.value = decodeLatLngEls(inputs.encodedPolygon.value);
updateMapPreview();
// ADD EVENT LISTENERS
inputs.encodedPolygon.addEventListener(\'change\', function() {
inputs.rawLatLng.value = decodeLatLngEls(inputs.encodedPolygon.value);
updateMapPreview();
}, false);
inputs.rawLatLng.addEventListener(\'paste\', function() {
window.setTimeout(function(){
inputs.rawLatLng.value = inputs.rawLatLng.value.replace(new RegExp(\' \', \'g\'), \'\');
},10);
}, false);
inputs.rawLatLng.addEventListener(\'change\', function() {
inputs.rawLatLng.value = inputs.rawLatLng.value.replace(new RegExp(\' \', \'g\'), \'\');
inputs.encodedPolygon.value = encodeLatLngEls(inputs.rawLatLng.value);
updateMapPreview();
}, false);
// FUNCTIONS TO UPDATE THE CONTENT OF THE TABS
function updateMapPreview() {
if ( inputs.encodedPolygon.value ) {
if ( polygon == null ) {
polygon = map.addPolygon({
encodedCoordinates: inputs.encodedPolygon.value,
color: \'#808080\'
});
map.gMap.panTo(polygon.getCenter());
} else {
polygon.setOptions({paths: google.maps.geometry.encoding.decodePath(inputs.encodedPolygon.value)});
map.gMap.panTo(polygon.getCenter());
}
} else {
if ( polygon !== null ) {
polygon.setMap(null);
polygon = null;
}
}
}
// FUNCTIONS TO ENCODE AND DECODE THE LATLNGS
function encodeLatLngEls(rawLatLngEls) {
console.log(rawLatLngEls)
if ( rawLatLngEls === \'\' ) {
return \'\';
} else {
var lnglatStrings = rawLatLngEls.split("\n");
var latLngObjects = [];
lnglatStrings.forEach(function(lnglatPair){
let parts = lnglatPair.replace(\',0\',\'\').split(\',\');
let lat = parseFloat(parts[1]);
let lng = parseFloat(parts[0]);
latLngObject = new google.maps.LatLng({lat: lat, lng: lng})
latLngObjects.push(latLngObject)
});
return google.maps.geometry.encoding.encodePath(latLngObjects);
}
}
function decodeLatLngEls(encodedPolygon) {
if ( encodedPolygon === \'\' ) {
return \'\';
} else {
var latLngObjects = google.maps.geometry.encoding.decodePath(encodedPolygon);
var lnglatStrings = [];
latLngObjects.forEach(function(latLngObject){
let parts = latLngObject.toString().replace(\')\',\'\').replace(\'(\',\'\').replace(\' \',\'\').split(\',\');
let lat = parseFloat(parseFloat(parts[0]).toFixed(6));
let lng = parseFloat(parseFloat(parts[1]).toFixed(6));
lnglatStrings.push(lng + \',\' + lat + \',0\');
});
return lnglatStrings.join("\n");
}
}
}, 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(
strtolower($this->prefix.'_'.$this->slug),
strtolower($this->prefix.'_'.$this->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 add_onetime_assets() {
echo '
<style>
tab-set {
position: relative;
display:block;
}
tab-set > label {
float: left;
display: block;
font-size:0.9em;
padding: 0px 10px;
margin:6px 6px 0 0;
cursor:pointer;
height:30px;
line-height:30px;
position: relative;
z-index: 1;
}
tab-set input {
position: absolute;
z-index: 1000;
width: 25%;
height: 50px;
left: -9999px;
top: 0;
opacity: 0;
cursor: pointer;
margin: 0;
}
tab-set input:hover + label {
background-color:rgb(245,245,245);
}
tab-set input:checked + label {
background-color:rgb(245,245,245);
z-index: 1;
top: 1px;
border-width:1px 1px 0 1px;
border-style:solid;
border-color:rgb(210, 210, 210);
}
tab-set > content {
height: auto;
width: 100%;
float: left;
box-sizing: border-box;
padding:0;
border: solid 1px rgb(210, 210, 210);
background: rgb(245,245,245);
}
tab-set > content > section {
position: relative;
float: left;
width: 0;
height: 0;
box-sizing: border-box;
top: 0;
left: 0;
z-index: 1;
opacity: 0;
}
tab-set .tab-1:checked ~ content .content-1,
tab-set .tab-2:checked ~ content .content-2,
tab-set .tab-3:checked ~ content .content-3,
tab-set .tab-4:checked ~ content .content-4,
tab-set .tab-5:checked ~ content .content-5,
tab-set .tab-6:checked ~ content .content-6,
tab-set .tab-7:checked ~ content .content-7 {
z-index: 100;
opacity: 1;
width: 100%;
height: auto;
padding:10px;
}
tab-set.sticky {
background-color:white;
padding-top:30px;
position:sticky;
top:150px;
z-index:5;
}
@media (min-width:901px) {
tab-set.sticky {
top:80px;
}
}
';
if ( $this->icon !== null ) {
echo '#adminmenu #menu-posts-' . strtolower($this->prefix.'_'.$this->slug) . ' div.wp-menu-image:before {
content: "' . $this->icon . '";
}';
}
echo '
</style>
';
}
}
}
@Kelderic
Copy link
Author

Kelderic commented Jun 16, 2016

Example usage:


/***********************************************************************/
/*********************  INITIALIZE TRACKER CLASS  **********************/
/***********************************************************************/

$custom_post_types = new Custom_Post_Types_Manager(
    array(
        'prefix' => 'azm'
    )
);


/***********************************************************************/
/********************  ADD NEW CUSTOM POST TYPES  **********************/
/***********************************************************************/

$custom_post_types->setup_ctp(
    array(
        'slug' => 'download',
        'icon' => '\f316',
        'display_page' => 'support/download',
    )
);

$custom_post_types->setup_ctp(
    array(
        'slug' => 'employee',
        'taxonomy_slug' => 'department',
        'supports' => array( 'title', 'thumbnail' ),
        'icon' => '\f338',
        'display_page' => 'about/our-people',
        'metaboxes' => array(
            array(
                'slug' => 'details',
                'label' => 'Details',
                'position' => 'normal',
                'metadata' => array(
                    array(
                        'slug' => 'jobtitle',
                        'label' => 'Job Title:',
                        'type' => 'text',
                    ),
                    array(
                        'slug' => 'linkedin',
                        'label' => 'LinkedIn Public URL:',
                        'type' => 'text',
                    ),
                    array(
                         'slug' => 'numThumbnails',
                         'label' => 'Max Thumbnails in List:',
                         'type' => 'select',
                         'options' => array(
                              'One' => '1',
                              'Two' => '2',
                              'Three' => '3',
                              'Four' => '4',
                              'Five' => '5'
                         ),
                         'default' => '5'
                    ),
                ),
            ),
        ),
    )
);

// RESTRICT INTERACTING (IN THE ADMIN AREA) WITH THE CTP TO USERS WHO HAVE THE ROLE OF EDITOR OR HIGHER

$custom_post_types->setup_ctp(
    array(
        'slug' => 'email',
        'icon' => '\f466',
        'display_page' => 'emails',
        'min_role' => 'editor'
    )
);

@hughbris
Copy link

Thanks for sharing, this is extremely useful.

I got an error on the lines which called the undefined log_it function, but they were easily commented out.

I noticed this created a 'x Type' taxonomy when I didn't pass in a taxonomy_slug array key. I think I will hack this further to only register the taxonomy if ( $this->taxonomy_slug ) as per line 81. However, I am just building this CPT out from basic and it will probably more likely utilise at least a couple of custom taxonomies eventually. I may need to extend this to cater for that if I can. Will fork and share if I do :)

Thanks again. So rare to see unconditional sharing in Wordpress these days.

@Kelderic
Copy link
Author

Kelderic commented Jun 9, 2017

Thanks for the kind words, hughbris. I just got tired of reinventing the wheel for every custom post type, so I abstracted it to save time. WP needs to fix up the API, honestly. Sorry about the log_it call, I'll remove it when I have a bit of time. It was just for debugging and must have slipped in.

As for the taxonomy slug thing, that just allows for overriding the slug, not for overriding whether or not their IS a taxonomy. It's fixed as just one taxonomy. Should be changed to allow zero or multiple eventually, but I've never needed that yet.

@Kelderic
Copy link
Author

Kelderic commented Oct 9, 2017

Not sure if anyone is still watching this, but I've added the ability to specify minimum user role. It only supports the official roles right now: administrator, editor, contributor. No need for subscriber because that is the same as putting nothing, ie anyone logged in can interact with the CTP. See the first comment with examples.

Also, I'm planning on converting this from a Gist to a full repo, given the interest I've seen from the forks and stars. I'll be able to give better examples there with a readme.

@Kelderic
Copy link
Author

Kelderic commented Oct 3, 2019

Most recent revision brings a complete rework of the special Google Maps Polygon textbox metabox. It's more self contained, but it also now has the ability to accept both encoded polygons AND raw coordindates (Lng,Lat and Lng,Lat,El format).

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