Last active
August 8, 2023 13:39
-
-
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.
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 | |
/***********************************************************************/ | |
/*************************** 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> | |
'; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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).