_slug = 'twitter-widget-pro'; $wpTwitterWidget = wpTwitterWidget::getInstance(); $widget_ops = array( 'classname' => '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 { private $_api_url; /** * @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( 'admin_init', array( $this, 'handle_actions' ) ); add_action( 'admin_notices', array( $this, 'show_messages' ) ); add_action( 'widgets_init', array( $this, 'register' ), 11 ); 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' ) ); $twp_version = get_option( 'twp_version' ); if ( TWP_VERSION != $twp_version ) update_option( 'twp_version', TWP_VERSION ); } protected function _postSettingsInit() { if ( ! in_array( $this->_settings['twp']['http_vs_https'], array( 'http', 'https' ) ) ) $this->_settings['twp']['http_vs_https'] = 'https'; $this->_api_url = $this->_settings['twp']['http_vs_https'] . '://api.twitter.com/1/'; } /** * 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 handle_actions() { if ( empty( $_GET['action'] ) || empty( $_GET['page'] ) || $_GET['page'] != $this->_hook ) return; if ( 'clear-locks' == $_GET['action'] ) { check_admin_referer( 'clear-locks' ); $redirect_args = array( 'message' => strtolower( $_GET['action'] ) ); global $wpdb; $locks_q = "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE '_transient_tlc_up__twp%'"; $redirect_args['locks_cleared'] = $wpdb->query( $locks_q ); wp_safe_redirect( add_query_arg( $redirect_args, remove_query_arg( array( 'action', '_wpnonce' ) ) ) ); exit; } } public function show_messages() { if ( ! empty( $_GET['message'] ) && 'clear-locks' == $_GET['message'] ) { if ( empty( $_GET['locks_cleared'] ) || 0 == $_GET['locks_cleared'] ) $msg = __( 'There were no locks to clear!', $this->_slug ); else $msg = sprintf( _n( 'Successfully cleared %d lock.', 'Successfully cleared %d locks.', $_GET['locks_cleared'], $this->_slug ), $_GET['locks_cleared'] ); echo "
" . esc_html( $msg ) . '
'; } } public function addOptionsMetaBoxes() { add_meta_box( $this->_slug . '-general-settings', __( 'General Settings', $this->_slug ), array( $this, 'generalSettingsMetaBox' ), 'xavisys-' . $this->_slug, 'main' ); add_meta_box( $this->_slug . '-defaults', __( 'Defaults', $this->_slug ), array( $this, 'defaultSettingsMetaBox' ), 'xavisys-' . $this->_slug, 'main' ); } public function generalSettingsMetaBox() { $clear_locks_url = wp_nonce_url( add_query_arg( array( 'action' => 'clear-locks' ) ), 'clear-locks' ); ?>
_slug );?> _settings['twp']['http_vs_https'], 'https' ); ?> />
_settings['twp']['http_vs_https'], 'http' ); ?> />
_slug ); ?>
_slug );?> _slug ); ?>
_slug ); ?>
_slug );?> _settings['twp']['showretweets'], 'true' ); ?> />
_settings['twp']['hidereplies'], 'true' ); ?> />
_settings['twp']['hidefrom'], 'true' ); ?> />
_settings['twp']['showintents'], 'true' ); ?> />
_settings['twp']['showfollow'], 'true' ); ?> />
_settings['twp']['targetBlank'], 'true' ); ?> />
_settings['twp']['showXavisysLink'], 'true' ); ?> />
'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, '_linkHashtagsCallback'), $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 _linkHashtagsCallback( $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() { // Fix conflict with Jetpack by disabling their Twitter widget unregister_widget( 'Wickett_Twitter_Widget' ); 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 ( 'true' == $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; $tweets = $this->_getTweets( $args ); if ( false === $tweets ) return ''; $widgetContent = $args['before_widget'] . '
'; if ( empty( $args['title'] ) ) $args['title'] = "Twitter: {$args['username']}"; $args['title'] = apply_filters( 'twitter-widget-title', $args['title'], $args ); $args['title'] = "{$args['title']}"; $widgetContent .= $args['before_title'] . $args['title'] . $args['after_title']; if ( !empty( $tweets[0] ) && !empty( $args['avatar'] ) ) { $widgetContent .= ''; } $widgetContent .= ''; if ( 'true' == $args['showfollow'] ) { $widgetContent .= ''; } if ( 'true' == $args['showXavisysLink'] ) { $widgetContent .= ''; } $widgetContent .= '
' . $args['after_widget']; if ( 'true' == $args['showintents'] || 'true' == $args['showfollow'] ) { $script = 'http://platform.twitter.com/widgets.js'; if ( is_ssl() ) $script = str_replace( 'http://', 'https://', $script ); wp_enqueue_script( 'twitter-widgets', $script, array(), '1.0.0', true ); if ( ! function_exists( '_wp_footer_scripts' ) ) { // This means we can't just enqueue our script (fixes in WP 3.3) add_action( 'wp_footer', array( $this, 'add_twitter_js' ) ); } } return $widgetContent; } private function _getTwitterLang() { $valid_langs = array( 'en', // English 'it', // Italian 'es', // Spanish 'fr', // French 'ko', // Korean 'ja', // Japanese ); $locale = get_locale(); $lang = strtolower( substr( get_locale(), 0, 2 ) ); if ( in_array( $lang, $valid_langs ) ) return $lang; return false; } public function add_twitter_js() { wp_print_scripts( 'twitter-widgets' ); } /** * Gets tweets, from cache if possible * * @param array $widgetOptions - options needed to get feeds * @return array - Array of objects */ private function _getTweets( $widgetOptions ) { $key = 'twp_' . md5( $this->_getFeedUrl( $widgetOptions ) ); return tlc_transient( $key ) ->expires_in( 300 ) // cache for 5 minutes ->updates_with( array( $this, 'parseFeed' ), array( $widgetOptions ) ) ->get(); } /** * Pulls the JSON feed from Twitter and returns an array of objects * * @param array $widgetOptions - settings needed to get feed url, etc * @return array */ public 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 ) { $decodedResponse = json_decode( $resp['body'] ); if ( empty( $decodedResponse ) || ! is_array( $decodedResponse ) ) { if ( empty( $widgetOptions['errmsg'] ) ) $widgetOptions['errmsg'] = __( 'Invalid Twitter Response.', $this->_slug ); } elseif( !empty( $decodedResponse->error ) ) { if ( empty( $widgetOptions['errmsg'] ) ) $widgetOptions['errmsg'] = $decodedResponse->error; } else { return $decodedResponse; } } else { // Failed to fetch url; if ( empty( $widgetOptions['errmsg'] ) ) $widgetOptions['errmsg'] = __( 'Could not connect to Twitter', $this->_slug ); } do_action( 'widget_twitter_parsefeed_error', $resp, $feedUrl, $widgetOptions ); throw new Exception( $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'; $req = $this->_api_url . "statuses/user_timeline.{$type}"; /** * user_id * screen_name * * since_id * count * max_id * page * trim_user * include_rts * * include_entities * exclude_replies * * contributor_details */ $req = add_query_arg( array( 'screen_name' => $widgetOptions['username'] ), $req ); if ( $count ) $req = add_query_arg( array( 'count' => $widgetOptions['items'] ), $req ); if ( 'true' == $widgetOptions['hidereplies'] ) $req = add_query_arg( array( 'exclude_replies' => 'true' ), $req ); if ( 'true' == $widgetOptions['showretweets'] ) $req = add_query_arg( array( 'include_rts' => 'true' ), $req ); return $req; } /** * Twitter displays all tweets that are less than 24 hours old 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 * @param array $args - Widget Arguments * @return string - Linked image ( XHTML ) */ private function _getProfileImage( $user, $args = array() ) { $linkAttrs = array( 'href' => "http://twitter.com/{$user->screen_name}", 'title' => $user->name ); $img = $this->_api_url . 'users/profile_image'; $img = add_query_arg( array( 'screen_name' => $user->screen_name ), $img ); $img = add_query_arg( array( 'size' => $args['avatar'] ), $img ); 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' => '', 'hidereplies' => 'false', 'showretweets' => 'true', 'hidefrom' => 'false', 'showintents' => 'true', 'showfollow' => 'true', 'avatar' => '', '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['hidereplies'] && $attr['hidereplies'] != 'false' && $attr['hidereplies'] != '0' ) $attr['hidereplies'] = 'true'; if ( $attr['showretweets'] && $attr['showretweets'] != 'false' && $attr['showretweets'] != '0' ) $attr['showretweets'] = 'true'; if ( $attr['hidefrom'] && $attr['hidefrom'] != 'false' && $attr['hidefrom'] != '0' ) $attr['hidefrom'] = 'true'; if ( $attr['showintents'] && $attr['showintents'] != 'true' && $attr['showintents'] != '1' ) $attr['showintents'] = 'false'; if ( $attr['showfollow'] && $attr['showfollow'] != 'true' && $attr['showfollow'] != '1' ) $attr['showfollow'] = 'false'; if ( !in_array( $attr['avatar'], array( 'bigger', 'normal', 'mini', 'original', '' ) ) ) $attr['avatar'] = 'normal'; 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' => '', 'http_vs_https' => 'https', 'hidereplies' => 'false', 'showretweets' => 'true', 'hidefrom' => 'false', 'showintents' => 'true', 'showfollow' => 'true', 'avatar' => '', 'showXavisysLink' => 'false', 'targetBlank' => 'false', 'items' => 10, 'showts' => 60 * 60 * 24, 'dateFormat' => __( 'h:i:s A F d, Y', $this->_slug ), ); return $this->fixAvatar( wp_parse_args( $settings, $defaultArgs ) ); } /** * Now that we support all the profile image sizes we need to convert * the old true/false to a size string */ private function fixAvatar( $settings ) { if ( false === $settings['avatar'] ) $settings['avatar'] = ''; elseif ( !in_array( $settings['avatar'], array( 'bigger', 'normal', 'mini', 'original', false ) ) ) $settings['avatar'] = 'normal'; return $settings; } public function getSettings( $settings ) { return $this->fixAvatar( wp_parse_args( $settings, $this->_settings['twp'] ) ); } } // Instantiate our class $wpTwitterWidget = wpTwitterWidget::getInstance();