Skip to content

Instantly share code, notes, and snippets.

Created Nov 8, 2011
Embed
What would you like to do?
<?php
/**
* Plugin Name: MMC Parks and Features
* Description: This plugin demonstrates how to route parent/child URLs like http://example.com/parks/yosemite/half-dome/in WordPress 3.3
* Author: Mike Schinkel
* Version: 2
* Author URL: http://about.me/mikeschinkel
* Notes:
* To answer http://lists.automattic.com/pipermail/wp-hackers/2011-November/041486.html
* Assumes a metabox that sets $post->post_parent field for a park feature with it's park's $post->ID.
*
*/
class MMC_Parks_and_Features {
static $PARK_FEATURE_URL_REGEX = '^parks/([^/]*)/([^/]*)/?';
/* MMC DEBUG BEGIN */
static $FEATURE_DETAIL_URL_REGEX = '^parks/([^/]*)/([^/]*)/([^/]*)/?';
/* MMC DEBUG END */
static function on_load() {
add_action( 'init', array( __CLASS__, 'init' ) );
add_filter( 'request', array( __CLASS__, 'request' ) );
add_filter( 'post_type_link', array( __CLASS__, 'post_type_link' ), 10, 2 );
//register_activation_hook( __FILE__, array( __CLASS__, 'activate_deactivate' ) );
//register_deactivation_hook( __FILE__, array( __CLASS__, 'activate_deactivate' ) );
}
/**
* Changes the permalink type for any post whose post_type is 'mmc_park_feature'
*
* @param string $link
* @param object $post
* @return string
*/
static function post_type_link( $link, $post ) {
if ( 'mmc_park_feature' == $post->post_type ) {
$parent = get_post( $post->post_parent );
$link = preg_replace( '#^(https?://[^/]+/).*$#', "$1parks/{$parent->post_name}/{$post->post_name}/", $link );
}
/* MMC DEBUG BEGIN */
if ( 'mmc_feature_detail' == $post->post_type ) {
$parent = get_post( $post->post_parent );
$grandparent = get_post( $parent->post_parent );
$link = preg_replace( '#^(https?://[^/]+/).*$#', "$1parks/{$grandparent->post_name}/{$parent->post_name}/{$post->post_name}/", $link );
}
/* MMC DEBUG END */
return $link;
}
/**
* Test to make sure the URL is valid per the specified park and park feature referenced in the URL.
*
* @param $query_vars
* @return array
*/
static function request( $query_vars ) {
global $wp;
/* MMC DEBUG BEGIN */
if ( $wp->matched_rule == self::$FEATURE_DETAIL_URL_REGEX ) {
$park_feature = get_page_by_path( $query_vars['mmc_park_feature_qv'], OBJECT, 'mmc_park_feature' );
$feature_detail_query = new WP_Query( array(
'name' => $query_vars['mmc_feature_detail_qv'],
'post_type' => 'mmc_feature_detail',
'fields' => 'ids',
'post_parent' => $park->ID,
'posts_per_page' => 1,
'suppress_filters' => true,
));
if ( 0 == count( $feature_detail_query->posts ) ) {
$query_vars = array( 'error' => '404' );
}
}
/* MMC DEBUG END */
/**
* Trigger this test if the match URL rewrite rule is the one added by this plugin
*/
if ( $wp->matched_rule == self::$PARK_FEATURE_URL_REGEX ) {
/**
* Lookup the park's post
*/
$park = get_page_by_path( $query_vars['mmc_park_qv'], OBJECT, 'mmc_park' );
/**
* Lookup the park feature's post, scoped to the park
*/
$park_feature_query = new WP_Query( array(
'name' => $query_vars['mmc_park_feature_qv'],
'post_type' => 'mmc_park_feature',
'fields' => 'ids',
'post_parent' => $park->ID,
'posts_per_page' => 1,
'suppress_filters' => true,
));
/**
* If the park feature was not found for the park, trigger 404 error.
*/
if ( 0 == count( $park_feature_query->posts ) ) {
$query_vars = array( 'error' => '404' );
}
}
return $query_vars;
}
/**
* Register both mmc_park and mmc_park_feature post types.
*
* Note: I mostly copied this code from the question, I would typically use a helper function for labels.
*
* @return void
*/
static function init() {
/**
* Add the rewrite rule to be checked before other rewrite rules (i.e. 'top')
*/
add_rewrite_rule(
self::$PARK_FEATURE_URL_REGEX,
'index.php?post_type=mmc_park_feature&mmc_park_qv=$matches[1]&mmc_park_feature_qv=$matches[2]',
'top'
);
/* MMC DEBUG BEGIN */
add_rewrite_rule(
self::$FEATURE_DETAIL_URL_REGEX,
'index.php?post_type=park_detail&mmc_park_qv=$matches[1]&mmc_park_feature_qv=$matches[2]&mmc_feature_detail_qv=$matches[3]',
'top'
);
/* MMC DEBUG END */
register_post_type( 'mmc_park', array(
'labels' => array(
'name' => _x( 'Parks', 'mmc_park' ),
'singular_name' => _x( 'Park', 'mmc_park' ),
'add_new' => _x( 'Add New', 'mmc_park' ),
'add_new_item' => _x( 'Add New Park', 'mmc_park' ),
'all_items' => _x( 'Parks', 'mmc_park' ),
'edit_item' => _x( 'Edit Park', 'mmc_park' ),
'new_item' => _x( 'New Park', 'mmc_park' ),
'view_item' => _x( 'View Park', 'mmc_park' ),
'search_items' => _x( 'Search Parks', 'mmc_park' ),
'not_found' => _x( 'No Parks found', 'mmc_park' ),
'not_found_in_trash' => _x( 'No Parks found in Trash', 'mmc_park' ),
'parent_item_colon' => _x( 'Parent Park', 'mmc_park' ),
'menu_name' => _x( 'Parks', 'mmc_park' ),
),
'hierarchical' => true,
'description' => '',
'supports' => array( 'title', 'editor', 'author', 'revisions',
'custom-fields', 'comments', 'thumbnail' ),
'public' => true,
'show_ui' => true,
'show_in_nav_menus' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'has_archive' => true,
'query_var' => 'mmc_park_qv',
'can_export' => true,
'rewrite' => array( 'slug' => 'parks', 'with_front' => false ),
'capability_type' => 'page'
));
register_post_type( 'mmc_park_feature', array(
'labels' => array(
'name' => _x( 'Features', 'mmc_park_feature' ),
'singular_name' => _x( 'Feature', 'mmc_park_feature' ),
'add_new' => _x( 'Add New', 'mmc_park_feature' ),
'add_new_item' => _x( 'Add New Feature', 'mmc_park_feature' ),
'all_items' => _x( 'Features', 'mmc_park_feature' ),
'edit_item' => _x( 'Edit Feature', 'mmc_park_feature' ),
'new_item' => _x( 'New Feature', 'mmc_park_feature' ),
'view_item' => _x( 'View Feature', 'mmc_park_feature' ),
'search_items' => _x( 'Search Features', 'mmc_park_feature' ),
'not_found' => _x( 'No Features found', 'mmc_park_feature' ),
'not_found_in_trash' => _x( 'No Features found in Trash', 'mmc_park_feature' ),
'parent_item_colon' => _x( 'Parent Feature', 'mmc_park_feature' ),
'menu_name' => _x( 'Features', 'mmc_park_feature' ),
),
'hierarchical' => true,
'supports' => array( 'title', 'editor', 'author', 'revisions', 'custom-fields', 'comments', 'thumbnail' ),
'public' => true,
'show_ui' => true,
'show_in_nav_menus' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'has_archive' => true,
'query_var' => 'mmc_park_feature_qv',
'can_export' => true,
'rewrite' => array( 'slug' => 'features', 'with_front' => false ),
'capability_type' => 'page'
));
register_post_type( 'mmc_feature_detail', array(
'labels' => array(
'name' => _x( 'Details', 'mmc_feature_detail' ),
'singular_name' => _x( 'Detail', 'mmc_feature_detail' ),
'add_new' => _x( 'Add New', 'mmc_feature_detail' ),
'add_new_item' => _x( 'Add New Detail', 'mmc_feature_detail' ),
'all_items' => _x( 'Details', 'mmc_feature_detail' ),
'edit_item' => _x( 'Edit Detail', 'mmc_feature_detail' ),
'new_item' => _x( 'New Detail', 'mmc_feature_detail' ),
'view_item' => _x( 'View Detail', 'mmc_feature_detail' ),
'search_items' => _x( 'Search Details', 'mmc_feature_detail' ),
'not_found' => _x( 'No Details found', 'mmc_feature_detail' ),
'not_found_in_trash' => _x( 'No Details found in Trash', 'mmc_feature_detail' ),
'parent_item_colon' => _x( 'Parent Detail', 'mmc_feature_detail' ),
'menu_name' => _x( 'Details', 'mmc_feature_detail' ),
),
'hierarchical' => true,
'supports' => array( 'title', 'editor', 'author', 'revisions', 'custom-fields', 'comments', 'thumbnail' ),
'public' => true,
'show_ui' => true,
'show_in_nav_menus' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'has_archive' => true,
'query_var' => 'mmc_feature_detail_qv',
'can_export' => true,
'rewrite' => array( 'slug' => 'details', 'with_front' => false ),
'capability_type' => 'page'
));
}
}
MMC_Parks_and_Features::on_load();
add_action( 'load-post.php', 'metabox_create' );
add_action( 'load-post-new.php', 'metabox_create' );
add_action( 'save_post', 'metabox_save' );
function metabox_create() {
add_meta_box( 'mmc_post_parent', 'Parent Park', 'metabox_display', 'mmc_park_feature', 'side', 'low', array( 'parent_type' => 'mmc_park' ) );
add_meta_box( 'mmc_post_parent', 'Parent Feature', 'metabox_display', 'mmc_feature_detail', 'side', 'low', array( 'parent_type' => 'mmc_park_feature' ) );
}
function metabox_display( $post, $metabox ) {
global $wpdb;
$parent_type = $metabox['args']['parent_type'];
$query = "SELECT ID, post_title
FROM $wpdb->posts
WHERE post_type = '{$parent_type}'
AND post_status = 'publish'
ORDER BY post_title";
$results = $wpdb->get_results( $query, OBJECT );
wp_nonce_field( plugin_basename( __FILE__ ), 'mmc_post_parent_noncename' );
?>
<select name="mmc_post_parent" id="mmc_post_parent">
<option value="0"></option>
<?php
$parent_post = $post->post_parent;
foreach ( $results as $result ) {
if ( $parent_post == $result->ID ) {
echo '<option value="'. $result->ID . '" selected="selected">' . $result->post_title . '</option>';
} else {
echo '<option value="'. $result->ID . '">' . $result->post_title . '</option>';
}
}
?>
</select>
<?php
}
function metabox_save( $post_id ) {
global $post;
global $wpdb;
if ( ! wp_verify_nonce( $_POST['mmc_post_parent_noncename'], plugin_basename(__FILE__) ) ) {
return $post_id;
}
$data = $_POST['mmc_post_parent'];
$wpdb->update( $wpdb->posts, array ( 'post_parent' => $data ), array ( 'ID' => $post_id ) );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment