<# } else { #>
<# } #>
<# if ( data.theme.screenshot && data.theme.screenshot[0] ) { #>
<# } else { #>
<# } #>
<# if ( 'installed' === data.theme.type && data.theme.hasUpdate ) { #>
<# if ( data.theme.updateResponse.compatibleWP && data.theme.updateResponse.compatiblePHP ) { #>
' . __( 'Update now' ) . ''
);
}
?>
<# } else { #>
<# if ( ! data.theme.updateResponse.compatibleWP && ! data.theme.updateResponse.compatiblePHP ) { #>
Please update WordPress, and then learn more about updating PHP .' ),
self_admin_url( 'update-core.php' ),
esc_url( wp_get_update_php_url() )
);
wp_update_php_annotation( '
', ' ' );
} elseif ( current_user_can( 'update_core' ) ) {
printf(
/* translators: %s: URL to WordPress Updates screen. */
' ' . __( 'Please update WordPress .' ),
self_admin_url( 'update-core.php' )
);
} elseif ( current_user_can( 'update_php' ) ) {
printf(
/* translators: %s: URL to Update PHP page. */
' ' . __( 'Learn more about updating PHP .' ),
esc_url( wp_get_update_php_url() )
);
wp_update_php_annotation( '
', ' ' );
}
?>
<# } else if ( ! data.theme.updateResponse.compatibleWP ) { #>
Please update WordPress.' ),
self_admin_url( 'update-core.php' )
);
}
?>
<# } else if ( ! data.theme.updateResponse.compatiblePHP ) { #>
Learn more about updating PHP.' ),
esc_url( wp_get_update_php_url() )
);
wp_update_php_annotation( '
', ' ' );
}
?>
<# } #>
<# } #>
<# } #>
<# if ( ! data.theme.compatibleWP || ! data.theme.compatiblePHP ) { #>
<# if ( ! data.theme.compatibleWP && ! data.theme.compatiblePHP ) { #>
Please update WordPress, and then learn more about updating PHP .' ),
self_admin_url( 'update-core.php' ),
esc_url( wp_get_update_php_url() )
);
wp_update_php_annotation( '
', ' ' );
} elseif ( current_user_can( 'update_core' ) ) {
printf(
/* translators: %s: URL to WordPress Updates screen. */
' ' . __( 'Please update WordPress .' ),
self_admin_url( 'update-core.php' )
);
} elseif ( current_user_can( 'update_php' ) ) {
printf(
/* translators: %s: URL to Update PHP page. */
' ' . __( 'Learn more about updating PHP .' ),
esc_url( wp_get_update_php_url() )
);
wp_update_php_annotation( '
', ' ' );
}
?>
<# } else if ( ! data.theme.compatibleWP ) { #>
Please update WordPress.' ),
self_admin_url( 'update-core.php' )
);
}
?>
<# } else if ( ! data.theme.compatiblePHP ) { #>
Learn more about updating PHP.' ),
esc_url( wp_get_update_php_url() )
);
wp_update_php_annotation( '
', ' ' );
}
?>
<# } #>
<# } #>
<# if ( data.theme.active ) { #>
'success',
'additional_classes' => array( 'notice-alt' ),
)
);
?>
<# } else if ( 'installed' === data.theme.type ) { #>
<# if ( data.theme.blockTheme ) { #>
{{ data.theme.name }}
<# if ( data.theme.actions.activate ) { #>
<# } #>
<# if ( data.theme.actions.activate ) { #>
activate this theme, and use the Site Editor to customize it.' ),
'{{{ data.theme.actions.activate }}}'
);
?>
<# } #>
'error',
'additional_classes' => array( 'notice-alt' ),
)
);
?>
<# } else { #>
{{ data.theme.name }}
<# if ( data.theme.compatibleWP && data.theme.compatiblePHP ) { #>
<# } else { #>
<# } #>
'success',
'additional_classes' => array( 'notice-alt' ),
)
);
?>
<# } #>
<# } else { #>
{{ data.theme.name }}
<# if ( data.theme.compatibleWP && data.theme.compatiblePHP ) { #>
<# } else { #>
<# } #>
<# } #>
-?\d+)\]$/';
const POST_TYPE = 'nav_menu_item';
const TYPE = 'nav_menu_item';
/**
* Setting type.
*
* @since 4.3.0
* @var string
*/
public $type = self::TYPE;
/**
* Default setting value.
*
* @since 4.3.0
* @var array
*
* @see wp_setup_nav_menu_item()
*/
public $default = array(
// The $menu_item_data for wp_update_nav_menu_item().
'object_id' => 0,
'object' => '', // Taxonomy name.
'menu_item_parent' => 0, // A.K.A. menu-item-parent-id; note that post_parent is different, and not included.
'position' => 0, // A.K.A. menu_order.
'type' => 'custom', // Note that type_label is not included here.
'title' => '',
'url' => '',
'target' => '',
'attr_title' => '',
'description' => '',
'classes' => '',
'xfn' => '',
'status' => 'publish',
'nav_menu_term_id' => 0, // This will be supplied as the $menu_id arg for wp_update_nav_menu_item().
'_invalid' => false,
);
/**
* Default transport.
*
* @since 4.3.0
* @since 4.5.0 Default changed to 'refresh'
* @var string
*/
public $transport = 'refresh';
/**
* The post ID represented by this setting instance. This is the db_id.
*
* A negative value represents a placeholder ID for a new menu not yet saved.
*
* @since 4.3.0
* @var int
*/
public $post_id;
/**
* Storage of pre-setup menu item to prevent wasted calls to wp_setup_nav_menu_item().
*
* @since 4.3.0
* @var array|null
*/
protected $value;
/**
* Previous (placeholder) post ID used before creating a new menu item.
*
* This value will be exported to JS via the customize_save_response filter
* so that JavaScript can update the settings to refer to the newly-assigned
* post ID. This value is always negative to indicate it does not refer to
* a real post.
*
* @since 4.3.0
* @var int
*
* @see WP_Customize_Nav_Menu_Item_Setting::update()
* @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response()
*/
public $previous_post_id;
/**
* When previewing or updating a menu item, this stores the previous nav_menu_term_id
* which ensures that we can apply the proper filters.
*
* @since 4.3.0
* @var int
*/
public $original_nav_menu_term_id;
/**
* Whether or not update() was called.
*
* @since 4.3.0
* @var bool
*/
protected $is_updated = false;
/**
* Status for calling the update method, used in customize_save_response filter.
*
* See {@see 'customize_save_response'}.
*
* When status is inserted, the placeholder post ID is stored in $previous_post_id.
* When status is error, the error is stored in $update_error.
*
* @since 4.3.0
* @var string updated|inserted|deleted|error
*
* @see WP_Customize_Nav_Menu_Item_Setting::update()
* @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response()
*/
public $update_status;
/**
* Any error object returned by wp_update_nav_menu_item() when setting is updated.
*
* @since 4.3.0
* @var WP_Error
*
* @see WP_Customize_Nav_Menu_Item_Setting::update()
* @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response()
*/
public $update_error;
/**
* Constructor.
*
* Any supplied $args override class property defaults.
*
* @since 4.3.0
*
* @throws Exception If $id is not valid for this setting type.
*
* @param WP_Customize_Manager $manager Customizer bootstrap instance.
* @param string $id A specific ID of the setting.
* Can be a theme mod or option name.
* @param array $args Optional. Setting arguments.
*/
public function __construct( WP_Customize_Manager $manager, $id, array $args = array() ) {
if ( empty( $manager->nav_menus ) ) {
throw new Exception( 'Expected WP_Customize_Manager::$nav_menus to be set.' );
}
if ( ! preg_match( self::ID_PATTERN, $id, $matches ) ) {
throw new Exception( "Illegal widget setting ID: $id" );
}
$this->post_id = (int) $matches['id'];
add_action( 'wp_update_nav_menu_item', array( $this, 'flush_cached_value' ), 10, 2 );
parent::__construct( $manager, $id, $args );
// Ensure that an initially-supplied value is valid.
if ( isset( $this->value ) ) {
$this->populate_value();
foreach ( array_diff( array_keys( $this->default ), array_keys( $this->value ) ) as $missing ) {
throw new Exception( "Supplied nav_menu_item value missing property: $missing" );
}
}
}
/**
* Clear the cached value when this nav menu item is updated.
*
* @since 4.3.0
*
* @param int $menu_id The term ID for the menu.
* @param int $menu_item_id The post ID for the menu item.
*/
public function flush_cached_value( $menu_id, $menu_item_id ) {
unset( $menu_id );
if ( $menu_item_id === $this->post_id ) {
$this->value = null;
}
}
/**
* Get the instance data for a given nav_menu_item setting.
*
* @since 4.3.0
*
* @see wp_setup_nav_menu_item()
*
* @return array|false Instance data array, or false if the item is marked for deletion.
*/
public function value() {
$type_label = null;
if ( $this->is_previewed && get_current_blog_id() === $this->_previewed_blog_id ) {
$undefined = new stdClass(); // Symbol.
$post_value = $this->post_value( $undefined );
if ( $undefined === $post_value ) {
$value = $this->_original_value;
} else {
$value = $post_value;
}
} elseif ( isset( $this->value ) ) {
$value = $this->value;
} else {
$value = false;
// Note that an ID of less than one indicates a nav_menu not yet inserted.
if ( $this->post_id > 0 ) {
$post = get_post( $this->post_id );
if ( $post && self::POST_TYPE === $post->post_type ) {
$is_title_empty = empty( $post->post_title );
$value = (array) wp_setup_nav_menu_item( $post );
if ( isset( $value['type_label'] ) ) {
$type_label = $value['type_label'];
}
if ( $is_title_empty ) {
$value['title'] = '';
}
}
}
if ( ! is_array( $value ) ) {
$value = $this->default;
}
// Cache the value for future calls to avoid having to re-call wp_setup_nav_menu_item().
$this->value = $value;
$this->populate_value();
$value = $this->value;
}
// These properties are read-only and are part of the setting for use in the Customizer UI.
if ( is_array( $value ) ) {
$value_obj = (object) $value;
$value['type_label'] = $type_label ?? $this->get_type_label( $value_obj );
$value['original_title'] = $this->get_original_title( $value_obj );
}
return $value;
}
/**
* Prepares the value for editing on the client.
*
* @since 6.8.3
*
* @return array|false Value prepared for the client.
*/
public function js_value() {
$value = parent::js_value();
if ( is_array( $value ) && isset( $value['original_title'] ) ) {
// Decode entities for the sake of displaying the original title as a placeholder.
$value['original_title'] = html_entity_decode( $value['original_title'], ENT_QUOTES, get_bloginfo( 'charset' ) );
}
return $value;
}
/**
* Get original title.
*
* @since 4.7.0
*
* @param object $item Nav menu item.
* @return string The original title, without entity decoding.
*/
protected function get_original_title( $item ) {
$original_title = '';
if ( 'post_type' === $item->type && ! empty( $item->object_id ) ) {
$original_object = get_post( $item->object_id );
if ( $original_object ) {
/** This filter is documented in wp-includes/post-template.php */
$original_title = apply_filters( 'the_title', $original_object->post_title, $original_object->ID );
if ( '' === $original_title ) {
/* translators: %d: ID of a post. */
$original_title = sprintf( __( '#%d (no title)' ), $original_object->ID );
}
}
} elseif ( 'taxonomy' === $item->type && ! empty( $item->object_id ) ) {
$original_term_title = get_term_field( 'name', $item->object_id, $item->object, 'raw' );
if ( ! is_wp_error( $original_term_title ) ) {
$original_title = $original_term_title;
}
} elseif ( 'post_type_archive' === $item->type ) {
$original_object = get_post_type_object( $item->object );
if ( $original_object ) {
$original_title = $original_object->labels->archives;
}
}
return $original_title;
}
/**
* Get type label.
*
* @since 4.7.0
*
* @param object $item Nav menu item.
* @return string The type label.
*/
protected function get_type_label( $item ) {
if ( 'post_type' === $item->type ) {
$object = get_post_type_object( $item->object );
if ( $object ) {
$type_label = $object->labels->singular_name;
} else {
$type_label = $item->object;
}
} elseif ( 'taxonomy' === $item->type ) {
$object = get_taxonomy( $item->object );
if ( $object ) {
$type_label = $object->labels->singular_name;
} else {
$type_label = $item->object;
}
} elseif ( 'post_type_archive' === $item->type ) {
$type_label = __( 'Post Type Archive' );
} else {
$type_label = __( 'Custom Link' );
}
return $type_label;
}
/**
* Ensure that the value is fully populated with the necessary properties.
*
* Translates some properties added by wp_setup_nav_menu_item() and removes others.
*
* @since 4.3.0
*
* @see WP_Customize_Nav_Menu_Item_Setting::value()
*/
protected function populate_value() {
if ( ! is_array( $this->value ) ) {
return;
}
if ( isset( $this->value['menu_order'] ) ) {
$this->value['position'] = $this->value['menu_order'];
unset( $this->value['menu_order'] );
}
if ( isset( $this->value['post_status'] ) ) {
$this->value['status'] = $this->value['post_status'];
unset( $this->value['post_status'] );
}
if ( ! isset( $this->value['nav_menu_term_id'] ) && $this->post_id > 0 ) {
$menus = wp_get_post_terms(
$this->post_id,
WP_Customize_Nav_Menu_Setting::TAXONOMY,
array(
'fields' => 'ids',
)
);
if ( ! empty( $menus ) ) {
$this->value['nav_menu_term_id'] = array_shift( $menus );
} else {
$this->value['nav_menu_term_id'] = 0;
}
}
foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) {
if ( ! is_int( $this->value[ $key ] ) ) {
$this->value[ $key ] = (int) $this->value[ $key ];
}
}
foreach ( array( 'classes', 'xfn' ) as $key ) {
if ( is_array( $this->value[ $key ] ) ) {
$this->value[ $key ] = implode( ' ', $this->value[ $key ] );
}
}
if ( ! isset( $this->value['title'] ) ) {
$this->value['title'] = '';
}
if ( ! isset( $this->value['_invalid'] ) ) {
$this->value['_invalid'] = false;
$is_known_invalid = (
( ( 'post_type' === $this->value['type'] || 'post_type_archive' === $this->value['type'] ) && ! post_type_exists( $this->value['object'] ) )
||
( 'taxonomy' === $this->value['type'] && ! taxonomy_exists( $this->value['object'] ) )
);
if ( $is_known_invalid ) {
$this->value['_invalid'] = true;
}
}
// Remove remaining properties available on a setup nav_menu_item post object which aren't relevant to the setting value.
$irrelevant_properties = array(
'ID',
'comment_count',
'comment_status',
'db_id',
'filter',
'guid',
'ping_status',
'pinged',
'post_author',
'post_content',
'post_content_filtered',
'post_date',
'post_date_gmt',
'post_excerpt',
'post_mime_type',
'post_modified',
'post_modified_gmt',
'post_name',
'post_parent',
'post_password',
'post_title',
'post_type',
'to_ping',
);
foreach ( $irrelevant_properties as $property ) {
unset( $this->value[ $property ] );
}
}
/**
* Handle previewing the setting.
*
* @since 4.3.0
* @since 4.4.0 Added boolean return value.
*
* @see WP_Customize_Manager::post_value()
*
* @return bool False if method short-circuited due to no-op.
*/
public function preview() {
if ( $this->is_previewed ) {
return false;
}
$undefined = new stdClass();
$is_placeholder = ( $this->post_id < 0 );
$is_dirty = ( $undefined !== $this->post_value( $undefined ) );
if ( ! $is_placeholder && ! $is_dirty ) {
return false;
}
$this->is_previewed = true;
$this->_original_value = $this->value();
$this->original_nav_menu_term_id = $this->_original_value['nav_menu_term_id'];
$this->_previewed_blog_id = get_current_blog_id();
add_filter( 'wp_get_nav_menu_items', array( $this, 'filter_wp_get_nav_menu_items' ), 10, 3 );
$sort_callback = array( __CLASS__, 'sort_wp_get_nav_menu_items' );
if ( ! has_filter( 'wp_get_nav_menu_items', $sort_callback ) ) {
add_filter( 'wp_get_nav_menu_items', array( __CLASS__, 'sort_wp_get_nav_menu_items' ), 1000, 3 );
}
// @todo Add get_post_metadata filters for plugins to add their data.
return true;
}
/**
* Filters the wp_get_nav_menu_items() result to supply the previewed menu items.
*
* @since 4.3.0
*
* @see wp_get_nav_menu_items()
*
* @param WP_Post[] $items An array of menu item post objects.
* @param WP_Term $menu The menu object.
* @param array $args An array of arguments used to retrieve menu item objects.
* @return WP_Post[] Array of menu item objects.
*/
public function filter_wp_get_nav_menu_items( $items, $menu, $args ) {
$this_item = $this->value();
$current_nav_menu_term_id = null;
if ( isset( $this_item['nav_menu_term_id'] ) ) {
$current_nav_menu_term_id = $this_item['nav_menu_term_id'];
unset( $this_item['nav_menu_term_id'] );
}
$should_filter = (
$menu->term_id === $this->original_nav_menu_term_id
||
$menu->term_id === $current_nav_menu_term_id
);
if ( ! $should_filter ) {
return $items;
}
// Handle deleted menu item, or menu item moved to another menu.
$should_remove = (
false === $this_item
||
( isset( $this_item['_invalid'] ) && true === $this_item['_invalid'] )
||
(
$this->original_nav_menu_term_id === $menu->term_id
&&
$current_nav_menu_term_id !== $this->original_nav_menu_term_id
)
);
if ( $should_remove ) {
$filtered_items = array();
foreach ( $items as $item ) {
if ( $item->db_id !== $this->post_id ) {
$filtered_items[] = $item;
}
}
return $filtered_items;
}
$mutated = false;
$should_update = (
is_array( $this_item )
&&
$current_nav_menu_term_id === $menu->term_id
);
if ( $should_update ) {
foreach ( $items as $item ) {
if ( $item->db_id === $this->post_id ) {
foreach ( get_object_vars( $this->value_as_wp_post_nav_menu_item() ) as $key => $value ) {
$item->$key = $value;
}
$mutated = true;
}
}
// Not found so we have to append it..
if ( ! $mutated ) {
$items[] = $this->value_as_wp_post_nav_menu_item();
}
}
return $items;
}
/**
* Re-apply the tail logic also applied on $items by wp_get_nav_menu_items().
*
* @since 4.3.0
*
* @see wp_get_nav_menu_items()
*
* @param WP_Post[] $items An array of menu item post objects.
* @param WP_Term $menu The menu object.
* @param array $args An array of arguments used to retrieve menu item objects.
* @return WP_Post[] Array of menu item objects.
*/
public static function sort_wp_get_nav_menu_items( $items, $menu, $args ) {
// @todo We should probably re-apply some constraints imposed by $args.
unset( $args['include'] );
// Remove invalid items only in front end.
if ( ! is_admin() ) {
$items = array_filter( $items, '_is_valid_nav_menu_item' );
}
if ( ARRAY_A === $args['output'] ) {
$items = wp_list_sort(
$items,
array(
$args['output_key'] => 'ASC',
)
);
$i = 1;
foreach ( $items as $k => $item ) {
$items[ $k ]->{$args['output_key']} = $i++;
}
}
return $items;
}
/**
* Get the value emulated into a WP_Post and set up as a nav_menu_item.
*
* @since 4.3.0
*
* @return WP_Post With wp_setup_nav_menu_item() applied.
*/
public function value_as_wp_post_nav_menu_item() {
$item = (object) $this->value();
unset( $item->nav_menu_term_id );
$item->post_status = $item->status;
unset( $item->status );
$item->post_type = 'nav_menu_item';
$item->menu_order = $item->position;
unset( $item->position );
if ( empty( $item->title ) && ! empty( $item->original_title ) ) {
$item->title = $item->original_title; // This is NOT entity-decoded. It comes from self::get_original_title().
}
if ( $item->title ) {
$item->post_title = $item->title;
}
// 'classes' should be an array, as in wp_setup_nav_menu_item().
if ( isset( $item->classes ) && is_scalar( $item->classes ) ) {
$item->classes = explode( ' ', $item->classes );
}
$item->ID = $this->post_id;
$item->db_id = $this->post_id;
$post = new WP_Post( (object) $item );
if ( empty( $post->post_author ) ) {
$post->post_author = get_current_user_id();
}
if ( ! isset( $post->type_label ) ) {
$post->type_label = $this->get_type_label( $post );
}
// Ensure nav menu item URL is set according to linked object.
if ( 'post_type' === $post->type && ! empty( $post->object_id ) ) {
$post->url = get_permalink( $post->object_id );
} elseif ( 'taxonomy' === $post->type && ! empty( $post->object ) && ! empty( $post->object_id ) ) {
$post->url = get_term_link( (int) $post->object_id, $post->object );
} elseif ( 'post_type_archive' === $post->type && ! empty( $post->object ) ) {
$post->url = get_post_type_archive_link( $post->object );
}
if ( is_wp_error( $post->url ) ) {
$post->url = '';
}
/** This filter is documented in wp-includes/nav-menu.php */
$post->attr_title = apply_filters( 'nav_menu_attr_title', $post->attr_title );
/** This filter is documented in wp-includes/nav-menu.php */
$post->description = apply_filters( 'nav_menu_description', wp_trim_words( $post->description, 200 ) );
/** This filter is documented in wp-includes/nav-menu.php */
$post = apply_filters( 'wp_setup_nav_menu_item', $post );
return $post;
}
/**
* Sanitize an input.
*
* Note that parent::sanitize() erroneously does wp_unslash() on $value, but
* we remove that in this override.
*
* @since 4.3.0
* @since 5.9.0 Renamed `$menu_item_value` to `$value` for PHP 8 named parameter support.
*
* @param array|false $value The menu item value to sanitize.
* @return array|false|null|WP_Error Null or WP_Error if an input isn't valid. False if it is marked for deletion.
* Otherwise the sanitized value.
*/
public function sanitize( $value ) {
// Restores the more descriptive, specific name for use within this method.
$menu_item_value = $value;
// Menu is marked for deletion.
if ( false === $menu_item_value ) {
return $menu_item_value;
}
// Invalid.
if ( ! is_array( $menu_item_value ) ) {
return null;
}
$default = array(
'object_id' => 0,
'object' => '',
'menu_item_parent' => 0,
'position' => 0,
'type' => 'custom',
'title' => '',
'url' => '',
'target' => '',
'attr_title' => '',
'description' => '',
'classes' => '',
'xfn' => '',
'status' => 'publish',
'original_title' => '',
'nav_menu_term_id' => 0,
'_invalid' => false,
);
$menu_item_value = array_merge( $default, $menu_item_value );
$menu_item_value = wp_array_slice_assoc( $menu_item_value, array_keys( $default ) );
$menu_item_value['position'] = (int) $menu_item_value['position'];
foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) {
// Note we need to allow negative-integer IDs for previewed objects not inserted yet.
$menu_item_value[ $key ] = (int) $menu_item_value[ $key ];
}
foreach ( array( 'type', 'object', 'target' ) as $key ) {
$menu_item_value[ $key ] = sanitize_key( $menu_item_value[ $key ] );
}
foreach ( array( 'xfn', 'classes' ) as $key ) {
$value = $menu_item_value[ $key ];
if ( ! is_array( $value ) ) {
$value = explode( ' ', $value );
}
$menu_item_value[ $key ] = implode( ' ', array_map( 'sanitize_html_class', $value ) );
}
// Apply the same filters as when calling wp_insert_post().
/** This filter is documented in wp-includes/post.php */
$menu_item_value['title'] = wp_unslash( apply_filters( 'title_save_pre', wp_slash( $menu_item_value['title'] ) ) );
/** This filter is documented in wp-includes/post.php */
$menu_item_value['attr_title'] = wp_unslash( apply_filters( 'excerpt_save_pre', wp_slash( $menu_item_value['attr_title'] ) ) );
/** This filter is documented in wp-includes/post.php */
$menu_item_value['description'] = wp_unslash( apply_filters( 'content_save_pre', wp_slash( $menu_item_value['description'] ) ) );
if ( '' !== $menu_item_value['url'] ) {
$menu_item_value['url'] = sanitize_url( $menu_item_value['url'] );
if ( '' === $menu_item_value['url'] ) {
return new WP_Error( 'invalid_url', __( 'Invalid URL.' ) ); // Fail sanitization if URL is invalid.
}
}
if ( 'publish' !== $menu_item_value['status'] ) {
$menu_item_value['status'] = 'draft';
}
$menu_item_value['_invalid'] = (bool) $menu_item_value['_invalid'];
/** This filter is documented in wp-includes/class-wp-customize-setting.php */
return apply_filters( "customize_sanitize_{$this->id}", $menu_item_value, $this );
}
/**
* Creates/updates the nav_menu_item post for this setting.
*
* Any created menu items will have their assigned post IDs exported to the client
* via the {@see 'customize_save_response'} filter. Likewise, any errors will be
* exported to the client via the customize_save_response() filter.
*
* To delete a menu, the client can send false as the value.
*
* @since 4.3.0
* @since 7.0.0 Return type updated from null|void to bool for compatibility with base class.
*
* @see wp_update_nav_menu_item()
*
* @param array|false $value The menu item array to update. If false, then the menu item will be deleted
* entirely. See WP_Customize_Nav_Menu_Item_Setting::$default for what the value
* should consist of.
* @return bool Whether updated.
*/
protected function update( $value ) {
if ( $this->is_updated ) {
return ( 'error' !== $this->update_status );
}
$this->is_updated = true;
$is_placeholder = ( $this->post_id < 0 );
$is_delete = ( false === $value );
// Update the cached value.
$this->value = $value;
add_filter( 'customize_save_response', array( $this, 'amend_customize_save_response' ) );
if ( $is_delete ) {
// If the current setting post is a placeholder, a delete request is a no-op.
if ( $is_placeholder ) {
$this->update_status = 'deleted';
} else {
$r = wp_delete_post( $this->post_id, true );
if ( false === $r ) {
$this->update_error = new WP_Error( 'delete_failure' );
$this->update_status = 'error';
} else {
$this->update_status = 'deleted';
}
// @todo send back the IDs for all associated nav menu items deleted, so these settings (and controls) can be removed from Customizer?
}
} else {
// Handle saving menu items for menus that are being newly-created.
if ( $value['nav_menu_term_id'] < 0 ) {
$nav_menu_setting_id = sprintf( 'nav_menu[%s]', $value['nav_menu_term_id'] );
$nav_menu_setting = $this->manager->get_setting( $nav_menu_setting_id );
if ( ! $nav_menu_setting || ! ( $nav_menu_setting instanceof WP_Customize_Nav_Menu_Setting ) ) {
$this->update_status = 'error';
$this->update_error = new WP_Error( 'unexpected_nav_menu_setting' );
return false;
}
if ( false === $nav_menu_setting->save() ) {
$this->update_status = 'error';
$this->update_error = new WP_Error( 'nav_menu_setting_failure' );
return false;
}
if ( (int) $value['nav_menu_term_id'] !== $nav_menu_setting->previous_term_id ) {
$this->update_status = 'error';
$this->update_error = new WP_Error( 'unexpected_previous_term_id' );
return false;
}
$value['nav_menu_term_id'] = $nav_menu_setting->term_id;
}
// Handle saving a nav menu item that is a child of a nav menu item being newly-created.
if ( $value['menu_item_parent'] < 0 ) {
$parent_nav_menu_item_setting_id = sprintf( 'nav_menu_item[%s]', $value['menu_item_parent'] );
$parent_nav_menu_item_setting = $this->manager->get_setting( $parent_nav_menu_item_setting_id );
if ( ! $parent_nav_menu_item_setting || ! ( $parent_nav_menu_item_setting instanceof WP_Customize_Nav_Menu_Item_Setting ) ) {
$this->update_status = 'error';
$this->update_error = new WP_Error( 'unexpected_nav_menu_item_setting' );
return false;
}
if ( false === $parent_nav_menu_item_setting->save() ) {
$this->update_status = 'error';
$this->update_error = new WP_Error( 'nav_menu_item_setting_failure' );
return false;
}
if ( (int) $value['menu_item_parent'] !== $parent_nav_menu_item_setting->previous_post_id ) {
$this->update_status = 'error';
$this->update_error = new WP_Error( 'unexpected_previous_post_id' );
return false;
}
$value['menu_item_parent'] = $parent_nav_menu_item_setting->post_id;
}
// Insert or update menu.
$menu_item_data = array(
'menu-item-object-id' => $value['object_id'],
'menu-item-object' => $value['object'],
'menu-item-parent-id' => $value['menu_item_parent'],
'menu-item-position' => $value['position'],
'menu-item-type' => $value['type'],
'menu-item-title' => $value['title'],
'menu-item-url' => $value['url'],
'menu-item-description' => $value['description'],
'menu-item-attr-title' => $value['attr_title'],
'menu-item-target' => $value['target'],
'menu-item-classes' => $value['classes'],
'menu-item-xfn' => $value['xfn'],
'menu-item-status' => $value['status'],
);
$r = wp_update_nav_menu_item(
$value['nav_menu_term_id'],
$is_placeholder ? 0 : $this->post_id,
wp_slash( $menu_item_data )
);
if ( is_wp_error( $r ) ) {
$this->update_status = 'error';
$this->update_error = $r;
} else {
if ( $is_placeholder ) {
$this->previous_post_id = $this->post_id;
$this->post_id = $r;
$this->update_status = 'inserted';
} else {
$this->update_status = 'updated';
}
}
}
return ( 'error' !== $this->update_status );
}
/**
* Export data for the JS client.
*
* @since 4.3.0
*
* @see WP_Customize_Nav_Menu_Item_Setting::update()
*
* @param array $data Additional information passed back to the 'saved' event on `wp.customize`.
* @return array Save response data.
*/
public function amend_customize_save_response( $data ) {
if ( ! isset( $data['nav_menu_item_updates'] ) ) {
$data['nav_menu_item_updates'] = array();
}
$data['nav_menu_item_updates'][] = array(
'post_id' => $this->post_id,
'previous_post_id' => $this->previous_post_id,
'error' => $this->update_error ? $this->update_error->get_error_code() : null,
'status' => $this->update_status,
);
return $data;
}
}
class-wp-customize-header-image-setting.php 0000644 00000003456 15220052407 0015014 0 ustar 00 manager->get_setting( 'header_image' )->post_value();
}
if ( is_array( $value ) && isset( $value['choice'] ) ) {
$custom_image_header->set_header_image( $value['choice'] );
} else {
$custom_image_header->set_header_image( $value );
}
return true;
}
}
class-wp-customize-nav-menu-location-control.php 0000644 00000004373 15220052407 0016042 0 ustar 00 json['locationId'] = $this->location_id;
}
/**
* Render content just like a normal select control.
*
* @since 4.3.0
* @since 4.9.0 Added a button to create menus.
*/
public function render_content() {
if ( empty( $this->choices ) ) {
return;
}
$value_hidden_class = '';
$no_value_hidden_class = '';
if ( $this->value() ) {
$value_hidden_class = ' hidden';
} else {
$no_value_hidden_class = ' hidden';
}
?>
label ) ) : ?>
label ); ?>
description ) ) : ?>
description; ?>
link(); ?>>
choices as $value => $label ) :
echo 'value(), $value, false ) . '>' . esc_html( $label ) . ' ';
endforeach;
?>
max_year;
$data['minYear'] = (int) $this->min_year;
$data['allowPastDate'] = (bool) $this->allow_past_date;
$data['twelveHourFormat'] = (bool) $this->twelve_hour_format;
$data['includeTime'] = (bool) $this->include_time;
return $data;
}
/**
* Renders a JS template for the content of date time control.
*
* @since 4.9.0
*/
public function content_template() {
$data = array_merge( $this->json(), $this->get_month_choices() );
$timezone_info = $this->get_timezone_info();
$date_format = get_option( 'date_format' );
$date_format = preg_replace( '/(?
<# _.defaults( data, ); #>
<# var idPrefix = _.uniqueId( 'el' ) + '-'; #>
<# if ( data.label ) { #>
{{ data.label }}
<# } #>
<# if ( data.description ) { #>
{{ data.description }}
<# } #>
get_month_abbrev( $wp_locale->get_month( $i ) );
/* translators: 1: Month number (01, 02, etc.), 2: Month abbreviation. */
$months[ $i ]['text'] = sprintf( __( '%1$s-%2$s' ), $i, $month_text );
$months[ $i ]['value'] = $i;
}
return array(
'month_choices' => $months,
);
}
/**
* Get timezone info.
*
* @since 4.9.0
*
* @return array {
* Timezone info. All properties are optional.
*
* @type string $abbr Timezone abbreviation. Examples: PST or CEST.
* @type string $description Human-readable timezone description as HTML.
* }
*/
public function get_timezone_info() {
$tz_string = get_option( 'timezone_string' );
$timezone_info = array();
if ( $tz_string ) {
try {
$tz = new DateTimeZone( $tz_string );
} catch ( Exception $e ) {
$tz = '';
}
if ( $tz ) {
$now = new DateTime( 'now', $tz );
$formatted_gmt_offset = $this->format_gmt_offset( $tz->getOffset( $now ) / HOUR_IN_SECONDS );
$tz_name = str_replace( '_', ' ', $tz->getName() );
$timezone_info['abbr'] = $now->format( 'T' );
$timezone_info['description'] = sprintf(
/* translators: 1: Timezone name, 2: Timezone abbreviation, 3: UTC abbreviation and offset, 4: UTC offset. */
__( 'Your timezone is set to %1$s (%2$s), currently %3$s (Coordinated Universal Time %4$s).' ),
$tz_name,
'
' . $timezone_info['abbr'] . ' ',
'
UTC ' . $formatted_gmt_offset,
$formatted_gmt_offset
);
} else {
$timezone_info['description'] = '';
}
} else {
$formatted_gmt_offset = $this->format_gmt_offset( (int) get_option( 'gmt_offset', 0 ) );
$timezone_info['description'] = sprintf(
/* translators: 1: UTC abbreviation and offset, 2: UTC offset. */
__( 'Your timezone is set to %1$s (Coordinated Universal Time %2$s).' ),
'
UTC ' . $formatted_gmt_offset,
$formatted_gmt_offset
);
}
return $timezone_info;
}
/**
* Format GMT Offset.
*
* @since 4.9.0
*
* @see wp_timezone_choice()
*
* @param float $offset Offset in hours.
* @return string Formatted offset.
*/
public function format_gmt_offset( $offset ) {
if ( 0 <= $offset ) {
$formatted_offset = '+' . (string) $offset;
} else {
$formatted_offset = (string) $offset;
}
$formatted_offset = str_replace(
array( '.25', '.5', '.75' ),
array( ':15', ':30', ':45' ),
$formatted_offset
);
return $formatted_offset;
}
}
class-wp-customize-nav-menu-name-control.php 0000644 00000002153 15220052407 0015144 0 ustar 00
<# if ( data.label ) { #>
{{ data.label }}
<# } #>
aria-describedby="{{ data.section }}-description"
<# } #>
/>
<# if ( data.description ) { #>
{{ data.description }}
<# } #>
action;
$exported['filter_type'] = $this->filter_type;
return $exported;
}
/**
* Renders a themes section as a JS template.
*
* The template is only rendered by PHP once, so all actions are prepared at once on the server side.
*
* @since 4.9.0
*/
protected function render_template() {
?>
{{ data.title }}
filter_drawer_content_template(); ?>
support forums.' ),
__( 'https://wordpress.org/support/forums/' )
);
?>
%s', __( 'Search WordPress.org themes' ) )
);
?>
<# if ( 'wporg' === data.action ) { #>
<# } else { #>
<# } #>
<# if ( 'wporg' === data.action ) { #>
0 ' );
?>
<# } #>
0 ' );
?>
<# if ( 'wporg' === data.action ) { #>
<# } #>
statuses = array( '' => __( 'Default' ) );
parent::__construct( $manager, $id, $args );
}
/**
* Enqueue scripts/styles for the color picker.
*
* @since 3.4.0
*/
public function enqueue() {
wp_enqueue_script( 'wp-color-picker' );
wp_enqueue_style( 'wp-color-picker' );
}
/**
* Refresh the parameters passed to the JavaScript via JSON.
*
* @since 3.4.0
* @uses WP_Customize_Control::to_json()
*/
public function to_json() {
parent::to_json();
$this->json['statuses'] = $this->statuses;
$this->json['defaultValue'] = $this->setting->default;
$this->json['mode'] = $this->mode;
}
/**
* Don't render the control content from PHP, as it's rendered via JS on load.
*
* @since 3.4.0
*/
public function render_content() {}
/**
* Render a JS template for the content of the color picker control.
*
* @since 4.1.0
*/
public function content_template() {
?>
<#
var defaultValue = '#RRGGBB',
defaultValueAttr = '',
inputId = _.uniqueId( 'customize-color-control-input-' ),
isHueSlider = data.mode === 'hue';
if ( data.defaultValue && _.isString( data.defaultValue ) && ! isHueSlider ) {
if ( '#' !== data.defaultValue.substring( 0, 1 ) ) {
defaultValue = '#' + data.defaultValue;
} else {
defaultValue = data.defaultValue;
}
defaultValueAttr = ' data-default-color=' + defaultValue; // Quotes added automatically.
}
#>
<# if ( data.label ) { #>
{{{ data.label }}}
<# } #>
<# if ( data.description ) { #>
{{{ data.description }}}
<# } #>
{{{ data.label }}}
<# if ( isHueSlider ) { #>
<# } else { #>
<# } #>
editor_settings = wp_enqueue_code_editor(
array_merge(
array(
'type' => $this->code_type,
'codemirror' => array(
'indentUnit' => 2,
'tabSize' => 2,
),
),
$this->editor_settings
)
);
}
/**
* Refresh the parameters passed to the JavaScript via JSON.
*
* @since 4.9.0
*
* @see WP_Customize_Control::json()
*
* @return array Array of parameters passed to the JavaScript.
*/
public function json() {
$json = parent::json();
$json['editor_settings'] = $this->editor_settings;
$json['input_attrs'] = $this->input_attrs;
return $json;
}
/**
* Don't render the control content from PHP, as it's rendered via JS on load.
*
* @since 4.9.0
*/
public function render_content() {}
/**
* Render a JS template for control display.
*
* @since 4.9.0
*/
public function content_template() {
?>
<# var elementIdPrefix = 'el' + String( Math.random() ); #>
<# if ( data.label ) { #>
{{ data.label }}
<# } #>
<# if ( data.description ) { #>
{{{ data.description }}}
<# } #>