'widget_twitter', 'description' => __( 'Follow a Twitter Feed', $wpTwitterWidget->get_slug() ) ); $control_ops = array( 'width' => 400, 'height' => 350, 'id_base' => 'twitter' ); $name = __( 'Twitter Widget Pro', $wpTwitterWidget->get_slug() ); $this->WP_Widget('twitter', $name, $widget_ops, $control_ops); } private function _getInstanceSettings ( $instance ) { $wpTwitterWidget = wpTwitterWidget::getInstance(); return $wpTwitterWidget->getSettings( $instance ); } public function form( $instance ) { $instance = $this->_getInstanceSettings( $instance ); $wpTwitterWidget = wpTwitterWidget::getInstance(); ?>

/>

/>

/>

/>

/>

getSupportForumLink(); ?>

_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 ); $wpTwitterWidget = wpTwitterWidget::getInstance(); echo $wpTwitterWidget->display( wp_parse_args( $instance, $args ) ); } } /** * 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 extends XavisysPlugin { /** * @var wpTwitterWidget - Static property to hold our singleton instance */ static $instance = false; protected function _init() { $this->_hook = 'twitterWidgetPro'; $this->_file = plugin_basename( __FILE__ ); $this->_pageTitle = __( 'Twitter Widget Pro', $this->_slug ); $this->_menuTitle = __( 'Twitter Widget', $this->_slug ); $this->_accessLevel = 'manage_options'; $this->_optionGroup = 'twp-options'; $this->_optionNames = array('twp'); $this->_optionCallbacks = array(); $this->_slug = 'twitter-widget-pro'; $this->_paypalButtonId = '9993090'; /** * Add filters and actions */ 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( $this->_slug .'-opt-twp', array( $this, 'filterSettings' ) ); add_shortcode( 'twitter-widget', array( $this, 'handleShortcodes' ) ); } /** * Function to instantiate our class and make it a singleton */ public static function getInstance() { if ( !self::$instance ) { self::$instance = new self; } return self::$instance; } public function get_slug() { return $this->_slug; } public function addOptionsMetaBoxes() { add_meta_box( $this->_slug . '-general-settings', __('General Settings', $this->_slug), array($this, 'generalSettingsMetaBox'), 'xavisys-' . $this->_slug, 'main'); } public function generalSettingsMetaBox() { ?>
_slug);?> _settings['twp']['hidereplies'], 'true'); ?> />
_settings['twp']['hiderss'], 'true'); ?> />
_settings['twp']['targetBlank'], 'true'); ?> />
_settings['twp']['avatar'], 'true'); ?> />
_settings['twp']['showXavisysLink'], 'true'); ?> />
"http://twitter.com/{$user->screen_name}", 'title' => $user->name ); return '' . $this->_buildLink($user->screen_name, $attrs) . ''; } /** * 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_callback('/(^|\s)@(\w+)/i', array($this, '_linkTwitterUsersCallback'), $text); return $text; } private function _linkTwitterUsersCallback($matches) { $linkAttrs = array( 'href' => 'http://twitter.com/' . urlencode($matches[2]), 'class' => 'twitter-user' ); return $matches[1] . $this->_buildLink('@'.$matches[2], $linkAttrs); } /** * 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) { $linkAttrs = array( 'href' => 'http://search.twitter.com/search?q=' . urlencode($matches[2]), 'class' => 'twitter-hashtag' ); return $matches[1] . $this->_buildLink($matches[2], $linkAttrs); } /** * 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_callback("/(^|\s)(([a-zA-Z]+:\/\/)([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", array($this, '_linkUrlsCallback'), $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_callback("/(^|\s)(www\.([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", array($this, '_linkUrlsCallback'), $text); return $text; } private function _linkUrlsCallback ($matches) { $linkAttrs = array( 'href' => $matches[2] ); return $matches[1] . $this->_buildLink($matches[2], $linkAttrs); } private function _notEmpty( $v ) { return !(empty($v)); } private function _buildLink( $text, $attributes = array(), $noFilter = false ) { $attributes = array_filter( wp_parse_args( $attributes ), array($this, '_notEmpty' ) ); $attributes = apply_filters( 'widget_twitter_link_attributes', $attributes ); $attributes = wp_parse_args( $attributes ); if ( strtolower( 'www' == substr($attributes['href'], 0, 3) ) ) { $attributes['href'] = 'http://' . $attributes['href']; } $text = apply_filters( 'widget_twitter_link_text', $text ); $link = ' $value ) { $link .= ' ' . esc_attr($name) . '="' . esc_attr($value) . '"'; } $link .= '>'; if ( $noFilter ) { $link .= $text; } else { $link .= esc_html($text); } $link .= ''; return $link; } public function register() { register_widget('WP_Widget_Twitter_Pro'); } public function targetBlank($attributes) { $attributes['target'] = '_blank'; return $attributes; } public function display( $args ) { $args = wp_parse_args( $args ); if ( $args['targetBlank'] ) { add_filter('widget_twitter_link_attributes', array($this, 'targetBlank')); } // Validate our options $args['items'] = (int) $args['items']; if ( $args['items'] < 1 || 20 < $args['items'] ) { $args['items'] = 10; } if (!isset($args['showts'])) { $args['showts'] = 86400; } try { $tweets = $this->_getTweets($args); } catch (wpTwitterWidgetException $e) { $tweets = $e; } $widgetContent = $args['before_widget'] . '
'; // If "hide rss" hasn't been checked, show the linked icon if ( $args['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($args, 'rss', false); $linkAttrs = array( 'class' => 'twitterwidget twitterwidget-rss', 'title' => __('Syndicate this content', $this->_slug), 'href' => $feedUrl ); $args['before_title'] .= $this->_buildLink("RSS", $linkAttrs, true); } $twitterLink = 'http://twitter.com/' . $args['username']; if (empty($args['title'])) { $args['title'] = "Twitter: {$args['username']}"; } $linkAttrs = array( 'class' => 'twitterwidget twitterwidget-title', 'title' => "Twitter: {$args['username']}", 'href' => $twitterLink ); $args['title'] = $this->_buildLink($args['title'], $linkAttrs, current_user_can('unfiltered_html')); $widgetContent .= $args['before_title'] . $args['title'] . $args['after_title']; if (!is_a($tweets, 'wpTwitterWidgetException') && !empty($tweets[0]) && $args['avatar'] == 'true') { $widgetContent .= ''; } $widgetContent .= '
' . $args['after_widget']; return $widgetContent; } /** * 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 { if ( !class_exists('Services_JSON') ) { require_once( 'class-json.php' ); } global $wp_json; if ( !is_a($wp_json, 'Services_JSON') ) { $wp_json = new Services_JSON(); } $decodedResponse = $wp_json->decode( $resp['body'] ); } if ( empty($decodedResponse) ) { if (empty($widgetOptions['errmsg'])) { $widgetOptions['errmsg'] = __('Invalid Twitter Response.', $this->_slug); } 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', $this->_slug); } 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, $dateFormat) { // 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( $dateFormat, $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, $this->_slug), 'month' => _n('about %s month ago', 'about %s months ago', $count, $this->_slug), 'week' => _n('about %s week ago', 'about %s weeks ago', $count, $this->_slug), 'day' => _n('about %s day ago', 'about %s days ago', $count, $this->_slug), 'hour' => _n('about %s hour ago', 'about %s hours ago', $count, $this->_slug), 'minute' => _n('about %s minute ago', 'about %s minutes ago', $count, $this->_slug), 'second' => _n('about %s second ago', 'about %s seconds ago', $count, $this->_slug), ); 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) { $linkAttrs = array( 'href' => "http://twitter.com/{$user->screen_name}", 'title' => $user->name ); return $this->_buildLink("{$user->name}", $linkAttrs, true); } /** * Replace our shortCode with the "widget" * * @param array $attr - array of attributes from the shortCode * @param string $content - Content of the shortCode * @return string - formatted XHTML replacement for the shortCode */ public function handleShortcodes($attr, $content = '') { $defaults = array( 'before_widget' => '', 'after_widget' => '', 'before_title' => '

', 'after_title' => '

', 'title' => '', 'errmsg' => '', 'fetchTimeOut' => '2', 'username' => '', 'hiderss' => false, 'hidereplies' => false, 'avatar' => false, 'showXavisysLink' => false, 'targetBlank' => false, 'items' => 10, 'showts' => 60 * 60 * 24, 'dateFormat' => __('h:i:s A F d, Y', $this->_slug), ); /** * Attribute names are strtolower'd, so we need to fix them to match * the names used through the rest of the plugin */ if ( array_key_exists( 'fetchtimeout', $attr ) ) { $attr['fetchTimeOut'] = $attr['fetchtimeout']; unset($attr['fetchtimeout']); } if ( array_key_exists( 'showxavisyslink', $attr ) ) { $attr['showXavisysLink'] = $attr['showxavisyslink']; unset($attr['showxavisyslink']); } if ( array_key_exists( 'targetblank', $attr ) ) { $attr['targetBlank'] = $attr['targetblank']; unset($attr['targetblank']); } if ( array_key_exists( 'dateformat', $attr ) ) { $attr['dateFormat'] = $attr['dateformat']; unset($attr['dateformat']); } if ( !empty($content) && empty($attr['title']) ) { $attr['title'] = $content; } $attr = shortcode_atts($defaults, $attr); if ( $attr['hiderss'] && $attr['hiderss'] != 'false' && $attr['hiderss'] != '0' ) { $attr['hiderss'] == true; } if ( $attr['hidereplies'] && $attr['hidereplies'] != 'false' && $attr['hidereplies'] != '0' ) { $attr['hidereplies'] == true; } if ( $attr['avatar'] && $attr['avatar'] != 'false' && $attr['avatar'] != '0' ) { $attr['avatar'] == true; } if ( $attr['showXavisysLink'] && $attr['showXavisysLink'] != 'false' && $attr['showXavisysLink'] != '0' ) { $attr['showXavisysLink'] == true; } if ( $attr['targetBlank'] && $attr['targetBlank'] != 'false' && $attr['targetBlank'] != '0' ) { $attr['targetBlank'] == true; } return $this->display($attr); } public function filterSettings($settings) { $defaultArgs = array( 'title' => '', 'errmsg' => '', 'fetchTimeOut' => '2', 'username' => '', 'hiderss' => false, 'hidereplies' => false, 'avatar' => false, 'showXavisysLink' => false, 'targetBlank' => false, 'items' => 10, 'showts' => 60 * 60 * 24, 'dateFormat' => __('h:i:s A F d, Y', $this->_slug), ); return wp_parse_args( $settings, $defaultArgs ); } public function getSettings( $settings ) { return wp_parse_args( $settings, $this->_settings['twp'] ); } } // Instantiate our class $wpTwitterWidget = wpTwitterWidget::getInstance();