Skip to content

Instantly share code, notes, and snippets.

@dtbaker
Created April 4, 2013 15:41
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dtbaker/5311512 to your computer and use it in GitHub Desktop.
Save dtbaker/5311512 to your computer and use it in GitHub Desktop.
hacking WordPress to support nested custom post types.
<?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