diff --git a/README.txt b/README.md similarity index 52% rename from README.txt rename to README.md index 8b7c6f8..36e0e96 100644 --- a/README.txt +++ b/README.md @@ -1,28 +1,44 @@ -// -// CheezCap - Cheezburger Custom Administration Panel -// (c) 2008 - 2011 Cheezburger Network (Pet Holdings, Inc.) -// LOL: http://cheezburger.com -// Source: http://github.com/cheezburger/cheezcap/ -// Authors: Kyall Barrows, Toby McKes, Stefan Rusek, Scott Porad -// License: GNU General Public License, version 2 (GPL), http://www.gnu.org/licenses/gpl-2.0.html -// +CheezCap - Cheezburger Custom Administration Panel +================ +* CheezCap - Cheezburger Custom Administration Panel +* (c) 2008 - 2011 Cheezburger Network (Pet Holdings, Inc.) +* LOL: http://cheezburger.com +* Source: http://github.com/cheezburger/cheezcap/ +* Authors: Kyall Barrows, Toby McKes, Stefan Rusek, Scott Porad +* UnLOLs by Mo Jangda (batmoo@gmail.com) +* License: GNU General Public License, version 2 (GPL), http://www.gnu.org/licenses/gpl-2.0.html + + +This is a fork of the original CheezCap developed by the fine Cheez-loving Cats over at ICHC. In has various bits of cleanup, the biggest being that it can be shared across multiple themes. + +The fork lives at https://github.com/mjangda/cheezcap + +"I'm In Yur Dashburd Tweakin' Yur Settings." ## ## Quick Start ## -1. Copy the CheezCap folder into your theme directory -2. Add the follwowing line to functions.php (if you don't have a functions.php, create one in your theme directory) - - require_once('cheezcap/cheezcap.php'); - -3. Edit cheezcap/config.php +1. Copy the cheezcap folder into an appropriate location (maybe where you store your other shared plugins). +2. Add the following line to functions.php (if you don't have a functions.php, create one in your theme directory). Adjust the path as needed. +


+	require_once( WP_PLUGINS_DIR . '/cheezcap/cheezcap.php');
+

+3. Use the included config-sample.php as a starting point to set up your options. Copy the modified version into your theme and include it. +


+	require_once( dirname( __FILE__ ) . '/cheezcap-config.php');
+

4. Sprinkle theme options around your code, like this: - +


+	global $cap;
 	if ($cap->my_boolean_option) {
 		// do stuff	
 	}
-
+

+4b. Or use the helper function +


+	cheezcap_get_option( 'my_boolean_option', true, 'esc_html' );
+

5. Enjoy! @@ -31,7 +47,7 @@ ## Background ## -In order to use the same Wordpress theme for many different Wordpress sites, one of the things we've +In order to use the same WordPress theme for many different Wordpress sites, one of the things we've done at Cheezburger is create themes with lots and lots of theme options. CheezCap is a simple library we've made for creating custom wp-admin panels really, really easily. @@ -43,13 +59,10 @@ It's just that easy! ## ## Installation ## -Installation is a two step process. First, copy the CheezCap folder into your theme directory. Next, -add the following line to functions.php in your theme (if you don't have a functions.php, create one -in the root of your theme directory). - require_once('cheezcap/cheezcap.php'); +Follow the "Quick Start" instructions above. -Finally, to verify that CheezCap has installed correctly, simply open wp-admin and look for the +Finally, to verify that CheezCap has installed correctly, simply open /wp-admin and look for the "CheezCap Settings" link on the left navigation panel toward the bottom. ## @@ -77,6 +90,7 @@ are three types of Options available to create: 1. Boolean Option 2. Text Option 3. Dropdown Option + 4. Multiple Checkboxes Option ## 1. Boolean Option The simplest form of option...creates a true or false dropdown that can be used to turn features on or off. @@ -91,7 +105,7 @@ The simplest form of option...creates a true or false dropdown that can be used ## 2. Text Option A simple text field that can be used for configurable text, etc. - new TextOption(Name, Description, OptionID, Default, UseTextArea) + new TextOption(Name, Description, OptionID, Default, UseTextArea, ValidationCallback) Name = a human readable name for the option. Description = a human readable description for the option. @@ -99,11 +113,12 @@ A simple text field that can be used for configurable text, etc. Default = a string as the default value for the option; if not specified, the default is "" UseTextArea = a boolean describing if the text option should be written as a text area; if not specified, the default is false; + ValidationCallback = optional custom validation callback (see example) ## 3. Dropdown Option Allows you to create a dropdown with custom values by passing the constructor an array of options - new DropdownOption(Name, Description, OptionID, OptionsArray, DefaultIndex) + new DropdownOption(Name, Description, OptionID, OptionsArray, DefaultIndex, OptionsLabelsArray, ValidationCallback) Name = a human readable name for the option. Description = a human readable description for the option. @@ -111,29 +126,49 @@ Allows you to create a dropdown with custom values by passing the constructor an OptionsArray = an array containing the values for the dropdown menu DefaultIndex = an integer identifying the item in the array that is the default value; if not specified, the default is 0. + OptionsLabelsArray = if you want to seperate the labels from values, pass in an array with the labels matching indexes in the + OptionsArray + ValidationCallback = optional custom validation callback (see example) + +## 4. Multiple Checkboxes Option +Allows you to create a multiple checkboxes option with custom values by passing the constructor an array of options + new MultipleCheezcapOption( Name, Description, OptionID, OptionsValues, OptionsLabels, OptionsChecked, ValidationCallback ) + + Name = a human readable name for the option. + Description = a human readable description for the option. + OptionID = a machine readable option identifier, cannot have spaces and must be unique + OptionsValues = an array containing the values for the dropdown menu + OptionsLabels = an array of labels correponding to OptionValues + OptionsChecked = an array of keys in OptionsValues that should be checked (selected) + ValidationCallback = optional custom validation callback (see example) ## ## Usage ## -CheezCap makes it easy to access the values that are set in your custom admin pages is easy. A global -variable $cap exists to allow you to access any variable by OptionID. (Hence, the need for OptionID -to be unique.) -For example, if you have created a DropdownOption with the OptionID "my_first_dropdown" then you -would access the value of that option like so: +CheezCap makes it easy to access the values that are set in your custom admin pages is easy. +You can use the built-in helper function: + + cheezcap_get_option( $option, $echo = false, $sanitize_callback = '' ) + +## +## Actions +## - $cap->my_first_dropdown +Below are all of the actions you can hook in to: -For example, you might want to write that value to the screen: +* cheezcap_update +* cheezcap_reset +* cheezcap_export +* cheezcap_import - echo($cap->my_first_dropdown); +## +## Methods +## -And, in many cases, you will be accessing $cap from inside a function, so you will need to call -the global variable declaration in order to access $cap, like so: +Calling the get_data method returns all of the input. - function some_function() { - global $cap; - echo($cap->my_first_dropdown); - } +``` + $data_arr = $cap->get_data(); +``` -*/ diff --git a/cheezcap.php b/cheezcap.php index 6db840e..78abfc3 100644 --- a/cheezcap.php +++ b/cheezcap.php @@ -1,66 +1,406 @@ -options as $option ) { - call_user_func( array( $option, $method ), $data ); - } - } - if ( $done ) - call_user_func( $done, $data ); - } - } - - $pgName = "$themename Settings"; - $hook = add_menu_page( $pgName, $pgName, isset( $req_cap_to_edit ) ? $req_cap_to_edit : 'manage_options', basename( __FILE__ ), 'top_level_settings', isset( $cap_icon_url ) ? $cap_icon_url : $default, isset( $cap_menu_position ) ? $cap_menu_position : $default ); - add_action( "admin_print_scripts-$hook", 'cap_admin_js_libs' ); - add_action( "admin_footer-$hook", 'cap_admin_js_footer' ); - add_action( "admin_print_styles-$hook", 'cap_admin_css' ); -} +post_ratings is the same as get_bool_option("cap_post_ratings", false) + */ +class CheezCap { + + private $data = false; + private $cache = array(); + private $settings = array(); + private $options = array(); + private $messages = array(); + + function __construct( $options, $settings = array() ) { + $settings = wp_parse_args( $settings, array( + 'themename' => 'CheezCap', + 'req_cap_to_edit' => 'manage_options', + 'cap_menu_position' => 99, + 'cap_icon_url' => '', + ) ); + + $settings['themeslug'] = sanitize_key( $settings['themename'] ); + + // Let's prevent accidentally allowing low-level users access to cap + if ( ! in_array( $settings['req_cap_to_edit'], apply_filters( 'cheezcap_req_cap_to_edit_whitelist', array( 'manage_network', 'manage_options', 'edit_others_posts', 'publish_posts' ) ) ) ) { + $settings['req_cap_to_edit'] = 'manage_options'; + } + + $this->settings = $settings; + $this->options = $options; + $this->messages = $this->get_default_messages(); + + add_action( 'admin_menu', array( $this, 'add_admin_page' ) ); + add_action( 'admin_init', array( $this, 'handle_admin_actions' ) ); + + } + + function init() { + if ( $this->data ) { + return; + } + + $this->data = array(); + $options = $this->get_options(); + + foreach ( $options as $group ) { + foreach ( $group->options as $option ) { + $this->data[ $option->_key ] = $option; + } + } + + } + + public function __get( $name ) { + $this->init(); + + if ( array_key_exists( $name, $this->cache ) ) { + return $this->cache[ $name ]; + } + + $option = $this->data[ $name ]; + if ( empty( $option ) && defined( 'WP_DEBUG' ) && WP_DEBUG ) { + throw new Exception( "Unknown key: $name" ); + } + elseif ( empty( $option ) ) { + $value = ''; + } + else { + $value = $this->cache[ $name ] = $option->get(); + } + + return $value; + + } + + public function get_options() { + return $this->options; + + } + + public function get_settings() { + return $this->settings; + + } + + public function get_setting( $setting, $default = '' ) { + if ( isset( $this->settings[ $setting ] ) ) { + return $this->settings[ $setting ]; + } + return $default; + + } + + // UI-related functions + function add_admin_page() { + $page_name = sprintf( __( '%s Settings', 'cheezcap' ), esc_html( $this->get_setting( 'themename' ) ) ); + $page_hook = add_menu_page( $page_name, $page_name, $this->get_setting( 'req_cap_to_edit' ), $this->get_setting( 'themeslug' ), array( $this, 'display_admin_page' ), $this->get_setting( 'cap_icon_url' ), $this->get_setting( 'cap_menu_position' ) ); + + add_action( "admin_print_scripts-$page_hook", array( $this, 'admin_js_libs' ) ); + add_action( "admin_footer-$page_hook", array( $this, 'admin_js_footer' ) ); + add_action( "admin_print_styles-$page_hook", array( $this, 'admin_css' ) ); + + } + + function handle_admin_actions() { + global $plugin_page; + + $themeslug = $this->get_setting( 'themeslug' ); + + if ( $plugin_page == $themeslug ) { + + $action = isset( $_POST['action'] ) ? strtolower( esc_attr( $_POST['action'] ) ) : ''; + + if ( ! $action ) { + return; + } + + check_admin_referer( $themeslug . '-action', $themeslug . '-nonce' ); + + if ( ! current_user_can( $this->get_setting( 'req_cap_to_edit' ) ) ) { + return; + } + + $options = $this->get_options(); + $method = false; + $done = false; + $redirect = false; + $data = new CheezCapImportData(); + + switch ( $action ){ + case 'save': + $method = 'update'; + $redirect = array( 'success' => $method ); + break; + + case 'reset': + $method = 'reset'; + $redirect = array( 'success' => $method ); + break; + + case 'export': + $method = 'export'; + $done = array( $this, 'serialize_export' ); + break; + + case 'import': + + $data = @ unserialize( file_get_contents( $_FILES['file']['tmp_name'] ) ); // We're using @ to suppress the E_NOTICE + + if ( $data && is_a( $data, 'CheezCapImportData' ) ) { + $method = 'import'; + $redirect = array( 'success' => $method ); + } + else { + $redirect = array( 'error' => 'import' ); + } + + break; + } + + if ( $method ) { + foreach ( $options as $group ) { + foreach ( $group->options as $option ) { + call_user_func( array( $option, $method ), $data ); + } + } + + $this->trigger_action( $method ); + + if ( $done ) { + call_user_func( $done, $data ); + } + } + + if ( ! empty( $redirect ) ) { + wp_redirect( add_query_arg( $redirect, menu_page_url( $plugin_page, false ) ) ); + } + } + + } + + function display_message( $type ) { + + $theme_name = $this->get_setting( 'themename' ); + $message_key = ''; + + if ( isset ( $_GET[ $type ] ) ) { + $message_key = sanitize_key( $_GET[ $type ] ); + } + + $message = isset( $this->messages[ $type ][ $message_key ] ) ? $this->messages[ $type ][ $message_key ] : ''; + + $message_class = ( $type != 'error' ) ? 'updated' : $type; + + if ( $message ) { + echo esc_attr( sprintf( '

%1$s

', sprintf( $message, esc_html( $theme_name ) ), $message_class ) ); + } + + } + + function display_admin_page() { + $themename = $this->get_setting( 'themename' ); + $themeslug = $this->get_setting( 'themeslug' ); + + if ( isset( $_GET['success'] ) ) { + $this->display_message( 'success' ); + } + elseif ( isset( $_GET['error'] ) ) { + $this->display_message( 'error' ); + } + ?> + +
+

+
+
+ + + +
+ write_html(); ?> +
+ +
+ +

+ + +

+ + +
+
+

+ +

+

+ + + +

+ +
+
+

+ + + + array( + 'update' => __( 'Sweet! The settings for %s were saved!', 'cheezcap' ), + 'reset' => __( 'Yay! The settings for %s were reset!', 'cheezcap' ), + 'import' => __( 'Woo! The settings for %s were imported!', 'cheezcap' ) + ), + 'error' => array( + 'import' => __( 'That doesn\'t look like a CheezCap Export file. Homie don\'t play that!', 'cheezcap' ), + ) + ); + + } + + function serialize_export( $data ) { + $filename = sprintf( '%s-%s-theme-export.txt', date( 'Y.m.d' ), sanitize_key( get_bloginfo( 'name' ) ) ); + header( 'Content-disposition: attachment; filename=' . $filename ); + echo serialize( $data ); + exit(); + + } + + function trigger_action( $method ) { + + switch ( $method ){ + + case 'update': + do_action( 'cheezcap_update' ); + break; + + case 'reset': + do_action( 'cheezcap_reset' ); + break; + + case 'export': + do_action( 'cheezcap_export' ); + break; + + case 'import': + do_action( 'cheezcap_import' ); + break; + } + + } + + /** + * returns all of the cheezcap data + * + * @return \CheezCapImportData + */ + public function get_data() { + + $options = $this->get_options(); + $data = new CheezCapImportData(); + + foreach ( $options as $group ) { + + foreach ( $group->options as $option ) { + + call_user_func( array( $option, 'export' ), $data ); + } + } + + return $data; + + } + +} + +/** + * Access $cap option using the CheezCap option name + * + * @param mixed $option Option name + * @param bool $echo Should the value be echoed? + * @param string $sanitize_callback Callback function used to sanitize the returned value + */ +function cheezcap_get_option( $option, $echo = false, $sanitize_callback = '' ) { + global $cap; + + $value = $cap->$option; + + if ( $sanitize_callback && is_callable( $sanitize_callback ) ) { + $value = call_user_func( $sanitize_callback, $value ); + } + + if ( $echo ) { + echo $value; + } + else { + return $value; + } + +} diff --git a/config-sample.php b/config-sample.php new file mode 100644 index 0000000..c73a3d4 --- /dev/null +++ b/config-sample.php @@ -0,0 +1,163 @@ + 'CheezCap', // used on the title of the custom admin page + 'req_cap_to_edit' => 'manage_options', // the user capability that is required to access the CheezCap settings page + 'cap_menu_position' => 99, // OPTIONAL: This value represents the order in the dashboard menu that the CheezCap menu will display in. Larger numbers push it further down. + 'cap_icon_url' => '', // OPTIONAL: Path to a custom icon for the CheezCap menu item. ex. $cap_icon_url = WP_CONTENT_URL . '/your-theme-name/images/awesomeicon.png'; Image size should be around 20px x 20px. + ) +); + + +/** + * Custom validation callback + * + * @param $key cheezcap option key + * @param mixed $value value of option + * + */ +function my_validation_cb( $key, $value ) { + switch ( $key ) { + case 'my-date': + // Perform date specific validation + break; + default: + // Treat everything else as string + $value = filter_var( $value, FILTER_SANITIZE_STRING ); + } + return $value; +} diff --git a/config.php b/config.php deleted file mode 100644 index e8a1e3f..0000000 --- a/config.php +++ /dev/null @@ -1,132 +0,0 @@ -name = $_name; $this->id = "cap_$_id"; $this->options = $_options; } - function WriteHtml() { + function write_html() { ?> - - + + - options ); $i++ ) { - $this->options[$i]->WriteHtml(); + $this->options[$i]->write_html(); } - ?> + ?>
OptionValue
name = $_name; $this->desc = $_desc; $this->id = "cap_$_id"; $this->_key = $_id; $this->std = $_std; + if ( $_validation_cb && is_callable( $_validation_cb ) ) { + $this->validation_cb = $_validation_cb; + } } - function WriteHtml() { - echo ''; + function write_html() { } - function Update( $ignored ) { - $value = stripslashes_deep( $_POST[$this->id] ); - update_option( $this->id, $value ); + function update( $ignored = '' ) { + $value = isset( $_POST[$this->id] ) ? $_POST[$this->id] : ''; + $this->save( $value ); } - function Reset( $ignored ) { - update_option( $this->id, $this->std ); + function reset( $ignored = '' ) { + $this->save( $this->std ); } - function Import( $data ) { + function import( $data ) { if ( array_key_exists( $this->id, $data->dict ) ) - update_option( $this->id, $data->dict[$this->id] ); + $this->save( $data->dict[$this->id] ); } - function Export( $data ) { + function export( $data ) { $data->dict[$this->id] = get_option( $this->id ); } + function save( $value ) { + if ( $this->validation_cb ) + $value = call_user_func($this->validation_cb, $this->id, $value); + else + $value = stripslashes_deep( $value ); + update_option( $this->id, $value ); + } + function get() { return get_option( $this->id ); } } -class TextOption extends Option { +class CheezCapTextOption extends CheezCapOption { var $useTextArea; - function TextOption( $_name, $_desc, $_id, $_std = '', $_useTextArea = false ) { - $this->Option( $_name, $_desc, $_id, $_std ); + function __construct( $_name, $_desc, $_id, $_std = '', $_useTextArea = false, $_validation_cb = false ) { + parent::__construct( $_name, $_desc, $_id, $_std, $_validation_cb ); $this->useTextArea = $_useTextArea; } - function WriteHtml() { + function save( $value ) { + parent::save( $this->sanitize( $value ) ); + } + + function write_html() { $stdText = $this->std; $stdTextOption = get_option( $this->id ); - if ( ! empty( $stdTextOption ) ) + if ( ! empty( $stdTextOption ) ) $stdText = $stdTextOption; ?> - name . ':' ); ?> - useTextArea ) : - $commentWidth = 1; - ?> - - - - + + useTextArea ) : + $commentWidth = 1; ?> + + + + + + - desc ); ?>
+ + + + + + +
+ useTextArea ) + return wp_filter_post_kses( $value ); + else + return strip_tags( $value ); + } + function get() { $value = get_option( $this->id ); if ( empty( $value ) ) return $this->std; - return $value; + return $this->sanitize( $value ); } } -class DropdownOption extends Option { +class CheezCapDropdownOption extends CheezCapOption { var $options; - function DropdownOption( $_name, $_desc, $_id, $_options, $_stdIndex = 0 ) { - $this->Option( $_name, $_desc, $_id, $_stdIndex ); + function __construct( $_name, $_desc, $_id, $_options, $_stdIndex = 0, $_options_labels = array(), $_validation_cb = false ) { + $_std = ! isset( $_options[$_stdIndex] ) ? $_options[0] : $_options[$_stdIndex]; + parent::__construct( $_name, $_desc, $_id, $_std, $_validation_cb ); $this->options = $_options; + $this->options_labels = $_options_labels; } - function WriteHtml() { + function save( $value ) { + if( ! in_array( $value, $this->options ) ) + $this->reset(); + parent::save( $value ); + } + + function write_html() { ?> - name ); ?> + - desc ); ?>
+
id, $this->std ); - if ( strtolower( $value ) == 'disabled' ) + if ( strtolower( $value ) == 'disabled' ) return false; - return $value; + return $this->sanitize( $value ); } } -class BooleanOption extends DropdownOption { +class CheezCapBooleanOption extends CheezCapDropdownOption { var $default; - function BooleanOption( $_name, $_desc, $_id, $_default = false ) { + function __construct( $_name, $_desc, $_id, $_default = false ) { $this->default = $_default; - $this->DropdownOption( $_name, $_desc, $_id, array( 'Disabled', 'Enabled' ), $_default ? 1 : 0 ); + parent::__construct( $_name, $_desc, $_id, array( 0, 1 ), $_default ? 0 : 1, array( 'Disabled', 'Enabled' ) ); } function get() { @@ -179,6 +221,8 @@ function get() { case 'true': case 'enable': case 'enabled': + case '1': + case 1: return true; default: return false; @@ -186,130 +230,45 @@ function get() { } } -// This class is the handy short cut for accessing config options -// -// $cap->post_ratings is the same as get_bool_option("cap_post_ratings", false) -// -class autoconfig { - private $data = false; - private $cache = array(); - - function init() { - if ( $this->data ) - return; - - $this->data = array(); - $options = cap_get_options(); +class CheezCapMultipleCheckboxesOption extends CheezCapOption { + var $options_checked; + var $options; + var $options_labels; - foreach ( $options as $group ) { - foreach( $group->options as $option ) { - $this->data[$option->_key] = $option; - } - } + function __construct( $_name, $_desc, $_id, $_options, $_options_labels = array(), $_options_checked, $_validation_cb = false ) { + $this->options = $_options; + $this->options_labels = $_options_labels; + parent::__construct( $_name, $_desc, $_id, '', $_validation_cb ); + $this->options_checked = is_array( $_options_checked ) ? $_options_checked : $this->get(); } - public function __get( $name ) { - $this->init(); - - if ( array_key_exists( $name, $this->cache ) ) - return $this->cache[$name]; - - $option = $this->data[$name]; - if ( empty( $option ) ) - throw new Exception( "Unknown key: $name" ); + function write_html() { + ?> + + + + + + options as $option ) : ?> + options_checked ) ? ' checked="checked" ' : ''; ?> + options_labels[$count] ) ? $this->options_labels[$count] : $option; ?> + /> + + +
+ - $value = $this->cache[$name] = $option->get(); - return $value; + + + + +
+ + + - -

' . esc_html( $themename . ' settings saved.' ) . '

'; - if ( isset( $_REQUEST['reset'] ) ) - echo '

' . esc_html( $themename . ' settings reset.' ) . '

'; - ?> - -
-

- -
- -
- - -
- WriteHtml(); - ?> -
- -
-

- - -

-
-
-

- -

-

- -

-

- - -

-
-
-

Preview (updated when options are saved)

- -