'widget_twitter', 'description' => __( 'Follow a Twitter Feed', 'twitter-widget-pro' ) ); $control_ops = array( 'width' => 400, 'height' => 350, 'id_base' => 'twitter' ); $name = __( 'Twitter Widget Pro', 'twitter-widget-pro' ); $this->WP_Widget('twitter', $name, $widget_ops, $control_ops); } private function _getInstanceSettings ( $instance ) { $defaultArgs = array( 'title' => '', 'errmsg' => '', 'fetchTimeOut' => '2', 'username' => '', 'hiderss' => false, 'hidereplies' => false, 'avatar' => false, 'showXavisysLink' => false, 'items' => 10, 'showts' => 60 * 60 * 24, ); return wp_parse_args( $instance, $defaultArgs ); } public function form( $instance ) { $instance = $this->_getInstanceSettings( $instance ); ?>

/>

/>

/>

/>

_getInstanceSettings( $new_instance ); // Clean up the free-form areas $instance['title'] = stripslashes($new_instance['title']); $instance['errmsg'] = stripslashes($new_instance['errmsg']); // If the current user isn't allowed to use unfiltered HTML, filter it if ( !current_user_can('unfiltered_html') ) { $instance['title'] = strip_tags($new_instance['title']); $instance['errmsg'] = strip_tags($new_instance['errmsg']); } return $instance; } public function flush_widget_cache() { wp_cache_delete('widget_twitter_widget_pro', 'widget'); } public function widget( $args, $instance ) { $instance = $this->_getInstanceSettings( $instance ); // Validate our options $instance['items'] = (int) $instance['items']; if ( $instance['items'] < 1 || 20 < $instance['items'] ) { $instance['items'] = 10; } if (!isset($instance['showts'])) { $instance['showts'] = 86400; } try { $tweets = $this->_getTweets($instance); } catch (wpTwitterWidgetException $e) { $tweets = $e; } echo $args['before_widget'] . '
'; // If "hide rss" hasn't been checked, show the linked icon if ( $instance['hiderss'] != 'true' ) { if ( file_exists(dirname(__FILE__) . '/rss.png') ) { $icon = str_replace(ABSPATH, get_option('siteurl').'/', dirname(__FILE__)) . '/rss.png'; } else { $icon = get_option('siteurl').'/wp-includes/images/rss.png'; } $feedUrl = $this->_getFeedUrl($instance, 'rss', false); $args['before_title'] .= "RSS "; } $twitterLink = 'http://twitter.com/' . $instance['username']; $args['before_title'] .= ""; $args['after_title'] = '' . $args['after_title']; if (empty($instance['title'])) { $instance['title'] = "Twitter: {$instance['username']}"; } echo $args['before_title'] . $instance['title'] . $args['after_title']; if (!is_a($tweets, 'wpTwitterWidgetException') && !empty($tweets[0]) && $instance['avatar'] == 'true') { echo '
'; echo $this->_getProfileImage($tweets[0]->user); echo '
'; } echo '
' . $args['after_widget']; } /** * Gets tweets, from cache if possible * * @param array $widgetOptions - options needed to get feeds * @return array - Array of objects */ private function _getTweets($widgetOptions) { $feedHash = sha1($this->_getFeedUrl($widgetOptions)); $tweets = get_option("wptw-{$feedHash}"); $cacheAge = get_option("wptw-{$feedHash}-time"); //If we don't have cache or it's more than 5 minutes old if ( empty($tweets) || (time() - $cacheAge) > 300 ) { try { $tweets = $this->_parseFeed($widgetOptions); update_option("wptw-{$feedHash}", $tweets); update_option("wptw-{$feedHash}-time", time()); } catch (wpTwitterWidgetException $e) { throw $e; } } return $tweets; } /** * Pulls the JSON feed from Twitter and returns an array of objects * * @param array $widgetOptions - settings needed to get feed url, etc * @return array */ private function _parseFeed($widgetOptions) { $feedUrl = $this->_getFeedUrl($widgetOptions); $resp = wp_remote_request($feedUrl, array('timeout' => $widgetOptions['fetchTimeOut'])); if ( !is_wp_error($resp) && $resp['response']['code'] >= 200 && $resp['response']['code'] < 300 ) { if (function_exists('json_decode')) { $decodedResponse = json_decode( $resp['body'] ); } else { global $wp_json; if ( !is_a($wp_json, 'Services_JSON') ) { require_once( 'class-json.php' ); $wp_json = new Services_JSON(); } $decodedResponse = $wp_json->decode( $resp['body'] ); } if ( empty($decodedResponse) ) { if (empty($widgetOptions['errmsg'])) { $widgetOptions['errmsg'] = __('Invalid Twitter Response.', 'twitter-widget-pro'); } throw new wpTwitterWidgetException($widgetOptions['errmsg']); } elseif( !empty($decodedResponse->error) ) { if (empty($widgetOptions['errmsg'])) { $widgetOptions['errmsg'] = $decodedResponse->error; } throw new wpTwitterWidgetException($widgetOptions['errmsg']); } else { return $decodedResponse; } } else { // Failed to fetch url; if (empty($widgetOptions['errmsg'])) { $widgetOptions['errmsg'] = __('Could not connect to Twitter', 'twitter-widget-pro'); } throw new wpTwitterWidgetException($widgetOptions['errmsg']); } } /** * Gets the URL for the desired feed. * * @param array $widgetOptions - settings needed such as username, feet type, etc * @param string[optional] $type - 'rss' or 'json' * @param bool[optional] $count - If true, it adds the count parameter to the URL * @return string - Twitter feed URL */ private function _getFeedUrl($widgetOptions, $type = 'json', $count = true) { if (!in_array($type, array('rss', 'json'))) { $type = 'json'; } if ( $count ) { $num = ($widgetOptions['hidereplies'])? 100:$widgetOptions['items']; $count = sprintf('?count=%u', $num); } else { $count = ''; } return sprintf('http://twitter.com/statuses/user_timeline/%1$s.%2$s%3$s', $widgetOptions['username'], $type, $count); } /** * Twitter displays all tweets that are less than 24 with something like * "about 4 hours ago" and ones older than 24 hours with a time and date. * This function allows us to simulate that functionality, but lets us * choose where the dividing line is. * * @param int $startTimestamp - The timestamp used to calculate time passed * @param int $max - Max number of seconds to conver to "ago" messages. 0 for all, -1 for none * @return string */ private function _timeSince($startTimestamp, $max) { // array of time period chunks $chunks = array( 'year' => 60 * 60 * 24 * 365, // 31,536,000 seconds 'month' => 60 * 60 * 24 * 30, // 2,592,000 seconds 'week' => 60 * 60 * 24 * 7, // 604,800 seconds 'day' => 60 * 60 * 24, // 86,400 seconds 'hour' => 60 * 60, // 3600 seconds 'minute' => 60, // 60 seconds 'second' => 1 // 1 second ); $since = time() - $startTimestamp; if ($max != '-1' && $since >= $max) { return date_i18n('h:i:s A F d, Y', $startTimestamp); } foreach ( $chunks as $key => $seconds ) { // finding the biggest chunk (if the chunk fits, break) if (($count = floor($since / $seconds)) != 0) { break; } } $messages = array( 'year' => _n('about %s year ago', 'about %s years ago', $count, 'twitter-widget-pro'), 'month' => _n('about %s month ago', 'about %s months ago', $count, 'twitter-widget-pro'), 'week' => _n('about %s week ago', 'about %s weeks ago', $count, 'twitter-widget-pro'), 'day' => _n('about %s day ago', 'about %s days ago', $count, 'twitter-widget-pro'), 'hour' => _n('about %s hour ago', 'about %s hours ago', $count, 'twitter-widget-pro'), 'minute' => _n('about %s minute ago', 'about %s minutes ago', $count, 'twitter-widget-pro'), 'second' => _n('about %s second ago', 'about %s seconds ago', $count, 'twitter-widget-pro'), ); return sprintf($messages[$key], $count); } /** * Returns the Twitter user's profile image, linked to that user's profile * * @param object $user - Twitter User * @return string - Linked image (XHTML) */ private function _getProfileImage($user) { return << {$user->name} profileImage; } } /** * wpTwitterWidget is the class that handles everything outside the widget. This * includes filters that modify tweet content for things like linked usernames. * It also helps us avoid name collisions. */ class wpTwitterWidget { /** * @var array Plugin settings */ private $_settings; /** * Plugin file to test against on plugins page * * @var string */ private $_pluginBasename; /** * Repository base url * * @since 1.4.4 * @var string */ private $_reposUrl = 'http://plugins.svn.wordpress.org/'; public function __construct() { /** * Add filters and actions */ add_filter( 'init', array( $this, 'init_locale') ); add_action( 'widgets_init', array($this, 'register') ); add_filter( 'widget_twitter_content', array($this, 'linkTwitterUsers') ); add_filter( 'widget_twitter_content', array($this, 'linkUrls') ); add_filter( 'widget_twitter_content', array($this, 'linkHashtags') ); add_filter( 'widget_twitter_content', 'convert_chars' ); add_filter( 'plugin_action_links', array($this, 'addSettingLink'), 10, 2 ); add_action ( 'in_plugin_update_message-'.plugin_basename ( __FILE__ ) , array ( $this , '_changelog' ), null, 2 ); } public function init_locale() { $lang_dir = basename(dirname(__FILE__)) . '/languages'; load_plugin_textdomain('twitter-widget-pro', 'wp-content/plugins/' . $lang_dir, $lang_dir); } public function _changelog ($pluginData, $newPluginData) { $url = "{$this->_reposUrl}/{$newPluginData->slug}/tags/{$newPluginData->new_version}/upgrade.html"; $response = wp_remote_get ( $url ); $code = (int) wp_remote_retrieve_response_code ( $response ); if ( $code == 200 ) { echo wp_remote_retrieve_body ( $response ); } } /** * Returns the user's screen name as a link inside strong tags. * * @param object $user - Twitter user * @return string - Username as link (XHTML) */ private function _getUserName($user) { return << {$user->screen_name} profileImage; } public function addSettingLink( $links, $file ){ if ( empty($this->_pluginBasename) ) { $this->_pluginBasename = plugin_basename(__FILE__); } if ( $file == $this->_pluginBasename ) { // Add settings link to our plugin $link = '' . __('Settings', 'twitter-widget-pro') . ''; array_unshift( $links, $link ); } return $links; } /** * Replace @username with a link to that twitter user * * @param string $text - Tweet text * @return string - Tweet text with @replies linked */ public function linkTwitterUsers($text) { $text = preg_replace('/(^|\s)@(\w*)/i', '$1', $text); return $text; } /** * Replace #hashtag with a link to search.twitter.com for that hashtag * * @param string $text - Tweet text * @return string - Tweet text with #hashtags linked */ public function linkHashtags($text) { $text = preg_replace_callback('/(^|\s)(#\w*)/i', array($this, '_hashtagLink'), $text); return $text; } /** * Replace #hashtag with a link to search.twitter.com for that hashtag * * @param array $matches - Tweet text * @return string - Tweet text with #hashtags linked */ private function _hashtagLink($matches) { return "{$matches[1]}"; } /** * Turn URLs into links * * @param string $text - Tweet text * @return string - Tweet text with URLs repalced with links */ public function linkUrls($text) { /** * match protocol://address/path/file.extension?some=variable&another=asf% * $1 is a possible space, this keeps us from linking href="[link]" etc * $2 is the whole URL * $3 is protocol:// * $4 is the URL without the protocol:// * $5 is the URL parameters */ $text = preg_replace("/(^|\s)(([a-zA-Z]+:\/\/)([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", "$1$2", $text); /** * match www.something.domain/path/file.extension?some=variable&another=asf% * $1 is a possible space, this keeps us from linking href="[link]" etc * $2 is the whole URL that was matched. The protocol is missing, so we assume http:// * $3 is www. * $4 is the URL matched without the www. * $5 is the URL parameters */ $text = preg_replace("/(^|\s)(www\.([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", "$1$2", $text); return $text; } function register() { register_widget('WP_Widget_Twitter_Pro'); } } // Instantiate our class $wpTwitterWidget = new wpTwitterWidget();