Instantly share code, notes, and snippets.
Last active
July 16, 2023 13:11
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save westonruter/7f2b9c18113f0576a72e0aca3ce3dbcb to your computer and use it in GitHub Desktop.
Code answering this WPSE question: https://wordpress.stackexchange.com/a/373092/8521
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
// Augment each menu item control once it is added and embedded. | |
wp.customize.control.bind( 'add', ( control ) => { | |
if ( control.extended( wp.customize.Menus.MenuItemControl ) ) { | |
control.deferred.embedded.done( () => { | |
extendControl( control ); | |
} ); | |
} | |
} ); | |
/** | |
* Extend the control with roles information. | |
* | |
* @param {wp.customize.Menus.MenuItemControl} control | |
*/ | |
function extendControl( control ) { | |
control.authFieldset = control.container.find( '.nav_menu_role_authentication' ); | |
control.rolesFieldset = control.container.find( '.nav_menu_roles' ); | |
// Set the initial UI state. | |
updateControlFields( control ); | |
// Update the UI state when the setting changes programmatically. | |
control.setting.bind( () => { | |
updateControlFields( control ); | |
} ); | |
// Update the setting when the inputs are modified. | |
control.authFieldset.find( 'input' ).on( 'click', function () { | |
setSettingRoles( control.setting, this.value ); | |
} ); | |
control.rolesFieldset.find( 'input' ).on( 'click', function () { | |
const checkedRoles = []; | |
control.rolesFieldset.find( ':checked' ).each( function () { | |
checkedRoles.push( this.value ); | |
} ); | |
setSettingRoles( control.setting, checkedRoles.length === 0 ? 'in' : checkedRoles ); | |
} ); | |
} | |
/** | |
* Extend the setting with roles information. | |
* | |
* @param {wp.customize.Setting} setting | |
* @param {string|Array} roles | |
*/ | |
function setSettingRoles( setting, roles ) { | |
setting.set( | |
Object.assign( | |
{}, | |
_.clone( setting() ), | |
{ roles } | |
) | |
); | |
} | |
/** | |
* Apply the control's setting value to the control's fields. | |
* | |
* @param {wp.customize.Menus.MenuItemControl} control | |
*/ | |
function updateControlFields( control ) { | |
const roles = control.setting().roles || ''; | |
const radioValue = _.isArray( roles ) ? 'in' : roles; | |
const checkedRoles = _.isArray( roles ) ? roles : []; | |
control.rolesFieldset.toggle( 'in' === radioValue ); | |
const authRadio = control.authFieldset.find( `input[type=radio][value="${ radioValue }"]` ); | |
authRadio.prop( 'checked', true ); | |
control.rolesFieldset.find( 'input[type=checkbox]' ).each( function () { | |
this.checked = checkedRoles.includes( this.value ); | |
} ); | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: Customize Nav Menu Roles | |
* Plugin URI: https://gist.github.com/westonruter/7f2b9c18113f0576a72e0aca3ce3dbcb | |
* Description: Extend nav menu item controls with UI for manipulating the user roles that are allowed to view them. Extension of the <a href="https://wordpress.org/plugins/nav-menu-roles/">Nav Menu Roles</a> plugin by Kathy Darling. | |
* Version: 0.1 | |
* Author: Weston Ruter | |
* Author URI: https://weston.ruter.net/ | |
* License: GNU General Public License v2 (or later) | |
* License URI: http://www.gnu.org/licenses/gpl-2.0.html | |
* Gist Plugin URI: https://gist.github.com/westonruter/7f2b9c18113f0576a72e0aca3ce3dbcb | |
*/ | |
namespace Customize_Nav_Menu_Roles; | |
use WP_Customize_Manager; | |
use WP_Customize_Nav_Menu_Item_Setting; | |
/** | |
* Display the fields in the Customizer. | |
* | |
* This function is copied from a Stack Overflow question. | |
* | |
* @link https://wordpress.stackexchange.com/questions/372493/add-settings-to-menu-items-in-the-customizer | |
*/ | |
function kia_customizer_custom_fields() { | |
global $wp_roles; | |
/** | |
* Pass the menu item to the filter function. | |
* This change is suggested as it allows the use of information from the menu item (and | |
* by extension the target object) to further customize what filters appear during menu | |
* construction. | |
*/ | |
$display_roles = apply_filters( 'nav_menu_roles', $wp_roles->role_names ); | |
if ( ! $display_roles ) { | |
return; | |
} | |
?> | |
<fieldset class="nav_menu_role_authentication"> | |
<legend class="customize-control-title"><?php _e( 'Display Mode', 'nav-menu-roles' ); ?></legend> | |
<label for="edit-menu-item-role_logged_in-{{ data.menu_item_id }}"> | |
<input type="radio" id="edit-menu-item-role_logged_in-{{ data.menu_item_id }}" value="in" name="menu-item-role-{{ data.menu_item_id }}" /> | |
<?php _e( 'Logged In Users', 'nav-menu-roles' ); ?><br/> | |
</label> | |
<label for="edit-menu-item-role_logged_out-{{ data.menu_item_id }}"> | |
<input type="radio" id="edit-menu-item-role_logged_out-{{ data.menu_item_id }}" value="out" name="menu-item-role-{{ data.menu_item_id }}" /> | |
<?php _e( 'Logged Out Users', 'nav-menu-roles' ); ?><br/> | |
</label> | |
<label for="edit-menu-item-role_everyone-{{ data.menu_item_id }}"> | |
<input type="radio" id="edit-menu-item-role_everyone-{{ data.menu_item_id }}" value="" name="menu-item-role-{{ data.menu_item_id }}" /> | |
<?php _e( 'Everyone', 'nav-menu-roles' ); ?><br/> | |
</label> | |
</fieldset> | |
<fieldset class="nav_menu_roles"> | |
<legend class="customize-control-title"><?php _e( 'Restrict menu item to minimum role', 'nav-menu-roles' ); ?></legend> | |
<?php foreach ( $display_roles as $role => $name ) : ?> | |
<label for="edit-menu-item-role_<?php echo $role; ?>-{{ data.menu_item_id }}"> | |
<input type="checkbox" id="edit-menu-item-role_<?php echo esc_attr( $role ); ?>-{{ data.menu_item_id }}" class="edit-menu-item-role" value="<?php echo esc_attr( $role ); ?>" /> | |
<?php echo esc_html( $name ); ?><br/> | |
</label> | |
<?php endforeach; ?> | |
</fieldset> | |
<?php | |
} | |
add_action( 'wp_nav_menu_item_custom_fields_customize_template', __NAMESPACE__ . '\kia_customizer_custom_fields' ); | |
// Enqueue script which extends nav menu item controls. | |
add_action( | |
'customize_controls_enqueue_scripts', | |
static function () { | |
wp_enqueue_script( | |
'customize-nav-menu-roles', | |
plugin_dir_url( __FILE__ ) . '/customize-nav-menu-roles.js', | |
[ 'customize-nav-menus' ], | |
filemtime( __DIR__ . '/customize-nav-menu-roles.js' ), | |
true | |
); | |
} | |
); | |
/** | |
* Sanitize roles value. | |
* | |
* @param string|array $value Roles. | |
* @return array|string Sanitized roles. | |
*/ | |
function sanitize_roles_value( $value ) { | |
global $wp_roles; | |
if ( is_array( $value ) ) { | |
return array_intersect( $value, array_keys( $wp_roles->role_names ) ); | |
} elseif ( in_array( $value, [ '', 'in', 'out' ], true ) ) { | |
return $value; | |
} | |
return ''; | |
} | |
/** | |
* Get sanitized posted value for a setting's roles. | |
* | |
* @param WP_Customize_Nav_Menu_Item_Setting $setting Setting. | |
* | |
* @return array|string|null Roles value or null if no posted value present. | |
*/ | |
function get_sanitized_roles_post_data( WP_Customize_Nav_Menu_Item_Setting $setting ) { | |
global $wp_roles; | |
if ( ! $setting->post_value() ) { | |
return null; | |
} | |
$unsanitized_post_value = $setting->manager->unsanitized_post_values()[ $setting->id ]; | |
if ( isset( $unsanitized_post_value['roles'] ) ) { | |
$value = $unsanitized_post_value['roles']; | |
if ( is_array( $value ) ) { | |
return array_intersect( $value, array_keys( $wp_roles->role_names ) ); | |
} elseif ( in_array( $value, [ '', 'in', 'out' ], true ) ) { | |
return $value; | |
} | |
} | |
return ''; | |
} | |
/** | |
* Preview changes to the nav menu item roles. | |
* | |
* Note the unimplemented to-do in the doc block for the setting's preview method. | |
* | |
* @see WP_Customize_Nav_Menu_Item_Setting::preview() | |
* | |
* @param WP_Customize_Nav_Menu_Item_Setting $setting Setting. | |
*/ | |
function preview_nav_menu_setting_postmeta( WP_Customize_Nav_Menu_Item_Setting $setting ) { | |
$roles = get_sanitized_roles_post_data( $setting ); | |
if ( null === $roles ) { | |
return; | |
} | |
add_filter( | |
'get_post_metadata', | |
static function ( $value, $object_id, $meta_key ) use ( $setting, $roles ) { | |
if ( $object_id === $setting->post_id && '_nav_menu_role' === $meta_key ) { | |
return [ $roles ]; | |
} | |
return $value; | |
}, | |
10, | |
3 | |
); | |
} | |
/** | |
* Save changes to the nav menu item roles. | |
* | |
* Note the unimplemented to-do in the doc block for the setting's preview method. | |
* | |
* @see WP_Customize_Nav_Menu_Item_Setting::update() | |
* | |
* @param WP_Customize_Nav_Menu_Item_Setting $setting Setting. | |
*/ | |
function save_nav_menu_setting_postmeta( WP_Customize_Nav_Menu_Item_Setting $setting ) { | |
$roles = get_sanitized_roles_post_data( $setting ); | |
if ( null !== $roles ) { | |
update_post_meta( $setting->post_id, '_nav_menu_role', $roles ); | |
} | |
} | |
// Set up previewing. | |
add_action( | |
'customize_register', | |
static function( WP_Customize_Manager $wp_customize ) { | |
if ( $wp_customize->settings_previewed() ) { | |
foreach ( $wp_customize->settings() as $setting ) { | |
if ( $setting instanceof WP_Customize_Nav_Menu_Item_Setting ) { | |
preview_nav_menu_setting_postmeta( $setting ); | |
} | |
} | |
} | |
}, | |
1000 | |
); | |
// Set up saving. | |
add_action( | |
'customize_save_after', | |
function ( WP_Customize_Manager $wp_customize ) { | |
foreach ( $wp_customize->settings() as $setting ) { | |
if ( $setting instanceof WP_Customize_Nav_Menu_Item_Setting && $setting->check_capabilities() ) { | |
save_nav_menu_setting_postmeta( $setting ); | |
} | |
} | |
} | |
); |
For others who might have the same problem, I found that reading $wp_customize->unsanitized_post_values()
returns the submitted data.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this code snippet. Very userful. However, I'm running into a problem. The preview functionality doesn't entirely work.
Scenario:
I was under the impression that the method hooked to
customize_register
would take care of this. It seems that upon pressing Publish, this is being called, and$wp_customize->settings_previewed()
does return true, but the next statement,$wp_customize->settings()
returns no settings to iterate.Edit: Please note, this is a menu item that already exists and has been saved to the database. I'd expect that if I update the custom field and press Publish, the preview pane would reflect the updated field value.
Could you advise me please? What could I be missing?