Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

546 lines (464 sloc) 16.722 kb
<?php
/**
* Post Options API
*
* This is not a plugin, this is a file you should bundle together with
* your theme or plugin where you'd like to use the Post Options API.
* View the Readme file for more details and examples.
*
* Version 1.0.1
* Author: Konstantin Kovshenin (kovshenin@gmail.com)
* http://theme.fm
*
* License: GPL2
**/
/**
* Post Options Fields
*
* This is a helper class with static methods that can be used in
* callbacks when registering post options. These are used to create
* simple fields like text boxes, textareas, checkboxes and more. If
* something more customizable is needed, you can always run your own
* callback function.
*
* Methods come in pairs, the creator function (factory) and the actual callback.
* The factory function returns the callback in an array compatible with
* the post options API.
*
**/
if ( ! class_exists( 'Post_Options_Fields_1_0_1' ) ):
class Post_Options_Fields_1_0_1 {
// Used to output description if present (for less redundancy)
public static function description( $args = array() ) {
if ( isset( $args['description'] ) && ! empty( $args['description'] ) )
echo "<br /><span class='description'>{$args['description']}</span>";
}
/**
* Checkbox
*
* Give this checkbox a label and a description through
* the arguments array for the best look and feel.
**/
public static function checkbox( $args = array() ) {
$defaults = array(
'label' => '',
'description' => '',
'sanitize_callback' => '',
'sanitize_callback_args' => ''
);
$args = wp_parse_args( $args, $defaults );
extract( $args, EXTR_SKIP );
return array(
'function' => array( __CLASS__, '_checkbox' ),
'sanitize_callback' => $sanitize_callback,
'sanitize_callback_args' => $sanitize_callback_args,
'args' => array(
'label' => $label,
'description' => $description
)
);
}
public static function _checkbox( $args = array() ) {
?>
<label><input type="checkbox" name="<?php echo $args['name_attr']; ?>" value="1" <?php echo checked( (bool) $args['value'] ); ?> /> <?php echo $args['label']; ?></label>
<?php
self::description( $args );
}
/**
* Text Input
*
* Factory function accepts a description and a
* sanitize_callback if you need some validation.
**/
public static function text( $args = array() ) {
$defaults = array(
'description' => '',
'sanitize_callback' => '',
'sanitize_callback_args' => '',
);
$args = wp_parse_args( $args, $defaults );
extract( $args, EXTR_SKIP );
return array(
'function' => array( __CLASS__, '_text' ),
'sanitize_callback' => $sanitize_callback,
'sanitize_callback_args' => $sanitize_callback_args,
'args' => array(
'description' => $description
)
);
}
public static function _text ( $args = array() ) {
?>
<input class="large-text" type="text" name="<?php echo $args['name_attr']; ?>" value="<?php echo esc_attr( $args['value'] ); ?>" />
<?php
self::description( $args );
}
/**
* Textarea (multi-line text)
*
* Function accepts a description, rows and
* a sanitize_callback for validation.
**/
public static function textarea( $args = array() ) {
$defaults = array(
'description' => '',
'rows' => 4,
'sanitize_callback' => '',
'sanitize_callback_args' => ''
);
$args = wp_parse_args( $args, $defaults );
extract( $args, EXTR_SKIP );
return array(
'function' => array( __CLASS__, '_textarea' ),
'sanitize_callback' => $sanitize_callback,
'sanitize_callback_args' => $sanitize_callback_args,
'args' => array(
'description' => $description,
'rows' => $rows
)
);
}
public static function _textarea( $args = array() ) {
?>
<textarea class="large-text" rows="<?php echo $args['rows']; ?>" name="<?php echo $args['name_attr']; ?>"><?php echo esc_textarea( $args['value'] ); ?></textarea>
<?php
self::description( $args );
}
/**
* Drop-down Select
*
* Give it a description and a select_data array where
* the array keys are the values of the options and the array
* values are the captions. The sanitize_callback argument
* is available too.
**/
public static function select( $args = array() ) {
$defaults = array(
'description' => '',
'select_data' => array(),
'sanitize_callback' => '',
'sanitize_callback_args' => ''
);
$args = wp_parse_args( $args, $defaults );
extract( $args, EXTR_SKIP );
return array(
'function' => array( __CLASS__, '_select' ),
'sanitize_callback' => $sanitize_callback,
'sanitize_callback_args' => $sanitize_callback_args,
'args' => array(
'description' => $description,
'select_data' => $select_data
)
);
}
public static function _select( $args = array() ) {
?>
<select name="<?php echo $args['name_attr']; ?>">
<?php foreach ( $args['select_data'] as $value => $caption ): ?>
<option <?php echo selected( $value == $args['value'] ); ?> value="<?php echo $value; ?>"><?php echo $caption; ?></option>
<?php endforeach; ?>
</select>
<?php
self::description( $args );
}
/**
* Radio Group
*
* Works very much like the drop-down select box. The radio
* data is passed in the radio_data array. Rest is the same.
**/
public static function radio( $args = array() ) {
$defaults = array(
'description' => '',
'radio_data' => array(),
'sanitize_callback' => '',
'sanitize_callback_args' => ''
);
$args = wp_parse_args( $args, $defaults );
extract( $args, EXTR_SKIP );
return array(
'function' => array( __CLASS__, '_radio' ),
'sanitize_callback' => $sanitize_callback,
'sanitize_callback_args' => $sanitize_callback_args,
'args' => array(
'description' => $description,
'radio_data' => $radio_data
)
);
}
public static function _radio( $args = array() ) {
?>
<?php foreach ( $args['radio_data'] as $value => $caption ): ?>
<label><input type="radio" name="<?php echo $args['name_attr']; ?>" value="<?php echo $value; ?>" <?php echo checked( $value == $args['value'] ); ?> > <?php echo $caption; ?></label><br />
<?php endforeach; ?>
<?php
self::description( $args );
}
// This is a Singleton class
private static $instance;
public static function singleton() {
if ( ! isset( self::$instance ) ) {
$className = __CLASS__;
self::$instance = new $className;
}
return self::$instance;
}
};
endif; // class_exists
/**
*
* Post Options Class
*
* All the post options logic and functions are implemented here
* and wrapper functions with simpler names could then be created
* outside the class for convenience and simplicity.
*
* This class handles registration of sections and post options to
* sections, sections to post types assignment and the actual
* metabox UI and the post meta IO operations.
*
**/
if ( ! class_exists( 'Post_Options_API_1_0_1' ) ):
class Post_Options_API_1_0_1 {
private $sections = array();
private $options = array();
private $post_types = array();
// Runs during 'init'
function __construct() {
add_action( 'admin_init', array( &$this, '_admin_init' ) );
add_action( 'submitpage_box', array( &$this, '_add_nonce_field' ) );
add_action( 'submitpost_box', array( &$this, '_add_nonce_field' ) );
}
// Runs during 'admin_init' doh!
function _admin_init() {
// Debug voodoo
//add_action('all', create_function('', 'var_dump(current_filter());'));
// Adds the metabox for each post type
foreach ( $this->post_types as $post_type => $sections )
foreach ( $sections as $section_id )
add_meta_box( 'post-options-' . $section_id, $this->sections[$section_id]['title'], array( &$this, '_meta_box_post_options' ), $post_type, $this->sections[$section_id]['context'], $this->sections[$section_id]['priority'], array( 'section_id' => $section_id ) );
// Register the save_post action (for all post types)
add_action( 'save_post', array( &$this, '_save_post' ), 10, 2 );
}
// Security check on edit pages
function _add_nonce_field() {
global $post;
if ( $post )
wp_nonce_field( 'edit_post_options_' . $post->ID , '_post_options_nonce', false );
}
// Runs during 'save_post'
function _save_post( $post_id, $post ) {
// Security check (nonce)
if ( ! isset( $_POST['_post_options_nonce'] ) || ! wp_verify_nonce( $_POST['_post_options_nonce'], 'edit_post_options_' . $post->ID ) )
return;
//check_admin_referer( '_post_options_nonce', 'aedit_post_options_' . $post->ID );
// Don't save revisions and auto-drafts
if ( wp_is_post_revision( $post_id ) || $post->post_status == 'auto-draft' )
return;
// If there are no sections in the current post type live no longer!
$post_type = $post->post_type;
if ( ! isset( $this->post_types[$post_type] ) )
return;
// Get the sections for the post type
$post_type_sections = $this->post_types[$post_type];
foreach ( $post_type_sections as $section_id ) {
// Skip inexisting sections
if ( ! $this->section_exists( $section_id ) )
continue;
$section = $this->sections[$section_id];
$section_options = $this->options[$section_id];
// Loop through the options in the section (priority voodoo).
foreach ( $section_options as $priority => $options ) {
foreach ( $options as $option_id => $option ) {
// Read the POST data, call the sanitize functions if they exist.
if ( isset( $_POST['post-options'][$option_id] ) ) {
$value = $_POST['post-options'][$option_id];
if ( isset( $option['callback']['sanitize_callback'] ) && is_callable( $option['callback']['sanitize_callback'] ) )
if ( isset( $option['callback']['sanitize_callback_args'] ) )
$value = call_user_func( $option['callback']['sanitize_callback'], $value, $option['callback']['sanitize_callback_args'] );
else
$value = call_user_func( $option['callback']['sanitize_callback'], $value );
// You can hook into here too
$value = apply_filters( 'post_options_update', $value, $option_id, $post_id );
// Update the post meta for this option.
update_post_meta( $post_id, $option_id, $value );
} else {
// You can hook here to override the delete action
$delete = (bool) apply_filters( 'post_options_delete', true, $option_id, $post_id );
if ( $delete )
delete_post_meta( $post_id, $option_id );
}
} // foreach (option)
} // foreach (priority, options)
} // foreach post type sections
}
// The meta box, oh the meta box! Runs for the meta box contents.
function _meta_box_post_options( $post, $metabox ) {
$args = $metabox['args'];
$section_id = $args['section_id'];
if ( ! $this->section_exists( $section_id ) )
return false;
$section = $this->sections[$section_id];
// Return if no fields defined
if ( empty( $this->options[$section_id] ) )
return;
// Sort the array by keys (priority)
ksort( $this->options[$section_id] );
?>
<!-- Put this in a more decent place when done with styling. -->
<style>
.post-option {
display: block;
padding: 8px 0;
border-bottom: solid 1px #eee;
line-height: 1.5;
}
.post-option label.post-option-label {
width: 20%;
display: block;
float: left;
}
.post-option .post-option-value {
display: block;
margin-left: 25%;
}
.post-option-value br+br {
display: none;
}
.post-option input,
.post-option textarea,
.post-option select {
font-size: 12px;
}
</style>
<?php
$section_options = $this->options[$section_id];
// Loop through the options.
foreach ( $section_options as $priority => $options ) {
foreach ( $options as $option_id => $option ) {
// Print the option title
echo "<div class='post-option option-{$option_id}'>";
echo "<label class='post-option-label'>{$option['title']}</label>";
echo "<div class='post-option-value'>";
// These will be sent to the callback as arguments
$args = array(
'name_attr' => "post-options[{$option_id}]",
'value' => $this->get_post_option( $post->ID, $option_id )
);
// There may be more arguments for the callback, merge them
if ( isset( $option['callback']['args'] ) && is_array( $option['callback']['args'] ) )
$args = array_merge( $args, $option['callback']['args'] );
// Fire the callback (prints the option value part).
if ( is_callable( $option['callback'] ) )
call_user_func( $option['callback'], $args );
elseif ( is_callable( $option['callback']['function'] ) )
call_user_func( $option['callback']['function'], $args );
echo "</div>";
echo "<div class='clear'></div></div>"; // Second div closes .post-option
}
}
}
// Register a post options section
public function register_post_options_section( $id, $title, $context = 'advanced', $priority = 'default' ) {
if ( ! isset( $this->sections[$id] ) ) {
$this->sections[$id] = array(
'title' => $title,
'context' => $context,
'priority' => $priority
);
return true;
}
return false;
}
/*
* Register Post Option
*
* Registers a new post option that can then be used in different
* sections for different post types. Each post option is also interpreted
* during post type save so you don't have to do the saving, we do it for you.
*/
public function register_post_option( $args ) {
$defaults = array(
'id' => null,
'title' => 'Untitled',
'callback' => '',
'section' => '',
'description' => '',
'priority' => 10
);
$args = wp_parse_args( $args, $defaults );
extract( $args, EXTR_SKIP );
// Madness eh? Well $this->options is an array of sections, each array of sections is an array
// of priorities and each array of priorities is an array of options, ex:
// $this->options[section][priority][option_id] = array of options, sorry! :)
if ( ! isset( $this->options[$section][$priority][$id] ) && ( is_callable( $callback ) || ( is_array( $callback ) && is_callable( $callback['function'] ) ) ) ) {
$this->options[$section][$priority][$id] = array(
'title' => $title,
'callback' => $callback,
'description' => $description
);
return true;
}
return false;
}
/*
* Add Section to Post Type
*
* Registers a given section_id to a given post type slug (post, page, etc)
* so this function makes the section actually appear as a meta box on
* the edit screen.
*/
public function add_section_to_post_type( $section_id, $post_type ) {
if ( $this->section_exists( $section_id ) && ( ! isset( $this->post_types[$post_type] ) || ! in_array( $section_id, $this->post_types[$post_type] ) ) ) {
$this->post_types[$post_type][] = $section_id;
return true;
}
return false;
}
// Returns true if given section_id exists
private function section_exists( $section_id ) {
return array_key_exists( $section_id, $this->sections );
}
// Get post option (just a wrapper around get_post_meta)
public function get_post_option( $post_id, $option_id ) {
return get_post_meta( $post_id, $option_id, true );
}
// This is a Singleton class
private static $instance;
public static function singleton() {
if ( ! isset( self::$instance ) ) {
$className = __CLASS__;
self::$instance = new $className;
}
return self::$instance;
}
};
endif; // class_exists
/**
* Singleton Creators
*
* Below are the singleton creators for the Post Options API
* and the Post Options Fields if needed. This is made to ensure
* compatibility, like when two plugins are running a different
* version of the Post Options API.
*/
// Returns the Post Options API object (singleton)
if ( ! function_exists( 'get_post_options_api') ) {
function get_post_options_api( $version ) {
$class_name = 'Post_Options_API_' . str_replace( '.', '_', $version );
if ( class_exists( $class_name ) )
return call_user_func( array( $class_name, 'singleton' ) );
else
return new WP_Error( 'post-options-api-init', 'You have requested a non-existing version of the Post Options API.' );
}
}
// Returns the Post Options Fields object (singleton)
if ( ! function_exists( 'get_post_options_api_fields' ) ) {
function get_post_options_api_fields( $version ) {
$class_name = 'Post_Options_Fields_' . str_replace( '.', '_', $version );
if ( class_exists( $class_name ) )
return call_user_func( array( $class_name, 'singleton' ) );
else
return new WP_Error( 'post-options-api-fields-init', 'You have requested a non-existing version of the Post Options API Fields.' );
}
}
Jump to Line
Something went wrong with that request. Please try again.