Changeset 2006169
- Timestamp:
- 01/04/2019 08:20:30 AM (7 years ago)
- Location:
- advanced-admin-search/trunk
- Files:
-
- 4 edited
-
README.md (modified) (3 diffs)
-
advanced-admin-search.php (modified) (2 diffs)
-
css/style.css (modified) (1 diff)
-
jquery-admin-search.js (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
advanced-admin-search/trunk/README.md
r1948389 r2006169 4 4 Tags: advance search, admin search, custom post type search 5 5 Requires at least: 3.9 6 Tested up to: 4.9.87 Stable tag: 0.9.16 Tested up to: 5.0.2 7 Stable tag: 1.0 8 8 License: GPLv2 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Advanced Admin Search adds extra searching feature into admin bar.11 Easily search everything in WordPress admin panel from one single search field. 12 12 13 13 == Description == 14 14 15 Advanced Admin Search will allow you to easily search WordPress database in admin bar.15 Advanced Admin Search provides you easy and simplest way to search anything in WordPress admin panel from one single search field. 16 16 17 == FEATURES == 17 == Why use Advanced Admin Search? == 18 19 By using this plugin you can quickly search posts, pages, users, custom post types, comments and more from one single search field on WordPress admin bar. 20 You won't have to go to each section to search any of this. It will also check for the content of every post to provide you exact results. 21 22 Example: 23 Not sure where you placed the shortcode for third party gallery or appointment plugin? 24 Simply search the shortcode in search field and it will provide you list of posts/pages or anywhere it is placed. 25 26 == Search Features == 18 27 19 28 Advanced Admin Search allows you to search: … … 25 34 5. Pages 26 35 6. Custom Post Types 36 7. Post Meta 37 8. Comments 27 38 28 39 == UPCOMING FEATURES == 29 * Post Meta Searching30 40 * Dedicated page to show more than 10 results 31 41 … … 49 59 == Changelog == 50 60 61 = 1.0 = 62 * Improved the code 63 * Bugs removed 64 * Post meta and comments search added 51 65 = 0.9.1 = 52 66 * Support for custom post types -
advanced-admin-search/trunk/advanced-admin-search.php
r1948389 r2006169 3 3 Plugin Name: Advanced Admin Search 4 4 Plugin URI: https://www.kuroit.com/product/advanced-admin-search 5 description: A WordPress plugin which adds extra searching feature into admin bar.6 Version: 0.9.15 description: Easily search everything in WordPress admin panel from one single search field. 6 Version: 1.0 7 7 Author: Kuroit 8 8 Author URI: https://www.kuroit.com … … 12 12 /* The Advanced Admin Search Plugin 13 13 * 14 * Advanced Admin Search is a wordpress plugin which adds extra searching feature into admin bar. 14 * Advanced Admin Search is a wordpress plugin which adds extra searching feature into admin bar. Easily search everything in WordPress admin panel from one single search field. 15 15 * 16 16 */ 17 namespace Kuroit\AdvancedAdminSearch; 18 19 if ( ! defined( 'ABSPATH' ) ) { 20 die(); 21 } 22 23 class AASKP_advancedAdminSearch{ 24 25 function __construct() { 17 18 namespace Kuroit\AdvancedAdminSearch; 19 20 if ( ! defined( 'ABSPATH' ) ) { 21 die(); 22 } 23 24 25 class AASKP_advancedAdminSearch{ 26 27 function __construct() { 26 28 27 29 // Hook to add input box in admin panel for searching. 28 add_action('admin_bar_menu', array( $this, 'AASKP_SearchBox'));29 add_action('in_admin_header', array( $this, 'AASKP_displayInput'));30 add_action('admin_bar_menu', array( $this, 'AASKP_SearchBox')); 31 add_action('in_admin_header', array( $this, 'AASKP_displayInput')); 30 32 31 33 // Hooks to get the searching data like admin menu, media labraries, post, pages. 32 add_action( 'admin_bar_menu', array( $this, 'AASKP_desktopSearchJavascript' ));33 add_action( 'in_admin_header', array( $this, 'AASKP_mobileSearchJavascript' ));34 add_action( 'wp_ajax_search_result', array( $this, 'AASKP_searchAction' ));34 add_action( 'admin_bar_menu', array( $this, 'AASKP_desktopSearchJavascript' )); 35 add_action( 'in_admin_header', array( $this, 'AASKP_mobileSearchJavascript' )); 36 add_action( 'wp_ajax_search_result', array( $this, 'AASKP_searchAction' )); 35 37 36 38 // Hook to add javascript. 37 add_action( 'admin_enqueue_scripts', array( $this, "AASKP_adminJavascript" ) ); 38 add_action( 'wp_enqueue_scripts', array( $this, "AASKP_adminJavascript" ) ); 39 } 40 41 function AASKP_adminJavascript() { 42 wp_enqueue_style( 'advaced_admin_search_style', plugin_dir_url( __FILE__ ) . 'css/style.css' ); 43 wp_enqueue_script('advaced_admin_search_script' , plugin_dir_url( __FILE__ ) . 'jquery-admin-search.js' ); 44 45 $params = array( 46 'ajaxurl' => admin_url('admin-ajax.php'), 47 'ajax_nonce' => wp_create_nonce('advanced_search_submit'), 48 ); 49 50 wp_register_script( 'advanced_admin_search' ); 51 wp_enqueue_script( 'advanced_admin_search' ); 52 wp_localize_script( 'advanced_admin_search', 'advanced_admin_search', $params ); 53 54 } 55 56 function AASKP_SearchBox() { 57 global $wp_admin_bar; 58 59 $wp_admin_bar->add_menu(array( 60 'id' => 'search_form', 61 'parent' => 'top-secondary', 62 'title' => '<ul class="post_search_box"> 63 <li class="advance_search_box"><span class="dashicons dashicons-search" onclick="AASKP_displayInputBox()"></span><div class="sf-d"><input name="autocomplete" type="text" placeholder="Search Database" id="post_search_box" autocomplete="off" style="height:20px;margin:5px 0;"/><label for="submit"><span class="dashicons dashicons-search" style="display:block !important;"></span></label><input type="submit" id="submit" name="search" value="Search" style="display:none;"><div class="ajax-loader"><img src="'.plugin_dir_url( __FILE__ ).'image/loading.gif" class="img-responsive" /></div><ul class="search_list"></ul></div></li> 64 </ul>' 65 )); 66 67 } 68 69 function AASKP_displayInput() { 70 echo '<div class="sf-m"><div id="search_fields" style="display:none;"><input type="text" placeholder="Search Database" id="mobile_search_fields" autocomplete="off" style="line-height:1em;"/><label for="submit"><span class="dashicons dashicons-search"></span></label><input type="submit" id="submit" name="search" value="Search" style="display:none;"></div><div class="ajax-loading"><img src="'.plugin_dir_url( __FILE__ ).'image/loading.gif" class="img-responsive" /></div><ul class="mobile_search_list"></ul></div>'; 71 } 72 73 function AASKP_desktopSearchJavascript() { 74 echo '<script type="text/javascript"> AASKP_desktopSearch(); </script>'; 75 } 76 77 function AASKP_mobileSearchJavascript() { 78 echo '<script type="text/javascript"> AASKP_mobileSearch(); </script>'; 79 } 80 81 function AASKP_searchAction() { 82 83 if (isset($_POST['post_search']) && isset($_POST['security'])) 84 { 85 $post_search = sanitize_text_field( $_POST['post_search'] ); 86 $security_check = sanitize_text_field( $_POST['security'] ); 87 88 $check = wp_create_nonce('advanced_search_submit'); 89 90 if($security_check == $check) 91 { 92 if(!empty($post_search)) 93 { 94 95 $post_types = get_post_types(array('public' => true)); 96 $post_types = array_values($post_types); 97 98 // get the register user. 39 add_action( 'admin_enqueue_scripts', array( $this, "AASKP_adminJavascript" ) ); 40 add_action( 'wp_enqueue_scripts', array( $this, "AASKP_adminJavascript" ) ); 41 } 42 43 function AASKP_adminJavascript() { 44 wp_enqueue_style( 'advaced_admin_search_style', plugin_dir_url( __FILE__ ) . 'css/style.css' ); 45 wp_register_script('advaced_admin_search_script' , plugin_dir_url( __FILE__ ) . 'jquery-admin-search.js' ); 46 47 $params = array( 48 'ajaxurl' => admin_url('admin-ajax.php'), 49 'ajax_nonce' => wp_create_nonce('advanced_search_submit'), 50 ); 51 52 //wp_register_script( 'advaced_admin_search_script', 'dummy.js' ); 53 wp_enqueue_script( 'advaced_admin_search_script' ); 54 wp_localize_script( 'advaced_admin_search_script', 'advanced_admin_search', $params ); 55 56 } 57 58 function AASKP_SearchBox() { 59 60 global $wp_admin_bar; 61 62 $wp_admin_bar->add_menu(array( 63 'id' => 'search_form', 64 'parent' => 'top-secondary', 65 'title' => '<ul class="post_search_box"> 66 <li class="advance_search_box"><span class="dashicons dashicons-search" onclick="AASKP_displayInputBox()"></span><div class="sf-d"><input name="autocomplete" type="text" placeholder="Search Database" id="post_search_box" autocomplete="off" style="height:20px;margin:5px 0;"/><label for="submit"><span class="dashicons dashicons-search" style="display:block !important;"></span></label><input type="submit" id="submit" name="search" value="Search" style="display:none;"><div class="ajax-loader" style="display: none;"><img src="'.plugin_dir_url( __FILE__ ).'image/loading.gif" class="img-responsive" /></div><ul class="search_list"></ul></div></li> 67 </ul>' 68 )); 69 70 } 71 72 function AASKP_displayInput() { 73 echo '<div class="sf-m"><div id="search_fields" style="display:none;"><input type="text" placeholder="Search Database" id="mobile_search_fields" autocomplete="off" style="line-height:1em;"/><label for="submit"><span class="dashicons dashicons-search"></span></label><input type="submit" id="submit" name="search" value="Search" style="display:none;"></div><div class="ajax-loading"><img src="'.plugin_dir_url( __FILE__ ).'image/loading.gif" class="img-responsive" /></div><ul class="mobile_search_list"></ul></div>'; 74 } 75 76 function AASKP_desktopSearchJavascript() { 77 echo '<script type="text/javascript"> AASKP_desktopSearch(); </script>'; 78 } 79 80 function AASKP_mobileSearchJavascript() { 81 echo '<script type="text/javascript"> AASKP_mobileSearch(); </script>'; 82 } 83 84 function AASKP_searchAction() { 85 86 if (isset($_POST['post_search']) && isset($_POST['security'])) 87 { 88 $post_search = sanitize_text_field( $_POST['post_search'] ); 89 $security_check = sanitize_text_field( $_POST['security'] ); 90 $check = wp_create_nonce('advanced_search_submit'); 91 if($security_check == $check) 92 { 93 if(!empty($post_search)) 94 { 95 $results = array(); // all results available for search 96 97 $post_types = get_post_types(array('public' => true)); 98 $post_types = array_values($post_types); 99 100 // get pre search results from hook 101 $pre_filtered_result = apply_filters('aaskp_pre_search', $post_search); 102 if( is_array($pre_filtered_result) ){ 103 $results = array_merge($results, $pre_filtered_result); 104 } 105 106 $results = array_merge( 107 $results, // pre search 108 $this->AASKP_getUsers($post_search), // user results 109 $this->AASKP_getPostsAndPages($post_search), // post types 110 $this->AASKP_getMedia($post_search), // attachments 111 $this->AASKP_getTaxonomies($post_search), // taxonomies 112 $this->AASKP_getPostMeta($post_search), // post meta 113 $this->AASKP_getComments($post_search) // comments 114 ); 115 116 // get post search results from hook 117 $post_filtered_result = apply_filters('aaskp_post_search', $post_search); 118 if( is_array($post_filtered_result) ){ 119 $results = array_merge($results, $post_filtered_result); 120 } 121 122 // finally print the results 123 $this->AASKP_printResults($results,$post_search); 124 125 } 126 } 127 else 128 { 129 echo "Invalid Request"; 130 } 131 } 132 else 133 { 134 echo "Refine Your Search"; 135 } 136 wp_die(); 137 } 138 139 function AASKP_printResults($results,$post_search){ 140 if(count($results)==0) 141 { 142 echo "<li class='count_result'><a class='count_post media_list' href='#'><span class='none_result' style='display:none;'>".count($results)."</span> Result not Found. Please Refine Your Search</a></li>"; 143 } 144 else{ 145 if(count($results)>10) 146 { 147 $results1=array_slice($results,0,10); 148 } 149 else{ 150 $results1=array_merge($results); 151 } 152 foreach ($results1 as $row) { 153 $image = ""; 154 if(isset($row['image'])){ 155 $image = "<img class='image_thumb' src='".$row['image']."'>"; 156 } 157 echo "<li class='search_rows'><a class='search_result' href='".$row['link']."'>" . $image . $row['title'] . "<p class='list_status'>" . $row['status'] . "</p><p class='list_type'>" . $row['info'] . "</p></a></li>"; 158 } 159 echo "<li class='count_result' onclick='javascript:alert(\'This feature is coming soon.\');'><a class='count_post media_list' href='#'>'".$post_search."' search has "; 160 echo "<span class='result-count'>".count($results)."</span>"; 161 echo " results.</a></li>"; 162 163 } 164 } 165 166 function AASKP_getUsers($post_search){ 167 $output = array(); 168 99 169 $users = get_users( array( 'search' => "*{$post_search}*", 'fields' => array( 'display_name', 'user_registered', 'id' ) ) ); 100 $countUser = 0;101 170 102 171 foreach ( $users as $user ) { 103 172 $url = admin_url( 'user-edit.php?user_id='.$user->id ); 104 173 105 174 $getUser = get_userdata( $user->id ); 106 175 $role = $getUser->roles; 107 $countUser++; 108 176 109 177 foreach ($role as $value) { 110 echo "<li class='search_rows'><a class='search_result' href='".$url."'><img class='image_thumb' src='".esc_url( get_avatar_url( $user->ID ) )."'>" . $user->display_name . "<p class='list_status'>" . $value . "</p><p class='list_type'>" . $user->user_registered . "</p></a></li>"; 111 } 112 } 113 114 // get posts and pages 115 if ($countUser < 10) 116 { 117 $postPerPage = 10 - $countUser; 118 119 $posts = get_posts( 120 array( 121 's' => $post_search, 122 'post_status' => array('publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'trash'), 123 'post_type' => 'any', 124 'posts_per_page' => $postPerPage 125 ) 178 $output[] = array( 179 'link' => $url, 180 'title' => $user->display_name, 181 'status' => $value, 182 'info' => $user->user_registered, 183 'image' => esc_url( get_avatar_url( $user->ID ) ) 184 ); 185 } 186 } 187 return $output; 188 } 189 190 function AASKP_getPostsAndPages($post_search){ 191 $output = array(); 192 $posts = get_posts( 193 array( 194 's' => $post_search, 195 'post_status' => array('publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'trash'), 196 'post_type' => 'any', 197 'posts_per_page' => -1 198 ) 199 ); 200 foreach ($posts as $post) { 201 $url = admin_url( 'post.php?post='.$post->ID.'&action=edit' ); 202 $post_type = $post->post_type; 203 204 $output[]=array( 205 'link' => $url, 206 'title' => $post->post_title, 207 'status' => $post->post_status, 208 'info' => 'Type: '.$post->post_type, 209 126 210 ); 127 211 128 $countPost = count($posts); 129 130 foreach ($posts as $post) { 131 $url = admin_url( 'post.php?post='.$post->ID.'&action=edit' ); 132 $post_type = $post->post_type; 133 134 echo "<li><a class='search_result' href='".$url."''>".$post->post_title."<p class='list_status'>". $post->post_status."</p> <p class='list_type'>Type: ".$post->post_type."</p></a></li>"; 135 } 136 } 137 138 $totalPost = $countPost + $countUser; 139 140 // get media libraries 141 if ($totalPost < 10) 142 { 143 $mediaPerPage = 10 - $totalPost; 144 145 $mediaPosts = get_posts( 146 array( 147 's' => $post_search, 148 'post_type' => 'attachment', 149 'post_status' => 'inherit', 150 'posts_per_page' => $mediaPerPage 151 ) 212 } 213 return $output; 214 } 215 216 217 function AASKP_getMedia($post_search){ 218 $output = array(); 219 $mediaPosts = get_posts( 220 array( 221 's' => $post_search, 222 'post_type' => 'attachment', 223 'post_status' => 'inherit', 224 'posts_per_page' => -1 225 ) 226 ); 227 foreach ($mediaPosts as $mediaPost) { 228 $url = admin_url( 'post.php?post='.$mediaPost->ID.'&action=edit' ); 229 $post_type = $mediaPost->post_type; 230 $image_url = wp_get_attachment_image_src($mediaPost->ID); 231 232 $output[]=array( 233 'link' => $url, 234 'title' => $mediaPost->post_title, 235 236 'info' => $mediaPost->post_date, 237 'image'=> $image_url[0] 152 238 ); 153 239 154 foreach ($mediaPosts as $mediaPost) { 155 $url = admin_url( 'post.php?post='.$mediaPost->ID.'&action=edit' ); 156 $post_type = $mediaPost->post_type; 157 $image_url = wp_get_attachment_image_src($mediaPost->ID); 158 159 echo "<li class='search_rows'><a class='search_result' href='".$url."''><img class='image_thumb' src='".$image_url[0]."'>".$mediaPost->post_title."<p class='list_type'>".$mediaPost->post_date."</p></a></li>"; 160 } 161 } 162 163 $queryPost = get_posts( 164 array( 165 's' => $post_search, 166 'post_type' => 'any', 167 'post_status' => array('publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash'), 168 'posts_per_page' => -1 169 ) 240 } 241 return $output; 242 } 243 244 245 function AASKP_getTaxonomies($post_search){ 246 $output = array(); 247 $taxonomies = get_terms( 248 array( 'search' => $post_search ) 170 249 ); 171 172 $countQueryPost = count($queryPost); 173 174 $total = $countUser + $countQueryPost; 175 if ($total == 0) 176 { 177 echo "<li class='count_result'><a class='count_post media_list' href='#'><span class='none_result' style='display:none;'>".$total."</span> Result not Found. Please Refine Your Search</a></li>"; 178 } 179 else if ($total > 10) 180 { 181 echo "<li class='count_result' onclick='javascript:alert(\'This feature is coming soon.\');'><a class='count_post media_list' href='#'>'".$post_search."' search has "; 182 echo "<span class='result-count'>".$total."</span>"; 183 echo " results.</a></li>"; 184 } 185 } 250 251 foreach ($taxonomies as $taxonomy) { 252 $url = admin_url( 'term.php?taxonomy='.$taxonomy->taxonomy.'&tag_ID='.$taxonomy->term_id.'&post_type=post&wp_http_referer=%2Fwp-admin%2Fedit-tags.php%3Ftaxonomy%3Dcategory' ); 253 $output[]=array( 254 'link' => $url, 255 'title' => $taxonomy->name, 256 'info' => 'Taxonomy: '.$taxonomy->taxonomy, 257 258 ); 259 } 260 261 return $output; 262 } 263 264 function AASKP_getPostMeta($post_search){ 265 global $wpdb; 266 $output = array(); 267 268 $postMeta = $wpdb->get_results("SELECT * FROM wp_postmeta WHERE meta_key LIKE '%".$post_search."%' OR meta_value LIKE '%".$post_search."%'"); 269 270 foreach ($postMeta as $meta) { 271 $url = admin_url( 'post.php?post='.$meta->post_id.'&action=edit' ); 272 $getPost = get_post( $meta->post_id ); 273 274 275 if (strpos($meta->meta_key,$post_search) !== false) { 276 277 $output[]=array( 278 'link' => $url, 279 'title' => $getPost->post_title, 280 'status' => 'Meta Key: '.$meta->meta_key, 281 'info' => $meta->meta_value, 282 283 ); 284 285 } 286 else 287 { 288 $output[]=array( 289 'link' => $url, 290 'title' => $getPost->post_title, 291 'info' => 'Meta Value: '.$meta->meta_value, 292 ); 293 } 294 } 295 return $output; 296 } 297 298 function AASKP_getComments($post_search){ 299 $output = array(); 300 $comments = get_comments( array( 'search' => $post_search ) ); 301 302 foreach ($comments as $comment) { 303 $url = admin_url( 'edit-comments.php' ); 304 $output[]=array( 305 'link' => $url, 306 'title' => $comment->comment_author_email, 307 308 'info' =>'Comment'. $comment->comment_content, 309 310 ); 311 312 } 313 314 return $output; 315 } 316 186 317 } 187 else 188 { 189 echo "Invalid Request"; 190 } 191 } 192 else 193 { 194 echo "Refine Your Search"; 195 } 196 wp_die(); // this is required to terminate immediately and return a proper response 197 } 198 199 } 200 new AASKP_advancedAdminSearch(); 318 319 new AASKP_advancedAdminSearch(); 320 201 321 ?> -
advanced-admin-search/trunk/css/style.css
r1948008 r2006169 239 239 input#post_search_box { 240 240 max-width: 100%; 241 text-indent: 10px;241 text-indent: 8px; 242 242 } 243 243 .dashicons-search { -
advanced-admin-search/trunk/jquery-admin-search.js
r1948008 r2006169 2 2 text = text.toLowerCase(); 3 3 4 var countMenu = 0; 4 5 jQuery("#adminmenu > li").each(function(){ 5 6 var parentMenu = jQuery(this).find('.wp-menu-name').text().toLowerCase(); … … 14 15 { 15 16 var parentMenuLink = jQuery(this).find('a').attr('href'); 16 17 17 jQuery( ".search_list" ).append("<li><a class='search_result' href="+parentMenuLink+">"+parentMenu+"<p class='list_type'>Type: admin link </p></a></li>"); 18 countMenu = countMenu+1; 18 19 } 19 20 }); 20 21 21 var currentCount = jQuery('.result-count').text(); 22 23 var countMenu = jQuery(".search_list").children().length; 22 var currentCount = parseInt(jQuery('.result-count').text()); 24 23 25 24 currentCount = currentCount + countMenu; … … 28 27 29 28 function AASKP_adminMenuMobileSearch(text) { 30 text = text.toLowerCase();31 29 text = text.toLowerCase(); 30 var countMenu = 0; 32 31 jQuery("#adminmenu > li").each(function(){ 33 32 var parentMenu = jQuery(this).find('.wp-menu-name').text().toLowerCase(); … … 42 41 { 43 42 var parentMenuLink = jQuery(this).find('a').attr('href'); 44 45 43 jQuery( ".mobile_search_list" ).append("<li><a class='search_result' href="+parentMenuLink+">"+parentMenu+"<p class='list_type'>Type: admin link </p></a></li>"); 44 countMenu = countMenu+1; 46 45 } 47 46 }); 48 47 49 var currentCount = jQuery('.result-count').text(); 50 51 var countMenu = jQuery(".mobile_search_list").children().length; 48 var currentCount = parseInt(jQuery('.result-count').text()); 52 49 53 50 currentCount = currentCount + countMenu; … … 71 68 }; 72 69 73 // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php 74 jQuery.post('/wp-admin/admin-ajax.php', data, function(response) { 70 jQuery.post(advanced_admin_search.ajaxurl, data, function(response) { 75 71 AASKP_adminMenuSearch(jQuery('#post_search_box').val()); 76 72 jQuery( ".search_list" ).append(response); … … 88 84 } 89 85 if (currentCount != '') { 90 currentCount = currentCount + countMenu;86 currentCount = parseInt(countMenu) - 1; //parseInt(currentCount) + 91 87 jQuery('.result-count').html(currentCount); 92 88 } … … 125 121 'post_search': jQuery('#mobile_search_fields').val() 126 122 }; 127 // points to admin-ajax.php 128 jQuery.post( '/wp-admin/admin-ajax.php', data, function(response) {123 124 jQuery.post(advanced_admin_search.ajaxurl, data, function(response) { 129 125 AASKP_adminMenuMobileSearch(jQuery('#mobile_search_fields').val()); 130 126 jQuery( ".mobile_search_list" ).append(response);
Note: See TracChangeset
for help on using the changeset viewer.