Switch back to the Twitter API for the Tweet widget
authorjond@apple.com <jond@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 11 Mar 2017 01:01:19 +0000 (01:01 +0000)
committerjond@apple.com <jond@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 11 Mar 2017 01:01:19 +0000 (01:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=168749

Reviewed by Lucas Forschler.

Updates the Twitter widget tile implementation to prefer the Twitter API to populate tweets
displayed through the widget. If the API fails for any reason, it falls back to using the
Tweet listener for updated tweets.

The Twitter API provides the benefit of embedding media and being able to display the full
URLs rather than Twitter-shortened URLs.

* wp-content/plugins/tweet-listener.php:
* wp-content/themes/webkit/style.css:
(.twitter-tile .tile-content):
(.twitter-tile.text-only):
(.twitter-tile .media):
(.twitter-tile img):
* wp-content/themes/webkit/widgets/twitter.php:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@213740 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Websites/webkit.org/ChangeLog
Websites/webkit.org/wp-content/plugins/tweet-listener.php
Websites/webkit.org/wp-content/themes/webkit/style.css
Websites/webkit.org/wp-content/themes/webkit/widgets/twitter.php

index 02eeb08..acd1d33 100644 (file)
@@ -1,3 +1,25 @@
+2017-03-10  Jon Davis  <jond@apple.com>
+
+        Switch back to the Twitter API for the Tweet widget
+        https://bugs.webkit.org/show_bug.cgi?id=168749
+
+        Reviewed by Lucas Forschler.
+        
+        Updates the Twitter widget tile implementation to prefer the Twitter API to populate tweets 
+        displayed through the widget. If the API fails for any reason, it falls back to using the 
+        Tweet listener for updated tweets.
+        
+        The Twitter API provides the benefit of embedding media and being able to display the full 
+        URLs rather than Twitter-shortened URLs.
+
+        * wp-content/plugins/tweet-listener.php:
+        * wp-content/themes/webkit/style.css:
+        (.twitter-tile .tile-content):
+        (.twitter-tile.text-only):
+        (.twitter-tile .media):
+        (.twitter-tile img):
+        * wp-content/themes/webkit/widgets/twitter.php:
+
 2017-03-04  Filip Pizlo  <fpizlo@apple.com>
 
         B3 should have comprehensive support for atomic operations
 2017-03-04  Filip Pizlo  <fpizlo@apple.com>
 
         B3 should have comprehensive support for atomic operations
index 9b283ea..a920ef5 100644 (file)
@@ -12,7 +12,7 @@ TweetListener::object();
 class TweetListener {
 
     private static $object = null;
 class TweetListener {
 
     private static $object = null;
-    
+
     const AUTH_TOKEN = TWITTER_LISTENER_AUTH;
     const DATA_KEY = 'recent_tweet';
 
     const AUTH_TOKEN = TWITTER_LISTENER_AUTH;
     const DATA_KEY = 'recent_tweet';
 
@@ -28,9 +28,12 @@ class TweetListener {
     }
 
     public function listen() {
     }
 
     public function listen() {
-        
+
+        if (!isset($_POST['token']) || $_POST['token'] != TWITTER_LISTENER_AUTH)
+            wp_die();
+
         $defaults = array(
         $defaults = array(
-            'text' => '', 
+            'text' => '',
             'username' => '@webkit',
             'link' => '',
             'created' => '',
             'username' => '@webkit',
             'link' => '',
             'created' => '',
@@ -41,10 +44,10 @@ class TweetListener {
         update_option(self::DATA_KEY, $data);
         wp_die();
     }
         update_option(self::DATA_KEY, $data);
         wp_die();
     }
-    
+
     public function tweet() {
         $data = (object)get_option(self::DATA_KEY);
     public function tweet() {
         $data = (object)get_option(self::DATA_KEY);
-        
+
         // Setup compatible Tweet structure
 
         $Tweet = new StdClass();
         // Setup compatible Tweet structure
 
         $Tweet = new StdClass();
@@ -64,7 +67,7 @@ class TweetListener {
                 $Tweet->entities->urls[] = $URL;
             }
         }
                 $Tweet->entities->urls[] = $URL;
             }
         }
-        
+
         return $Tweet;
     }
 
         return $Tweet;
     }
 
index e33a2b1..7c3df40 100644 (file)
@@ -653,12 +653,18 @@ footer .menu {
     position: relative;
     box-sizing: border-box;
     font-size: 2rem;
     position: relative;
     box-sizing: border-box;
     font-size: 2rem;
+    text-align: center;
 }
 
 .twitter-tile.text-only {
     display: -webkit-flex;
     display: flex;
     justify-content: center;
 }
 
 .twitter-tile.text-only {
     display: -webkit-flex;
     display: flex;
     justify-content: center;
+    text-align: left;
+}
+
+.twitter-tile .media {
+    text-align: center;
 }
 
 .twitter-tile.text-only .tile-content {
 }
 
 .twitter-tile.text-only .tile-content {
@@ -670,8 +676,10 @@ footer .menu {
 }
 
 .twitter-tile img {
 }
 
 .twitter-tile img {
-    width: 100%;
+    display: inline-block;
+    width: auto;
     height: auto;
     height: auto;
+    max-width: 100%;
     max-height: 256px;
 }
 
     max-height: 256px;
 }
 
@@ -699,6 +707,7 @@ footer .menu {
     display: inline-block;
     margin: 0 1.3rem;
 }
     display: inline-block;
     margin: 0 1.3rem;
 }
+
 .twitter-controls a {
     opacity: 0.66;
     transition: opacity 300ms ease-out;
 .twitter-controls a {
     opacity: 0.66;
     transition: opacity 300ms ease-out;
index 55eae2a..4d357c0 100644 (file)
@@ -11,6 +11,7 @@ if ( ! class_exists('WP_Widget') ) return;
 class WebKitTwitterTileWidget extends WebKitPostTileWidget {
 
     const CACHEKEY = 'webkit_twitter_feed';
 class WebKitTwitterTileWidget extends WebKitPostTileWidget {
 
     const CACHEKEY = 'webkit_twitter_feed';
+    const ENDPOINT = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
 
     function __construct() {
         parent::WP_Widget(false,
 
     function __construct() {
         parent::WP_Widget(false,
@@ -30,46 +31,44 @@ class WebKitTwitterTileWidget extends WebKitPostTileWidget {
         if ( empty($Tweet) || empty($Tweet->text) )
             return $this->follow_markup($options);
 
         if ( empty($Tweet) || empty($Tweet->text) )
             return $this->follow_markup($options);
 
-        // Show "Follow @webkit" instead of tweet for blog URLs
-        if ( ! empty($Tweet->entities)
-                && ! empty($Tweet->entities->urls)
-                && count($Tweet->entities->urls) == 1
-                && preg_match('!webkit.org/blog/\d+/!', $Tweet->entities->urls[0]->expanded_url) == 1)
-            return $this->follow_markup($options);
-
         $classes = array('tile', 'third-tile', 'twitter-tile');
         $classes = array('tile', 'third-tile', 'twitter-tile');
-
         $text = (string)$options['text'];
         $text = (string)$options['text'];
+        $link = "https://twitter.com/webkit/status/$Tweet->id";
 
         if ( ! empty($Tweet->text) )
             $text = $Tweet->text;
 
 
         if ( ! empty($Tweet->text) )
             $text = $Tweet->text;
 
-        if ( ! empty($options['url']) )
-            $url = (string)$options['url'];
-
         // Expand URLs
         foreach ( $Tweet->entities->urls as $entry ) {
             $expanded = '<a href="' . esc_url($entry->expanded_url) . '">'
                                  . $entry->display_url . '</a>';
         // Expand URLs
         foreach ( $Tweet->entities->urls as $entry ) {
             $expanded = '<a href="' . esc_url($entry->expanded_url) . '">'
                                  . $entry->display_url . '</a>';
+
+            // Don't show webkit.org links, just change the tile to use the link in the tweet
+            if ( preg_match('!webkit.org/.+?!', $entry->expanded_url) == 1 ) {
+                $expanded = '';
+                $link = $entry->expanded_url;
+            }
+
             $text = str_replace($entry->url, $expanded, $text);
         }
 
         $text = preg_replace('/RT @[^:]+:\s+/', '', $text, 1);
             $text = str_replace($entry->url, $expanded, $text);
         }
 
         $text = preg_replace('/RT @[^:]+:\s+/', '', $text, 1);
-
-        if ( empty($Tweet->entities) || empty($Tweet->entities->media) ) {
-
+        if ( ! ( empty($Tweet->entities) || empty($Tweet->entities->media) ) ) {
             $Image = $Tweet->entities->media[0];
             $Image = $Tweet->entities->media[0];
+
             if ( empty($Image) ) $classes[] = 'text-only';
 
             // Strip media links
             foreach ( $Tweet->entities->media as $entry ) {
                 $text = str_replace($entry->url, '', $text);
             }
             if ( empty($Image) ) $classes[] = 'text-only';
 
             // Strip media links
             foreach ( $Tweet->entities->media as $entry ) {
                 $text = str_replace($entry->url, '', $text);
             }
+        } else {
+            $classes[] = 'text-only';
         }
 
         ?>
         <div class="<?php echo esc_attr(join(' ', $classes)); ?>">
         }
 
         ?>
         <div class="<?php echo esc_attr(join(' ', $classes)); ?>">
-            <a href="http://twitter.com/webkit/status/<?php echo esc_attr($Tweet->id); ?>" class="tile-link">Clickable link to tweet</a>
+            <a href="<?php echo esc_url($link); ?>" class="tile-link">Clickable link to tweet</a>
             <div class="tile-content">
             <?php if ( ! empty($Image) ): ?>
             <img src="<?php echo esc_url($Image->media_url_https); ?>">
             <div class="tile-content">
             <?php if ( ! empty($Image) ): ?>
             <img src="<?php echo esc_url($Image->media_url_https); ?>">
@@ -79,7 +78,7 @@ class WebKitTwitterTileWidget extends WebKitPostTileWidget {
 
             </div>
             <ul class="twitter-controls">
 
             </div>
             <ul class="twitter-controls">
-                <li><a href="https://twitter.com/webkit" target="twitter-modal"><span class="twitter-icon">Twitter</span> @webkit</a></li>
+                <li><a href="https://twitter.com/webkit" target="twitter-modal"><span class="twitter-icon">Twitter</span>@webkit</a></li>
                 <li><a href="https://twitter.com/intent/tweet?in-reply-to=<?php echo esc_attr($Tweet->id); ?>" class="twitter-icon reply-icon" target="twitter-modal">Reply</a></li>
                 <li><a href="https://twitter.com/intent/retweet?tweet_id=<?php echo esc_attr($Tweet->id); ?>" class="twitter-icon retweet-icon" target="twitter-modal">Retweet</a></li>
                 <li><a href="https://twitter.com/intent/favorite?tweet_id=<?php echo esc_attr($Tweet->id); ?>" class="twitter-icon favorite-icon" target="twitter-modal">Favorite</a></li>
                 <li><a href="https://twitter.com/intent/tweet?in-reply-to=<?php echo esc_attr($Tweet->id); ?>" class="twitter-icon reply-icon" target="twitter-modal">Reply</a></li>
                 <li><a href="https://twitter.com/intent/retweet?tweet_id=<?php echo esc_attr($Tweet->id); ?>" class="twitter-icon retweet-icon" target="twitter-modal">Retweet</a></li>
                 <li><a href="https://twitter.com/intent/favorite?tweet_id=<?php echo esc_attr($Tweet->id); ?>" class="twitter-icon favorite-icon" target="twitter-modal">Favorite</a></li>
@@ -111,51 +110,44 @@ class WebKitTwitterTileWidget extends WebKitPostTileWidget {
 
     function tweet () {
 
 
     function tweet () {
 
-        // Prefer pushed tweet
+        if ( false !== ( $cached = get_transient(self::CACHEKEY) ) )
+            return json_decode($cached);
+
+        // Get pushed Tweet as a fallback
+        $pushedTweet = false;
         if ( class_exists('TweetListener') ) {
             $TweetListener = TweetListener::object();
         if ( class_exists('TweetListener') ) {
             $TweetListener = TweetListener::object();
-            return $TweetListener->tweet();
+            $pushedTweet = $TweetListener->tweet();
         }
 
         }
 
-        // Poll for tweet
-        if ( false !== ( $cached = get_transient(self::CACHEKEY) ) )
-            return json_decode($cached);
-
-        $endpoint = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
+        // Connect to Twitter API
         $parameters = array();
         $options = array(
             'method' => 'GET',
         );
 
         $parameters = array();
         $options = array(
             'method' => 'GET',
         );
 
-        $oauth_consumer_key = 'ypSvRp37vmyt3ldMPhs0e62c9';
-        $oauth_consumer_key_secret = 'mLuYD3AjUehUZKOgQIICA5Na69te45aSJTkdIDTGSg4cfHd6Lz';
-        $oauth_token = '14315023-7pHbcI5bk2QZhNiHR9uFudaksBzMPubEuOEmYj7YQ';
-        $oauth_token_secret = '0K9T9znxG4S9PUGunYZ5LwyKL9AR6v3eAXp6WKY2oi7Bg';
+        list($oauth_consumer_key, $oauth_consumer_key_secret) = explode(':', TWITTER_CONSUMER_KEY);
+        list($oauth_token, $oauth_token_secret) = explode(':', TWITTER_OAUTH_TOKEN);
         $oauth_timestamp = time();
         $oauth_nonce = sha1(rand() . $oauth_timestamp);
 
         $fields = array(
             'oauth_consumer_key' => $oauth_consumer_key,
         $oauth_timestamp = time();
         $oauth_nonce = sha1(rand() . $oauth_timestamp);
 
         $fields = array(
             'oauth_consumer_key' => $oauth_consumer_key,
-            'oauth_nonce' => sha1(rand() . time()),
+            'oauth_nonce' => $oauth_nonce,
             'oauth_signature_method' => 'HMAC-SHA1',
             'oauth_signature_method' => 'HMAC-SHA1',
-            'oauth_timestamp' => time(),
+            'oauth_timestamp' => $oauth_timestamp,
             'oauth_token' => $oauth_token,
             'oauth_version' => '1.0',
         );
         $fields = array_merge($parameters, $fields);
 
             'oauth_token' => $oauth_token,
             'oauth_version' => '1.0',
         );
         $fields = array_merge($parameters, $fields);
 
-        $base = array(
-            'GET', $endpoint, http_build_query($fields, '', '&')
-        );
-        $base = join('&', array_map('rawurlencode', $base));
+        $requestParts = array('GET', self::ENDPOINT, http_build_query($fields, '', '&'));
+        $request = join('&', array_map('rawurlencode', $requestParts));
 
 
+        $authkeys = array($oauth_consumer_key_secret, $oauth_token_secret);
+        $auth = join('&', array_map('rawurlencode', $authkeys));
 
 
-        $key = array(
-            $oauth_consumer_key_secret, $oauth_token_secret
-        );
-        $key = join('&', array_map('rawurlencode', $key));
-
-        $signature = base64_encode( hash_hmac('sha1', $base, $key, true) );
+        $signature = base64_encode( hash_hmac('sha1', $request, $auth, true) );
 
         $oauth = array(
             'oauth_consumer_key' => $oauth_consumer_key,
 
         $oauth = array(
             'oauth_consumer_key' => $oauth_consumer_key,
@@ -166,8 +158,9 @@ class WebKitTwitterTileWidget extends WebKitPostTileWidget {
             'oauth_token' => $oauth_token,
             'oauth_version' => '1.0',
         );
             'oauth_token' => $oauth_token,
             'oauth_version' => '1.0',
         );
-        $oauth = array_map(create_function('$h','return "\"$h\"";'), $oauth);
 
 
+        // Wrap values in double-quotes.
+        $oauth = array_map(create_function('$h','return "\"$h\"";'), $oauth);
         $oauth = http_build_query($oauth, '', ', ');
         $oauth = str_replace('%22', '"', $oauth);
 
         $oauth = http_build_query($oauth, '', ', ');
         $oauth = str_replace('%22', '"', $oauth);
 
@@ -177,10 +170,10 @@ class WebKitTwitterTileWidget extends WebKitPostTileWidget {
 
         $options['headers'] = $headers;
 
 
         $options['headers'] = $headers;
 
-        $response = wp_remote_get($endpoint, $options);
+        $response = wp_remote_get(self::ENDPOINT, $options);
 
         if ( is_a($response, 'WP_Error') )
 
         if ( is_a($response, 'WP_Error') )
-            return false;
+            return $pushedTweet;
 
         if ( 200 == $response['response']['code'] && ! empty($response['body']) ) {
             $body = json_decode($response['body']);
 
         if ( 200 == $response['response']['code'] && ! empty($response['body']) ) {
             $body = json_decode($response['body']);
@@ -189,7 +182,12 @@ class WebKitTwitterTileWidget extends WebKitPostTileWidget {
             return $data;
         }
 
             return $data;
         }
 
-        return false;
+        return $pushedTweet;
+    }
+
+    function isWebKitLink ($Tweet) {
+        return ( ! empty($Tweet->entities->urls[0]->expanded_url)
+                && preg_match('!webkit.org/.+?!', $Tweet->entities->urls[0]->expanded_url) == 1 );
     }
 
 } // END class WebKitTwitterTileWidget
     }
 
 } // END class WebKitTwitterTileWidget