delete_themes WordPress user capability

delete_themes WordPress user capability

delete_themes

What capability to add or remove in order to allow or prohibit WordPress theme deletion? That’s simple, you can say, of course delete_themes user capability. It is clear from its name, isn’t it? You are right. But it is not full true. It’s useful to know, that if you have not switch_themes capability under single-site WordPress installation, you can not delete selected theme, even if you click ‘Delete’ command from WordPress interface. Interesting? Continue reading and see more detailed description or (if you curious enough) even look inside WordPress core source code together with me.
I found delete_themes capability in this WordPress core files:
wp-admin\themes.php;
wp-admin\network\themes.php;
wp-admin\includes\class-wp-ms-themes-list-table.php;
wp-admin\includes\class-wp-themes-list-table.php;
wp-includes\capabilities.php;
wp-admin\includes\schema.php.

wp-admin\themes.php

Themes administration panel, menu “Appearance”. As you can see below: delete_theme() function is called here (line #27), just after permission check, if you can delete themes or not. And, important, in order to delete theme user should have switch_themes capability, additionally:

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 if ( current_user_can( 'switch_themes' ) && isset($_GET['action'] ) ) {
	if ( 'activate' == $_GET['action'] ) {
		check_admin_referer('switch-theme_' . $_GET['template']);
		switch_theme($_GET['template'], $_GET['stylesheet']);
		wp_redirect( admin_url('themes.php?activated=true') );
		exit;
	} elseif ( 'delete' == $_GET['action'] ) {
		check_admin_referer('delete-theme_' . $_GET['template']);
		if ( !current_user_can('delete_themes') )
			wp_die( __( 'Cheatin’ uh?' ) );
		delete_theme($_GET['template']);
		wp_redirect( admin_url('themes.php?deleted=true') );
		exit;
	}
 }

It is wise decision, as if you have not access to the “Appearance” menu of WordPress admin dashboard, you can not execute theme deletion request without “cheating”, uh?

wp-admin\network\themes.php

Multisite themes administration panel. It is other place, where code, that deletes themes, lives really. WordPress checks, if you have permission to delete themes here again:

108
109
 if ( ! current_user_can( 'delete_themes' ) )
	wp_die( __('You do not have sufficient permissions to delete themes for this site.') );

wp-admin\includes\class-wp-ms-themes-list-table.php

For WordPress multisite environment: Decision to show/hide ‘Delete’ bulk action in the drop-down menu is taken here:
class WP_MS_Themes_List_Table function get_bulk_actions()

239
240
241
 if ( ! $this->is_site_themes ) {
	if ( current_user_can( 'delete_themes' ) )
		$actions['delete-selected'] = __( 'Delete' );

For WordPress multisite environment: Single row action link is shown or hidden here, if you have or not delete_themes capability:
class WP_MS_Themes_List_Table function single_row()

290
291
 if ( empty( $theme['enabled'] ) && current_user_can( 'delete_themes' ) && ! $this->is_site_themes && $theme_key != get_option( 'stylesheet' ) && $theme_key != get_option( 'template' ) )
	$actions['delete'] = '<a href="' . esc_url( wp_nonce_url( 'themes.php?action=delete-selected&amp;checked[]=' . $theme_key . '&amp;theme_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-themes' ) ) . '" title="' . esc_attr__( 'Delete this theme' ) . '" class="delete">' . __( 'Delete' ) . '</a>';

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

Single site WordPress configuration: theme delete action link is configured here:
class WP_Themes_List_Table, function display_rows()

154
155
 if ( ! is_multisite() && current_user_can( 'delete_themes' ) )
	$actions[] = '<a class="submitdelete deletion" href="' . wp_nonce_url( "themes.php?action=delete&amp;template=$stylesheet", 'delete-theme_' . $stylesheet ) . '" onclick="' . "return confirm( '" . esc_js( sprintf( __( "You are about to delete this theme '%s'\n  'Cancel' to stop, 'OK' to delete." ), $theme_name ) ) . "' );" . '">' . __( 'Delete' ) . '</a>';

wp-includes\capabilities.php

Function map_meta_cap(), which maps meta capabilities to primitive capabilities. This does not actually compare whether the user ID has the actual capability, just what the capability or capabilities are. Interesting to discover that you can implement additional security layer, prohibit any file operation, if define PHP constant DISALLOW_FILE_MODS and set it to ‘true’ or ‘1’:

1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
 case 'delete_themes':
 case 'install_themes':
 case 'update_core':
	// Disallow anything that creates, deletes, or edits core, plugin, or theme files.
	// Files in uploads are excepted.
	if ( defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS ) {
		$caps[] = 'do_not_allow';
		break;
	}
	// Fall through if not DISALLOW_FILE_MODS.

wp-admin\includes\schema.php

delete_themes capability was added since WordPress version 3.0:

774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
 /**
  * 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' );
	$role->add_cap( 'delete_themes' );
	$role->add_cap( 'export' );
  }
 }

Tags: , , ,