activity = ( empty( $wpdb->base_prefix ) ? $wpdb->prefix : $wpdb->base_prefix ) .'bsocial_activity'; $this->urlmap = ( empty( $wpdb->base_prefix ) ? $wpdb->prefix : $wpdb->base_prefix ) .'bsocial_urlmap'; $this->urlinfo = ( empty( $wpdb->base_prefix ) ? $wpdb->prefix : $wpdb->base_prefix ) .'bsocial_urlinfo'; $this->terms = ( empty( $wpdb->base_prefix ) ? $wpdb->prefix : $wpdb->base_prefix ) .'bsocial_terms'; $this->users = ( empty( $wpdb->base_prefix ) ? $wpdb->prefix : $wpdb->base_prefix ) .'bsocial_users'; // $this->pop = ( empty( $wpdb->base_prefix ) ? $wpdb->prefix : $wpdb->base_prefix ) .'bsocial_pop'; // get a wpdb object to work with, can be the default or a custom instance $this->get_db(); // create the tables // $this->createtables(); $this->type_array = array( 0 => 'url_local', 1 => 'url', 2 => 'url_clean', 3 => 'domain', ); } function get_db() { if( ! empty( $this->db )) return $this->db; global $wpdb; if( defined( 'BSUITE_DB_NAME' )) $this->db = new wpdb( BSUITE_DB_USER, BSUITE_DB_PASSWORD, BSUITE_DB_NAME, BSUITE_DB_HOST ); else $this->db = $wpdb; return $this->db; } function get_type( $type ) { return array_search( $type , $this->type_array ); } function get_term( $id ) { if ( !$name = wp_cache_get( $id, 'bsocial_terms' )) { $name = $this->db->get_var( "SELECT name FROM $this->terms WHERE ". $this->db->prepare( "term_id = %s", (int) $id )); wp_cache_add( $id, $name, 'bsocial_terms', 0 ); } return $name; } function is_term( $term ) { $cache_key = md5( substr( $term, 0, 255 ) ); if ( !$term_id = wp_cache_get( $cache_key, 'bsocial_termids' )) { $term_id = (int) $this->db->get_var( "SELECT term_id FROM $this->terms WHERE ". $this->db->prepare( "name = %s", substr( $term, 0, 255 ))); wp_cache_add( $cache_key, $term_id, 'bsocial_termids', 0 ); } return $term_id; } function insert_term( $term ) { if ( !$term_id = $this->is_term( $term )) { if ( false === $this->db->insert( $this->terms, array( 'name' => $term ))) { new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $this->db->last_error ); return( 1 ); } $term_id = (int) $this->db->insert_id; } return $term_id; } function get_user( $user_id ) { if ( ! $user_name = wp_cache_get( $user_id, 'bsocial_users' )) { $user_name = $this->db->get_var( "SELECT name FROM $this->users WHERE ". $this->db->prepare( "user_id = %s", (int) $user_id )); wp_cache_add( $user_id, $user_name, 'bsocial_users', 0 ); } return $user_name; } function is_user( $user_name ) { $cache_key = md5( substr( $user_name, 0, 128 ) ); if ( ! $user_id = wp_cache_get( $cache_key, 'bsocial_userids' )) { $user_id = (int) $this->db->get_var( "SELECT user_id FROM $this->users WHERE ". $this->db->prepare( "user_name = %s", substr( $user_name, 0, 128 ))); wp_cache_add( $cache_key, $user_id, 'bsocial_userids', 0 ); } return $user_id; } function insert_user( $user_name ) { if ( ! $user_id = $this->is_user( $user_name )) { if ( FALSE === $this->db->insert( $this->users, array( 'user_name' => $user_name ))) { new WP_Error('db_insert_error', __('Could not insert user into the database'), $this->db->last_error); return; } $user_id = (int) $this->db->insert_id; } return $user_id; } function get_urlinfo( $url_or_id ) { // validate the input as either an object ID or URL if( is_numeric( $url_or_id ) && $this->get_term( $url_or_id )) $object_id = (int) $url_or_id; else if ( ! $object_id = $this->is_term( $url_or_id )) return FALSE; if ( ! $info = wp_cache_get( $object_id, 'bsocial_urlinfo' )) { $info = $this->db->get_results( "SELECT * FROM $this->urlinfo WHERE ". $this->db->prepare( "object_id = %s", (int) $object_id )); wp_cache_set( $object_id, $info, 'bsocial_urlinfo', 0 ); } return $info; } function insert_urlinfo( $url_or_id ) { if( ! $info = $this->get_urlinfo( $url_or_id )) { // validate the input as either an object ID or URL if( is_numeric( $url_or_id ) && ( $object_id = $url_or_id ) && ( ! $url = $this->get_term( $url_or_id ))) return FALSE; else if( ! is_numeric( $url_or_id ) && ( $object_id = $this->insert_term( $url_or_id ))) $url = $url_or_id; // scrape the data from the page if( ! $info = $this->scrape_url( $url )) return FALSE; // insert the data if( false === $this->db->insert( $this->urlinfo , array( 'object_id' => $object_id, 'url_date' => $info->url_date, 'title' => $info->title, 'description' => $info->description, 'author_name' => $info->author_name, 'author_url' => $info->author_url, 'image_url' => $info->image_url, ))) { $error = new WP_Error( 'db_insert_error', __( 'Could not insert urlinfo into the database' ), $this->db->last_error ); return $error; } } return $info; } function lazy_insert_urlinfo( $url_or_id ) { // validate the input as either an object ID or URL if( is_numeric( $url_or_id ) && $this->get_term( $url_or_id )) $object_id = (int) $url_or_id; else if ( ! $object_id = $this->insert_term( $url_or_id )) return FALSE; // hand the fetch off to a external process via HTTP request wp_remote_post( site_url( '/wp-admin/admin-ajax.php?action=bsocial_urlinfo&object_id='. $object_id ) , array( 'timeout' => 5, 'body' => array( 'action' => 'bsocial_urlinfo', 'screenname' => $object_id, ), )); } function scrape_url( $url ) { $temp_results = wp_remote_get( $url ); if ( is_wp_error( $temp_results )) return $temp_results; $html = wp_remote_retrieve_body( $temp_results ); preg_match_all( '# $v ) { switch( $v ) { case 'title': $data['title'] = $matches[2][ $k ]; break; case 'description': $data['description'] = $matches[2][ $k ]; break; case 'image': $data['image_url'] = $matches[2][ $k ]; break; } } preg_match_all( "#]*>([^<]*)#' , $html , $matches ); $data['author_name'] = $matches[2][0]; $data['author_url'] = $matches[1][0]; return (object) $data; } function user_history( $user_name ) { $user_id = $this->is_user( $user_name ); if( ! (int) $user_id ) return FALSE; $query = " SELECT i.* , t.name AS url , `date` FROM ( SELECT s.object_id , s.urlmap_date AS `date` FROM ( SELECT object_id, urlmap_date FROM wp_bsocial_urlmap WHERE user_id = $user_id AND object_type IN (0) LIMIT 15 ) s ORDER BY s.urlmap_date DESC ) h JOIN $this->terms t ON t.term_id = h.object_id LEFT JOIN $this->urlinfo i ON i.object_id = h.object_id "; $object_ids = $this->db->get_results( $query ); if( empty( $object_ids )) return FALSE; return apply_filters( 'bsocial_link_info' , $object_ids ); } function get_related_by_user( $user_name ) { $user_id = $this->is_user( $user_name ); if( ! (int) $user_id ) return FALSE; $old_popular = $this->get_old_popular(); $the_date = date( 'Y-m-d' , strtotime( '-11 days' )); $query = "SELECT object_id FROM wp_bsocial_urlmap WHERE user_id = $user_id AND object_type IN (0,1,2)"; $object_ids = $this->db->get_col( $query ); $query = " SELECT i.* , t.name AS url , users , hits FROM ( SELECT ha.object_id , MIN(urlmap_date) AS the_date , GROUP_CONCAT( u.user_name ) as users , COUNT(*) AS hits FROM ( SELECT user_id FROM $this->urlmap WHERE object_id IN ( ". implode( ',' , $object_ids ) ." ) GROUP BY user_id DESC LIMIT 250 ) s JOIN $this->urlmap ha ON ha.user_id = s.user_id JOIN $this->users u ON u.user_id = ha.user_id WHERE 1=1 AND ha.object_id NOT IN ( ". implode( ',' , array_merge( (array) $object_ids , (array) array_slice( $old_popular , 0 , 3 ))) ." ) AND ha.object_type = 0 GROUP BY ha.object_id ORDER BY the_date DESC LIMIT 0,50 ) h JOIN $this->terms t ON t.term_id = h.object_id LEFT JOIN $this->urlinfo i ON i.object_id = h.object_id ORDER BY hits DESC , url_date DESC "; $urls = $this->db->get_results( $query ); return apply_filters( 'bsocial_link_info' , $urls ); } function get_related( $objects , $ignore = FALSE ) { foreach( (array) $objects as $object ) { // get the ID for the unmolested URL $object_ids[] = $this->insert_term( $object ); // parse the URL and get IDs for various components of it $url = parse_url( $object ); $url['host'] = preg_replace( '/www[^\.]*\./i', '', $url['host'] ); // remove www and similar components from the hostname $object_ids[] = $this->insert_term( $url['scheme'] .'://'. $url['host'] . ( isset( $url['port'] ) ? ':'. $url['port'] : '' ) . ( isset( $url['path'] ) ? $url['path'] : '/' )); // $object_ids[] = $this->insert_term( $url['host'] ); } $object_ids = array_unique( $object_ids ); if( $ignore ) $ignore_sql = 'AND h.object_id NOT IN( '. implode( ',' , $object_ids ) .' )'; else $ignore_sql = ''; $query = " SELECT i.* , t.name AS url , users , hits FROM ( SELECT ha.object_id , MIN(urlmap_date) AS the_date , GROUP_CONCAT( u.user_name ) as users , COUNT(*) AS hits FROM ( SELECT user_id FROM $this->urlmap WHERE object_id IN ( ". implode( ',' , $object_ids ) ." ) GROUP BY user_id DESC LIMIT 250 ) s JOIN $this->urlmap ha ON ha.user_id = s.user_id JOIN $this->users u ON u.user_id = ha.user_id WHERE 1=1 $ignore_sql AND ha.object_type = 0 GROUP BY ha.object_id ORDER BY the_date DESC LIMIT 0,50 ) h JOIN $this->terms t ON t.term_id = h.object_id LEFT JOIN $this->urlinfo i ON i.object_id = h.object_id ORDER BY hits DESC , url_date DESC "; $urls = $this->db->get_results( $query ); return apply_filters( 'bsocial_link_info' , $urls ); } function get_popular() { if ( $urls = wp_cache_get( 'popular', 'bsocial_analytics' )) return $urls; $the_date = date( 'Y-m-d' , strtotime( '-2 days' )); $query = " SELECT i.* , t.name AS url , users , hits FROM ( SELECT ha.object_id , MIN(urlmap_date) AS the_date , GROUP_CONCAT( u.user_name ) as users , COUNT(*) AS hits FROM $this->urlmap ha JOIN $this->users u ON u.user_id = ha.user_id WHERE 1=1 AND urlmap_date >= '$the_date' AND ha.object_type = 0 GROUP BY ha.object_id ORDER BY the_date DESC LIMIT 0,50 ) h JOIN $this->terms t ON t.term_id = h.object_id LEFT JOIN $this->urlinfo i ON i.object_id = h.object_id ORDER BY hits DESC , url_date DESC "; $urls = $this->db->get_results( $query ); $urls = apply_filters( 'bsocial_link_info' , $urls ); wp_cache_add( 'popular' , $urls , 'bsocial_analytics' , 600 ); return $urls; } function get_old_popular() { $the_date = date( 'Y-m-d' , strtotime( '-3 days' )); $query = " SELECT ha.object_id , COUNT(*) AS hits FROM $this->urlmap ha WHERE 1=1 AND urlmap_date <= '$the_date' AND ha.object_type = 0 GROUP BY ha.object_id ORDER BY hits DESC LIMIT 0,25 "; $ids = $this->db->get_col( $query ); return $ids; } function map_insert( $user_name , $date , $object ) { $user_id = $this->insert_user( $user_name ); $action_date = date( 'Y-m-d' , strtotime( $date )); // parse the URL and get IDs for various components of it $url = parse_url( $object ); if( ! isset( $url['host'] )) return FALSE; $url['host'] = preg_replace( '/www[^\.]*\./i', '', $url['host'] ); // remove www and similar components from the hostname $clean_url = $url['scheme'] .'://'. $url['host'] . ( isset( $url['port'] ) ? ':'. $url['port'] : '' ) . ( isset( $url['path'] ) ? $url['path'] : '/' ); $clean_object_id = $this->insert_term( $clean_url ); $domain_object_id = $this->insert_term( $url['host'] ); // get the ID for the unmolested URL $object_id = $this->insert_term( $object ); if( empty( $user_id ) || empty( $action_date ) || empty( $object_id )) return FALSE; // insert the domain relationship $this->_map_insert( $user_id , $action_date , $domain_object_id , $this->get_type( 'domain' ) ); // insert the cleaned URL if( $clean_object_id != $object_id ) // only insert the clean url if it differs from the regular URL $this->_map_insert( $user_id , $action_date , $clean_object_id , $this->get_type( 'url_clean' ) ); // insert the URL relationship $local_domain = apply_filters( 'bsocial_local_domain' , parse_url( site_url() , PHP_URL_HOST )); if( preg_match( '/'. preg_quote( $local_domain , '/' ) .'/' , $url['host'] )) { return $this->_map_insert( $user_id , $action_date , $clean_object_id , $this->get_type( 'url_local' ) ); // $this->lazy_insert_urlinfo( $clean_object_id ); $this->insert_urlinfo( $clean_object_id ); } else { return $this->_map_insert( $user_id , $action_date , $object_id , $this->get_type( 'url' ) ); } } function _map_insert( $user_id , $action_date , $object_id , $object_type ) { if( FALSE === $this->db->insert( $this->urlmap, array( 'user_id' => $user_id, 'urlmap_date' => $action_date, 'object_id' => $object_id, 'object_type' => $object_type, ))) { $error = new WP_Error( 'db_insert_error' , __('Could not insert map item into the database') , $this->db->last_error ); return $error; } $map_id = (int) $this->db->insert_id; return $map_id; } function createtables() { global $wpdb; $charset_collate = ''; if ( version_compare(mysql_get_server_info(), '4.1.0', '>=') ) { if ( ! empty($wpdb->charset) ) $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset"; if ( ! empty($wpdb->collate) ) $charset_collate .= " COLLATE $wpdb->collate"; } $old_wpdb = $wpdb; $wpdb = $this->db; require_once( ABSPATH . 'wp-admin/upgrade-functions.php' ); dbDelta(" CREATE TABLE $this->urlmap ( urlmap_id BIGINT NOT NULL AUTO_INCREMENT , user_id BIGINT NULL , urlmap_date DATE NULL , object_id BIGINT NULL , object_type BIGINT NULL , PRIMARY KEY (urlmap_id), UNIQUE INDEX the_unique (user_id ASC, urlmap_date ASC, object_type ASC, object_id ASC), KEY user_id (user_id ASC), KEY object_id (object_id ASC) ) ENGINE=MyISAM $charset_collate "); dbDelta(" CREATE TABLE $this->urlinfo ( object_id BIGINT NOT NULL , url_date DATE NULL , title text NULL , description text NULL , author_name text NULL , author_url text NULL , image_url text NULL , PRIMARY KEY (object_id) ) ENGINE=MyISAM $charset_collate "); dbDelta(" CREATE TABLE $this->terms ( term_id bigint(20) NOT NULL auto_increment, name varchar(255) NOT NULL default '', status varchar(40) NOT NULL, PRIMARY KEY (term_id), UNIQUE KEY name_uniq (name), KEY name (name(8)), KEY status (status(1)) ) ENGINE=MyISAM $charset_collate "); dbDelta(" CREATE TABLE $this->users ( user_id bigint(20) NOT NULL auto_increment, user_name varchar(128) NOT NULL default '', PRIMARY KEY (user_id), UNIQUE KEY name_uniq (user_name), KEY user_name (user_name(3)) ) ENGINE=MyISAM $charset_collate "); $wpdb = $old_wpdb; } } // a function to return an instance of the bSuite_Social_Analytics class function bsuite_sa() { global $bsa; if( ! is_object( $bsa )) $bsa = new bSuite_Social_Analytics; return $bsa; } // map tweets matching a search query, queue the individual tweeters for more thorough mapping function bsuite_sa_map_tweets_from_search( $search ) { if( empty( $search )) return FALSE; if( strlen( get_option( 'bsa-search-'. $search ))) $most_recent_tweet = get_option( 'bsa-search-'. $search ); else add_option( 'bsa-search-'. $search , '' , '' , 'no' ); // add an empty option with the autoload disabled $bsa = bsuite_sa(); $twitter_search = new Twitter_Search; $twitter_search->get_user_info = FALSE; // suppress user lookups to avoid rate limits $twitter_search->search( array( 'q' => urlencode( $search ) , 'rpp' => 100 , 'since_id' => $most_recent_tweet , )); foreach( (array) $twitter_search->tweets() as $tweet ) { // exit on api failure if( isset( $tweet->error )) return FALSE; // map any urls that exist in the tweet if( ! empty( $tweet->entities->urls )) { foreach( (array) $tweet->entities->urls as $url ) { // allow some time for each URL (follow_url() can be time-consuming) set_time_limit( 30 ); if( ! empty( $url->url )) { $resolved_url = follow_url( $url->url ); if( ! empty( $resolved_url )) $bsa->map_insert( $tweet->from_user_id_str .'@twitter.id' , $tweet->created_at , $resolved_url ); } } } // allow some more time to queue the user search set_time_limit( 90 ); // queue the user who tweeted this for a more thorough index // fork an external process via HTTP request if( empty( $done[ $tweet->from_user ] )) wp_remote_post( site_url( '/wp-admin/admin-ajax.php?action=bsocial_map_twitter_user&screenname='. urlencode( $tweet->from_user )) , array( 'timeout' => 5, 'body' => array( 'action' => 'bsocial_map_twitter_user', 'screenname' => urlencode( $tweet->from_user ), ), )); // create a temporary log so that we don't re-requeue users who we've already queued within this cycle $done[ $tweet->from_user ] = TRUE; } // update the option for the most recent tweet update_option( 'bsa-search-'. $search , $twitter_search->api_response->max_id_str ); return $twitter_search->api_response->max_id_str; } // map the most recent 200 tweets from a user (or most recent since we last checked) function bsuite_sa_map_tweets_from_user( $screenname ) { if( empty( $screenname )) return FALSE; if( strlen( get_option( 'bsa-stream-'. $screenname ))) $most_recent_tweet = get_option( 'bsa-stream-'. $screenname ); else add_option( 'bsa-stream-'. $screenname , '' , '' , 'no' ); // add an empty option with the autoload disabled $bsa = bsuite_sa(); $twitter_feed = new Twitter_User_Stream; $twitter_feed->stream( array( 'screen_name' => urlencode( $screenname ) , 'count' => 200 , 'since_id' => $most_recent_tweet , )); foreach( (array) $twitter_feed->tweets() as $tweet ) { // exit on api failure if( isset( $tweet->error )) return FALSE; // allow some time for each URL (follow_url() can be time-consuming) set_time_limit( 30 ); // iterate over each url in the tweet, if any if( ! empty( $tweet->entities->urls )) { foreach( (array) $tweet->entities->urls as $url ) { if( ! empty( $url->url )) { $resolved_url = follow_url( $url->url ); if( ! empty( $resolved_url )) $bsa->map_insert( $tweet->user->id_str .'@twitter.id' , $tweet->created_at , $resolved_url ); } } } } // update the option for the most recent tweet update_option( 'bsa-stream-'. $screenname , $twitter_feed->max_id_str ); return $twitter_feed->max_id_str; } add_action( 'wp_ajax_bsocial_map_twitter_user' , 'bsuite_sa_map_twitter_user' ); add_action( 'wp_ajax_nopriv_bsocial_map_twitter_user' , 'bsuite_sa_map_twitter_user' ); function bsuite_sa_map_twitter_user() { ignore_user_abort( TRUE ); nocache_headers(); bsuite_sa_map_tweets_from_user( $_REQUEST['screenname'] ); die; } add_action( 'wp_ajax_bsocial_urlinfo' , 'bsuite_sa_insert_urlinfo' ); add_action( 'wp_ajax_nopriv_bsocial_urlinfo' , 'bsuite_sa_insert_urlinfo' ); function bsuite_sa_insert_urlinfo() { $bsa = bsuite_sa(); if( empty( $_REQUEST['object_id'] ) || ! $bsa->get_term( $_REQUEST['object_id'] )) die; ignore_user_abort( TRUE ); nocache_headers(); $bsa->insert_urlinfo( $_REQUEST['object_id'] ); die; }