Created
April 4, 2013 15:41
-
-
Save dtbaker/5311512 to your computer and use it in GitHub Desktop.
hacking WordPress to support nested custom post types.
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 | |
// get_page_by_path called in query.php 2119 ! | |
//remove_action('template_redirect', 'redirect_canonical'); | |
class dtbakerWiki{ | |
public function __construct(){ | |
add_action( 'init', array($this, 'register_custom_post_type')); | |
add_action( 'init', array($this, 'wiki_custom_rewrite_stuff')); | |
add_action( 'init', array($this, 'wiki_endpoints') ); | |
// the nastiest hacks ever: | |
add_action('parse_query',array($this,'wiki_parse_query_hack')); | |
add_action('query',array($this,'wiki_worst_wordpress_hack_of_all')); | |
// front end stuff | |
add_action("template_redirect", array($this, 'wiki_page_template')); | |
add_shortcode( 'wiki_subpages', array($this, 'dtbaker_wiki_list_subpages' )); | |
add_shortcode( 'wiki_subpages_changes', array($this, 'dtbaker_wiki_list_subpages_changes' )); | |
add_shortcode( 'wiki_page_changes', array($this, 'list_post_revisions' )); | |
// show our parent page selection box. | |
add_action('admin_menu', function() { | |
remove_meta_box('pageparentdiv', 'wiki_page', 'normal'); | |
}); | |
add_action('add_meta_boxes', array($this,'wiki_meta_box')); | |
add_filter('nav_menu_css_class' , array($this,'wiki_nav_class') , 4 , 2); | |
// wiki perissions: | |
// map meta capabilities | |
add_filter( 'map_meta_cap', array($this, 'map_meta_cap'), 10, 4 ); | |
// give all users wiki edit permissionss | |
if(is_user_logged_in() && !current_user_can('administrator')){ | |
// this gives all logged in users edit permissions on the wiki | |
add_filter( 'user_has_cap', array($this, 'wiki_user_has_cap'), 10, 3); | |
} | |
} | |
function wiki_worst_wordpress_hack_of_all($query){ | |
// we are going to try hack our get_page_by_path SQL query which looks like this: | |
// this change will make wordpress support nested posts of different post_types (page and wiki_page) | |
// $pages = $wpdb->get_results( "SELECT ID, post_name, post_parent, post_type FROM $wpdb->posts WHERE post_name IN ($in_string) AND (post_type = '$post_type_sql' OR post_type = 'attachment')", OBJECT_K ); | |
if(preg_match('#SELECT ID, post_name, post_parent, post_type FROM [^\s]+ WHERE post_name IN \([^\)]+\) AND \(post_type = \'wiki_page\'#',$query)){ | |
$query = str_replace("(post_type = 'wiki_page' OR","(post_type = 'wiki_page' OR post_type = 'page' OR",$query); | |
remove_filter('query','dtbaker_query'); | |
} | |
return $query; | |
} | |
function wiki_parse_query_hack($query){ | |
if(!isset($query->query['post_type'])){ | |
// use get_page_by_path to see if this url is a page. | |
// if it isn't, assume it's a wiki_post, and try that. | |
$url = isset($query->query['name']) ? $query->query['name'] : isset($query->query['pagename']) ? $query->query['pagename'] : false; | |
if($url){ | |
if(get_page_by_path($url,OBJECT,'page')){ | |
// it's a page, no messin! | |
}else{ | |
// see if it's a wiki_page. | |
if(get_page_by_path($url,OBJECT,'wiki_page')){ | |
$query->query['page'] = ''; | |
$query->query['post_type'] = 'wiki_page'; | |
$query->query['wiki_page'] = $url; | |
$query->query['name'] = $url; | |
$query->query_vars['page'] = ''; | |
$query->query_vars['post_type'] = 'wiki_page'; | |
$query->query_vars['wiki_page'] = $url; | |
$query->query_vars['name'] = $url; | |
if(isset($query->query['pagename'])){ | |
unset($query->query['pagename']); | |
} | |
} | |
} | |
//print_r($query); | |
} | |
} | |
return $query; | |
} | |
// meta box on wiki pages: | |
function wiki_meta_box() { | |
add_meta_box('wiki-parent', 'Parent', array($this,'wiki_meta_chapter_attributes_meta_box'), 'wiki_page', 'side', 'high'); | |
} | |
function wiki_meta_chapter_attributes_meta_box($post) { | |
$post_type_object = get_post_type_object($post->post_type); | |
if ( $post_type_object->hierarchical ) { | |
// todo: wp_dropdown_pages doesn't support multiple post_types. | |
// todo: work out how to show both pages and wiki_pages in this list. | |
$pages = wp_dropdown_pages(array( | |
'post_type' => 'page', | |
'selected' => $post->post_parent, | |
'name' => 'parent_id', | |
'show_option_none' => __('(no parent)'), | |
'sort_column'=> | |
'menu_order, post_title', | |
'echo' => 0) | |
); | |
if ( ! empty($pages) ) { | |
echo $pages; | |
} // end empty pages check | |
} // end hierarchical check. | |
} | |
function wiki_nav_class($classes, $item){ | |
if( in_array('current-wiki_page-ancestor', $classes) ){ | |
$classes[] = 'current-page-ancestor '; // just for kicks. | |
$classes[] = 'active '; // our bootstrap class. | |
} | |
return $classes; | |
} | |
function wiki_endpoints(){ | |
add_rewrite_endpoint( 'wikiedit', EP_ALL); | |
add_rewrite_endpoint( 'wikirevision', EP_ALL); | |
} | |
function wiki_custom_rewrite_stuff($wp) { | |
// try this | |
global $wp_rewrite; | |
// print_r($wp_rewrite->extra_permastructs['wiki_page']); | |
$wp_rewrite->extra_permastructs['wiki_page']['struct'] = '%wiki_page%'; | |
/* global $wp_query; | |
if($wp_query){ | |
$wp_query->get_queried_object(); | |
if($wp_query->queried_object){ | |
//print_r($wp_query->queried_object); | |
//$wp_query->queried_object->post_parent = 611; | |
//$wp_query->post = $wp_query->queried_object; | |
} | |
}*/ | |
} | |
function wiki_page_template() { | |
global $wp,$wp_query,$post; | |
// we look for any custom endpoints | |
// if this is not a request for json or a singular object then bail | |
if (is_singular() && $post && $post->post_type == 'wiki_page'){ | |
//!empty($wp_query->queried_object_id) && dtbaker_wiki_is_page_a_wiki_page($wp_query->queried_object_id)) { | |
if(isset( $wp_query->query_vars['wikiedit'] )){ | |
$templatefilename = 'wiki-edit.php'; | |
}else if(isset( $wp_query->query_vars['wikirevision'] )){ | |
$revision_id = isset($_GET['wikirevision']) && (int)$_GET['wikirevision']>0 ? $_GET['wikirevision']: false; | |
if($revision_id && $post && $post->ID){ | |
$newpost = get_post($revision_id); | |
if ($newpost != null && $newpost->post_type == 'revision' && $newpost->post_parent == $post->ID) { | |
# reset posts so the revision is displayed instead of the original | |
global $posts; | |
$posts = array($newpost); | |
} | |
} | |
$templatefilename = 'wiki-revisions.php'; | |
}else{ | |
$templatefilename = 'wiki-template.php'; | |
} | |
if( file_exists( get_template_directory() .'/'.$templatefilename)){ | |
$return_template = get_template_directory() .'/'.$templatefilename; | |
}else if (file_exists(dirname( __FILE__ ) . '/templates/' . $templatefilename)) { | |
$return_template = dirname( __FILE__ ) . '/templates/' . $templatefilename; | |
} | |
if (have_posts() && isset($return_template)) { | |
include($return_template); | |
die(); | |
} else { | |
$wp_query->is_404 = true; | |
} | |
} | |
} | |
// Register Custom Post Type | |
public function register_custom_post_type() { | |
$labels = array( | |
'name' => 'Wiki Pages', | |
'singular_name' => 'Wiki Page', | |
'menu_name' => 'Wiki Pages', | |
'parent_item_colon' => 'Parent Wiki Page:', | |
'all_items' => 'All Wiki Pages', | |
'view_item' => 'View Wiki Page', | |
'add_new_item' => 'Add New Wiki Page', | |
'add_new' => 'New Wiki Page', | |
'edit_item' => 'Edit Wiki Page', | |
'update_item' => 'Update Wiki Page', | |
'search_items' => 'Search wiki pages', | |
'not_found' => 'No wiki pages found', | |
'not_found_in_trash' => 'No wiki pages found in Trash', | |
); | |
$rewrite = array( | |
'slug' => 'wiki', | |
// 'slug' => 'support/documentation-wiki', // todo: option this out. | |
//'slug' => '/', // todo: option this out. | |
'with_front' => false, | |
'pages' => true, | |
'feeds' => true, | |
); | |
$capabilities = array( | |
'edit_post' => 'edit_wiki_page', | |
'read_post' => 'read_wiki_page', | |
'delete_post' => 'delete_wiki_page', | |
'edit_posts' => 'edit_wiki_pages', | |
'edit_others_posts' => 'edit_others_wiki_pages', | |
'publish_posts' => 'publish_wiki_pages', | |
'read_private_posts' => 'read_private_wiki_pages', | |
'delete_posts' => 'delete_wiki_pages', | |
'delete_private_posts' => 'delete_private_wiki_pages', | |
'delete_published_posts' => 'delete_published_wiki_pages', | |
'delete_others_posts' => 'delete_others_wiki_pages', | |
'edit_private_posts' => 'edit_private_wiki_pages', | |
'edit_published_posts' => 'edit_published_wiki_pages', | |
); | |
$args = array( | |
'label' => 'wiki_page', | |
'description' => 'Wiki pages', | |
'labels' => $labels, | |
'supports' => array( 'title', 'editor', 'revisions', 'comments', 'page-attributes'), | |
'taxonomies' => array( ),//'category', 'post_tag' | |
'hierarchical' => true, | |
'public' => true, | |
'show_ui' => true, | |
'show_in_menu' => true, | |
'show_in_nav_menus' => true, | |
'show_in_admin_bar' => true, | |
'menu_position' => 25, | |
'menu_icon' => '', | |
'can_export' => true, | |
'has_archive' => false, // important for our support/documentation-wiki slug | |
'exclude_from_search' => false, | |
'publicly_queryable' => true, | |
'rewrite' => false,//$rewrite, | |
//'capabilities' => $capabilities, | |
'capability_type' => array('wiki','wikis'), | |
'map_meta_cap' => true, | |
); | |
if(current_user_can('administrator')){ | |
$args['capability_type'] = array('page','pages'); | |
} | |
// flush_rewrite_rules(); | |
register_post_type( 'wiki_page', $args ); | |
} | |
public function map_meta_cap($caps, $cap, $user_id, $args){ | |
/* If editing, deleting, or reading a wiki_page, get the post and post type object. */ | |
if ( 'edit_wiki_page' == $cap || 'delete_wiki_page' == $cap || 'read_wiki_page' == $cap ) { | |
$post = get_post( $args[0] ); | |
$post_type = get_post_type_object( $post->post_type ); | |
/* Set an empty array for the caps. */ | |
$caps = array(); | |
} | |
/* If editing a wiki_page, assign the required capability. */ | |
if ( 'edit_wiki_page' == $cap ) { | |
if ( $user_id == $post->post_author ) | |
$caps[] = $post_type->cap->edit_posts; | |
else | |
$caps[] = $post_type->cap->edit_others_posts; | |
} | |
/* If deleting a wiki_page, assign the required capability. */ | |
elseif ( 'delete_wiki_page' == $cap ) { | |
if ( $user_id == $post->post_author ) | |
$caps[] = $post_type->cap->delete_posts; | |
else | |
$caps[] = $post_type->cap->delete_others_posts; | |
} | |
/* If reading a private wiki_page, assign the required capability. */ | |
elseif ( 'read_wiki_page' == $cap ) { | |
if ( 'private' != $post->post_status ) | |
$caps[] = 'read'; | |
elseif ( $user_id == $post->post_author ) | |
$caps[] = 'read'; | |
else | |
$caps[] = $post_type->cap->read_private_posts; | |
} | |
/* Return the capabilities required by the user. */ | |
return $caps; | |
} | |
function wiki_user_has_cap($allcaps, $cap, $args){ | |
if(isset($_REQUEST['capstest'])){ | |
echo "Checking if user has caps "; print_r($args); | |
} | |
// Bail out if we're not asking about a post: | |
$caps_to_give = array( | |
'read_wiki_page', | |
'edit_others_wiki_pages', | |
'edit_wiki_page', | |
'edit_wiki_pages', | |
'publish_wiki_pages', | |
'edit_published_wiki_pages', | |
); | |
if( !in_array($args[0], $caps_to_give)){ | |
return $allcaps; | |
} | |
$allcaps[$cap[0]] = true; | |
return $allcaps; | |
} | |
public static function get_post_revisions($post, $skip_autosave=true, $limit_revision=0){ | |
if ( $post && $post->post_type == "revision" ) { | |
$post = get_post($post->post_parent); | |
} | |
$revision_count=1; | |
$sorted_revisions = array(); | |
if ( $revisions = wp_get_post_revisions( $post->ID ) ) { | |
$revision_id = isset($_GET['wikirevision']) && (int)$_GET['wikirevision']>0 ? $_GET['wikirevision']: $post->ID; | |
$modified = strtotime($post->post_modified_gmt . ' +0000'); | |
$current_revision_date = sprintf('%s at %s', date(get_option('date_format'), $modified), date(get_option('time_format'), $modified)); | |
$current_revision_author = get_the_author_meta('display_name', $post->post_author); | |
/*$sorted_revisions[$post->ID] = array( | |
'url' => get_permalink($post), | |
'post_title' => $post->post_title, | |
'current' => ($revision_id == $post->ID), | |
'date' => $current_revision_date, | |
'author' => $current_revision_author, | |
'timestamp' => $modified, | |
);*/ | |
foreach ( $revisions as $revision ) { | |
if ( $skip_autosave && wp_is_post_autosave($revision) ) { | |
continue; | |
} | |
if($limit_revision>0 && $revision_count>=$limit_revision)break; | |
$revision_count++; | |
$modified = strtotime($revision->post_modified_gmt . ' +0000'); | |
$rev_date = sprintf('%s at %s', date(get_option('date_format'), $modified), date(get_option('time_format'), $modified)); | |
$name = get_the_author_meta('display_name', $revision->post_author ); | |
$sorted_revisions[$revision->ID] = array( | |
'url' => add_query_arg('wikirevision',$revision->ID,get_permalink($post->ID)), | |
'post_title' => $post->post_title, | |
'current' => ($revision_id == $revision->ID), | |
'date' => $rev_date, | |
'author' => $name, | |
'timestamp' => $modified, | |
); | |
} | |
} | |
return $sorted_revisions; | |
} | |
function list_post_revisions($args) { | |
global $post; | |
$limit_revision = 7; // todo- restrict a user from performing more than this many chagnes to a single post one after the other. | |
$revisions = $this->get_post_revisions($post, true, $limit_revision); | |
$items = ''; | |
if(count($revisions)){ | |
$items .= "<h4>Latest ".count($revisions)." revisions</h4>"; | |
$items .= '<ul>'; | |
foreach($revisions as $revision){ | |
$items .= '<li>'; | |
if($revision['current']){ | |
$items .= $revision['date'] .' by ' . $revision['author'] .' (<em>'.(isset($args['currenttext'])?htmlspecialchars($args['currenttext']):'displayed above').'</em>)'; | |
}else{ | |
$items .= '<a href="'.$revision['url'].'">'.$revision['date'].'</a>' .' by ' . $revision['author']; | |
} | |
$items .= '</li>'; | |
} | |
$items .= '</ul>'; | |
} | |
return $items; | |
} | |
function dtbaker_wiki_list_subpages( $atts ) { | |
global $post; | |
$return = ''; | |
extract( shortcode_atts( array( | |
'depth' => '0', | |
'child_of' => '0', | |
'exclude' => '0', | |
'exclude_tree' => '', | |
'include' => '0', | |
'title_li' => '', | |
'number' => '', | |
'offset' => '', | |
'meta_key' => '', | |
'meta_value' => '', | |
'show_date' => '', | |
'sort_column' => 'menu_order, post_title', | |
'sort_order' => 'ASC', | |
'link_before' => '', | |
'link_after' => '', | |
'class' => '', | |
'post_type' => 'wiki_page', | |
), $atts ) ); | |
$page_list_args = array( | |
'depth' => $depth, | |
'child_of' => $child_of, //$post->ID, | |
'exclude' => $exclude, | |
'exclude_tree' => $exclude_tree, | |
'include' => $include, | |
'title_li' => $title_li, | |
'number' => $number, | |
'offset' => $offset, | |
'meta_key' => $meta_key, | |
'meta_value' => $meta_value, | |
'show_date' => $show_date, | |
'date_format' => get_option('date_format'), | |
'echo' => 0, | |
'authors' => '', | |
'sort_column' => $sort_column, | |
'sort_order' => $sort_order, | |
'link_before' => $link_before, | |
'link_after' => $link_after, | |
'walker' => '', | |
'post_type' => $post_type, | |
); | |
$list_pages = wp_list_pages( $page_list_args ); | |
$return = ''; | |
if ($list_pages) { | |
$return .= '<ul class="page-list subpages-page-list '.$class.'">'."\n".$list_pages."\n".'</ul>'; | |
}else{ | |
$return .= '<!-- no pages to show -->'; | |
} | |
return $return; | |
} | |
function dtbaker_wiki_list_subpages_changes_sort($a,$b){ | |
return $a['timestamp'] <= $b['timestamp']; | |
} | |
function dtbaker_wiki_list_subpages_changes( $atts ) { | |
global $post; | |
$return = ''; | |
extract( shortcode_atts( array( | |
'limit_changes' => 10, | |
'depth' => '0', | |
'child_of' => '0', | |
'exclude' => '0', | |
'exclude_tree' => '', | |
'include' => '0', | |
'title_li' => '', | |
'number' => '', | |
'offset' => '', | |
'meta_key' => '', | |
'meta_value' => '', | |
'show_date' => '', | |
'sort_column' => 'menu_order, post_title', | |
'sort_order' => 'ASC', | |
'link_before' => '', | |
'link_after' => '', | |
'class' => '', | |
'post_type' => 'wiki_page', | |
), $atts ) ); | |
$page_list_args = array( | |
'depth' => $depth, | |
'child_of' => $child_of, | |
'exclude' => $exclude, | |
'exclude_tree' => $exclude_tree, | |
'include' => $include, | |
'title_li' => $title_li, | |
'number' => $number, | |
'offset' => $offset, | |
'meta_key' => $meta_key, | |
'meta_value' => $meta_value, | |
'show_date' => $show_date, | |
'date_format' => get_option('date_format'), | |
'echo' => 0, | |
'authors' => '', | |
'sort_column' => $sort_column, | |
'sort_order' => $sort_order, | |
'link_before' => $link_before, | |
'link_after' => $link_after, | |
'walker' => '', | |
'post_type' => $post_type, | |
); | |
$pages = get_pages($page_list_args); | |
$all_revisions = array(); | |
foreach($pages as $page){ | |
$all_revisions = array_merge($all_revisions, $this->get_post_revisions($page, true, $limit_changes)); | |
} | |
usort($all_revisions,array($this,'dtbaker_wiki_list_subpages_changes_sort')); | |
$limit_count=0; | |
$items = ''; | |
if(count($all_revisions)){ | |
$items .= "<h4>Latest ".min($limit_changes,count($all_revisions))." Wiki Changes</h4>"; | |
$items .= '<ul>'; | |
foreach($all_revisions as $revision){ | |
if($limit_changes > 0 && $limit_count > $limit_changes){ | |
break; | |
} | |
$limit_count++; | |
$items .= '<li>'; | |
$items .= $revision['date'].' change to <a href="'.$revision['url'].'">'.htmlspecialchars($revision['post_title']) .'</a> by ' . $revision['author']; | |
$items .= '</li>'; | |
} | |
$items .= '</ul>'; | |
} | |
return $items; | |
} | |
} | |
new dtbakerWiki(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment