Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active October 25, 2018 04:10
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/9e9510c7f57dfbd37eb8c0162b971aa2 to your computer and use it in GitHub Desktop.
Save westonruter/9e9510c7f57dfbd37eb8c0162b971aa2 to your computer and use it in GitHub Desktop.
<?php
/**
* Class Customize_Postmeta_Setting.
*
* @package WPSE_257322
*/
namespace WPSE_257322;
/**
* Class Customize_Postmeta_Setting
*
* This is adapted from `WP_Customize_Postmeta_Setting` in the Customize Posts plugin.
*
* @link https://github.com/xwp/wp-customize-posts/blob/0.8.5/php/class-wp-customize-postmeta-setting.php
*/
class Customize_Single_Postmeta_Setting extends \WP_Customize_Setting {
const ID_PATTERN = '#^wpse_257322_postmeta\[(?P<post_id>\d+)]\[(?P<meta_key>.+?)]$#';
const TYPE = 'wpse_257322_postmeta';
/**
* Type of setting.
*
* @access public
* @var string
*/
public $type = self::TYPE;
/**
* Post type.
*
* @access public
* @var string
*/
public $post_type;
/**
* Post ID.
*
* @access public
* @var string
*/
public $post_id;
/**
* Meta key.
*
* @access public
* @var string
*/
public $meta_key;
/**
* Parse setting ID.
*
* @param string $setting_id Setting ID.
* @return array|bool Parsed setting ID or false if parse error.
*/
static function parse_setting_id( $setting_id ) {
if ( ! preg_match( self::ID_PATTERN, $setting_id, $matches ) ) {
return false;
}
$matches['post_id'] = intval( $matches['post_id'] );
if ( $matches['post_id'] <= 0 ) {
return false;
}
return wp_array_slice_assoc( $matches, array( 'post_id', 'meta_key' ) );
}
/**
* Create setting ID.
*
* @param int $post_id Post ID.
* @param string $meta_key Meta key.
* @return string Setting ID.
*/
static function create_setting_id( $post_id, $meta_key ) {
return sprintf( '%s[%d][%s]', self::TYPE, $post_id, $meta_key );
}
/**
* WP_Customize_Post_Setting constructor.
*
* @access public
*
* @param \WP_Customize_Manager $manager Manager.
* @param string $id Setting ID.
* @param array $args Setting args.
* @throws \Exception If the ID is in an invalid format.
* @global array $wp_meta_keys
*/
public function __construct( \WP_Customize_Manager $manager, $id, $args = array() ) {
global $wp_meta_keys;
$parsed_setting_id = self::parse_setting_id( $id );
if ( ! $parsed_setting_id ) {
throw new \Exception( 'Illegal setting ID format.' );
}
$args = array_merge( $args, $parsed_setting_id );
$post = get_post( $args['post_id'] );
if ( ! $post ) {
throw new \Exception( 'Unknown post' );
}
$post_type_obj = get_post_type_object( $post->post_type );
if ( ! $post_type_obj ) {
throw new \Exception( 'Unrecognized post type: ' . $post->post_type );
}
if ( ! registered_meta_key_exists( 'post', $args['meta_key'] ) ) {
throw new \Exception( 'Unregistered meta key: ' . $args['meta_key'] );
}
$registered_meta = $wp_meta_keys['post'][ $args['meta_key'] ];
if ( empty( $registered_meta['single'] ) ) {
throw new \Exception( 'Only single postmeta are currently supported.' );
}
if ( empty( $args['capability'] ) ) {
// See \WPSE_257322\filter_map_meta_cap().
$args['capability'] = sprintf( 'edit_post_meta[%d][%s]', $args['post_id'], $args['meta_key'] );
}
parent::__construct( $manager, $id, $args );
}
/**
* Return a post's setting value.
*
* @return mixed Meta value.
*/
public function value() {
$single = false; // For the sake of disambiguating empty values in filtering.
$values = get_post_meta( $this->post_id, $this->meta_key, $single );
$value = array_shift( $values );
if ( ! isset( $value ) ) {
$value = $this->default;
}
return $value;
}
/**
* Sanitize (and validate) an input.
*
* Note for non-single postmeta, the validation and sanitization callbacks will be applied on each item in the array.
*
* @see update_metadata()
* @access public
*
* @param string $value The value to sanitize.
* @return mixed|\WP_Error|null Sanitized post array or WP_Error if invalid (or null if not WP 4.6-alpha).
*/
public function sanitize( $value ) {
$has_setting_validation = method_exists( 'WP_Customize_Setting', 'validate' );
$meta_type = 'post';
$object_id = $this->post_id;
$meta_key = $this->meta_key;
$prev_value = ''; // Updating plural meta is not supported.
/**
* Filter a Customize setting value in form.
*
* @param mixed $value Value of the setting.
* @param \WP_Customize_Setting $this WP_Customize_Setting instance.
*/
$value = apply_filters( "customize_sanitize_{$this->id}", $value, $this );
// Apply sanitization if value didn't fail validation.
if ( ! is_wp_error( $value ) && ! is_null( $value ) ) {
$value = sanitize_meta( $meta_key, $value, $meta_type );
}
if ( is_wp_error( $value ) ) {
return $has_setting_validation ? $value : null;
}
/** This filter is documented in wp-includes/meta.php */
$check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $value, $prev_value );
if ( null !== $check ) {
/* translators: placeholder is meta key */
return $has_setting_validation ? new \WP_Error( 'not_allowed', sprintf( __( 'Update to post meta "%s" blocked.', 'wpse-257322' ), $meta_key ) ) : null;
}
return $value;
}
/**
* Add filter to preview customized value.
*
* @return bool
*/
public function preview() {
if ( ! $this->is_previewed ) {
add_filter( 'get_post_metadata', array( $this, 'filter_get_post_meta_to_preview' ), 1000, 4 );
$this->is_previewed = true;
}
return true;
}
/**
* Filter postmeta to inject customized post meta values.
*
* @param null|array|string $value The value get_metadata() should return - a single metadata value, or an array of values.
* @param int $object_id Object ID.
* @param string $meta_key Meta key.
* @param bool $single Whether to return only the first value of the specified $meta_key.
* @return mixed Value.
*/
public function filter_get_post_meta_to_preview( $value, $object_id, $meta_key, $single ) {
// Short-circuit if a plugin already modified the value.
if ( null !== $value ) {
return $value;
}
// Short circuit if object is not the post associated with this setting.
if ( intval( $object_id ) !== $this->post_id ) {
return $value;
}
// Abort if not filtering this key. Note that get_post_meta( $id ) to get all values is not implemented here.
if ( $this->meta_key !== $meta_key ) {
return $value;
}
// Abort if there is no customized value in the changeset.
$customized_value = $this->post_value( null );
if ( null === $customized_value ) {
return $value;
}
$value = $customized_value;
return $single ? $value : array( $value );
}
/**
* Update the post.
*
* Please note that the capability check will have already been done.
*
* @see \WP_Customize_Setting::save()
*
* @param string $meta_value The value to update.
* @return bool The result of saving the value.
*/
protected function update( $meta_value ) {
$result = update_post_meta( $this->post_id, $this->meta_key, $meta_value );
return ( false !== $result );
}
}
<?php
/**
* Plugin Name: Post/Page Content SHOUTING!
* Description: Demonstration of a PHP-centric approach to registering page/post-specific sections and controls in the customizer.
* Plugin URI: http://wordpress.stackexchange.com/questions/257322/customizer-loading-settings-controls-sections-panels-based-on-a-id-page-id
* Author: Weston Ruter, XWP
* Author URI: https://make.xwp.co/
* License: GPLv2+
*/
/*
* Copyright (c) 2017 XWP (https://make.xwp.co/)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2 or, at
* your discretion, any later version, as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
namespace WPSE_257322;
/**
* Register meta key.
*/
function register_meta_key() {
register_meta( 'post', 'should_shout', array(
'single' => true,
) );
}
add_action( 'init', __NAMESPACE__ . '\register_meta_key' );
/**
* Apply filters on the_content to SHOUT!
*
* @param string $content Content.
* @return string Content.
*/
function filter_post_content_to_shout( $content ) {
if ( get_post_meta( get_the_ID(), 'should_shout', true ) ) {
$content = strtoupper( $content );
}
return $content;
}
add_filter( 'the_content', __NAMESPACE__ . '\filter_post_content_to_shout' );
/**
* Autoload class.
*
* @param string $class Class to autoload.
*/
function load_classes( $class ) {
if ( __NAMESPACE__ . '\Customize_Single_Postmeta_Setting' === $class ) {
require_once dirname( __FILE__ ) . '/class-customize-single-postmeta-setting.php';
}
}
spl_autoload_register( __NAMESPACE__ . '\load_classes' );
/**
* Filters a dynamic setting's constructor args to ensure they are recognized when updating or publishing a changeset.
*
* @param false|array $setting_args The arguments to the WP_Customize_Setting constructor.
* @param string $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`.
* @return false|array Setting args.
*/
function filter_dynamic_setting_args( $setting_args, $setting_id ) {
$parsed_setting_id = Customize_Single_Postmeta_Setting::parse_setting_id( $setting_id );
if ( false === $parsed_setting_id ) {
return $setting_args;
}
$post = get_post( $parsed_setting_id['post_id'] );
if ( ! $post || ! post_type_exists( $post->post_type ) ) {
return $setting_args;
}
if ( false === $setting_args ) {
$setting_args = array();
}
$setting_args = array_merge( $setting_args, array(
'type' => 'wpse_257322_postmeta', // See wpse_257322_filter_dynamic_setting_class().
'transport' => 'refresh',
'default' => false,
'sanitize_callback' => function( $value ) {
return (bool) $value;
},
) );
return $setting_args;
}
add_filter( 'customize_dynamic_setting_args', __NAMESPACE__ . '\filter_dynamic_setting_args', 10, 2 );
/**
* Filter dynamic setting class.
*
* @param string $setting_class WP_Customize_Setting or a subclass.
* @param string $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`.
* @param array $setting_args WP_Customize_Setting or a subclass.
* @return string Setting class.
*/
function filter_dynamic_setting_class( $setting_class, $setting_id, $setting_args ) {
unset( $setting_id ); // Unused.
if ( isset( $setting_args['type'] ) && 'wpse_257322_postmeta' === $setting_args['type'] ) {
$setting_class = __NAMESPACE__ . '\Customize_Single_Postmeta_Setting';
}
return $setting_class;
}
add_filter( 'customize_dynamic_setting_class', __NAMESPACE__ . '\filter_dynamic_setting_class', 10, 3 );
/**
* Map dynamic postmeta capabilities to static capabilities.
*
* This is used so that a static capability string can be used in the setting but
* have it map dynamically at runtime.
*
* @param array $caps Returns the user's actual capabilities.
* @param string $cap Capability name.
* @param int $user_id The user ID.
* @return array Caps.
*/
function filter_map_meta_cap( $caps, $cap, $user_id ) {
if ( preg_match( '/^edit_post_meta\[\d+/', $cap ) ) {
$keys = explode( '[', str_replace( ']', '', $cap ) );
$map_meta_cap_args = array(
array_shift( $keys ),
$user_id,
intval( array_shift( $keys ) ),
array_shift( $keys ),
);
$caps = call_user_func_array( 'map_meta_cap', $map_meta_cap_args );
}
return $caps;
}
add_filter( 'map_meta_cap', __NAMESPACE__ . '\filter_map_meta_cap', 10, 3 );
/**
* Register section, control, and setting for given post.
*
* @param int $post_id Post/page ID.
* @global \WP_Customize_Manager $wp_customize
*/
function register_post_constructs( $post_id ) {
global $wp_customize;
$post = get_post( $post_id );
if ( empty( $post ) ) {
return;
}
$section = $wp_customize->add_section( "wpse-257322-post-meta-{$post->ID}", array(
/* translators: post title */
'title' => sprintf( __( 'Meta: %s', 'wpse-257322' ), $post->post_title ),
) );
// Used for both the setting and the control.
$customize_id = Customize_Single_Postmeta_Setting::create_setting_id( $post_id, 'should_shout' );
$dynamic_settings = $wp_customize->add_dynamic_settings( array( $customize_id ) ); // See wpse_257322_filter_dynamic_setting_args().
$wp_customize->add_control( $customize_id, array(
'label' => __( 'SHOUT TEXT?', 'wpse-257322' ),
'type' => 'checkbox',
'settings' => $customize_id,
'section' => $section->id,
) );
/**
* Dynamically-added settings.
*
* @var \WP_Customize_Setting[] $dynamic_settings
*/
foreach ( $dynamic_settings as $dynamic_setting ) {
$dynamic_setting->preview();
}
}
/**
* Register for controls.
*
* @global \WP_Customize_Manager $wp_customize
*/
function customize_register_for_controls() {
global $wp_customize;
$queried_object_id = url_to_postid( $wp_customize->get_preview_url() );
if ( $queried_object_id ) {
register_post_constructs( $queried_object_id );
}
}
add_action( 'customize_controls_init', __NAMESPACE__ . '\customize_register_for_controls', 9 ); // Priority 9 so before prepare_controls.
/**
* Register for preview.
*/
function customize_register_for_preview() {
if ( ! is_customize_preview() ) {
return;
}
$post_id = get_queried_object_id();
if ( $post_id ) {
register_post_constructs( $post_id );
}
}
add_action( 'wp', __NAMESPACE__ . '\customize_register_for_preview' );
@mrtieutien90
Copy link

I have problem same @ParhamG.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment