diff --git a/README.txt b/README.txt index 8b7c6f8..a827ddd 100644 --- a/README.txt +++ b/README.txt @@ -19,9 +19,9 @@ 3. Edit cheezcap/config.php 4. Sprinkle theme options around your code, like this: - if ($cap->my_boolean_option) { - // do stuff - } + if ($cap->my_boolean_option) { + // do stuff + } 5. Enjoy! @@ -77,6 +77,7 @@ are three types of Options available to create: 1. Boolean Option 2. Text Option 3. Dropdown Option + 4. MediaOption ## 1. Boolean Option The simplest form of option...creates a true or false dropdown that can be used to turn features on or off. @@ -112,6 +113,16 @@ Allows you to create a dropdown with custom values by passing the constructor an DefaultIndex = an integer identifying the item in the array that is the default value; if not specified, the default is 0. +## 4. Media Option +Allows you to open a media library modal and get the URL for an item. If it's an image, a preview will display + + new MediaOption(Name, Description, OptionID, Default ) + + 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 + Default = a string as the default value for the option; if not specified, the default is "" + ## ## Usage ## diff --git a/cheezcap.php b/cheezcap.php index 6db840e..ff4bc40 100644 --- a/cheezcap.php +++ b/cheezcap.php @@ -1,66 +1,317 @@ 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 cap_add_admin() { - global $themename, $req_cap_to_edit, $cap_menu_position, $cap_icon_url; + function init() { + if ( $this->data ) + return; - if ( ! current_user_can ( $req_cap_to_edit ) ) - return; + $this->data = array(); + $options = $this->get_options(); - if ( isset( $_GET['page'] ) && $_GET['page'] == basename( __FILE__ ) ) { - $options = cap_get_options(); - $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : ''; - $method = false; - $done = false; - $data = new ImportData(); - switch ( $action ) { - case 'save': - $method = 'Update'; - break; - case 'Reset': - $method = 'Reset'; - break; - case 'Export': - $method = 'Export'; - $done = 'cap_serialize_export'; - break; - case 'Import': - $method = 'Import'; - $data = unserialize( file_get_contents( $_FILES['file']['tmp_name'] ) ); - break; + 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]; - if ( $method ) { - foreach ( $options as $group ) { - foreach ( $group->options as $option ) { - call_user_func( array( $option, $method ), $data ); - } - } - if ( $done ) - call_user_func( $done, $data ); + $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( $_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 ); + } + } + + 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 = 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 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(); + } +} + +/** + * 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 ); - $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' ); + if( $echo ) + echo $value; + else + return $value; } diff --git a/config.php b/config.php index e8a1e3f..11060f0 100644 --- a/config.php +++ b/config.php @@ -1,132 +1,168 @@ '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/library.php b/library.php index 6891a6c..2b2f49a 100644 --- a/library.php +++ b/library.php @@ -1,174 +1,433 @@ 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 { +/** + * Adds support for selecting any media libarary + * content. If image, the URL will be saved, otherwise + * the attachment Id will be saved in wp_options + * + */ +class CheezCapMediaOption extends CheezCapOption { + var $options; + + function __construct( $_name, $_desc, $_id, $_std = '' ) { + parent::__construct( $_name, $_desc, $_id, $_std ); + } + + function write_html() { + + // Pre-reqs for loading the WP media-upload modal + wp_enqueue_script( 'jquery' ); + wp_enqueue_script( 'media-upload' ); + wp_enqueue_script( 'thickbox' ); + wp_enqueue_style( 'thickbox' ); + wp_enqueue_media(); + + $is_img = false; + + // Populate the default option or the saved one + $stdText = $this->std; + $stdTextOption = get_option( $this->id ); + + $val = (int) $stdTextOption; + + // User chose an image + if ( $val == 0 ) { + $stdText = $stdTextOption; + $is_img = true; + } + + // User chose a non-image file + else { + + $stdText = $val; + + // Get the attachment object + $attach = get_post( $val ); + + // Extract filename + if ( isset( $attach->guid ) ) { + $guid = $attach->guid; + $arr = explode( '/', $guid ); + $filename = $arr[ count( $arr ) - 1 ]; + } + } + + ?> + + + + + name ); ?> + + + + + + + + desc ); ?>
+ + + + + id, $this->std ); + if ( strtolower( $value ) == 'disabled' ) + return false; + return $value; + } +} + +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 +438,8 @@ function get() { case 'true': case 'enable': case 'enabled': + case '1': + case 1: return true; default: return false; @@ -186,130 +447,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)

- -