edit_theme_options capability for WordPress user

edit-theme-options WordPress user capability

edit-theme-options user capability


‘edit_theme_options’ capability seems to be self-explained. It’s primary purpose is to provide access to WordPress front-end theme options changing.
On practice, if you have ‘edit_theme_options’ capability, then you have access to the ‘Appearance’ menu at WordPress admin back-end and to its menu items: “Themes” (select a theme for your WordPress front-end) and “Menus” (edit menus supported by your selected theme). Customize theme header image, background color, etc. section and ‘Widgets’ menu items require ‘edit_theme_options’ capability also.

How it is realized? How often and at what places WordPress uses/checks this user capability – ‘edit_theme_options’ in order to decide give current user permission to edit theme options or prohibit access to such functionality? Let’s check this together. We will make a quick look into WordPress version 3.4.2 core source code and comment its related fragments.


Simple search through WordPress source code subdirectories for ‘edit_theme_options’ keyword shows, that WordPress uses ‘edit_theme_options’ capability in 21 files.

Files with ‘edit_theme_options’ capability inside

wp-admin/admin-header.php

This is technological permission check before apply customize body classes. There is nothing occur here in concern to the user interface and user access to WordPress functionality.

103
104
105
106
// If the customize-loader script is enqueued, make sure the customize
// body classes are correct as early as possible.
if ( wp_script_is( 'customize-loader', 'queue' ) && current_user_can( 'edit_theme_options' ) )
	wp_customize_support_script();

wp_customize_support_script() function is located at the wp-includes/theme.php file and has such comments:

1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
/**
 * Prints a script to check whether or not the customizer is supported,
 * and apply either the no-customize-support or customize-support class
 * to the body.
 *
 * This function MUST be called inside the body tag.
 *
 * Ideally, call this function immediately after the body tag is opened.
 * This prevents a flash of unstyled content.
 *
 * It is also recommended that you add the "no-customize-support" class
 * to the body tag by default.
 *
 * @since 3.4.0
 */
function wp_customize_support_script() {

wp-admin/custom-background.php

This is the custom background script. ‘edit_theme_options’ capability is located here in this file:

61
62
63
64
65
66
67
68
69
70
/**
 * Set up the hooks for the Custom Background admin page.
 *
 * @since 3.0.0
 */
function init() {
	if ( ! current_user_can('edit_theme_options') )
		return;
 
	$this->page = $page = add_theme_page(__('Background'), __('Background'), 'edit_theme_options', 'custom-background', array(&$this, 'admin_page'));

Thus, if user has correspondent permission, he will see ‘Background’ menu item at the ‘Appearance’ menu and will be able to setup custom background image and select background color for the selected theme.
We see ‘edit_theme_options’ capability second time almost at the end of file:

417
418
public function wp_set_background_image() {
	if ( ! current_user_can('edit_theme_options') || ! isset( $_POST['attachment_id'] ) ) exit;

It’s additional check of user permission before process of image upload and replace theme background image.

wp-admin/custom-header.php

This is the custom header image script. According to the init() function code below

78
79
80
81
82
83
84
85
86
87
/**
 * Set up the hooks for the Custom Header admin page.
 *
 * @since 2.1.0
 */
function init() {
	if ( ! current_user_can('edit_theme_options') )
		return;
 
	$this->page = $page = add_theme_page(__('Header'), __('Header'), 'edit_theme_options', 'custom-header', array(&$this, 'admin_page'));

Only user with ‘edit_theme_options’ capability will see the ‘Header’ menu item at the ‘Appearance’ menu and be capable to change image header for the selected theme.
Second time this script uses ‘edit_theme_options’ capability before proceed with real change of the theme header image:

198
199
200
201
202
203
204
205
/**
 * Execute custom header modification.
 *
 * @since 2.6.0
 */
function take_action() {
	if ( ! current_user_can('edit_theme_options') )
		return;

Additionally custom-header.php script checks discussed user permission to prevent possible hacker intervention in the middle of the multi-step theme header image change process:

915
916
917
918
919
920
921
922
/**
 * Display the page based on the current step.
 *
 * @since 2.1.0
 */
function admin_page() {
	if ( ! current_user_can('edit_theme_options') )
		wp_die(__('You do not have permission to customize headers.'));

wp-admin/customize.php

This is the Customize Controls script. It checks ‘edit_theme_options’ permission at very begin and stops execution if current user has no such capability:

13
14
if ( ! current_user_can( 'edit_theme_options' ) )
	wp_die( __( 'Cheatin’ uh?' ) );

wp-admin/includes/ajax-actions.php

This is WordPress Core Ajax Handlers script. It checks if current user has ‘edit_theme_options’ capability before proceed with adding of new menu item:

851
852
853
854
855
function wp_ajax_add_menu_item() {
check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
 
if ( ! current_user_can( 'edit_theme_options' ) )
	wp_die( -1 );

That was interesting for me to discover that in order to hide ‘Welcome panel’ shown after WordPress core install or update, you need to have ‘edit_theme_options’ capability. But see the code below:

1183
1184
1185
1186
1187
1188
1189
function wp_ajax_update_welcome_panel() {
	check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
 
	if ( ! current_user_can( 'edit_theme_options' ) )
		wp_die( -1 );
 
	update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );

Some functionality concerning menu metaboxes construction and editing via AJAX is also available if user has ‘edit_theme_options’ capability only:

1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
function wp_ajax_menu_get_metabox() {
if ( ! current_user_can( 'edit_theme_options' ) )
	wp_die( -1 );
 
require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
 
if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
	$type = 'posttype';
	$callback = 'wp_nav_menu_item_post_type_meta_box';
	$items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
} elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) {
	$type = 'taxonomy';
	$callback = 'wp_nav_menu_item_taxonomy_meta_box';
	$items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
}

You can save menu changes if you have ‘edit_theme_options’ capability:

1255
1256
1257
1258
1259
1260
1261
1262
1263
function wp_ajax_menu_locations_save() {
	if ( ! current_user_can( 'edit_theme_options' ) )
		wp_die( -1 );
	check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
	if ( ! isset( $_POST['menu-locations'] ) )
		wp_die( 0 );
	set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
	wp_die( 1 );
}

You can make quick search in the menu via AJAX just if you have ‘edit_theme_options’ capability:

1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
function wp_ajax_menu_quick_search() {
	if ( ! current_user_can( 'edit_theme_options' ) )
		wp_die( -1 );
 
	require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
 
	_wp_ajax_menu_quick_search( $_POST );
 
	wp_die();
}

Widgets ordering is available to the user with ‘edit_theme_options’ capability only:

1492
1493
1494
1495
1496
function wp_ajax_widgets_order() {
	check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
 
	if ( !current_user_can('edit_theme_options') )
		wp_die( -1 );

It is impossible to save changes made to the widget without ‘edit_theme_options’ capability too:

1523
1524
1525
1526
1527
1528
1529
function wp_ajax_save_widget() {
	global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
 
	check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
 
	if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
		wp_die( -1 );

wp-admin/includes/class-wp-themes-list-table.php

This is Themes List Table class. 1st time we meet ‘edit_theme_options’ capability at line #141 of display_rows() function:

141
142
143
		if ( current_user_can( 'edit_theme_options' ) )
			$actions['preview'] .= '<a href="' . wp_customize_url( $stylesheet ) . '" class="load-customize hide-if-no-customize">'
				. __( 'Live Preview' ) . '</a>';

This code ads ‘Live Preview’ action link to the available theme in case user has ‘edit_theme_options’ capability.

wp-admin/includes/dashboard.php

This file contains WordPress Dashboard Widget Administration Screen API. At line #382 WordPress builds text with title of active theme and quant of active widgets configured for it. WordPress shows this title at ‘Right Now’ widget. If user has ‘edit_theme_options’ capability, then text contains links to the widgets area and switch themes area:

382
383
384
385
386
	if ( current_user_can( 'edit_theme_options' ) ) {
		printf(_n('Theme <span class="b">%1$s</span> with <span class="b"><a href="widgets.php">%2$s Widget</a></span>', 'Theme <span class="b">%1$s</span> with <span class="b"><a href="widgets.php">%2$s Widgets</a></span>', $num_widgets), $switch_themes, $num);
	} else {
		printf(_n('Theme <span class="b">%1$s</span> with <span class="b">%2$s Widget</span>', 'Theme <span class="b">%1$s</span> with <span class="b">%2$s Widgets</span>', $num_widgets), $switch_themes, $num);
	}

That’s interesting to discover that WordPress shows its ‘Welcome’ screen to the users with ‘edit_theme_options’ capability only:

1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
/**
 * Displays a welcome panel to introduce users to WordPress.
 *
 * @since 3.3.0
 */
function wp_welcome_panel() {
	global $wp_version;
 
	if ( ! current_user_can( 'edit_theme_options' ) )
		return;

wp-admin/includes/schema.php

This is WordPress Administration Scheme API, where developers keep the DB structure and option values. From this file we know that ‘edit_theme_options’ capability was introduced with WordPress version 3.0:

777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
/**
 * Create and modify WordPress roles for WordPress 3.0.
 *
 * @since 3.0.0
 */
function populate_roles_300() {
	$role =& get_role( 'administrator' );
 
	if ( !empty( $role ) ) {
		$role->add_cap( 'update_core' );
		$role->add_cap( 'list_users' );
		$role->add_cap( 'remove_users' );
		$role->add_cap( 'add_users' );
		$role->add_cap( 'promote_users' );
		$role->add_cap( 'edit_theme_options' );

wp-admin/includes/screen.php

This is WordPress Administration Screen API. With user of ‘edit_theme_options’ WordPress take decision to show or not its ‘Welcome’ screen:

891
892
893
894
	if ( 'dashboard' === $this->id && current_user_can( 'edit_theme_options' ) ) {
		if ( isset( $_GET['welcome'] ) ) {
			$welcome_checked = empty( $_GET['welcome'] ) ? 0 : 1;
			update_user_meta( get_current_user_id(), 'show_welcome_panel', $welcome_checked );

wp-admin/includes/template.php

This is Template WordPress Administration API. As WordPress developers comment – “A Big Mess. Also some neat functions that are nicely written”.
This code fragment shows again that ‘edit_theme_options’ capability is critical for saving widgets, access to the theme customizer and it is important if you wish to choose image from media library:

1722
1723
1724
1725
1726
1727
	$caps_required = array(
		'wp330_media_uploader' => array( 'upload_files' ),
		'wp330_saving_widgets' => array( 'edit_theme_options', 'switch_themes' ),
		'wp340_customize_current_theme_link' => array( 'edit_theme_options' ),
		'wp340_choose_image_from_library' => array( 'edit_theme_options' ),
	);

wp-admin/index.php

This is Dashboard Administration Screen. It uses ‘edit_theme_options’ screen only once to remind you at the Help text, that:

83
84
if ( current_user_can( 'edit_theme_options' ) )
	$help .= '<p>' . __('<strong>Welcome</strong> - Shows links for some of the most common tasks when setting up a new site.') . '</p>';

wp-admin/menu.php

This file builds Administration Menu. Capability ‘edit_theme_options’ is used actively at fragment which builds the ‘Appearance’ menu. It is responsible for ‘Menus’ and ‘Themes’ menu items. If user has no ‘switch_themes’ capability, then ‘edit_theme_options’ is checked to show or hide ‘Appearance’ menu.

125
126
127
128
129
130
131
132
133
134
135
if ( current_user_can( 'switch_themes') ) {
	$menu[60] = array( __('Appearance'), 'switch_themes', 'themes.php', '', 'menu-top menu-icon-appearance', 'menu-appearance', 'div' );
		$submenu['themes.php'][5]  = array(__('Themes'), 'switch_themes', 'themes.php');
		if ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) )
			$submenu['themes.php'][10] = array(__('Menus'), 'edit_theme_options', 'nav-menus.php');
} else {
	$menu[60] = array( __('Appearance'), 'edit_theme_options', 'themes.php', '', 'menu-top menu-icon-appearance', 'menu-appearance', 'div' );
		$submenu['themes.php'][5]  = array(__('Themes'), 'edit_theme_options', 'themes.php');
		if ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) )
			$submenu['themes.php'][10] = array(__('Menus'), 'edit_theme_options', 'nav-menus.php' );
}

wp-admin/nav-menus.php

This is WordPress Administration for Navigation Menus Interface functions. It checks ‘edit_theme_options’ capability at very begin and stops execution immediately in case user has no such capability:

21
22
23
// Permissions Check
if ( ! current_user_can('edit_theme_options') )
	wp_die( __( 'Cheatin&#8217; uh?' ) );

wp-admin/themes.php

This is Themes administration panel. It checks ‘edit_theme_options’ capability twice earlier then previous nav-menus.php and similar – stop execution if user has no such capability:

12
13
if ( !current_user_can('switch_themes') && !current_user_can('edit_theme_options') )
	wp_die( __( 'Cheatin&#8217; uh?' ) );

Later for user with ‘edit_theme_options’ capability WordPress offers customized help text:

71
72
73
if ( current_user_can( 'edit_theme_options' ) ) {
	$help_customize =
		'<p>' . __('Click on the "Live Preview" link under any theme to preview that theme and change theme options in a separate, full-screen view. Any installed theme can be previewed and customized in this way.') . '</p>'.

If user has ‘edit_theme_options’ capability WordPress shows him link to the widgets area:

110
111
112
113
		if ( isset( $_GET['previewed'] ) ) { ?>
		<div id="message2" class="updated"><p><?php printf( __( 'Settings saved and theme activated. <a href="%s">Visit site</a>.' ), home_url( '/' ) ); ?></p></div>
		<?php } elseif ( isset($wp_registered_sidebars) && count( (array) $wp_registered_sidebars ) && current_user_can('edit_theme_options') ) { ?>
<div id="message2" class="updated"><p><?php printf( __('New theme activated. This theme supports widgets, please visit the <a href="%s">widgets settings</a> screen to configure them.'), admin_url( 'widgets.php' ) ); ?></p></div><?php

User can see current theme preview. He just should have ‘edit_theme_options’ capability for that:

131
132
133
134
135
		<?php if ( current_user_can( 'edit_theme_options' ) ) : ?>
		<a href="<?php echo wp_customize_url(); ?>" class="load-customize hide-if-no-customize" title="<?php echo esc_attr( $customize_title ); ?>">
			<img src="<?php echo esc_url( $screenshot ); ?>" alt="<?php esc_attr_e( 'Current theme preview' ); ?>" />
		</a>
		<?php endif; ?>

In order to have ability customize theme presentation: header image, background, etc. ‘edit_theme_options’ capability is critical:

181
182
183
184
185
186
187
	if ( $options || current_user_can( 'edit_theme_options' ) ) :
	?>
	<div class="theme-options">
		<?php if ( current_user_can( 'edit_theme_options' ) ) : ?>
		<a id="customize-current-theme-link" href="<?php echo wp_customize_url(); ?>" class="load-customize hide-if-no-customize" title="<?php echo esc_attr( $customize_title ); ?>"><?php _e( 'Customize' ); ?></a>
		<?php
		endif; // edit_theme_options

wp-admin/widgets.php

This is Widgets administration panel. At line #15, very begin of this file, WordPress checks if user has ‘edit_theme_options’ capability and stops execution if he doesn’t.

15
16
if ( ! current_user_can('edit_theme_options') )
	wp_die( __( 'Cheatin&#8217; uh?' ));

wp-content/themes/twentyeleven/inc/theme-options.php

This is Twenty Eleven Theme Options. As expected ‘Twenty Eleven’ theme uses ‘edit_theme_options’ capability when adds its own menu item for edit ‘Twenty Eleven’ options:

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
 * Add our theme options page to the admin menu, including some help documentation.
 *
 * This function is attached to the admin_menu action hook.
 *
 * @since Twenty Eleven 1.0
 */
function twentyeleven_theme_options_add_page() {
	$theme_page = add_theme_page(
		__( 'Theme Options', 'twentyeleven' ),   // Name of page
		__( 'Theme Options', 'twentyeleven' ),   // Label in menu
		'edit_theme_options',                    // Capability required
		'theme_options',                         // Menu slug, used to uniquely identify the page
		'twentyeleven_theme_options_render_page' // Function that renders the options page
	);
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/**
 * Implements Twenty Eleven theme options into Theme Customizer
 *
 * @param $wp_customize Theme Customizer object
 * @return void
 *
 * @since Twenty Eleven 1.3
 */
function twentyeleven_customize_register( $wp_customize ) {
	$wp_customize->get_setting( 'blogname' )->transport = 'postMessage';
	$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';
 
	$options  = twentyeleven_get_theme_options();
	$defaults = twentyeleven_get_default_theme_options();
 
	$wp_customize->add_setting( 'twentyeleven_theme_options[color_scheme]', array(
		'default'    => $defaults['color_scheme'],
		'type'       => 'option',
		'capability' => 'edit_theme_options',
	) );

wp-includes/admin-bar.php

This is Admin Bar. This code handles the building and rendering of the press bar. This file uses ‘edit_theme_options’ capability to check if it’s legal to add ‘Appearance’ menu with ‘Customize’, ‘Widgets’, ‘Menus’, ‘Background’, ‘Header’ items for this user.

565
566
567
568
569
570
571
572
573
574
575
576
577
/**
 * Add appearance submenu items to the "Site Name" menu.
 *
 * @since 3.1.0
 */
function wp_admin_bar_appearance_menu( $wp_admin_bar ) {
	$wp_admin_bar->add_group( array( 'parent' => 'site-name', 'id' => 'appearance' ) );
 
	if ( current_user_can( 'switch_themes' ) || current_user_can( 'edit_theme_options' ) )
		$wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'themes', 'title' => __('Themes'), 'href' => admin_url('themes.php') ) );
 
	if ( ! current_user_can( 'edit_theme_options' ) )
		return;

wp-includes/class-wp-customize-manager.php

This is WP_Customize_Manager class definition used for the themes Customize functionality. It checks ‘edit_theme_options’ capability before start preview and customize theme.

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
	/**
	* Start preview and customize theme.
	*
	* Check if customize query variable exist. Init filters to filter the current theme.
	 *
	 * @since 3.4.0
	 */
	public function setup_theme() {
		send_origin_headers();
 
		if ( is_admin() && ! $this->doing_ajax() )
		    auth_redirect();
		elseif ( $this->doing_ajax() && ! is_user_logged_in() )
		    $this->wp_die( 0 );
 
		show_admin_bar( false );
 
		if ( ! current_user_can( 'edit_theme_options' ) )
			$this->wp_die( -1 );

wp-includes/class-wp-customize-section.php

This is Customize Section Class. At line #14 it defines key user capability for himself

14
	public $capability     = 'edit_theme_options';

and checks it further in the code using check_capabilities() method

43
44
45
46
47
48
49
50
51
52
/**
	 * Check if the theme supports the section and check user capabilities.
	 *
	 * @since 3.4.0
	 *
	 * @return bool False if theme doesn't support the section or user doesn't have the capability.
	 */
	public final function check_capabilities() {
		if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
			return false;

wp-includes/class-wp-customize-setting.php

This is Customize Setting Class. At line #15 it defines key user capability for himself

14
	public $capability     = 'edit_theme_options';

and checks it further in the code using check_capabilities() method

254
255
256
257
258
259
260
261
262
263
/**
	 * Check if the theme supports the setting and check user capabilities.
	 *
	 * @since 3.4.0
	 *
	 * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true.
	 */
	public final function check_capabilities() {
		if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
			return false;

wp-includes/functions.php

This is Main WordPress API file. It adds ‘Widgets’ menu with check of ‘edit_theme_options’ capability

2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
/**
 * Append the Widgets menu to the themes main menu.
 *
 * @since 2.2.0
 * @uses $submenu The administration submenu list.
 */
function wp_widgets_add_menu() {
	global $submenu;
	$submenu['themes.php'][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' );
	ksort( $submenu['themes.php'], SORT_NUMERIC );
}

Tags: , ,