Create WP_Customize_Control to separate the process of rendering a control from fetching, previewing, and saving its values. see #19910.

Many-to-many mapping between settings and controls.
* Settings and controls have been separated in both the PHP (WP_Customize_Setting, WP_Customize_Control) and the JS (wp.customize.Setting, wp.customize.Control).
* While most settings are tied to a single control, some require multiple controls. The 'header_textcolor' control is a good example: to hide the header text, header_textcolor is set to 'blank'.

Add 'Display Header Text' control.

A handful of miscellaneous bugfixes along the way.

Notes:
* Controls should be separated out a bit more; juggling type-specific arguments in the switch statement is rather inelegant.
* Page dropdowns are currently inactive and need to be re-linked.

git-svn-id: http://svn.automattic.com/wordpress/trunk@20295 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
koopersmith 2012-03-28 04:14:09 +00:00
parent 7dd14bbd09
commit 30d798ec8d
8 changed files with 653 additions and 386 deletions

View File

@ -0,0 +1,329 @@
<?php
/**
* Customize Control Class
*
* @package WordPress
* @subpackage Customize
* @since 3.4.0
*/
class WP_Customize_Control {
public $manager;
public $id;
public $settings;
public $setting;
public $priority = 10;
public $section = '';
public $label = '';
// @todo: remove control_params
public $control_params = array();
// @todo: remove choices
public $choices = array();
public $visibility;
public $type = 'text';
/**
* Constructor.
*
* If $args['settings'] is not defined, use the $id as the setting ID.
*
* @since 3.4.0
*/
function __construct( $manager, $id, $args = array() ) {
$keys = array_keys( get_class_vars( __CLASS__ ) );
foreach ( $keys as $key ) {
if ( isset( $args[ $key ] ) )
$this->$key = $args[ $key ];
}
$this->manager = $manager;
$this->id = $id;
// Process settings.
if ( empty( $this->settings ) )
$this->settings = $id;
$settings = array();
if ( is_array( $this->settings ) ) {
foreach ( $this->settings as $key => $setting ) {
$settings[ $key ] = $this->manager->get_setting( $setting );
}
} else {
$this->setting = $this->manager->get_setting( $this->settings );
$settings['default'] = $this->setting;
}
$this->settings = $settings;
}
/**
* Enqueue control related scripts/styles.
*
* @since 3.4.0
*/
public function enqueue() {
switch( $this->type ) {
case 'color':
wp_enqueue_script( 'farbtastic' );
wp_enqueue_style( 'farbtastic' );
break;
case 'upload':
wp_enqueue_script( 'wp-plupload' );
break;
}
}
/**
* Fetch a setting's value.
* Grabs the main setting by default.
*
* @since 3.4.0
*/
public final function value( $setting_key = 'default' ) {
if ( isset( $this->settings[ $setting_key ] ) )
return $this->settings[ $setting_key ]->value();
}
public function json( $args = array() ) {
$settings = array();
foreach ( $this->settings as $key => $setting ) {
$settings[ $key ] = $setting->id;
}
return array(
'type' => $this->type,
'params' => wp_parse_args( wp_parse_args( $args, array(
'settings' => $settings,
) ), $this->control_params ),
);
}
/**
* Check if the theme supports the control and check user capabilities.
*
* @since 3.4.0
*
* @return bool False if theme doesn't support the control or user doesn't have the required permissions, otherwise true.
*/
public final function check_capabilities() {
foreach ( $this->settings as $setting ) {
if ( ! $setting->check_capabilities() )
return false;
}
$section = $this->manager->get_section( $this->section );
if ( isset( $section ) && ! $section->check_capabilities() )
return false;
return true;
}
/**
* Check capabiliites and render the control.
*
* @since 3.4.0
*/
public final function maybe_render() {
if ( ! $this->check_capabilities() )
return;
do_action( 'customize_render_control', $this );
do_action( 'customize_render_control_' . $this->id, $this );
$this->render();
}
/**
* Render the control. Renders the control wrapper, then calls $this->render_content().
*
* @since 3.4.0
*/
protected function render() {
$id = 'customize-control-' . $this->id;
$class = 'customize-control customize-control-' . $this->type;
$style = '';
if ( $this->visibility ) {
if ( is_string( $this->visibility ) ) {
$visibility_id = $this->visibility;
$visibility_value = true;
} else {
$visibility_id = $this->visibility[0];
$visibility_value = $this->visibility[1];
}
$visibility_setting = $this->manager->get_setting( $visibility_id );
if ( $visibility_setting && $visibility_value != $visibility_setting->value() )
$style = 'style="display:none;"';
}
?><li id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $class ); ?>" <?php echo $style; ?>>
<?php $this->render_content(); ?>
</li><?php
}
public function link( $setting_key = 'default' ) {
if ( ! isset( $this->settings[ $setting_key ] ) )
return;
echo 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"';
}
/**
* Render the control's content.
*
* Allows the content to be overriden without having to rewrite the wrapper.
*
* @since 3.4.0
*/
protected function render_content() {
switch( $this->type ) {
case 'text':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<input type="text" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> />
</label>
<?php
break;
case 'color':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<div class="color-picker">
<input type="hidden" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> />
<a href="#"></a>
<div class="color-picker-controls">
<div class="farbtastic-placeholder"></div>
<div class="color-picker-details">
<div class="color-picker-hex">
<span>#</span>
<input type="text" <?php $this->link(); ?> />
</div>
</div>
</div>
</div>
</label>
<?php
break;
case 'checkbox':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<input type="checkbox" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); checked( $this->value() ); ?> class="customize-control-content" />
</label>
<?php
break;
case 'radio':
if ( empty( $this->choices ) )
return;
$name = '_customize-radio-' . $this->id;
?>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<?php
foreach ( $this->choices as $value => $label ) :
?>
<label>
<input type="radio" value="<?php echo esc_attr( $value ); ?>" name="<?php echo esc_attr( $name ); ?>" <?php $this->link(); checked( $this->value(), $value ); ?> />
<?php echo esc_html( $label ); ?><br/>
</label>
<?php
endforeach;
break;
case 'select':
if ( empty( $this->choices ) )
return;
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<select <?php $this->link(); ?> class="customize-control-content">
<?php
foreach ( $this->choices as $value => $label )
echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>';
?>
</select>
</label>
<?php
break;
case 'upload':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<div>
<input type="hidden" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> />
<a href="#" class="button-secondary upload"><?php _e( 'Upload' ); ?></a>
<a href="#" class="remove"><?php _e( 'Remove' ); ?></a>
</div>
</label>
<?php
break;
case 'image':
$value = $this->value();
$image = $value;
if ( isset( $this->control_params['get_url'] ) )
$image = call_user_func( $this->control_params['get_url'], $image );
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<input type="hidden" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> />
<div class="customize-image-picker">
<div class="thumbnail">
<?php if ( empty( $image ) ): ?>
<img style="display:none;" />
<?php else: ?>
<img src="<?php echo esc_url( $image ); ?>" />
<?php endif; ?>
</div>
<div class="actions">
<a href="#" class="upload"><?php _e( 'Upload New' ); ?></a>
<a href="#" class="change"><?php _e( 'Change Image' ); ?></a>
<a href="#" class="remove"><?php _e( 'Remove Image' ); ?></a>
</div>
<div class="library">
<ul>
<?php foreach ( $this->control_params['tabs'] as $tab ): ?>
<li data-customize-tab='<?php echo esc_attr( $tab[0] ); ?>'>
<?php echo esc_html( $tab[1] ); ?>
</li>
<?php endforeach; ?>
</ul>
<?php foreach ( $this->control_params['tabs'] as $tab ): ?>
<div class="library-content" data-customize-tab='<?php echo esc_attr( $tab[0] ); ?>'>
<?php call_user_func( $tab[2] ); ?>
</div>
<?php endforeach; ?>
</div>
</div>
</label>
<?php
break;
case 'dropdown-pages':
printf(
'<label class="customize-control-select"><span class="customize-control-title">%s</span> %s</label>',
$this->label,
wp_dropdown_pages(
array(
// @todo: this is going to need fixing.
// 'name' => $this->get_name(),
'echo' => 0,
'show_option_none' => __( '&mdash; Select &mdash;' ),
'option_none_value' => '0',
'selected' => $this->value(),
)
)
);
break;
}
}
}

View File

@ -15,7 +15,7 @@ class WP_Customize_Section {
public $theme_supports = '';
public $title = '';
public $description = '';
public $settings;
public $controls;
/**
* Constructor.
@ -35,7 +35,7 @@ class WP_Customize_Section {
$this->manager = $manager;
$this->id = $id;
$this->settings = array(); // Users cannot customize the $settings array.
$this->controls = array(); // Users cannot customize the $controls array.
return $this;
}
@ -84,8 +84,8 @@ class WP_Customize_Section {
<h3 class="customize-section-title" title="<?php echo esc_attr( $this->description ); ?>"><?php echo esc_html( $this->title ); ?></h3>
<ul class="customize-section-content">
<?php
foreach ( $this->settings as $setting )
$setting->maybe_render();
foreach ( $this->controls as $control )
$control->maybe_render();
?>
</ul>
</li>

View File

@ -10,18 +10,12 @@
class WP_Customize_Setting {
public $manager;
public $id;
public $priority = 10;
public $section = '';
public $label = '';
public $control = 'text';
public $control_params = array();
public $type = 'theme_mod';
public $choices = array();
public $capability = 'edit_theme_options';
public $theme_supports = '';
public $default = '';
public $sanitize_callback = '';
public $visibility;
protected $id_data = array();
private $_post_value; // Cached, sanitized $_POST value.
@ -63,23 +57,6 @@ class WP_Customize_Setting {
return $this;
}
/**
* Enqueue setting related scripts/styles.
*
* @since 3.4.0
*/
public function enqueue() {
switch( $this->control ) {
case 'color':
wp_enqueue_script( 'farbtastic' );
wp_enqueue_style( 'farbtastic' );
break;
case 'upload':
wp_enqueue_script( 'wp-plupload' );
break;
}
}
/**
* Handle previewing the setting.
*
@ -275,229 +252,9 @@ class WP_Customize_Setting {
if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) )
return false;
$section = $this->manager->get_section( $this->section );
if ( isset( $section ) && ! $section->check_capabilities() )
return false;
return true;
}
/**
* Check capabiliites and render the control.
*
* @since 3.4.0
*/
public final function maybe_render() {
if ( ! $this->check_capabilities() )
return;
do_action( 'customize_render_setting', $this );
do_action( 'customize_render_setting_' . $this->id, $this );
$this->render();
}
/**
* Render the control. Renders the control wrapper, then calls $this->render_content().
*
* @since 3.4.0
*/
protected function render() {
$id = 'customize-control-' . $this->id;
$class = 'customize-control customize-control-' . $this->control;
$style = '';
if ( $this->visibility ) {
if ( is_string( $this->visibility ) ) {
$visibility_id = $this->visibility;
$visibility_value = true;
} else {
$visibility_id = $this->visibility[0];
$visibility_value = $this->visibility[1];
}
$visibility_setting = $this->manager->get_setting( $visibility_id );
if ( $visibility_setting && $visibility_value != $visibility_setting->value() )
$style = 'style="display:none;"';
}
?><li id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $class ); ?>" <?php echo $style; ?>>
<?php $this->render_content(); ?>
</li><?php
}
/**
* Render the control's content.
*
* Allows the content to be overriden without having to rewrite the wrapper.
*
* @since 3.4.0
*/
protected function render_content() {
switch( $this->control ) {
case 'text':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<input type="text" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); ?> />
</label>
<?php
break;
case 'color':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<div class="color-picker">
<input type="hidden" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); ?> />
<a href="#"></a>
<div class="color-picker-controls">
<div class="farbtastic-placeholder"></div>
<div class="color-picker-details">
<div class="color-picker-hex">
<span>#</span>
<input type="text" />
</div>
</div>
</div>
</div>
</label>
<?php
break;
case 'checkbox':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<input type="checkbox" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); checked( $this->value() ); ?> class="customize-control-content" />
</label>
<?php
break;
case 'radio':
if ( empty( $this->choices ) )
return;
?>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<?php
foreach ( $this->choices as $value => $label ) :
?>
<label>
<input type="radio" value="<?php echo esc_attr( $value ); ?>" <?php $this->name(); checked( $this->value(), $value ); ?> />
<?php echo esc_html( $label ); ?><br/>
</label>
<?php
endforeach;
break;
case 'select':
if ( empty( $this->choices ) )
return;
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<select <?php $this->name(); ?> class="customize-control-content">
<?php
foreach ( $this->choices as $value => $label )
echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>';
?>
</select>
</label>
<?php
break;
case 'upload':
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<div>
<input type="hidden" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); ?> />
<a href="#" class="button-secondary upload"><?php _e( 'Upload' ); ?></a>
<a href="#" class="remove"><?php _e( 'Remove' ); ?></a>
</div>
</label>
<?php
break;
case 'image':
$value = $this->value();
$image = $value;
if ( isset( $this->control_params['get_url'] ) )
$image = call_user_func( $this->control_params['get_url'], $image );
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<input type="hidden" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); ?> />
<div class="customize-image-picker">
<div class="thumbnail">
<?php if ( empty( $image ) ): ?>
<img style="display:none;" />
<?php else: ?>
<img src="<?php echo esc_url( $image ); ?>" />
<?php endif; ?>
</div>
<div class="actions">
<a href="#" class="upload"><?php _e( 'Upload New' ); ?></a>
<a href="#" class="change"><?php _e( 'Change Image' ); ?></a>
<a href="#" class="remove"><?php _e( 'Remove Image' ); ?></a>
</div>
<div class="library">
<ul>
<?php foreach ( $this->control_params['tabs'] as $tab ): ?>
<li data-customize-tab='<?php echo esc_attr( $tab[0] ); ?>'>
<?php echo esc_html( $tab[1] ); ?>
</li>
<?php endforeach; ?>
</ul>
<?php foreach ( $this->control_params['tabs'] as $tab ): ?>
<div class="library-content" data-customize-tab='<?php echo esc_attr( $tab[0] ); ?>'>
<?php call_user_func( $tab[2] ); ?>
</div>
<?php endforeach; ?>
</div>
</div>
</label>
<?php
break;
case 'dropdown-pages':
printf(
'<label class="customize-control-select"><span class="customize-control-title">%s</span> %s</label>',
$this->label,
wp_dropdown_pages(
array(
'name' => $this->get_name(),
'echo' => 0,
'show_option_none' => __( '&mdash; Select &mdash;' ),
'option_none_value' => '0',
'selected' => get_option( $this->id )
)
)
);
break;
}
}
/**
* Retrieve the name attribute for an input.
*
* @since 3.4.0
*
* @return string The name.
*/
public final function get_name() {
return self::name_prefix . esc_attr( $this->id );
}
/**
* Echo the HTML name attribute for an input.
*
* @since 3.4.0
*
* @return string The HTML name attribute.
*/
public final function name() {
echo 'name="' . $this->get_name() . '"';
}
/**
* Multidimensional helper function.
*

View File

@ -14,6 +14,7 @@ final class WP_Customize {
protected $settings = array();
protected $sections = array();
protected $controls = array();
/**
* Constructor.
@ -23,6 +24,7 @@ final class WP_Customize {
public function __construct() {
require( ABSPATH . WPINC . '/class-wp-customize-setting.php' );
require( ABSPATH . WPINC . '/class-wp-customize-section.php' );
require( ABSPATH . WPINC . '/class-wp-customize-control.php' );
add_action( 'setup_theme', array( $this, 'setup_theme' ) );
add_action( 'admin_init', array( $this, 'admin_init' ) );
@ -357,12 +359,15 @@ final class WP_Customize {
*
* @since 3.4.0
*
* @param string $id An specific ID of the setting. Can be a
* @param string $id A specific ID of the setting. Can be a
* theme mod or option name.
* @param array $args Setting arguments.
*/
public function add_setting( $id, $args = array() ) {
$setting = new WP_Customize_Setting( $this, $id, $args );
if ( is_a( $id, 'WP_Customize_Setting' ) )
$setting = $id;
else
$setting = new WP_Customize_Setting( $this, $id, $args );
$this->settings[ $setting->id ] = $setting;
}
@ -372,7 +377,7 @@ final class WP_Customize {
*
* @since 3.4.0
*
* @param string $id An specific ID of the setting.
* @param string $id A specific ID of the setting.
* @return object The settings object.
*/
public function get_setting( $id ) {
@ -385,7 +390,7 @@ final class WP_Customize {
*
* @since 3.4.0
*
* @param string $id An specific ID of the setting.
* @param string $id A specific ID of the setting.
*/
public function remove_setting( $id ) {
unset( $this->settings[ $id ] );
@ -396,11 +401,14 @@ final class WP_Customize {
*
* @since 3.4.0
*
* @param string $id An specific ID of the section.
* @param string $id A specific ID of the section.
* @param array $args Section arguments.
*/
public function add_section( $id, $args = array() ) {
$section = new WP_Customize_Section( $this, $id, $args );
if ( is_a( $id, 'WP_Customize_Section' ) )
$section = $id;
else
$section = new WP_Customize_Section( $this, $id, $args );
$this->sections[ $section->id ] = $section;
}
@ -410,7 +418,7 @@ final class WP_Customize {
*
* @since 3.4.0
*
* @param string $id An specific ID of the section.
* @param string $id A specific ID of the section.
* @return object The section object.
*/
public function get_section( $id ) {
@ -423,12 +431,53 @@ final class WP_Customize {
*
* @since 3.4.0
*
* @param string $id An specific ID of the section.
* @param string $id A specific ID of the section.
*/
public function remove_section( $id ) {
unset( $this->sections[ $id ] );
}
/**
* Add a customize control.
*
* @since 3.4.0
*
* @param string $id A specific ID of the control.
* @param array $args Setting arguments.
*/
public function add_control( $id, $args = array() ) {
if ( is_a( $id, 'WP_Customize_Control' ) )
$control = $id;
else
$control = new WP_Customize_Control( $this, $id, $args );
$this->controls[ $control->id ] = $control;
}
/**
* Retrieve a customize control.
*
* @since 3.4.0
*
* @param string $id A specific ID of the control.
* @return object The settings object.
*/
public function get_control( $id ) {
if ( isset( $this->controls[ $id ] ) )
return $this->controls[ $id ];
}
/**
* Remove a customize setting.
*
* @since 3.4.0
*
* @param string $id A specific ID of the control.
*/
public function remove_control( $id ) {
unset( $this->controls[ $id ] );
}
/**
* Helper function to compare two objects by priority.
*
@ -452,20 +501,20 @@ final class WP_Customize {
* @since 3.4.0
*/
public function prepare_controls() {
// Prepare settings
// Prepare controls
// Reversing makes uasort sort by time added when conflicts occur.
$this->settings = array_reverse( $this->settings );
$settings = array();
$this->controls = array_reverse( $this->controls );
$controls = array();
foreach ( $this->settings as $id => $setting ) {
if ( ! isset( $this->sections[ $setting->section ] ) || ! $setting->check_capabilities() )
foreach ( $this->controls as $id => $control ) {
if ( ! isset( $this->sections[ $control->section ] ) || ! $control->check_capabilities() )
continue;
$this->sections[ $setting->section ]->settings[] = $setting;
$settings[ $id ] = $setting;
$this->sections[ $control->section ]->controls[] = $control;
$controls[ $id ] = $control;
}
$this->settings = $settings;
$this->controls = $controls;
// Prepare sections
$this->sections = array_reverse( $this->sections );
@ -473,10 +522,10 @@ final class WP_Customize {
$sections = array();
foreach ( $this->sections as $section ) {
if ( ! $section->check_capabilities() || ! $section->settings )
if ( ! $section->check_capabilities() || ! $section->controls )
continue;
usort( $section->settings, array( $this, '_cmp_priority' ) );
usort( $section->controls, array( $this, '_cmp_priority' ) );
$sections[] = $section;
}
$this->sections = $sections;
@ -488,8 +537,8 @@ final class WP_Customize {
* @since 3.4.0
*/
public function enqueue_control_scripts() {
foreach ( $this->settings as $setting ) {
$setting->enqueue();
foreach ( $this->controls as $control ) {
$control->enqueue();
}
}
@ -508,36 +557,37 @@ final class WP_Customize {
) );
$this->add_setting( 'header_textcolor', array(
'label' => 'Text Color',
'section' => 'header',
'sanitize_callback' => 'sanitize_hexcolor',
'control' => 'color',
'theme_supports' => array( 'custom-header', 'header-text' ),
'default' => get_theme_support( 'custom-header', 'default-text-color' ),
// @todo: replace with a new accept() setting method
// 'sanitize_callback' => 'sanitize_hexcolor',
'control' => 'color',
'theme_supports' => array( 'custom-header', 'header-text' ),
'default' => get_theme_support( 'custom-header', 'default-text-color' ),
) );
/*
$this->add_setting( 'display_header', array(
'label' => 'Display Text',
'section' => 'header',
'type' => 'radio',
'choices' => array(
'show' => 'Yes',
'hide' => 'No'
),
// Showing header text is actually done by setting header_textcolor to 'blank'.
// @todo: Do some JS magic to make this work (since we'll be hiding the textcolor input).
'theme_mod' => false,
$this->add_control( 'display_header_text', array(
'settings' => 'header_textcolor',
'label' => __( 'Display Header Text' ),
'section' => 'header',
'type' => 'checkbox',
) );
$this->add_control( 'header_textcolor', array(
'label' => __( 'Text Color' ),
'section' => 'header',
'type' => 'color',
) );
*/
// Input type: checkbox
// With custom value
$this->add_setting( 'header_image', array(
'default' => get_theme_support( 'custom-header', 'default-image' ),
'theme_supports' => 'custom-header',
) );
$this->add_control( 'header_image', array(
'label' => 'Header Image',
'section' => 'header',
'control' => 'image',
'default' => get_theme_support( 'custom-header', 'default-image' ),
'type' => 'image',
'control_params' => array(
'context' => 'custom-header',
'removed' => 'remove-header',
@ -559,60 +609,80 @@ final class WP_Customize {
// Input type: Color
// With sanitize_callback
$this->add_setting( 'background_color', array(
'label' => 'Background Color',
'section' => 'background',
'control' => 'color',
'default' => get_theme_support( 'custom-background', 'default-color' ),
'sanitize_callback' => 'sanitize_hexcolor',
'theme_supports' => 'custom-background',
) );
$this->add_control( 'background_color', array(
'label' => __( 'Background Color' ),
'section' => 'background',
'type' => 'color',
) );
$this->add_setting( 'background_image', array(
'label' => 'Background Image',
'section' => 'background',
'control' => 'upload',
'default' => get_theme_support( 'custom-background', 'default-image' ),
'theme_supports' => 'custom-background',
) );
$this->add_control( 'background_image', array(
'label' => __( 'Background Image' ),
'section' => 'background',
'type' => 'upload',
'control_params' => array(
'context' => 'custom-background',
),
) );
$this->add_setting( 'background_repeat', array(
'label' => 'Background Repeat',
'default' => 'repeat',
'theme_supports' => 'custom-background',
) );
$this->add_control( 'background_repeat', array(
'label' => __( 'Background Repeat' ),
'section' => 'background',
'visibility' => 'background_image',
'control' => 'radio',
'type' => 'radio',
'choices' => array(
'no-repeat' => __('No Repeat'),
'repeat' => __('Tile'),
'repeat-x' => __('Tile Horizontally'),
'repeat-y' => __('Tile Vertically'),
),
'default' => 'repeat',
) );
$this->add_setting( 'background_position_x', array(
'label' => 'Background Position',
'default' => 'left',
'theme_supports' => 'custom-background',
) );
$this->add_control( 'background_position_x', array(
'label' => __( 'Background Position' ),
'section' => 'background',
'visibility' => 'background_image',
'control' => 'radio',
'type' => 'radio',
'choices' => array(
'left' => __('Left'),
'center' => __('Center'),
'right' => __('Right'),
),
'default' => 'left',
) );
$this->add_setting( 'background_attachment', array(
'label' => 'Background Attachment',
'default' => 'fixed',
'theme_supports' => 'custom-background',
) );
$this->add_control( 'background_attachment', array(
'label' => __( 'Background Attachment' ),
'section' => 'background',
'visibility' => 'background_image',
'control' => 'radio',
'type' => 'radio',
'choices' => array(
'fixed' => __('Fixed'),
'scroll' => __('Scroll'),
),
'default' => 'fixed',
) );
/* Nav Menus */
@ -636,13 +706,18 @@ final class WP_Customize {
$choices[ $menu->term_id ] = $truncated_name;
}
$this->add_setting( "nav_menu_locations[{$location}]", array(
'label' => $description,
'theme_supports' => 'menus', // Todo: Needs also widgets -- array( 'menus', 'widgets' )
'section' => 'nav',
'control' => 'select',
'choices' => $choices,
$menu_setting_id = "nav_menu_locations[{$location}]";
$this->add_setting( $menu_setting_id, array(
'sanitize_callback' => 'absint',
'theme_supports' => 'menus',
) );
$this->add_control( $menu_setting_id, array(
'label' => $description,
'section' => 'nav',
'type' => 'select',
'choices' => $choices,
) );
}
@ -660,34 +735,43 @@ final class WP_Customize {
$choices['page'] = __( 'A static page (select below)' );
$this->add_setting( 'show_on_front', array(
'label' => __( 'Front page displays' ),
// 'theme_supports' => 'static-front-page',
'section' => 'static_front_page',
'control' => 'radio',
'choices' => $choices,
'default' => get_option( 'show_on_front' ),
'type' => 'option',
'capability' => 'manage_options',
'type' => 'option',
// 'theme_supports' => 'static-front-page',
) );
$this->add_control( 'show_on_front', array(
'label' => __( 'Front page displays' ),
'section' => 'static_front_page',
'type' => 'radio',
'choices' => $choices,
) );
$this->add_setting( 'page_on_front', array(
'label' => __( 'Front page' ),
'type' => 'option',
'capability' => 'manage_options',
// 'theme_supports' => 'static-front-page',
'section' => 'static_front_page',
'control' => 'dropdown-pages',
'type' => 'option',
'capability' => 'manage_options',
'visibility' => array( 'show_on_front', 'page' ),
) );
$this->add_control( 'page_on_front', array(
'label' => __( 'Front page' ),
'section' => 'static_front_page',
'type' => 'dropdown-pages',
'visibility' => array( 'show_on_front', 'page' ),
) );
$this->add_setting( 'page_for_posts', array(
'label' => __( 'Posts page' ),
// 'theme_supports' => 'static-front-page',
'section' => 'static_front_page',
'control' => 'dropdown-pages',
'type' => 'option',
'capability' => 'manage_options',
'visibility' => array( 'show_on_front', 'page' ),
// 'theme_supports' => 'static-front-page',
) );
$this->add_control( 'page_for_posts', array(
'label' => __( 'Posts page' ),
'section' => 'static_front_page',
'type' => 'dropdown-pages',
'visibility' => array( 'show_on_front', 'page' ),
) );
/* Site Title & Tagline */
@ -697,19 +781,25 @@ final class WP_Customize {
) );
$this->add_setting( 'blogname', array(
'label' => __( 'Site Title' ),
'section' => 'strings',
'default' => get_option( 'blogname' ),
'type' => 'option',
'capability' => 'manage_options',
'default' => get_option( 'blogname' ),
'type' => 'option',
'capability' => 'manage_options',
) );
$this->add_control( 'blogname', array(
'label' => __( 'Site Title' ),
'section' => 'strings',
) );
$this->add_setting( 'blogdescription', array(
'label' => __( 'Tagline' ),
'section' => 'strings',
'default' => get_option( 'blogdescription' ),
'type' => 'option',
'capability' => 'manage_options',
'default' => get_option( 'blogdescription' ),
'type' => 'option',
'capability' => 'manage_options',
) );
$this->add_control( 'blogdescription', array(
'label' => __( 'Tagline' ),
'section' => 'strings',
) );
}
};

View File

@ -95,27 +95,30 @@ do_action( 'customize_controls_print_scripts' );
$scheme = is_ssl() ? 'https' : 'http';
$settings = array(
'preview' => esc_url( home_url( '/', $scheme ) ),
'settings' => array(),
'controls' => array(),
'prefix' => WP_Customize_Setting::name_prefix,
);
foreach ( $this->settings as $id => $setting ) {
$settings['controls'][ $id ] = array(
$settings['settings'][ $id ] = array(
'value' => $setting->value(),
'control' => $setting->control,
'params' => $setting->control_params,
);
}
if ( $setting->visibility ) {
if ( is_string( $setting->visibility ) ) {
foreach ( $this->controls as $id => $control ) {
$settings['controls'][ $id ] = $control->json();
if ( $control->visibility ) {
if ( is_string( $control->visibility ) ) {
$settings['controls'][ $id ]['visibility'] = array(
'id' => $setting->visibility,
'id' => $control->visibility,
'value' => true,
);
} else {
$settings['controls'][ $id ]['visibility'] = array(
'id' => $setting->visibility[0],
'value' => $setting->visibility[1],
'id' => $control->visibility[0],
'value' => $control->visibility[1],
);
}

View File

@ -397,7 +397,7 @@ if ( typeof wp === 'undefined' )
add: function( id, value ) {
if ( this.has( id ) )
return;
return this.value( id );
this._value[ id ] = value;
this._value[ id ]._parent = this._value;

View File

@ -6,19 +6,24 @@
* - previewer - The Previewer instance to sync with.
* - method - The method to use for syncing. Supports 'refresh' and 'postMessage'.
*/
api.Control = api.Value.extend({
api.Setting = api.Value.extend({
initialize: function( id, value, options ) {
var name = '[name="' + api.settings.prefix + id + '"]';
var element;
this.params = {};
api.Value.prototype.initialize.call( this, value, options );
this.id = id;
this.container = $( '#customize-control-' + id );
this.element = this.element || new api.Element( this.container.find( name ) );
this.method = this.method || 'refresh';
element = $( '<input />', {
type: 'hidden',
value: this.get(),
name: api.settings.prefix + id
});
element.appendTo( this.previewer.form );
this.element = new api.Element( element );
this.element.link( this );
this.link( this.element );
@ -34,12 +39,67 @@
}
});
api.ColorControl = api.Control.extend({
initialize: function( id, value, options ) {
var self = this,
picker, ui, text, toggle, update;
api.Control = api.Class.extend({
initialize: function( id, options ) {
var control = this,
nodes, radios, settings;
api.Control.prototype.initialize.call( this, id, value, options );
this.params = {};
$.extend( this, options || {} );
this.id = id;
this.container = $( '#customize-control-' + id );
settings = $.map( this.params.settings, function( value ) {
return value;
});
api.apply( api, settings.concat( function() {
var key;
control.settings = {};
for ( key in control.params.settings ) {
control.settings[ key ] = api( control.params.settings[ key ] );
}
control.setting = control.settings['default'] || null;
control.ready();
}) );
control.elements = [];
nodes = this.container.find('[data-customize-setting-link]');
radios = {};
nodes.each( function() {
var node = $(this),
name;
if ( node.is(':radio') ) {
name = node.prop('name');
if ( radios[ name ] )
return;
radios[ name ] = true;
node = nodes.filter( '[name="' + name + '"]' );
}
api( node.data('customizeSettingLink'), function( setting ) {
var element = new api.Element( node );
control.elements.push( element );
element.link( setting ).bind( function( to ) {
setting( to );
});
});
});
},
ready: function() {}
});
api.ColorControl = api.Control.extend({
ready: function() {
var control = this,
picker, ui, text, toggle, update;
picker = this.container.find( '.color-picker' );
ui = picker.find( '.color-picker-controls' );
@ -47,52 +107,48 @@
update = function( color ) {
color = '#' + color;
toggle.css( 'background', color );
self.farbtastic.setColor( color );
control.farbtastic.setColor( color );
};
this.input = new api.Element( ui.find( 'input' ) ); // Find text input.
this.link( this.input );
this.input.link( this );
picker.on( 'click', 'a', function() {
ui.toggle();
});
this.farbtastic = $.farbtastic( picker.find('.farbtastic-placeholder'), function( color ) {
self.set( color.replace( '#', '' ) );
control.setting.set( color.replace( '#', '' ) );
});
this.bind( update );
update( this() );
},
validate: function( to ) {
return /^[a-fA-F0-9]{3}([a-fA-F0-9]{3})?$/.test( to ) ? to : null;
this.setting.bind( update );
update( this.setting() );
}
// ,
// validate: function( to ) {
// return /^[a-fA-F0-9]{3}([a-fA-F0-9]{3})?$/.test( to ) ? to : null;
// }
});
api.UploadControl = api.Control.extend({
initialize: function( id, value, options ) {
ready: function() {
var control = this;
api.Control.prototype.initialize.call( this, id, value, options );
this.params.removed = this.params.removed || '';
this.uploader = new wp.Uploader({
browser: this.container.find('.upload'),
success: function( attachment ) {
control.set( attachment.url );
control.setting.set( attachment.url );
}
});
this.remover = this.container.find('.remove');
this.remover.click( function( event ) {
control.set( control.params.removed );
control.setting.set( control.params.removed );
event.preventDefault();
});
this.bind( this.removerVisibility );
this.removerVisibility( this.get() );
this.removerVisibility = $.proxy( this.removerVisibility, this );
this.setting.bind( this.removerVisibility );
this.removerVisibility( this.setting.get() );
if ( this.params.context )
control.uploader.param( 'post_data[context]', this.params.context );
@ -103,13 +159,12 @@
});
api.ImageControl = api.UploadControl.extend({
initialize: function( id, value, options ) {
ready: function( id, value, options ) {
var control = this;
api.UploadControl.prototype.initialize.call( this, id, value, options );
this.thumbnail = this.container.find('.thumbnail img');
this.bind( this.thumbnailSrc );
this.thumbnail = this.container.find('.thumbnail img');
this.thumbnailSrc = $.proxy( this.thumbnailSrc, this );
this.setting.bind( this.thumbnailSrc );
this.library = this.container.find('.library');
this.changer = this.container.find('.change');
@ -137,7 +192,7 @@
});
this.library.on( 'click', 'a', function( event ) {
control.set( $(this).attr('href') );
control.setting.set( $(this).attr('href') );
event.preventDefault();
});
},
@ -152,6 +207,9 @@
// Change objects contained within the main customize object to Settings.
api.defaultConstructor = api.Setting;
// Create the collection of Control objects.
api.control = new api.Values({ defaultConstructor: api.Control });
api.Previewer = api.Messenger.extend({
refreshBuffer: 250,
@ -272,7 +330,7 @@
* Ready.
* ===================================================================== */
api.controls = {
api.controlConstructor = {
color: api.ColorControl,
upload: api.UploadControl,
image: api.ImageControl
@ -289,11 +347,17 @@
url: api.settings.preview
});
$.each( api.settings.settings, function( id, data ) {
api.set( id, id, data.value, {
previewer: previewer
} );
});
$.each( api.settings.controls, function( id, data ) {
var constructor = api.controls[ data.control ] || api.Control,
var constructor = api.controlConstructor[ data.type ] || api.Control,
control;
control = api.add( id, new constructor( id, data.value, {
control = api.control.add( id, new constructor( id, {
params: data.params,
previewer: previewer
} ) );
@ -326,9 +390,29 @@
});
// Background color uses postMessage by default
api( 'background_color', function( control ) {
api.control( 'background_color', function( control ) {
control.method = 'postMessage';
});
api.control( 'display_header_text', function( control ) {
var last = '';
control.elements[0].unlink();
control.element = new api.Element( control.container.find('input') );
control.element.set( 'blank' !== control.setting() );
control.element.bind( function( to ) {
if ( ! to )
last = api.get( 'header_textcolor' );
control.setting.set( to ? last : 'blank' );
});
control.setting.bind( function( to ) {
control.element.set( 'blank' !== to );
});
});
});
})( wp, jQuery );

View File

@ -66,7 +66,11 @@ if ( typeof wp === 'undefined' )
this.uploader.bind( 'UploadProgress', this.progress );
this.uploader.bind( 'FileUploaded', function( up, file, response ) {
response = JSON.parse( response.response );
try {
response = JSON.parse( response.response );
} catch ( e ) {
return self.error( pluploadL10n.default_error, e );
}
if ( ! response || ! response.type || ! response.data )
return self.error( pluploadL10n.default_error );