Admin Panel. 15+ Hooks for functions.php

Another collection of hacks that can be used for the WordPress admin panel. All codes have been tested for functionality up to WP version 4.7. Here are popular hooks sorted by popularity (based on the site wordpress.stackexchange.com).

All Site Settings on One Page

Enabling a hidden page in the admin panel. This small piece of code will add another item to the site settings menu. By going to the added page, you will see all the site settings at once. This page can be accessed via the link: example.com/wp-admin/options.php.

The code only works for the site administrator; other roles will not see the new menu item.

## Adds a link to the all settings page in the admin menu item "Settings"
add_action('admin_menu', 'all_settings_link');
function all_settings_link(){
	add_options_page( __('All Settings'), __('All Settings'), 'manage_options', 'options.php?foo' );
}

Removing Update Notifications in WordPress for Everyone Except Admin

add_action( 'admin_head', function () {
	if ( ! current_user_can( 'manage_options' ) ) {
		remove_action( 'admin_notices', 'update_nag', 3 );
		remove_action( 'admin_notices', 'maintenance_nag', 10 );
	}
} );

Old version, comments said it no longer works:

## removes the message about a new version of WordPress for all users except the administrator
if( is_admin() && ! current_user_can('manage_options') ){
	add_action( 'init', function(){
		remove_action( 'init', 'wp_version_check' );
	}, 2 );
	add_filter( 'pre_option_update_core', '__return_null');
}

Removing Meta Boxes on the Post Edit Page

On the post edit page, there are often meta boxes that are never used. To speed up the admin panel a bit and remove unnecessary elements from the interface, they can be deleted. Read more in the function description remove_meta_box()

## Removing meta boxes on the post edit page
add_action('admin_menu','remove_default_post_screen_metaboxes');
function remove_default_post_screen_metaboxes() {
	// for posts
	remove_meta_box( 'postcustom','post','normal' ); // custom fields
	remove_meta_box( 'postexcerpt','post','normal' ); // excerpt
	remove_meta_box( 'commentstatusdiv','post','normal' ); // comments
	remove_meta_box( 'trackbacksdiv','post','normal' ); // notification block
	remove_meta_box( 'slugdiv','post','normal' ); // alternative title block
	remove_meta_box( 'authordiv','post','normal' ); // author

	// for pages
	remove_meta_box( 'postcustom','page','normal' ); // custom fields
	remove_meta_box( 'postexcerpt','page','normal' ); // excerpt
	remove_meta_box( 'commentstatusdiv','page','normal' ); // comments
	remove_meta_box( 'trackbacksdiv','page','normal' ); // notification block
	remove_meta_box( 'slugdiv','page','normal' ); // alternative title block
	remove_meta_box( 'authordiv','page','normal' ); // author
}

WordPress has a feature that allows you to change the order of items in the main menu in the admin panel in any way. Not everyone knows about this feature. By default, it is not enabled - it needs to be turned on via the hook custom_menu_order. See the code below for more details.

The code below will change the menu order. "Pages" will come first, followed by "Posts," and then the custom post type "events." By changing the order of the array elements, you can change the menu item order in any way.

## Custom order of items in the main menu of the admin panel
if( is_admin() ){
	add_filter('custom_menu_order', '__return_true'); // enable manual sorting
	add_filter('menu_order', 'custom_menu_order'); // manual sorting
	function custom_menu_order( $menu_order ){
		/*
		$menu_order - an array where the menu items are set in the desired order.
		Array(
			[0] => index.php
			[1] => separator1
			[2] => edit.php
			[3] => upload.php
			[4] => edit.php?post_type=page
			[5] => edit-comments.php
			[6] => edit.php?post_type=events
			[7] => separator2
			[8] => themes.php
			[9] => plugins.php
			[10] => snippets
			[11] => users.php
			[12] => tools.php
			[13] => options-general.php
			[14] => separator-last
			[15] => edit.php?post_type=cfs
		)
		*/
		if( ! $menu_order ) return true;

		return array(
			'index.php', // dashboard
			'edit.php?post_type=page', // pages
			'edit.php', // posts
			'edit.php?post_type=events', // events type posts
		);
	}
}

Move Selected Category to its Place in the Hierarchy

  1. There is a tree structure of categories.
  2. I publish a post and place it in a child category.
  3. As a result, the selected category is at the top, but it needs to be in its place in the category hierarchy.

##  cancel showing the selected term at the top in the checkbox list of terms
add_filter( 'wp_terms_checklist_args', 'set_checked_ontop_default', 10 );
function set_checked_ontop_default( $args ) {
	// change the default parameter to false
	if( ! isset($args['checked_ontop']) )
		$args['checked_ontop'] = false;

	return $args;
}

Source

Removing the "All Categories" and "Frequently Used" Tabs in the Categories Meta Box in the Admin Panel

This hack I often use here and there. I think it's convenient!

The code removes such tabs and from the meta boxes of custom hierarchical taxonomies.

# Removes the "Frequently Used" tab for the categories meta box for all posts
add_action( 'admin_head', 'func_hook_admin_styles' );

function func_hook_admin_styles() {

	$cs = get_current_screen();

	// not the post edit page
	if ( $cs->base !== 'post' || empty( $cs->post_type ) ) {
		return;
	}
	?>
	<style>
		.postbox div.tabs-panel{ max-height:1200px; border:0; }

		.category-tabs{ display:none; }
	</style>
	<?php
}

Change Checkbox to Radio in the Categories Widget

If the site is intended to always specify only one category for a post, it can be more convenient to change the checkboxes in the categories widget to radio buttons. This way, when changing the category, the previously selected category will be automatically deselected, and only one category will always be specified for the post.

The example below shows how to do this for the taxonomies category and my_category.

Option 1:

## Change checkbox to radio on post pages
add_action( 'admin_print_footer_scripts', 'func_hook_admin_footer_scripts', 99 );
function func_hook_admin_footer_scripts(){
	if( get_current_screen()->base !== 'post' )
		return;
	?>
	<script>
		[ 'category', 'my_category' ].forEach(function(taxname){
			jQuery( '#' + taxname + 'div input[type="checkbox"]' ).prop( 'type', 'radio' );
		})
	</script>
	<?php
}

Option 2 (for perfectionists - adds the script only when there is a taxonomy widget)

## Change checkbox to radio on post pages
add_action( 'admin_print_footer_scripts', 'func_hook_admin_footer_scripts', 99 );
function func_hook_admin_footer_scripts(){

	// for which taxonomies to change checkbox to radio
	$taxonomies = [ 'category', 'my_category' ];
	$cs = get_current_screen();
	if( $cs->base === 'post' && ($taxonomies = array_intersect( get_object_taxonomies( $cs->post_type ), $taxonomies )) ){
		?>
		<script>
			<?= json_encode($taxonomies) ?>.forEach(function(taxname){
				jQuery( '#' + taxname + 'div input[type="checkbox"]' ).prop( 'type', 'radio' );
			})
		</script>
		<?php
	}

}

Adding Post Types, Taxonomies to the "Right Now" Widget in the Dashboard

To see the numbers - how many posts of each type have been created right in the dashboard, you can extend the "Right Now" widget (previously called "At a Glance").

Widget Right Now
## Adds all post types to the "Right Now" widget in the dashboard
add_action( 'dashboard_glance_items' , 'add_right_now_info' );
function add_right_now_info( $items ){

	if( ! current_user_can('edit_posts') ) return $items; // exit

	// post types
	$args = array( 'public' => true, '_builtin' => false );

	$post_types = get_post_types( $args, 'object', 'and' );

	foreach( $post_types as $post_type ){
		$num_posts = wp_count_posts( $post_type->name );
		$num       = number_format_i18n( $num_posts->publish );
		$text      = _n( $post_type->labels->singular_name, $post_type->labels->name, intval( $num_posts->publish ) );

		$items[] = "<a href=\"edit.php?post_type=$post_type->name\">$num $text</a>";
	}

	// taxonomies
	$taxonomies = get_taxonomies( $args, 'object', 'and' );

	foreach( $taxonomies as $taxonomy ){
		$num_terms = wp_count_terms( $taxonomy->name );
		$num       = number_format_i18n( $num_terms );
		$text      = _n( $taxonomy->labels->singular_name, $taxonomy->labels->name , intval( $num_terms ) );

		$items[] = "<a href='edit-tags.php?taxonomy=$taxonomy->name'>$num $text</a>";
	}

	// users
	global $wpdb;

	$num  = $wpdb->get_var("SELECT COUNT(ID) FROM $wpdb->users");
	$text = _n( 'User', 'Users', $num );

	$items[] = "<a href='users.php'>$num $text</a>";

	return $items;
}

Read more about the code in a separate article.

Adds Post Thumbnails to the Posts Table in the Admin Panel

In the posts table in the admin panel, it is sometimes very convenient to see which thumbnail or first uploaded image is set for the post. This column can be easily added by installing a small piece of code.

Column in the posts list with post thumbnails

It works automatically for all post types. But if the thumbnail is needed for specific ones, uncomment the line in the code and specify the post types for which the thumbnail should be displayed.

## Adds post thumbnails to the posts table in the admin panel
if(1){
	add_action('init', 'add_post_thumbs_in_post_list_table', 20 );
	function add_post_thumbs_in_post_list_table(){
		// check which posts support thumbnails
		$supports = get_theme_support('post-thumbnails');

		// $ptype_names = array('post','page'); // specifies types for which the column is needed separately

		// Automatically determine post types
		if( ! isset($ptype_names) ){
			if( $supports === true ){
				$ptype_names = get_post_types(array( 'public'=>true ), 'names');
				$ptype_names = array_diff( $ptype_names, array('attachment') );
			}
			// for individual post types
			elseif( is_array($supports) ){
				$ptype_names = $supports[0];
			}
		}

		// add filters for all found post types
		foreach( $ptype_names as $ptype ){
			add_filter( "manage_{$ptype}_posts_columns", 'add_thumb_column' );
			add_action( "manage_{$ptype}_posts_custom_column", 'add_thumb_value', 10, 2 );
		}
	}

	// add column
	function add_thumb_column( $columns ){
		// adjust column width via css
		add_action('admin_notices', function(){
			echo '
			<style>
				.column-thumbnail{ width:80px; text-align:center; }
			</style>';
		});

		$num = 1; // after which column to insert new ones

		$new_columns = array( 'thumbnail' => __('Thumbnail') );

		return array_slice( $columns, 0, $num ) + $new_columns + array_slice( $columns, $num );
	}

	// fill the column
	function add_thumb_value( $colname, $post_id ){
		if( 'thumbnail' == $colname ){
			$width  = $height = 45;

			// thumbnail
			if( $thumbnail_id = get_post_meta( $post_id, '_thumbnail_id', true ) ){
				$thumb = wp_get_attachment_image( $thumbnail_id, array($width, $height), true );
			}
			// from the gallery...
			elseif( $attachments = get_children( array(
				'post_parent'    => $post_id,
				'post_mime_type' => 'image',
				'post_type'      => 'attachment',
				'numberposts'    => 1,
				'order'          => 'DESC',
			) ) ){
				$attach = array_shift( $attachments );
				$thumb = wp_get_attachment_image( $attach->ID, array($width, $height), true );
			}

			echo empty($thumb) ? ' ' : $thumb;
		}
	}
}

Disabling Pings on Your Posts

When a post is published with a link in the text to another post on your site that has the ability to receive notifications enabled, a ping appears in the comments - a notification that the post was referenced somewhere. This is convenient to see that someone referred to your post, but when your site links to your site - it is illogical and even bothersome.

To disable this strange behavior, insert the following code into functions.php:

# Disabling pings on your own posts
add_action( 'pre_ping', 'kama_disable_inner_ping' );
function kama_disable_inner_ping( & $links ){

	foreach( $links as $k => $val ){
		if( false !== strpos( $val, str_replace('www.', '', $_SERVER['HTTP_HOST']) ) )
			unset( $links[$k] );
	}
}

Read more in the post: Disabling Pings on Your Posts

Disabling All Default WordPress Widgets

To disable all default WordPress widgets that exist by default, insert the following code into functions.php:

add_action( 'widgets_init', 'unregister_basic_widgets' );

function unregister_basic_widgets(){

	unregister_widget( 'WP_Widget_Pages' );            // Pages widget
	unregister_widget( 'WP_Widget_Calendar' );         // Calendar
	unregister_widget( 'WP_Widget_Archives' );         // Archives
	unregister_widget( 'WP_Widget_Links' );            // Links
	unregister_widget( 'WP_Widget_Meta' );             // Meta widget
	unregister_widget( 'WP_Widget_Search' );           // Search
	unregister_widget( 'WP_Widget_Text' );             // Text
	unregister_widget( 'WP_Widget_Categories' );       // Categories
	unregister_widget( 'WP_Widget_Recent_Posts' );     // Recent posts
	unregister_widget( 'WP_Widget_Recent_Comments' );  // Recent comments
	unregister_widget( 'WP_Widget_RSS' );              // RSS
	unregister_widget( 'WP_Widget_Tag_Cloud' );        // Tag cloud
	unregister_widget( 'WP_Nav_Menu_Widget' );         // Menu
}

Changing the Post Type Name

By default, WordPress has two post types: Posts and Pages. This example shows how to change their names throughout the admin area.

Sometimes it is convenient or logical to replace the word "Posts" and all related names of "Posts" in the admin area with another word, for example, "Articles":

Replacing the post type name with another

The code is simple and works very quickly:

add_filter( 'post_type_labels_post', 'rename_posts_labels' );

# replace the word "posts" with "Articles" for post type 'post'
function rename_posts_labels( $labels ){
	// automatic replacement is not possible: Post = Article, and in the text we will get "View article"

	$new = [
		'name'                  => 'Articles',
		'singular_name'         => 'Article',
		'add_new'               => 'Add article',
		'add_new_item'          => 'Add article',
		'edit_item'             => 'Edit article',
		'new_item'              => 'New article',
		'view_item'             => 'View article',
		'search_items'          => 'Search articles',
		'not_found'             => 'Articles not found.',
		'not_found_in_trash'    => 'Articles not found in trash.',
		'parent_item_colon'     => '',
		'all_items'             => 'All articles',
		'archives'              => 'Article archives',
		'insert_into_item'      => 'Insert into article',
		'uploaded_to_this_item' => 'Uploaded for this article',
		'featured_image'        => 'Article thumbnail',
		'filter_items_list'     => 'Filter articles list',
		'items_list_navigation' => 'Articles list navigation',
		'items_list'            => 'Articles list',
		'menu_name'             => 'Articles',
		'name_admin_bar'        => 'Article',
	];

	return (object) array_merge( (array) $labels, $new );
}

Read more about how this code appeared in the question.

Disabling Update Notifications for Inactive Plugins

add_filter( 'transient_update_plugins', 'update_active_plugins' );    // Hook for 2.8.+
//add_filter( 'option_update_plugins', 'update_active_plugins' );    // Hook for 2.7.x
function update_active_plugins( $value = '' ){
	/*
	The $value array passed in contains the list of plugins with time
	marks when the last time the groups was checked for version match
	The $value->response node contains an array of the items that are
	out of date. This response node is used by the 'Plugins' menu
	for example to indicate there are updates. Also on the actual
	plugins listing to provide the yellow box below a given plugin
	to indicate action is needed by the user.
	*/
	if( (isset($value->response)) && (count($value->response)) ){

		// Get the list cut current active plugins
		$active_plugins = get_option('active_plugins');
		if ($active_plugins) {

			//  Here we start to compare the $value->response
			//  items checking each against the active plugins list.
			foreach($value->response as $plugin_idx => $plugin_item) {

				// If the response item is not an active plugin then remove it.
				// This will prevent WordPress from indicating the plugin needs update actions.
				if (!in_array($plugin_idx, $active_plugins))
					unset($value->response[$plugin_idx]);
			}
		}
		else {
			 // If no active plugins then ignore the inactive out of date ones.
			foreach($value->response as $plugin_idx => $plugin_item) {
				unset($value->response);
			}
		}
	}
	return $value;
}

Important: disabling update notifications even for inactive plugins can be risky. When a plugin may have a vulnerable file that can be exploited regardless of the plugin's activity. However, this is quite rare...

Auto-Deleting license.txt and readme.html

In the main WordPress directory, there are these two files. Anyone can open them and determine that the site runs on WordPress and which version is being used.

This code will be a great addition to the Clearfy plugin.

How does it work? It checks for the file's existence when you enter the admin panel. If the file exists, PHP attempts to delete it automatically. If there are insufficient permissions to delete, the admin will see a message to manually delete the files license.txt and readme.html.

## Deleting files license.txt and readme.html for protection
## ver 2
if( is_admin() && ! defined('DOING_AJAX') ){
	add_action( 'init', 'remove_license_txt_readme_html' );
	function remove_license_txt_readme_html(){

		$license_file = ABSPATH .'/license.txt';
		$readme_file  = ABSPATH .'/readme.html';

		if( file_exists($license_file) && current_user_can('manage_options') ){

			$deleted = unlink($license_file) && unlink($readme_file);

			if( ! $deleted  )
				$GLOBALS['readmedel'] = 'Failed to delete files: license.txt and readme.html from the folder `'. ABSPATH .'`. Please delete them manually!';
			else
				$GLOBALS['readmedel'] = 'Files: license.txt and readme.html deleted from the folder `'. ABSPATH .'`.';

			add_action( 'admin_notices', function(){
				echo '<div class="error is-dismissible"><p>'. $GLOBALS['readmedel'] .'</p></div>';
			} );
		}
	}
}

Search-Filter Taxonomy Elements by Names in the Admin Meta Box

Suppose you have many categories on your site, for example, 200. Or these can be non-standard categories but sections for WooCommerce. Finding the desired category (taxonomy element) in such a list is extremely inconvenient.

It would be convenient to have a filter so that you can type part of the category name and only terms that contain the entered substring remain in the meta box.

Insert the following code into the theme's functions.php, and you will get such a term filter:

## Filter elements in taxonomy for the taxonomy meta box in the admin panel.
## Allows convenient filtering (searching) of taxonomy elements by name when there are many
add_action( 'admin_print_scripts', 'my_admin_term_filter', 99 );
function my_admin_term_filter() {
	$screen = get_current_screen();

	if( 'post' !== $screen->base ) return; // only for the editing page of any post
	?>
	<script>
	jQuery(document).ready(function($){
		var $categoryDivs = $('.categorydiv');

		$categoryDivs.prepend('<input type="search" class="fc-search-field" placeholder="filter..." style="width:100%" />');

		$categoryDivs.on('keyup search', '.fc-search-field', function (event) {

			var searchTerm = event.target.value,
				$listItems = $(this).parent().find('.categorychecklist li');

			if( $.trim(searchTerm) ){
				$listItems.hide().filter(function () {
					return $(this).text().toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1;
				}).show();
			}
			else {
				$listItems.show();
			}
		});
	});
	</script>
	<?php
}

This code shows the entire branch if categories are in hierarchical form:

Source. Taken from the Post Category Filter

If there are updates for plugins, we see a circle with the number of plugins in the menu. Such circles with numbers can be added to any menu item. For example, we need to show such a notification if there are posts pending approval:

add_action( 'admin_menu', 'add_user_menu_bubble' );
function add_user_menu_bubble(){
	global $menu;

	// posts
	$count = wp_count_posts()->pending; // pending approval
	if( $count ){
		foreach( $menu as $key => $value ){
			if( $menu[$key][2] == 'edit.php' ){
				$menu[$key][0] .= ' <span class="awaiting-mod"><span class="pending-count">' . $count . '</span></span>';
				break;
			}
		}
	}
}

notification for the menu in the admin panel

Recoloring the Admin Bar (Toolbar) for Different Environments

Use wp_get_environment_type() to get the current development environment.

  • standard black for Local development environment
  • blue for QA (testing) and for development
  • orange for staging
  • red for production.

GitHub

<?php
/**
 * Adminpanel environment color for WordPress.
 * It helps to highlight environments in color. Developers, Content Managers,
 * and others will never confuse the environment where they work.
 *
 * Author: Andrei Pisarevskii
 * Author Email: [email protected]
 * Author Site: https://wp-yoda.com/en/
 *
 * Version: 0.3
 * Source Code: https://gist.github.com/renakdup/36f4a8474d0cb13ecadf0393811d5330
 *
 * Licence: MIT License
 */

namespace Renakdup\AdminpanelEnvColor;

AdminpanelEnvColor::init();

final class AdminpanelEnvColor {

	public static function init() {
		add_action( 'admin_head', [ __CLASS__, 'add_admin_bar_style' ] );
		add_action( 'wp_head', [ __CLASS__, 'add_admin_bar_style' ] );
		add_action( 'admin_bar_menu', [ __CLASS__, 'add_admin_bar_env_item' ], 100 );
	}

	public static function add_admin_bar_style() {
		$adminpanel_colors = apply_filters(
			'renakdup/adminpanel_env_color/colors',
			[
				'local'       => null, // default wp color
				'development' => '#2271b1', // blue
				'staging'     => '#cc6f00', // orange
				'production'  => '#6d0d0f', // red
			]
		);

		// phpcs:disable WordPress.CodeAnalysis.AssignmentInCondition.Found, Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure, WordPress.Security.EscapeOutput.OutputNotEscaped
		if ( $color = $adminpanel_colors[ wp_get_environment_type() ] ) {
			echo '<style>
				#wpadminbar { background-color: ' . $color . '!important; }
				#adminmenu li.wp-has-current-submenu a.wp-has-current-submenu { background-color: ' . $color . '!important; }
			</style>';
		}
		// phpcs:enable

		echo '<style>.rd_adminpanel_env_color a {
			box-shadow: inset 0 32px 5px rgba(0, 0, 0, 0.5) !important;
			padding-left: 30px !important;
			padding-right: 30px !important;
			}
		</style>';
	}

	/**
	 * @param $wp_admin_bar \WP_Admin_Bar
	 *
	 * @return void
	 */
	public static function add_admin_bar_env_item( $wp_admin_bar ) {
		$args = [
			'id'     => 'rd_adminpanel_env_color',
			'parent' => 'top-secondary',
			'title'  => 'ENV: ' . ucfirst( wp_get_environment_type() ),
			'href'   => '#',
			'meta'   => [
				'class' => 'rd_adminpanel_env_color',
				'title' => 'Your environment',
			],
		];
		$wp_admin_bar->add_node( $args );
	}
}

To change the colors or names of environments, use the filter renakdup/adminpanel_env_color/colors.

To disable coloring for specific roles, use remove_filter().

--

Read more in the original article.

-

Source: wordpress.stackexchange.com, personal experience, questions from this site, etc.