For me this is such a clear and natural need that I can only wonder to find discussions from 5 years ago where this was expressed already, and up-to-date stays unsolved. In most scenarios my clients, and I guess the clients of nearly every other webdesigner, don’t have much knowledge or intuition about design – that’s why they hire us. But we give them WordPress because we want to enable them editing their content by themselves: texts, images, media. They also want to add pages, and here we stumble into a strange issue that disqualifies WordPress to be called a true CMS out-of-the-box. The ability to edit the menu and to add new pages to appear there is not treated as content editing, but placed in the theme section. Even if from a coding point of view menu’s are part of the theme’s framework, the editing is not and needs to be accessible for clients WITHOUT giving access to all the manifold theme customizations of which they should in their own interest not touch any button.
I patchworked my solution that works in actual WP 4.4.1 as I desire it, hoping it will last a while. The first two (gp_) functions are the plugin Editor Menu and Widget Access by Guy Primavera.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// Allow Editors access to the Appearance menu function gp_allow_edit_theme_options( $caps ) { /* check if the user can edit_pages */ if( ! empty( $caps[ 'edit_pages' ] ) ) { /* allow the user to edit theme options */ $caps[ 'edit_theme_options' ] = true; } /* return the new capabilities */ return $caps; } add_filter( 'user_has_cap', 'gp_allow_edit_theme_options' ); // Hide the other pages in the Appearance menu function gp_new_admin_menu() { $user = new WP_User(get_current_user_id()); if (!empty( $user->roles) && is_array($user->roles)) { foreach ($user->roles as $role) $role = $role; } if($role == "editor") { remove_submenu_page( 'themes.php', 'themes.php' ); } } add_action('admin_init', 'gp_new_admin_menu'); |
But he left the customization option open, I wanted to close it, so I had to add another piece of code from the depth of the internet, and since it removed the entry “Customize” but not “Background” in my case, I had to add one more code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function remove_customize() { $customize_url_arr = array(); $customize_url_arr[] = 'customize.php'; // 3.x $customize_url = add_query_arg( 'return', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'customize.php' ); $customize_url_arr[] = $customize_url; // 4.0 & 4.1 if ( current_theme_supports( 'custom-header' ) && current_user_can( 'customize') ) { $customize_url_arr[] = add_query_arg( 'autofocus[control]', 'header_image', $customize_url ); // 4.1 $customize_url_arr[] = 'custom-header'; // 4.0 } if ( current_theme_supports( 'custom-background' ) && current_user_can( 'customize') ) { $customize_url_arr[] = add_query_arg( 'autofocus[control]', 'background_image', $customize_url ); // 4.1 $customize_url_arr[] = 'custom-background'; // 4.0 } foreach ( $customize_url_arr as $customize_url ) { remove_submenu_page( 'themes.php', $customize_url ); } } add_action( 'admin_menu', 'remove_customize', 999 ); function remove_unnecessary_wordpress_menus(){ global $submenu; foreach($submenu['themes.php'] as $menu_index => $theme_menu){ if($theme_menu[0] == 'Header' || $theme_menu[0] == 'Background') unset($submenu['themes.php'][$menu_index]); } } add_action('admin_menu', 'remove_unnecessary_wordpress_menus', 999); |
And then I discovered one more thing. In the widget and menu screen there is a button called “Manage in Customizer”. Another entrance into the whooooole world of theme customization, which in case of feature rich themes means basically everything can be ruined with a few clicks. I wanted to remove them via CSS but then I found THE hope on the horizon of my needs: http://wptavern.com/new-plugin-removes-all-traces-of-the-customizer-in-wordpress
With this plugin the customizer can be completely disabled, which is a bit too much since I as the admin still want to enjoy it. So I continued my patchwork that for sure makes every serious PHP coder’s hair stand on end, but what can I do. Please comment the correct programming solution if you are one of those. (The “if role = editor” entries are from me).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
// Exit if accessed directly if ( __FILE__ == $_SERVER['SCRIPT_FILENAME'] ) { exit; } if (!class_exists('Customizer_Remove_All')) : class Customizer_Remove_All { /** * @var Customizer_Remove_All */ private static $instance; /** * Main Instance * * Allows only one instance of Customizer_Remove_All in memory. * * @static * @staticvar array $instance * @return Big mama, Customizer_Remove_All */ public static function instance() { if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Customizer_Remove_All ) ) { // Start your engines! self::$instance = new Customizer_Remove_All; // Load the structures to trigger initially add_action( 'plugins_loaded', array( self::$instance, 'load_languages' ) ); add_action( 'init', array( self::$instance, 'init' ), 10 ); // was priority 5 add_action( 'admin_init', array( self::$instance, 'admin_init' ), 10 ); // was priority 5 } return self::$instance; } /** * Run all plugin stuff on init. * * @return void */ public function init() { // Remove customize capability add_filter( 'map_meta_cap', array( self::$instance, 'filter_to_remove_customize_capability'), 10, 4 ); } /** * Run all of our plugin stuff on admin init. * * @return void */ public function admin_init() { $user = new WP_User(get_current_user_id()); if (!empty( $user->roles) && is_array($user->roles)) { foreach ($user->roles as $role) $role = $role; } if ($role == 'editor') { // Drop some customizer actions remove_action( 'plugins_loaded', '_wp_customize_include', 10); remove_action( 'admin_enqueue_scripts', '_wp_customize_loader_settings', 11); // Manually overrid Customizer behaviors add_action( 'load-customize.php', array( self::$instance, 'override_load_customizer_action') ); } } /** * Load our language files * * @access public * @return void */ public function load_languages() { // Set textdomain string $textdomain = 'wp-crap'; // The 'plugin_locale' filter is also used by default in load_plugin_textdomain() $locale = apply_filters( 'plugin_locale', get_locale(), $textdomain ); // Set filter for WordPress languages directory $wp_languages_dir = apply_filters( 'crap_wp_languages_dir', WP_LANG_DIR . '/wp-crap/' . $textdomain . '-' . $locale . '.mo' ); // Translations: First, look in WordPress' "languages" folder load_textdomain( $textdomain, $wp_languages_dir ); // Translations: Next, look in plugin's "languages" folder (default) $plugin_dir = basename( dirname( __FILE__ ) ); $languages_dir = apply_filters( 'crap_languages_dir', $plugin_dir . '/languages' ); load_plugin_textdomain( $textdomain, FALSE, $languages_dir ); } /** * Remove customize capability * * This needs to be in public so the admin bar link for 'customize' is hidden. */ public function filter_to_remove_customize_capability( $caps = array(), $cap = '', $user_id = 0, $args = array() ) { $user = new WP_User(get_current_user_id()); if (!empty( $user->roles) && is_array($user->roles)) { foreach ($user->roles as $role) $role = $role; } if ($cap == 'customize' && $role == 'editor') { return array('nope'); // thanks @ScreenfeedFr, http://bit.ly/1KbIdPg } return $caps; } /** * Manually overriding specific Customizer behaviors */ public function override_load_customizer_action() { // If accessed directly wp_die( __( 'The Customizer is currently disabled.', 'wp-crap' ) ); } } // End Class endif; /** * The main function. Use like a global variable, except no need to declare the global. * * @return object The one true Customizer_Remove_All Instance */ function Customizer_Remove_All() { return Customizer_Remove_All::instance(); } // GO! Customizer_Remove_All(); |
Anything else than ideal, but working and making me happy : ) Just add all of those to your child-theme’s functions.php if you share my interest of client-friendliness.
The developers of the Customizer Remove All Parts Plugin aim to make it work selectively and with user roles, so I hope that soon this patchwork can be completely replaced by their plugin. If you improve this solution, please share here or contact the developers. Everything runs faster in a group.
Hi Sofian!
Thanks for mentioning my plugin here, and I’m happy to see that it was useful for you!
I’ve released an update which now includes an options page to allow the Admin user to hide specific options from the Editor (including the “customizer”), which should solve your issue.
You can get version 2.0 here: https://wordpress.org/plugins/editor-menu-and-widget-access/ .
Thanks!