table_hits = $wpdb->blogs . "_zap_hits"; /* * Time related details */ $this->timezone = get_settings('gmt_offset') * 3600; $this->current_time = strtotime(gmdate('Y-m-d g:i:s a'))+$this->timezone; } /****************************************************************** * Method: maybe_create_table() * Purpose: This function comes from mu's admin functions, but * I couldn't find a way to reliably ensure that it was * available, so I've included it here. What it basically * does is check for the exsitance of a table, and if the * table doesn't exist then it creates it using the create * query string passed in. *****************************************************************/ function maybe_create_table($table_name, $create_ddl) { //echo ""; global $wpdb; foreach ($wpdb->get_col("SHOW TABLES",0) as $table ) { if ($table == $table_name) { //echo ""; return true; } } //didn't find it try to create it. $q = $wpdb->query($create_ddl); // we cannot directly tell that whether this succeeded! foreach ($wpdb->get_col("SHOW TABLES",0) as $table ) { if ($table == $table_name) { //echo ""; return true; } } //echo ""; return false; } /****************************************************************** * Method: setup() * Purpose: creates our tracking table if needed. *****************************************************************/ function setup() { //echo ""; $table_hits_query = "CREATE TABLE $this->table_hits ( id int(11) unsigned NOT NULL auto_increment, blog_id BIGINT(20) unsigned NOT NULL, post_id BIGINT(20) unsigned, hit_time int(10) unsigned NOT NULL default '0', UNIQUE KEY id (id) )"; $this->maybe_create_table($this->table_hits, $table_hits_query); } /****************************************************************** * Method: recordhit() * Purpose: records a hit to a page/post *****************************************************************/ function recordhit() { global $wpdb; global $blog_id; $post_id = 0; /* in case of non-single post hits, we store a 0 for the post_id */ $hit_time = $this->current_time; /* What time is it? */ /* * Things we don't track: admin hits, 404's, previews, and login. */ if ( is_admin() || is_404() || is_preview() || strstr($_SERVER['PHP_SELF'], 'wp-login.php') ) return; /* * If this is a "single" post page, then we record it as a post hit * otherwise we just record it as a blog hit. */ if(is_single()) { $post_id = get_the_ID(); } /* * We are inserting these hits even in non-single post cases, because * we may eventually implement support for "top blogs" based on * hits to the blog. Right now, we only support accessor functions for * top_posts. */ $query = "INSERT INTO $this->table_hits (blog_id,post_id,hit_time) VALUES ('$blog_id','$post_id',$hit_time)"; $wpdb->query($query); } /********************************************************************************** * * Method : get_top_posts() * Purpose : * Similar to WP's standard get_posts() function, it is used to return a list * of posts, that qualify as "top posts". * Parameters: * max_per_blog * (integer) Number of posts allowed in list per blog. * * - Default to -1 which means no maximum. * * - Currently only one other value is allowed which is 1. * If you set this parameter to 1 then you'll only get * the top post per blog in the list. * * days_back * (integer) Number of days back to allow stats from. * * - Default to -1 which means allow all hits. * * - If for example you want the top posts from the last week * you could call this with days_back = 7; * * numberposts * (integer) Number of posts to return. Defaults to 5. * * offset * (integer) Offset from the "top most" post. Defaults to 0. * * orderby * ("string") Sort Posts by one of various values, including: * 'post_hits' - Sort by number of hits to the post (Default). * 'post_id' - Sort by numeric Post ID. * 'blog_id' - Sort by numeric Blog ID. * * order * (string) Sort order for options. Valid values: * 'DESC' - Sort from highest to lowest (Default). * 'ASC' - Sort from lowest to highest. * * Note: Unlike get_posts(), the default order is DESC so that the "top_posts" * has a "top" behavior where the post with the "most hits" is listed first. * * include/exclude * same behavior as get_posts() * */ function get_top_posts($args = '') { global $wpdb, $wpmuBaseTablePrefix; /* * Same behavior as get_posts(), this function accepts an array of args or * a URL encoded list of args. */ if ( is_array($args) ) $r = &$args; else parse_str($args, $r); /* * default vaules array here. * * Note: This is different from get_posts() defaults as noted in the comment * of this function. */ $defaults = array('max_per_blog' => -1, 'days_back' => -1, 'numberposts' => 5, 'offset' => 0, 'category' => '', 'orderby' => 'post_hits', 'order' => 'DESC', 'include' => '', 'exclude' => '', 'meta_key' => '', 'meta_value' =>''); $r = array_merge($defaults, $r); extract($r); /* * Ok, like get_posts() we will attempt to support include='' and exclude='' */ $inclusions = ''; if ( !empty($include) ) { $offset = 0; //ignore offset, category, exclude, meta_key, and meta_value params if using include $category = ''; $exclude = ''; $meta_key = ''; $meta_value = ''; $incposts = preg_split('/[\s,]+/',$include); $numberposts = count($incposts); // only the number of posts included if ( count($incposts) ) { foreach ( $incposts as $incpost ) { if (empty($inclusions)) $inclusions = ' AND ( ID = ' . intval($incpost) . ' '; else $inclusions .= ' OR ID = ' . intval($incpost) . ' '; } } } if (!empty($inclusions)) $inclusions .= ')'; /* * Like get_posts() we support exclude='' */ $exclusions = ''; if ( !empty($exclude) ) { $exposts = preg_split('/[\s,]+/',$exclude); if ( count($exposts) ) { foreach ( $exposts as $expost ) { if (empty($exclusions)) $exclusions = ' AND ( ID <> ' . intval($expost) . ' '; else $exclusions .= ' AND ID <> ' . intval($expost) . ' '; } } } if (!empty($exclusions)) $exclusions .= ')'; if ($days_back != -1) $days_back_where = "AND TO_DAYS(NOW()) - TO_DAYS(FROM_UNIXTIME(hit_time)) <= $days_back"; else $days_back_where = ""; /* * Here's where we start to siginficantly diverge from get_posts(), * namely we are reading from a very different table, and so we * have to construct a different query. * */ $query = "SELECT blog_id, post_id, COUNT(post_id) AS 'post_hits' FROM $this->table_hits WHERE post_id <> '0' $exclusions $inclusions $days_back_where GROUP BY blog_id, post_id ORDER BY $orderby $order LIMIT $offset , $numberposts "; /* * If the caller asked us to limit our posts per blog to 1 then * we actually use the above query as a subquery. Sneaky eh? */ if ($max_per_blog == 1) { $query = "SELECT blog_id,post_id,MAX(post_hits) as 'post_hits' FROM ($query) as top_posts GROUP BY blog_id ORDER BY $orderby $order LIMIT $offset , $numberposts "; } /************************************************************************** From: get_posts() left here as a reference for later support of more features. $query ="SELECT DISTINCT * FROM $wpdb->posts " ; $query .= ( empty( $category ) ? "" : ", $wpdb->post2cat " ) ; $query .= ( empty( $meta_key ) ? "" : ", $wpdb->postmeta " ) ; $query .= " WHERE (post_type = 'post' AND post_status = 'publish') $exclusions $inclusions " ; $query .= ( empty( $category ) ? "" : "AND ($wpdb->posts.ID = $wpdb->post2cat.post_id AND $wpdb->post2cat.category_id = " . $category. ") " ) ; $query .= ( empty( $meta_key ) | empty($meta_value) ? "" : " AND ($wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = '$meta_key' AND $wpdb->postmeta.meta_value = '$meta_value' )" ) ; $query .= " GROUP BY $wpdb->posts.ID ORDER BY " . $orderby . " " . $order . " LIMIT " . $offset . ',' . $numberposts ; **************************************************************************/ /* * Note: this doesn't actually contain a nice "posts" style array. * It only contains an array of blog_id, post_ids.Next we need to * get the actaual table details from the prefix_{blog_id}_posts tables. */ $top_posts_mappings = $wpdb->get_results($query); /* * Start with a fresh array, fill it in below. */ $posts = array(); foreach($top_posts_mappings as $post_mapping) { $table_blog_posts = $wpmuBaseTablePrefix.$post_mapping->blog_id."_posts"; $query ="SELECT *,'$post_mapping->post_hits' AS 'post_hits','$post_mapping->blog_id' AS 'blog_id' FROM $table_blog_posts WHERE ID = $post_mapping->post_id"; $results = $wpdb->get_results($query); if ( !empty($results) ) { /* sleazy - should really be certain this only returned 1 row... */ $single_post = $results[0]; $posts[] = $single_post; } } /* * We used to call update_post_caches() but it turns out that was a bad idea because it would * mix in posts from other blogs into our post cache. So now we've eliminated this caching. * However, it might be a good idea in the furture to implement a cache of the top posts here. */ //update_post_caches($posts); return $posts; } /* * format some HTML for a top posts list. */ function get_top_posts_html($args = '') { global $post; /* needs to be global if you want get_permalink() and get_the_title() to work. */ $top_posts = $this->get_top_posts($args); if ( is_array($args) ) $r = &$args; else parse_str($args, $r); /* * default vaules array here. * * Note: This is different from get_posts() defaults as noted in the comment * of this function. */ $defaults = array( 'before_list' => '