Search Meter Statistics page to see what your visitors are searching for on your blog. Version: 2.1 Author: Bennett McElwee Author URI: http://www.thunderguy.com/semicolon/ $Revision$ INSTRUCTIONS 1. Copy this file into the plugins directory in your WordPress installation (wp-content/plugins). 2. Log in to WordPress administration. Go to the Plugins page and Activate this plugin. To see search statistics, log in to WordPress Admin, go to the Dashboard page and click Search Meter. To control search statistics, log in to WordPress Admin, go to the Options page and click Search Meter. TEMPLATE TAGS sm_list_popular_searches() Show a list of the five most popular search terms that have produced hits at your site during the last 30 days. Readers can click the search term to repeat the search. sm_list_popular_searches('
  • Popular Searches

    ', '
  • ') Show the list as above, with the heading "popular Searches". If there have been no searches, then this tag displays nothing. This form of the tag should be used in the default WordPress theme. Put it in the sidebar.php file. sm_list_popular_searches('
  • Popular Searches

    ', '
  • ', 10) As above, but show the ten most popular searches. sm_list_recent_searches() Show a list of the five most recent searches that produced hits at your site. Readers can click the search term to repeat the search. This tag has the same options as sm_list_popular_searches(). THANKS Kaufman (http://www.terrik.com/wordpress/) for valuable coding suggestions. The many other users who have offered suggestions. Copyright (C) 2005-06 Bennett McElwee (bennett at thunderguy dotcom) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA The license is also available at http://www.gnu.org/copyleft/gpl.html */ if (!is_plugin_page()) : // Parameters (you can change these if you know what you're doing) $tguy_sm_history_size = 500; // The number of recent searches that will be saved. The table can // contain up to 100 more rows than this number $tguy_sm_allow_empty_referer = false; // Searches with an empty referer header are often bogus requests // from Google's AdSense crawler or something similar, so they are // excluded. Set this to true to record all such searches. $tguy_sm_allow_duplicate_saves = false; // It may be that the filter gets called more than once for a given // request. Search Meter ignores these duplicates. Set this to true // to record duplicates (the fact that it's a dupe will be recorded // in the details). This will mess up the stats, but could be useful // for troubleshooting. // Blacklist for specific search phrases (not regex! please rewrite) $tguy_sm_blacklist = array( 'None', 'penis', 'enlarge', 'phentermine', 'viagra', 'cialis', 'discount', 'generic', 'tramadol', 'xanax', 'levitra' ); // Minimum length of a search word $tguy_sm_min_length = 3; // Allow referrers outside this blog? (not recommended because of // referrer spam from e.g. google.com) $tguy_sm_allow_external_refs = false; // Template Tags function sm_list_popular_searches($before = '', $after = '', $count = 5) { // List the most popular searches in the last month in decreasing order of popularity. global $wpdb, $table_prefix; $count = intval($count); // This is a simpler query than the report query, and may produce // slightly different results. This query returns searches if they // have ever had any hits, even if the last search yielded no hits. // This makes for a more efficient search -- important if this // function will be used in a sidebar. $results = $wpdb->get_results( "SELECT `terms`, SUM( `count` ) AS countsum FROM `{$table_prefix}searchmeter` WHERE DATE_SUB( CURDATE( ) , INTERVAL 30 DAY ) <= `date` AND 0 < `last_hits` GROUP BY `terms` ORDER BY countsum DESC, `terms` ASC LIMIT $count"); if (count($results) > 0) { echo "$before\n\n$after\n"; } } function sm_list_recent_searches($before = '', $after = '', $count = 5) { // List the most recent successful searches. global $wpdb, $table_prefix; $count = intval($count); $results = $wpdb->get_results( "SELECT `terms`, `datetime` FROM `{$table_prefix}searchmeter_recent` WHERE 0 < `hits` ORDER BY `datetime` DESC LIMIT $count"); if (count($results)) { echo "$before\n\n$after\n"; } } // Hooks if (function_exists('register_activation_hook')) { register_activation_hook(__FILE__, 'tguy_sm_init'); } else { add_action('init', 'tguy_sm_init'); } add_filter('the_posts', 'tguy_sm_save_search', 20); // run after other plugins add_action('admin_head', 'tguy_sm_stats_css'); add_action('admin_menu', 'tguy_sm_add_admin_pages'); // Functionality function tguy_sm_init() { tguy_sm_create_summary_table(); tguy_sm_create_recent_table(); } // Keep track of how many times SM has been called for this request. // Normally we only record the first time. $tguy_sm_action_count = 0; // Check if the request is a search, and if so then save details. // This is a filter but does not change the posts. function tguy_sm_save_search(&$posts) { global $wpdb, $wp_query, $table_prefix, $tguy_sm_blacklist, $tguy_sm_min_length, $tguy_sm_history_size, $tguy_sm_allow_empty_referer, $tguy_sm_allow_duplicate_saves, $tguy_sm_action_count, $tguy_sm_allow_external_refs; ++$tguy_sm_action_count; if (is_search() && !is_paged() // not the second or subsequent page of a previuosly-counted search && !is_admin() // not using the administration console && ( ( $tguy_sm_action_count == 1 ) || ( $tguy_sm_allow_duplicate_saves ) ) && ( !empty ( $_SERVER['HTTP_REFERER'] ) || $tguy_sm_allow_empty_referer ) // proper referrer (otherwise could be search engine, cache...) && ( !empty ( $_SERVER['HTTP_REFERER'] ) && substr ( $_SERVER['HTTP_REFERER'], 0, strlen ( get_bloginfo ( 'url' ) ) ) != get_bloginfo ( 'url' ) || $tguy_sm_allow_external_refs ) ) { // Get all details of this search // search string is the raw query $search_string = trim(htmlentities2(strip_tags(urldecode($wp_query->query_vars['s'])), ENT_QUOTES)); // Block HTML requests here if ($search_string != $wp_query->query_vars['s']) { // HTML code or empty (trimmed) string found return; } if (get_magic_quotes_gpc()) { $search_string = stripslashes($search_string); } // Replace unterscore and dash with space $search_string = str_replace('_', ' ', str_replace('-', ' ', $search_string)); // Blacklisted or minimum length not reached? if (in_array($search_string, $tguy_sm_blacklist) || strlen($search_string) < $tguy_sm_min_length) { return; } // Maybe somewhere in the string? $blocked = false; $shortest = -1; foreach ($tguy_sm_blacklist as $blocker) { foreach (explode(" ", $seach_string) as $search) { // Check levenshtein, soundex and metaphone matches $lev = levenshtein($search, $blocker); $soundex = (soundex($search) == soundex($blocker)); $metaphone = (metaphone($search) == metaphone($blocker)); if ($lev == 0 || $soundex || $metaphone) { // Exact match! $blocked = true; $shortest = 0; break; } elseif ($lev <= $shortest || $shortest < 0) { // Set closest match $shortest = $lev; } } // Skip searching for more words if ($blocked) break; } // Shall we skip this because it is spam? if ($shortest === 0 || $blocked) { // abort here return; } // search terms is the words in the query $search_terms = $search_string; $search_terms = preg_replace('/[," ]+/', ' ', $search_terms); $search_terms = trim($search_terms); // This actually only returns a maximum of the number of posts per page $hit_count = count($posts); // Other useful details of the search $details = ''; $options = get_option('tguy_search_meter'); if ($options['sm_details_verbose']) { if ($tguy_sm_allow_duplicate_saves) { $details .= "Search Meter action count: $tguy_sm_action_count\n"; } foreach (array('REQUEST_URI','REQUEST_METHOD','QUERY_STRING','REMOTE_ADDR','HTTP_USER_AGENT','HTTP_REFERER') as $header) { $details .= sprintf("%s: %s\n", $header, $_SERVER[$header]); } } // Sanitise as necessary $search_string = $wpdb->escape($search_string); $search_terms = $wpdb->escape($search_terms); $details = $wpdb->escape($details); // Save the individual search to the DB $query = "INSERT INTO `{$table_prefix}searchmeter_recent` (`terms`,`datetime`,`hits`,`details`) VALUES ('$search_string',NOW(),$hit_count,'$details')"; $success = $wpdb->query($query); // If it failed, maybe the table was never created. // Try to create it and then try again. if (!$success) { if (tguy_sm_create_recent_table()) { $success = $wpdb->query($query); } } if ($success) { // Ensure table never grows larger than $tguy_sm_history_size + 100 $rowcount = $wpdb->get_var( "SELECT count(`datetime`) as rowcount FROM `{$table_prefix}searchmeter_recent`"); if (($tguy_sm_history_size + 100) < $rowcount) { // find time of ($tguy_sm_history_size)th entry $dateZero = $wpdb->get_var( "SELECT `datetime` FROM `{$table_prefix}searchmeter_recent` ORDER BY `datetime` DESC LIMIT $tguy_sm_history_size, 1"); $query = "DELETE FROM `{$table_prefix}searchmeter_recent` WHERE `datetime` < '$dateZero'"; $success = $wpdb->query($query); } } // Save search summary into the DB. Usually this will be a new query, so try to insert first $query = sprintf("INSERT INTO `{$table_prefix}searchmeter` (`terms`,`date`,`count`,`last_hits`) VALUES ('%s',CURDATE(),1,$hit_count)", $search_terms); $success = $wpdb->query($query); if (!$success) { $query = "UPDATE `{$table_prefix}searchmeter` SET `count` = `count` + 1, `last_hits` = $hit_count WHERE `terms` = '$search_terms' AND `date` = CURDATE()"; $success = $wpdb->query($query); // Table should always exist, so don't try to create again } } return $posts; } function tguy_sm_create_summary_table() { // Create the table if not already there. Return true if we had to create the table. global $wpdb, $table_prefix; $table_name = $table_prefix . "searchmeter"; if ($wpdb->get_var("show tables like '$table_name'") != $table_name) { require_once(ABSPATH . 'wp-admin/upgrade-functions.php'); dbDelta("CREATE TABLE `{$table_name}` ( `terms` VARCHAR(50) NOT NULL, `date` DATE NOT NULL, `count` INT(11) NOT NULL, `last_hits` INT(11) NOT NULL, PRIMARY KEY (`terms`,`date`) ); "); return true; } else { return false; } } function tguy_sm_create_recent_table() { // Create the table if not already there. Return true if we had to create the table. global $wpdb, $table_prefix; $table_name = $table_prefix . "searchmeter_recent"; if ($wpdb->get_var("show tables like '$table_name'") != $table_name) { require_once(ABSPATH . 'wp-admin/upgrade-functions.php'); dbDelta("CREATE TABLE `{$table_name}` ( `terms` VARCHAR(50) NOT NULL, `datetime` DATETIME NOT NULL, `hits` INT(11) NOT NULL, `details` TEXT NOT NULL, KEY `datetimeindex` (`datetime`) ); "); return true; } else { return false; } } function tguy_sm_reset_stats() { global $wpdb, $table_prefix; tguy_sm_create_recent_table(); // Delete all records $wpdb->query("DELETE FROM `{$table_prefix}searchmeter`"); $wpdb->query("DELETE FROM `{$table_prefix}searchmeter_recent`"); } function tguy_sm_add_admin_pages() { add_submenu_page('index.php', 'Search Meter Statistics', 'Search Meter', 1, __FILE__, 'tguy_sm_stats_page'); add_options_page('Search Meter', 'Search Meter', 10, __FILE__, 'tguy_sm_options_page'); } // Display information function tguy_sm_stats_css() { ?> query( "DELETE FROM `{$table_prefix}searchmeter` WHERE `date` < DATE_SUB( CURDATE() , INTERVAL 30 DAY)"); echo "\n"; ?>

    Search summary

    These tables show the most popular searches on your blog for the given time periods. Term is the text that was searched for; you can click it to see which posts contain that term. (This won't be counted as another search.) Searches is the number of times the term was searched for. Results is the number of posts that were returned from the last search for that term.

    Yesterday and today

    Last 7 days

    Last 30 days

    Unsuccessful search summary

    These tables show only the search terms for which the last search yielded no results. People are searching your blog for these terms; maybe you should give them what they want.

    Yesterday and today

    Last 7 days

    Last 30 days

    Notes

    To manage your search statistics, go to your Search Meter Options page.

    For information and updates, see the Search Meter home page. You can also offer suggestions, request new features or report problems.

    get_results( "SELECT `terms`, SUM( `count` ) AS countsum, SUBSTRING( MAX( CONCAT( `date` , ' ', `last_hits` ) ) , 12 ) AS hits FROM `{$table_prefix}searchmeter` WHERE DATE_SUB( CURDATE( ) , INTERVAL $days DAY ) <= `date` GROUP BY `terms` $hits_selector ORDER BY countsum DESC, `terms` ASC LIMIT 20"); if (count($results)) { ?>
    TermSearches Results
    terms) ?> countsum ?> hits ?>

    No searches recorded for this period.

    Recent searches

    This table shows the last searches on this blog. Term is the text that was searched for; you can click it to see which posts contain that term. (This won't be counted as another search.) Results is the number of posts that were returned from the search.

    get_results($query); if (!$results) { if (tguy_sm_create_recent_table()) { $results = $wpdb->get_results($query); } } if (count($results)) { ?>
    Date & timeTermResults Details Show details
    datetime ?> terms) ?> hits ?> ", htmlspecialchars($result->details)) ?>

    No searches recorded.

    Notes

    To manage your search statistics, go to your Search Meter Options page.

    For information and updates, see the Search Meter home page. You can also offer suggestions, request new features or report problems.

    Plugin settings saved.

    '; } else if (isset($_POST['tguy_sm_reset'])) { tguy_sm_reset_stats(); echo '

    Statistics have been reset.

    '; } $options = get_option('tguy_search_meter'); ?>

    Search Meter Options

    Reset statistics

    Click this button to reset all search statistics. This will delete all information about previuos searches.

    Notes

    To see your search statistics, go to your Search Meter Statistics page.

    For information and updates, see the Search Meter home page. At that page, you can also offer suggestions, request new features or report problems.