date( 'm' ) ) {
$date = strtotime( $str . ' ' . date( 'Y', strtotime( '-1 year' ) ) );
} else {
$date = strtotime( $str . ' ' . date( 'Y' ) );
}
$r[ 'year' ] = date( 'Y', $date );
$r[ 'month' ] = date( 'm', $date );
break;
// Any other native format
default :
$date = strtotime( $str );
$r[ 'year' ] = date( 'Y', $date );
$r[ 'month' ] = date( 'm', $date );
$r[ 'day' ] = date( 'd', $date );
}
return $r;
}
/*
* Add a nice title to external website names
*/
function admin_search_get_website_title( $host ) {
// Some common websites
$website_titles = array(
'bing.com' => 'Bing',
'gettyimages.com' => 'Getty Images',
'google.ca' => 'Google',
'google.ch' => 'Google',
'google.co.nz' => 'Google',
'google.co.uk' => 'Google',
'google.com' => 'Google',
'google.com.au' => 'Google',
'google.in' => 'Google',
'istockphoto.com' => 'iStock',
'shutterstock.com' => 'Shutterstock',
'stackoverflow.com' => 'Stack Overflow',
'unsplash.com' => 'Unsplash',
'wikipedia.org' => 'Wikipedia',
'en.wikipedia.org' => 'Wikipedia',
'wordpress.org' => 'WordPress',
'wordpress.stackexchange.com' => 'WordPress Development Stack Exchange',
'wpbeginner.com' => 'WPBeginner',
'yahoo.com' => 'Yahoo'
);
// Apply any filters to the array
$website_titles = apply_filters( 'admin_search_website_titles', $website_titles );
// Check if array is valid and host exists, then return matching label
if ( is_array( $website_titles ) && isset( $website_titles[ $host ] ) ) {
return $website_titles[ $host ];
}
// Otherwise, return the host name as is
return $host;
}
/*
* Prepare JSON for AJAX call
*/
function admin_search_ajax_return( $message = '', $error = true, $results = array(), $total_results = 0 ) {
wp_send_json( array(
'error' => $error,
'message' => $message,
'results' => $results,
'total_results' => $total_results
) );
wp_die();
return ! $error;
}
/*
* Validate search query and return search results with AJAX
*/
function admin_search_ajax() {
// Only logged in users can search
if ( ! is_user_logged_in() ) {
return admin_search_ajax_return( __( 'Not authorized', 'admin-search' ) );
}
// Only users who can edit other users' posts can search
if ( ! current_user_can( 'edit_others_pages' ) ) {
return admin_search_ajax_return( __( 'Not authorized', 'admin-search' ) );
}
// JSON headers and default values
$results = array();
// Perform basic checks on q
if ( ! ( isset( $_GET[ 'q' ] ) && is_string( trim( strip_tags( $_GET[ 'q' ] ) ) ) ) ) {
return admin_search_ajax_return( __( 'No search query supplied', 'admin-search' ) );
}
// Clean q up
$q = trim( strtolower( strip_tags( $_GET[ 'q' ] ) ) );
// Apply any filters to q
$q = apply_filters( 'admin_search_query', $q );
// Check if source is supplied, otherwise build list from preferences
if ( isset( $_GET[ 'source' ] ) && is_string( trim( $_GET[ 'source' ] ) ) ) {
$sources = array( $_GET[ 'source' ] );
} else {
$sources = array_merge( admin_search_setting( 'post_types', array() ), admin_search_setting( 'taxonomies', array() ) );
if ( admin_search_setting( 'include_comments' ) ) {
$sources[] = 'comment';
}
if ( admin_search_setting( 'include_users' ) ) {
$sources[] = 'user';
}
if ( admin_search_setting( 'include_admin_pages' ) ) {
$sources[] = 'admin';
}
if ( admin_search_setting( 'external_websites' ) ) {
$sources[] = 'external_sites';
}
}
// Apply any filters to sources array
$sources = apply_filters( 'admin_search_sources', $sources, $q );
// Make sure the filters didn't break the array
if ( ! is_array( $sources ) ) {
return admin_search_ajax_return( __( 'Invalid sources list', 'admin-search' ) );
}
// Check if q is an ID
$id = NULL;
if ( preg_match( '/^#([0-9]+)$/', $q, $match ) ) {
$id = $match[ 1 ];
}
// Check if q contains a status, if it does, limit it to just posts, pages, attachments and custom post types
$status = 'any';
$status_pattern = '/\s' . sprintf( __( 'status:%s', 'admin-search' ), '([a-z]+)' ) . '$/';
if ( preg_match( $status_pattern, $q, $match ) ) {
$status = $match[ 1 ];
$q = preg_replace( $status_pattern, '', $q );
$sources = admin_search_setting( 'post_types', array() );
}
// Check if paged is supplied and valid, if not, set as 1
$paged = isset( $_GET[ 'paged' ] ) && $_GET[ 'paged' ] > 0 ? $_GET[ 'paged' ] : 1;
// Check if q is long enough, afterall, anything shorter than 2 characters is not a very good search
if ( strlen( $q ) < 2 ) {
return admin_search_ajax_return( __( 'Query too short', 'admin-search' ) );
}
// Apply any filters to $results
$results = apply_filters( 'admin_search_pre_results', $results, $q );
// Cycle through all the searchable post types
foreach ( admin_search_setting( 'post_types', array() ) as $post_type ) {
// Check if the post type is in the sources array, if not, skip it
if ( ! in_array( $post_type, $sources ) ) {
continue;
}
// Setup author and date searches
$author = NULL;
$date = NULL;
$date_range = 'on';
// Check if q matches any of these author search terms
foreach ( array(
__( 'posts by %author%', 'admin-search' ),
__( 'posted by %author%', 'admin-search' ),
__( 'authored by %author%', 'admin-search' ),
__( 'author is %author%', 'admin-search' ),
__( 'author %author%', 'admin-search' ),
__( 'made by %author%', 'admin-search' ),
__( 'credted by %author%', 'admin-search' )
) as $author_q ) {
if ( preg_match( '/^' . str_replace( '%author%', '(.+)', $author_q ) . '$/', $q, $match ) ) {
$author = $match[ 1 ]; break;
}
}
// Check if q matches any of these date search term
if ( $author == NULL ) {
// Start with dates before a given date
foreach ( array(
__( 'posted before %date%', 'admin-search' ),
__( 'published before %date%', 'admin-search' )
) as $date_q ) {
if ( preg_match( '/^' . str_replace( '%date%', '(.+)', $date_q ) . '$/', $q, $match ) ) {
$date = admin_search_convert_date_str( $match[ 1 ] );
$date_range = 'before'; break;
}
}
// Followed by dates after a given date
if ( $date == NULL ) {
foreach ( array(
__( 'posted after %date%', 'admin-search' ),
__( 'published after %date%', 'admin-search' )
) as $date_q ) {
if ( preg_match( '/^' . str_replace( '%date%', '(.+)', $date_q ) . '$/', $q, $match ) ) {
$date = admin_search_convert_date_str( $match[ 1 ] );
$date_range = 'after'; break;
}
}
}
// Otherwise, does it match any of these date search terms?
if ( $date == NULL ) {
foreach ( array(
__( 'posted on %date%', 'admin-search' ),
__( 'posted in %date%', 'admin-search' ),
__( 'posted %date%', 'admin-search' ),
__( 'published on %date%', 'admin-search' ),
__( 'published in %date%', 'admin-search' ),
__( 'published %date%', 'admin-search' )
) as $date_q ) {
if ( preg_match( '/^' . str_replace( '%date%', '(.+)', $date_q ) . '$/', $q, $match ) ) {
$date = admin_search_convert_date_str( $match[ 1 ] ); break;
}
}
}
}
// Setup `WP_Post` arguments
$posts_args = array(
'post_type' => $post_type,
'post_status' => $status,
'posts_per_page' => admin_search_setting( 'result_count' ),
'paged' => $paged,
'ignore_sticky_posts' => true
);
// If there is an author or a date in the search, add it to the arguments, otherwise, check for an ID and finally, just pass the q into the s argument
if ( $author != NULL ) {
if ( is_numeric( $author ) ) {
$posts_args[ 'author' ] = $author;
} else {
$posts_args[ 'author_name' ] = $author;
}
} else if ( $date != NULL ) {
switch ( $date_range ) {
case 'on' :
$posts_args[ 'date_query' ] = $date;
break;
case 'before' :
$posts_args[ 'date_query' ]['before'] = $date;
break;
case 'after' :
$posts_args[ 'date_query' ]['after'] = $date;
break;
}
} else if ( $id ) {
$posts_args[ 'p' ] = $id;
} else {
$posts_args[ 's' ] = $q;
}
// Apply any filters to the `WP_Query` arguments
$posts_args = apply_filters( 'admin_search_posts_query', $posts_args, $q );
$posts_args = apply_filters( 'admin_search_' . $post_type . '_query', $posts_args, $q );
// Perform `WP_Query`
$posts = new WP_Query( $posts_args );
// Check if there are any posts that match all that criteria
if ( $posts -> have_posts() ) {
// There are!
while ( $posts -> have_posts() ) {
$posts -> the_post();
// If the source information hasn't already been defined, define it. This has to be done within the loop, so if it is defined, just ignore it. Gross
if ( ! isset( $results[ $post_type ] ) ) {
// Get the post type fields to add to the source information
$post_type_object = get_post_type_object( get_post_type( get_the_id() ) );
$results[ $post_type ][ 'post_type' ] = array(
'name' => $post_type,
'label' => $post_type_object -> label,
'search_url' => add_query_arg( array(
'post_type' => $post_type,
's' => $q
), get_admin_url( '', 'edit.php' ) )
);
// The attachment post type has a different search URL
if ( $post_type == 'attachment' ) {
$results[ $post_type ][ 'post_type' ][ 'search_url' ] = add_query_arg( array(
'search' => $q
), get_admin_url( '', 'upload.php' ) );
}
}
$title = get_the_title();
// If the highlight query setting is enabled, replace the matching content with the highlight HTML
if ( admin_search_setting( 'highlight_query' ) ) {
$title = preg_replace( '/(' . addcslashes( $q, '/' ) . ')/i', '$1', $title );
}
// Build the result array
$result = array(
'id' => get_the_id(),
'edit_post_link' => get_edit_post_link(),
'title' => $title,
'date' => get_the_date(),
'status' => get_post_status() == 'publish' ? NULL : __( ucfirst( get_post_status() ), 'admin-search' )
);
// Special items for attachments
if ( $post_type == 'attachment' ) {
$result[ 'attachment' ] = wp_get_attachment_image( get_the_id(), 'medium' );
$result[ 'mime_type' ] = get_post_mime_type();
$result[ 'mime_icon' ] = wp_mime_type_icon( get_the_id() );
}
// Add the result array to $return
$results[ $post_type ][ 'posts' ][] = $result;
}
// Add addition information about this source to $return
$results[ $post_type ][ 'total_results' ] = $posts -> found_posts;
$results[ $post_type ][ 'total_pages' ] = $posts -> max_num_pages;
$results[ $post_type ][ 'current_page' ] = $paged;
$results[ $post_type ][ 'next_page' ] = NULL;
// If there is more than one page of results, add the page number
if ( $posts -> max_num_pages > $paged ) {
$results[ $post_type ][ 'next_page' ] = $paged + 1;
}
}
wp_reset_query();
}
// Cycle through all the searchable taxonomies
foreach ( admin_search_setting( 'taxonomies', array() ) as $taxonomy ) {
// Check if this taxonomy is in the sources array, if not, skip it
if ( ! in_array( $taxonomy, $sources ) ) {
continue;
}
// Setup arguments for `WP_Term_Query`
$terms_args = array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
'number' => admin_search_setting( 'result_count' ),
'offset' => ( $paged - 1 ) * admin_search_setting( 'result_count' )
);
// If q is an ID, only look for the term with that ID, otherwise, pass q to the `name__like` argument
if ( $id ) {
$terms_args[ 'include' ] = array( $id );
} else {
$terms_args[ 'name__like' ] = $q;
}
// Apply any filters to the `WP_Term_Query` arguments
$terms_args = apply_filters( 'admin_search_terms_query', $terms_args, $q );
$terms_args = apply_filters( 'admin_search_' . $taxonomy . '_query', $terms_args, $q );
// Perform `WP_Term_Query`
$terms = new WP_Term_Query( $terms_args );
// Check if there are any terms matching that criteria
if ( $terms -> get_terms() ) {
// There are!
foreach ( $terms -> get_terms() as $term ) {
if ( ! isset( $results[ $taxonomy ] ) ) {
// Get the taxonomy fields to add to the source information
$taxonomy_object = get_taxonomy( $term -> taxonomy );
// Set the post type for the queried taxonomy
$taxonomy_post_type = '';
if ( is_array( $taxonomy_object -> object_type ) ) {
$taxonomy_post_type = $taxonomy_object -> object_type[ 0 ];
}
// If the source information hasn't already been defined, define it. This has to be done within the loop, so if it is defined, just ignore it. Gross
$results[ $taxonomy ][ 'post_type' ] = array(
'name' => $taxonomy,
'label' => $taxonomy_object -> label,
'search_url' => add_query_arg( array(
'taxonomy' => $taxonomy,
'post_type' => $taxonomy_post_type,
's' => $q
), get_admin_url( '', 'edit-tags.php' ) )
);
}
$title = $term -> name;
// If the highlight query setting is enabled, replace the matching content with the highlight HTML
if ( admin_search_setting( 'highlight_query' ) ) {
$title = preg_replace( '/(' . addcslashes( $q, '/' ) . ')/i', '$1', $title );
}
// Build the result array and add it to $return
$results[ $taxonomy ][ 'posts' ][] = array(
'id' => $term -> term_id,
'edit_post_link' => get_edit_term_link( $term -> term_id, $taxonomy ),
'title' => $title
);
}
// Add addition information about this source to $return. A little more work is required because `WP_Term_Query` doesn't return total found items or number of pages
$found_terms = wp_count_terms( $taxonomy, $terms_args );
$total_pages = ceil( $found_terms / admin_search_setting( 'result_count' ) );
$results[ $taxonomy ][ 'total_results' ] = $found_terms;
$results[ $taxonomy ][ 'total_pages' ] = $total_pages;
$results[ $taxonomy ][ 'current_page' ] = $paged;
$results[ $taxonomy ][ 'next_page' ] = NULL;
// If there is more than one page of results, add the page number
if ( $total_pages > $paged ) {
$results[ $taxonomy ][ 'next_page' ] = $paged + 1;
}
}
}
// Check if comment is in the sources array, if not, skip it
if ( in_array( 'comment', $sources ) ) {
// Setup arguments for `WP_Comment_Query`
$comments_args = array(
'search' => $q,
'number' => admin_search_setting( 'result_count' ),
'offset' => ( $paged - 1 ) * admin_search_setting( 'result_count' )
);
// Apply any filters to the `WP_Comment_Query` arguments
$comments_args = apply_filters( 'admin_search_comments_query', $comments_args, $q );
// Perform `WP_Comment_Query`
$comments = new WP_Comment_Query( $comments_args );
// Check if there any comments matching that criteria
if ( $comments -> comments ) {
// There are!
// Define source information
$results[ 'comment' ][ 'post_type' ] = array(
'name' => 'comment',
'label' => __( 'Comments' ),
'search_url' => add_query_arg( array(
's' => $q
), get_admin_url( '', 'edit-comments.php' ) )
);
foreach ( $comments -> comments as $comment ) {
$title = get_the_title( $comment -> comment_post_ID );
// If the highlight query setting is enabled, replace the matching content with the highlight HTML
if ( admin_search_setting( 'highlight_query' ) ) {
$title = preg_replace( '/(' . addcslashes( $q, '/' ) . ')/i', '$1', $title );
}
// Build the result array and add it to $return
$results[ 'comment' ][ 'posts' ][] = array(
'id' => $comment -> comment_ID,
'edit_post_link' => get_edit_comment_link( $comment -> comment_ID ),
'title' => sprintf( __( 'Comment by %s on %s' , 'admin-search' ), $comment -> comment_author, $title ),
'date' => date( get_option( 'date_format' ), strtotime( $comment -> comment_date ) ),
'status' => $comment -> comment_approved ? 'Published' : 'Pending'
);
}
// Add addition information about this source to $return
$results[ 'comment' ][ 'total_results' ] = $comments -> found_comments;
$results[ 'comment' ][ 'total_pages' ] = $comments -> max_num_pages;
$results[ 'comment' ][ 'current_page' ] = $paged;
$results[ 'comment' ][ 'next_page' ] = NULL;
// If there is more than one page of results, add the page number
if ( $comments -> max_num_pages > $paged ) {
$results[ 'comment' ][ 'next_page' ] = $paged + 1;
}
}
}
// Check if user is in the sources array, if not, skip it
if ( in_array( 'user', $sources ) ) {
// Check if the logged in user can edit users. Don't want users having access to other user profiles!
if ( current_user_can( 'edit_user' ) ) {
// Fetch a list of all user roles
$user_roles = new WP_Roles();
$role_names = $user_roles -> get_names();
$role_q = $q;
// Add extra terms for administrator
if ( in_array( $role_q, array( 'admin', 'admins' ) ) ) {
$role_q = 'administrator';
}
// Setup arguments for `WP_User_Query`
$user_args = array(
'number' => admin_search_setting( 'result_count' ),
'paged' => $paged
);
// Depluralize role names (this isn't perfect), then check if q is a role, if it is, pass it to the role argument, otherwise, pass q to the search argument and add search columns
if ( isset( $role_names[ preg_replace( '/s$/', '', $role_q ) ] ) ) {
$user_args[ 'role' ] = preg_replace( '/s$/', '', $role_q );
} else {
if ( $id ) {
$user_args = array(
'search' => '*' . $id . '*',
'search_columns' => array(
'ID'
)
);
} else {
$user_args = array(
'search' => '*' . $q . '*',
'search_columns' => array(
'ID',
'user_login',
'user_nicename',
'user_email',
'display_name',
)
);
}
}
// Apply any filters to `WP_User_Query` arguments
$user_args = apply_filters( 'admin_search_users_query', $user_args, $q );
// Perform `WP_User_Query`
$users = new WP_User_Query( $user_args );
// Check if there are any users matching that criteria
if ( $users -> get_results() ) {
// There are!
// If q is a role, then change the search URL to the role URL
if ( isset( $user_args[ 'role' ] ) ) {
$results[ 'user' ][ 'post_type' ][ 'search_url' ] = add_query_arg( array(
'role' => $role_q
), get_admin_url( '', 'users.php' ) );
}
foreach ( $users -> get_results() as $user ) {
// Check if user is hidden to Admin Search
if ( get_the_author_meta( 'hide_in_admin_search', $user -> ID ) ) {
continue;
}
if ( admin_search_setting( 'display_users_as' ) == 'username' ) {
$title = $user -> data -> user_login;
} elseif ( admin_search_setting( 'display_users_as' ) == 'full_name' ) {
$title = get_user_meta( $user -> ID, 'first_name', true ) . ' ' . get_user_meta( $user -> ID, 'last_name', true );
} elseif ( admin_search_setting( 'display_users_as' ) == 'nickname' ) {
$title = get_user_meta( $user -> ID, 'nickname', true );
}
// If the highlight query setting is enabled, replace the matching content with the highlight HTML
if ( admin_search_setting( 'highlight_query' ) ) {
$title = preg_replace( '/(' . addcslashes( $q, '/' ) . ')/i', '$1', $title );
}
// Build the result array and add it to $return
$results[ 'user' ][ 'posts' ][] = array(
'id' => $user -> ID,
'edit_post_link' => get_edit_user_link( $user -> ID ),
'title' => $title,
'date' => NULL,
'status' => $role_names[ $user -> roles[ 0 ] ]
);
}
// Define source information
if ( isset( $results[ 'user' ] ) ) {
$results[ 'user' ][ 'post_type' ] = array(
'name' => 'user',
'label' => __( 'Users' ),
'search_url' => add_query_arg( array(
's' => $q
), get_admin_url( '', 'users.php' ) )
);
// Add addition information about this source to $return. A little more work is required because `WP_User_Query` doesn't return total pages
$total_pages = ceil( $users -> get_total() / admin_search_setting( 'result_count' ) );
$results[ 'user' ][ 'total_results' ] = $users -> get_total();
$results[ 'user' ][ 'total_pages' ] = $total_pages;
$results[ 'user' ][ 'current_page' ] = $paged;
$results[ 'user' ][ 'next_page' ] = NULL;
// If there is more than one page of results, add the page number
if ( $total_pages > $paged ) {
$results[ 'user' ][ 'next_page' ] = $paged + 1;
}
}
}
}
}
// Check if admin is in the sources array, if not, skip it
if ( in_array( 'admin', $sources ) ) {
// Add admin page search terms
require_once plugin_dir_path( __FILE__ ) . 'admin-results.php';
// Cycle through the admin page search terms
foreach ( $admin_results as $admin_result ) {
// Check if the logged in user has the cability to access this admin page
if ( current_user_can( $admin_result[ 'capability' ] ) ) {
// Check if q contains the admin page term
$contains_value = false;
foreach ( $admin_result[ 'q' ] as $sub_q ) {
if ( strpos( strtolower( $sub_q ), $q ) !== false ) {
$contains_value = true; break;
}
}
// It does!
if ( $contains_value ) {
$title = $admin_result[ 'title' ];
// If the highlight query setting is enabled, replace the matching content with the highlight HTML
if ( admin_search_setting( 'highlight_query' ) ) {
$title = preg_replace( '/(' . addcslashes( $q, '/' ) . ')/i', '$1', $title );
}
// Build the result array and add it to $return
$results[ 'admin' ][ 'posts' ][] = array(
'id' => '',
'edit_post_link' => $admin_result[ 'url' ],
'title' => $title,
'status' => $admin_result[ 'status' ]
);
}
}
}
// If there are results, define source information
if ( isset( $results[ 'admin' ] ) ) {
$results[ 'admin' ][ 'post_type' ] = array(
'name' => 'admin',
'label' => __( 'Admin Pages', 'admin-search' )
);
}
}
// Check if admin is in the sources array, if not, skip it
if ( in_array( 'external_sites', $sources ) ) {
// Define source information
$results[ 'external_sites' ][ 'post_type' ] = array(
'name' => 'external_sites',
'label' => __( 'Other Websites', 'admin-search' )
);
// Break the setting field up into an array and cycle through it
foreach ( explode( "\n", admin_search_setting( 'external_websites' ) ) as $external_website ) {
// Format the host name and check if a label exists for it
$external_website_title = admin_search_get_website_title( str_replace( 'www.', '', strtolower( parse_url( $external_website, PHP_URL_HOST ) ) ) );
// Build the result array and add it to $return
$results[ 'external_sites' ][ 'posts' ][] = array(
'id' => '',
'edit_post_link' => str_replace( '%q%', urlencode( $q ), $external_website ),
'title' => sprintf( __( 'View results on %s', 'admin-search' ), $external_website_title ),
'status' => NULL
);
}
}
// Apply any filters to $results
$results = apply_filters( 'admin_search_post_results', $results, $q );
// Ensure attachments are the last results returned
if ( isset( $results[ 'attachment' ] ) ) {
$attachments = $results[ 'attachment' ];
unset( $results[ 'attachment' ] );
$results[ 'attachment' ] = $attachments;
}
// Calculate and return total result count
$total_results = 0;
foreach ( $results as $key => $value ) {
$total_results += count( $value[ 'posts' ] );
}
// Display error message if there are no results
if ( $total_results > 0 ) {
return admin_search_ajax_return( __( 'Results found', 'admin-search' ), false, $results, $total_results );
} else {
return admin_search_ajax_return( __( 'No results found', 'admin-search' ), false );
}
}
add_action( 'wp_ajax_admin_search_ajax', 'admin_search_ajax' );
add_action( 'wp_ajax_nopriv_admin_search_ajax', 'admin_search_ajax' );