" . str_replace( array(
'[code]',
'[/code]'
), array(
'',
''
), esc_html__( 'When enabled, the Admin Search box will display instantly on keypress except when a text field is in focus. The Admin Search box can be opened anytime by clicking the search icon in the admin bar.', 'admin-search' ) ) . "
";
}
/*
* Set up 'autosearch' setting
*/
function admin_search_setting_autosearch() {
echo "
" . str_replace( array(
'[code]',
'[/code]'
), array(
'',
''
), esc_html__( 'When enabled, search results will be displayed as you type. When disabled, press [code]Enter[/code] to search.', 'admin-search' ) ) . "
";
}
/*
* Set up 'result_count' setting
*/
function admin_search_setting_result_count() {
$field = "";
/* translators: %s: the settings field, rendered as a dropdown inline with this label */
echo "
" . esc_html__( 'If a search returns more results than the limit, an option to load more will be displayed in the search box. A high result limit may cause longer load times when searching.', 'admin-search' ) . "
";
}
/*
* Set up 'highlight_query' setting
*/
function admin_search_setting_highlight_query() {
echo "";
}
/*
* Set up 'show_when_viewing_site' setting
*/
function admin_search_setting_show_when_viewing_site() {
echo "
" . esc_html__( 'When enabled, the Admin Search box will be accessible to admins when viewing the website.', 'admin-search' ) . "
";
}
/*
* Set up 'result_previews' setting
*/
function admin_search_setting_result_previews() {
echo "
" . esc_html__( 'When enabled, permalinks of supported search results can be previewed directly in the Admin Search box.', 'admin-search' ) . "
";
}
/*
* Set up 'show_suggestions' setting
*/
function admin_search_setting_show_suggestions() {
echo "
";
global $wpdb;
$table_name = $wpdb -> prefix . 'admin_search__searches';
$top_searches = $wpdb -> get_results( $wpdb -> prepare( "SELECT * FROM {$table_name} ORDER BY LENGTH(query) DESC, occurrences DESC, results DESC LIMIT 100" ) );
if ( empty( $top_searches ) ) {
esc_html_e( 'When enabled, search suggestions will be displayed as you type. These suggestions are based on past searches – the more you search, the more accurate the suggestions become.', 'admin-search' );
} else {
echo str_replace(
array(
'[span clearSuggestionContainer]',
'[a clearSuggestions]',
'[/a]',
'[/span]'
),
array(
'',
'',
'',
''
),
esc_html__( 'When enabled, search suggestions will be displayed as you type. These suggestions are based on past searches – the more you search, the more accurate the suggestions become.[span clearSuggestionContainer] [a clearSuggestions]Clear search suggestions[/a].[/span]', 'admin-search' )
);
}
echo "
' . esc_html__( 'Choose what appears in search results.', 'admin-search' ) . '
';
}
/*
* Return all distinct meta keys that exist in the database for a given post type,
* excluding WordPress internal housekeeping keys that are never useful to search.
*/
function admin_search_get_post_type_meta_keys( $post_type ) {
global $wpdb;
$excluded = array(
'_edit_lock', '_edit_last',
'_wp_trash_meta_status', '_wp_trash_meta_time',
'_wp_desired_post_slug', '_wp_old_date', '_wp_old_slug',
);
$keys = $wpdb->get_col( $wpdb->prepare(
"SELECT DISTINCT pm.meta_key
FROM {$wpdb->postmeta} pm
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE p.post_type = %s
AND pm.meta_key NOT LIKE '\_%%'
ORDER BY pm.meta_key",
$post_type
) );
return array_values( array_diff( $keys ?: array(), $excluded ) );
}
/*
* Set up 'post_types' setting
*/
function admin_search_setting_post_types() {
$current = admin_search_setting( 'post_types', array() );
echo '
';
// WordPress internal / system post types that should never appear in the
// settings list. Exposed via a filter so plugin/theme authors can adjust.
//
// We intentionally exclude FSE types (wp_template, wp_template_part, …) even
// though they have show_ui = true, because they are low-level site-building
// primitives rather than content a site admin would want to search for.
$_excluded_pt = apply_filters( 'admin_search_excluded_post_types', array(
'revision', // post revisions — never directly editable
'nav_menu_item', // navigation menu entries
'custom_css', // Customizer custom CSS
'customize_changeset', // Customizer draft changesets
'oembed_cache', // oEmbed response cache
'user_request', // GDPR data erasure requests
'wp_template', // FSE site templates
'wp_template_part', // FSE template parts
'wp_global_styles', // FSE global styles
'wp_navigation', // block-based nav menus
'wp_font_family', // Font Library families
'wp_font_face', // Font Library faces
) );
// Use show_ui rather than public: this is an admin tool, so the right criterion
// is whether a post type has an edit screen — not whether it's front-end queryable.
// This naturally includes non-public admin-only CPTs (e.g. internal CRM types)
// and wp_block (Patterns) without needing special-cases.
foreach ( get_post_types( array( 'show_ui' => true ), 'object' ) as $post_type ) {
if ( in_array( $post_type->name, $_excluded_pt, true ) ) {
continue;
}
$slug = $post_type->name;
$label = $post_type->label;
$is_enabled = array_key_exists( $slug, $current );
$enabled_fields = $is_enabled ? (array) $current[ $slug ] : array();
// Merge meta keys from DB with any saved keys that may no longer be in the DB
// (e.g. all posts with that meta deleted) so saved values are still shown.
$db_meta_keys = admin_search_get_post_type_meta_keys( $slug );
$saved_meta_keys = array_values( array_filter( $enabled_fields, function ( $f ) {
return $f !== 'body';
} ) );
$all_meta_keys = array_unique( array_merge( $db_meta_keys, $saved_meta_keys ) );
sort( $all_meta_keys );
$toggle_id = 'as-pt-toggle-' . esc_attr( $slug );
// Build the post type icon HTML.
// menu_icon can be a dashicons class, a data URI, a full URL, or empty.
$raw_icon = ! empty( $post_type->menu_icon ) ? $post_type->menu_icon : '';
if ( empty( $raw_icon ) || $raw_icon === 'none' ) {
$icon_defaults = array(
'post' => 'dashicons-admin-post',
'page' => 'dashicons-admin-page',
'attachment' => 'dashicons-admin-media',
'wp_block' => 'dashicons-block-default',
);
$raw_icon = isset( $icon_defaults[ $slug ] ) ? $icon_defaults[ $slug ] : 'dashicons-admin-post';
}
if ( strpos( $raw_icon, 'dashicons-' ) === 0 ) {
$icon_html = '';
} elseif ( strpos( $raw_icon, 'data:' ) === 0 ) {
$icon_html = '';
} else {
$icon_html = '';
}
echo '
';
// Hidden checkbox drives both the visual toggle and the CSS expand/collapse
echo '';
// Row label: icon + post type name on the left, visual toggle track on the right
echo '';
// Fields panel — CSS shows/hides this via :checked ~ .as-pt-fields
echo '
';
echo '
';
// Body (post_content + post_excerpt)
$body_id = 'as-field-' . esc_attr( $slug ) . '-body';
echo '';
// Custom / meta fields
foreach ( $all_meta_keys as $meta_key ) {
// Sanitise the meta key to a safe string for use in an HTML id attribute
$safe_suffix = preg_replace( '/[^a-z0-9_-]/', '-', strtolower( $meta_key ) );
$field_id = 'as-field-' . esc_attr( $slug ) . '-' . $safe_suffix;
$display = ltrim( $meta_key, '_' ); // strip leading _ for the readable label
echo '';
}
// Taxonomy toggles — allows filtering posts by category, tag, or any custom taxonomy
$pt_taxonomies = get_object_taxonomies( $slug, 'objects' );
$public_taxonomies = array_filter( $pt_taxonomies, function ( $tax ) {
return $tax->public || ( isset( $tax->show_ui ) && $tax->show_ui );
} );
if ( ! empty( $public_taxonomies ) ) {
echo '
'
. esc_html__( 'Title, slug, and ID are always searchable. With no fields enabled, posts are only findable by title, slug, ID, author, or date.', 'admin-search' )
. '
';
echo '
'; // .as-pt-fields
echo '
'; // .as-pt-item
}
echo '
'; // #admin_search_plugin_setting_post_types
}
/*
* Set up 'taxonomies' setting
*/
function admin_search_setting_taxonomies() {
echo "
";
}
/*
* Set up 'include_admin_pages' setting
*/
function admin_search_setting_include_admin_pages() {
echo "
" . esc_html__( 'Users will only see admin pages they have access to.', 'admin-search' ) . "
";
}
/*
* Set up 'include_comments' setting
*/
function admin_search_setting_include_comments() {
echo "";
}
/*
* Set up 'include_users' setting
*/
function admin_search_setting_include_users() {
echo "";
}
/*
* Set up 'external_websites' setting
*/
function admin_search_setting_external_websites() {
echo "
" . str_replace( array(
'[code]',
'[/code]'
), array(
'',
''
), esc_html__( 'Display links to external websites in search results. Use [code]%q%[/code] in place of the query value in the URL. Separate multiple website URLs with line breaks.', 'admin-search' ) ) . "
" . str_replace( array(
'[code]',
'[/code]'
), array(
'',
''
), esc_html__( 'Example: Add [code]https://wordpress.org/search/%q%[/code] to display a link to the WordPress.org search pre-populated with your search query.', 'admin-search' ) ) . "
";
}
/*
* Apply per-post-type field settings to search queries.
*
* These filters run at priority 100 so that they override any other plugin that
* has added fields at the default priority (10). When a post type has been
* explicitly configured in Admin Search settings the stored configuration is
* authoritative; when it hasn't been configured the filters are a no-op.
*/
add_filter( 'admin_search_fields', function( $fields, $post_type ) {
$pt_settings = admin_search_setting( 'post_types', array() );
if ( ! array_key_exists( $post_type, $pt_settings ) ) {
return $fields;
}
$enabled = (array) $pt_settings[ $post_type ];
// Title and slug are always included regardless of user configuration
$result = array( 'post_title', 'post_name' );
// Body (content + excerpt) is opt-in
if ( in_array( 'body', $enabled ) ) {
$result[] = 'post_content';
$result[] = 'post_excerpt';
}
return $result;
}, 100, 2 );
add_filter( 'admin_search_meta_queries', function( $meta_fields, $post_type ) {
$pt_settings = admin_search_setting( 'post_types', array() );
if ( ! array_key_exists( $post_type, $pt_settings ) ) {
return $meta_fields;
}
$enabled = (array) $pt_settings[ $post_type ];
// Return only the explicitly enabled meta keys (everything that isn't 'body' or a taxonomy slug)
return array_values( array_filter( $enabled, function( $f ) {
return $f !== 'body' && ! taxonomy_exists( $f );
} ) );
}, 100, 2 );
/*
* Feed the per-post-type enabled taxonomy list into the field-level taxonomy search.
* Runs at priority 100 to override any lower-priority defaults.
*/
add_filter( 'admin_search_taxonomies', function( $taxonomies, $post_type ) {
$pt_settings = admin_search_setting( 'post_types', array() );
if ( ! array_key_exists( $post_type, $pt_settings ) ) {
return $taxonomies;
}
$enabled = (array) $pt_settings[ $post_type ];
// Return only the taxonomy slugs stored in the enabled fields list
return array_values( array_filter( $enabled, function( $f ) {
return $f !== 'body' && taxonomy_exists( $f );
} ) );
}, 100, 2 );