Example #1
0
/**
 * Pull latest tweets with some caching of raw data.
 * @param string account whose tweets we're pulling
 * @param int number of tweets to get and display
 * @param bool whether to show retweets
 * @param bool whether to show at replies
 * @return array blocks of html expected by the widget
 */
function latest_tweets_render($screen_name, $count, $rts, $ats, $pop = 0)
{
    try {
        if (!function_exists('twitter_api_get')) {
            require_once dirname(__FILE__) . '/api/twitter-api.php';
            twitter_api_load_textdomain();
        }
        // caching full data set, not just twitter api caching
        // caching is disabled by default in debug mode, but still filtered.
        $cachettl = (int) apply_filters('latest_tweets_cache_seconds', WP_DEBUG ? 0 : 300);
        if ($cachettl) {
            $arguments = func_get_args();
            $cachekey = 'latest_tweets_' . implode('_', $arguments);
            if (!function_exists('_twitter_api_cache_get')) {
                twitter_api_include('core');
            }
            if ($rendered = _twitter_api_cache_get($cachekey)) {
                return $rendered;
            }
        }
        // Check configuration before use
        if (!twitter_api_configured()) {
            throw new Exception(__('Plugin not fully configured', 'twitter-api'));
        }
        // Build API params for "statuses/user_timeline" // https://dev.twitter.com/docs/api/1.1/get/statuses/user_timeline
        $trim_user = false;
        $include_rts = !empty($rts);
        $exclude_replies = empty($ats);
        $params = compact('exclude_replies', 'include_rts', 'trim_user', 'screen_name');
        // Stripping tweets means we may get less than $count tweets.
        // we'll keep going until we get the amount we need, but may as well get more each time.
        if ($exclude_replies || !$include_rts || $pop) {
            $params['count'] = 100;
        } else {
            $params['count'] = max($count, 2);
        }
        // pull tweets until we either have enough, or there are no more
        $tweets = array();
        while ($batch = twitter_api_get('statuses/user_timeline', $params)) {
            $max_id = null;
            foreach ($batch as $tweet) {
                if (isset($params['max_id']) && $tweet['id_str'] === $params['max_id']) {
                    // previous max included in results, even though docs say it won't be
                    continue;
                }
                $max_id = $tweet['id_str'];
                if (!$include_rts && preg_match('/^(?:RT|MT)[ :\\-]*@/i', $tweet['text'])) {
                    // skipping manual RT
                    continue;
                }
                if ($pop > $tweet['retweet_count'] + $tweet['favorite_count']) {
                    // skipping tweets not deemed popular enough
                    continue;
                }
                $tweets[] = $tweet;
            }
            if (isset($tweets[$count])) {
                $tweets = array_slice($tweets, 0, $count);
                break;
            }
            if (!$max_id) {
                // infinite loop would occur if user had only tweeted once, ever.
                break;
            }
            $params['max_id'] = $max_id;
        }
        // Fix Wordpress's broken timezone implementation
        $os_timezone = date_default_timezone_get() or $os_timezone = 'UTC';
        $wp_timezone = get_option('timezone_string') or $wp_timezone = $os_timezone;
        if ($os_timezone !== $wp_timezone) {
            date_default_timezone_set($wp_timezone);
        }
        // Let theme disable or override emoji rendering
        $emoji_callback = apply_filters('latest_tweets_emoji_callback', 'twitter_api_replace_emoji_callback');
        // render each tweet as a block of html for the widget list items
        $rendered = array();
        foreach ($tweets as $tweet) {
            extract($tweet);
            $handle = $user['screen_name'] or $handle = $screen_name;
            $link = esc_html('http://twitter.com/' . $handle . '/status/' . $id_str);
            // render nice datetime, unless theme overrides with filter
            $date = apply_filters('latest_tweets_render_date', $created_at);
            if ($date === $created_at) {
                function_exists('twitter_api_relative_date') or twitter_api_include('utils');
                $time = strtotime($created_at);
                $date = esc_html(twitter_api_relative_date($time));
                $date = '<time datetime="' . date_i18n('Y-m-d H:i:sO', $time) . '">' . $date . '</time>';
            }
            // handle original retweet text as RT may be truncated
            if ($include_rts && isset($retweeted_status) && preg_match('/^RT\\s+@[a-z0-9_]{1,15}[\\s:]+/i', $text, $prefix)) {
                $text = $prefix[0] . $retweeted_status['text'];
                unset($retweeted_status);
            }
            // render and linkify tweet, unless theme overrides with filter
            $html = apply_filters('latest_tweets_render_text', $text);
            if ($html === $text) {
                if (!function_exists('twitter_api_html')) {
                    twitter_api_include('utils');
                }
                // htmlify tweet, using entities if we can
                if (isset($entities) && is_array($entities)) {
                    $html = twitter_api_html_with_entities($text, $entities);
                    unset($entities);
                } else {
                    $html = twitter_api_html($text);
                }
                // render emoji, unless filtered out
                if ($emoji_callback) {
                    $html = twitter_api_replace_emoji($html, $emoji_callback);
                }
                // strip characters that will choke mysql cache.
                if ($cachettl && !TWITTER_CACHE_APC) {
                    $html = twitter_api_strip_quadruple_bytes($html);
                }
            }
            // piece together the whole tweet, allowing override
            $final = apply_filters('latest_tweets_render_tweet', $html, $date, $link, $tweet);
            if ($final === $html) {
                // SOFI SPECIFIC EDIT - INCLUDE TWEET ICON
                $final = '<p class="tweet-text"><a href="' . $link . '" target="_blank" class="tweeticn"></a>' . $html . '</p>' . '<p class="tweet-details"><a href="' . $link . '" target="_blank">' . $date . '</a></p>';
            }
            $rendered[] = $final;
        }
        // cache rendered tweets
        if ($cachettl) {
            _twitter_api_cache_set($cachekey, $rendered, $cachettl);
        }
        // put altered timezone back
        if ($os_timezone !== $wp_timezone) {
            date_default_timezone_set($os_timezone);
        }
        return $rendered;
    } catch (Exception $Ex) {
        return array('<p class="tweet-text"><strong>Error:</strong> ' . esc_html($Ex->getMessage()) . '</p>');
    }
}
 /**
  * Call API method over HTTP and return raw data
  * @param string API method, e.g. "users/show"
  * @param array method arguments
  * @param string http request method
  * @return array unserialized data returned from twitter
  * @throws TwitterApiException
  */
 public function call($path, array $_args, $http_method)
 {
     // all calls must be authenticated in API 1.1
     if (!$this->has_auth()) {
         throw new TwitterApiException(__('Twitter client not authenticated', 'twitter-api'), 0, 401);
     }
     // transform some arguments and ensure strings
     // no further validation is performed
     $args = array();
     foreach ($_args as $key => $val) {
         if (is_string($val)) {
             $args[$key] = $val;
         } else {
             if (true === $val) {
                 $args[$key] = 'true';
             } else {
                 if (false === $val || null === $val) {
                     $args[$key] = 'false';
                 } else {
                     if (!is_scalar($val)) {
                         throw new TwitterApiException(__('Invalid Twitter parameter', 'twitter-api') . ' (' . gettype($val) . ') ' . $key . ' in ' . $path, -1);
                     } else {
                         $args[$key] = (string) $val;
                     }
                 }
             }
         }
     }
     // Fetch response from cache if possible / allowed / enabled
     if ($http_method === 'GET' && isset($this->cache_ttl)) {
         $cachekey = $this->cache_ns . $path . '_' . md5(serialize($args));
         if (preg_match('/^(\\d+)-/', $this->AccessToken->key, $reg)) {
             $cachekey .= '_' . $reg[1];
         }
         $data = _twitter_api_cache_get($cachekey);
         if (is_array($data)) {
             return $data;
         }
     }
     // @todo could validate args against endpoints here.
     // Using Wordpress WP_Http for requests, see class-http.php
     $conf = array('method' => $http_method, 'redirection' => 0);
     // build signed URL and request parameters
     $endpoint = TWITTER_API_BASE . '/' . $path . '.json';
     $params = new TwitterOAuthParams($args);
     $params->set_consumer($this->Consumer);
     $params->set_token($this->AccessToken);
     $params->sign_hmac($http_method, $endpoint);
     if ('GET' === $http_method) {
         $endpoint .= '?' . $params->serialize();
     } else {
         //$conf['headers'] = $params->oauth_header();
         $conf['body'] = $params->serialize();
     }
     $http = self::http_request($endpoint, $conf);
     $data = json_decode($http['body'], true);
     $status = $http['response']['code'];
     // remember current rate limits for this endpoint
     $this->last_call = $path;
     if (isset($http['headers']['x-rate-limit-limit'])) {
         $this->last_rate[$path] = array('limit' => (int) $http['headers']['x-rate-limit-limit'], 'remaining' => (int) $http['headers']['x-rate-limit-remaining'], 'reset' => (int) $http['headers']['x-rate-limit-reset']);
     }
     // unserializable array assumed to be serious error
     if (!is_array($data)) {
         $err = array('message' => '', 'code' => -1);
         TwitterApiException::chuck($err, $status);
     }
     // else could be well-formed error
     if (isset($data['errors'])) {
         while ($err = array_shift($data['errors'])) {
             $err['message'] = __($err['message'], 'twitter-api');
             if ($data['errors']) {
                 $message = sprintf(__('Twitter error #%d', 'twitter-api'), $err['code']) . ' "' . $err['message'] . '"';
                 trigger_error($message, E_USER_WARNING);
             } else {
                 TwitterApiException::chuck($err, $status);
             }
         }
     }
     // some errors appear to use a single key and have no code
     // e.g. not authorized to view specific content.
     if (isset($data['error'])) {
         $code = isset($data['code']) ? $data['code'] : $status;
         $message = sprintf(__('Twitter error #%d', 'twitter-api'), $code) . ' "' . $data['error'] . '"';
         TwitterApiException::chuck(compact('message', 'code'), $status);
     }
     if (isset($cachekey)) {
         _twitter_api_cache_set($cachekey, $data, $this->cache_ttl);
     }
     return $data;
 }