Twitter - Version 1.3.0

Version Description

Adds embedded Tweets grid template, Vine embeds, and Periscope On Air buttons.

Download this release

Release Info

Developer niallkennedy
Plugin Icon 128x128 Twitter
Version 1.3.0
Comparing to
See all releases

Code changes from version 1.2.0 to 1.3.0

readme.txt CHANGED
@@ -1,33 +1,75 @@
1
  === Plugin Name ===
2
  Contributors: Twitter, niallkennedy
3
- Tags: twitter, embedded tweet, twitter moment, twitter video, twitter cards, tweet button, follow button, twitter analytics, twitter ads
4
  Requires at least: 3.9
5
- Tested up to: 4.3.1
6
- Stable tag: 1.2.0
7
  License: MIT
8
  License URI: http://opensource.org/licenses/MIT
9
 
10
- Official Twitter plugin for WordPress. Embed Twitter content and grow your audience on Twitter. Requires PHP 5.4 or greater.
11
 
12
  == Description ==
13
 
14
- The Twitter plugin for WordPress optimizes your website for a Twitter audience through easy to use sharing buttons, embedded Tweets, auto-generated markup indexed by Twitter, and Follow buttons to help grow your Twitter audience. All features are deeply integrated with WordPress APIs to make building your webpages and administrative features as easy as possible with the extensibility you expect from WordPress.
 
 
15
 
16
  Requires PHP version 5.4 or greater.
17
 
18
- * [Embedded Moment](https://dev.twitter.com/web/embedded-moments) - showcase multiple Tweets in a media rich grid layout
19
- * [Embedded Tweet](https://dev.twitter.com/web/embedded-tweets) - customize backgrounds and color schemes to match your site's theme
20
- * [Embedded Twitter video](https://dev.twitter.com/web/embedded-video) - showcase video uploaded to Twitter
21
- * [Tweet button](https://dev.twitter.com/web/tweet-button) - simple sharing of your site's content on Twitter
22
- * [Twitter Cards](https://dev.twitter.com/cards/overview) - highlight your site's content when shared on Twitter
23
- * [Twitter Analytics](https://analytics.twitter.com/) - track impressions and top distributors of your site's content on Twitter
24
- * [Follow button](https://dev.twitter.com/web/follow-button) - grow your Twitter audience
25
- * [Twitter ads conversion tracking](https://support.twitter.com/articles/20170807-conversion-tracking-for-websites) - easily track actions on your WordPress site triggered by a Twitter ad or build a custom targeting audience
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- Contribute to the plugin, submit pull requests, or run test suites through the [Twitter plugin for WordPress GitHub repository](https://github.com/twitter/wordpress). View [Twitter for WordPress documentation](https://dev.twitter.com/web/wordpress) to learn more about customization through filters.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  == Upgrade Notice ==
30
 
 
 
 
31
  = 1.2.0 =
32
  Support Twitter Moment embeds.
33
 
@@ -39,6 +81,12 @@ Display admin notice if current PHP version does not meet minimum requirements.
39
 
40
  == Changelog ==
41
 
 
 
 
 
 
 
42
  = 1.2.0 =
43
  * Embed a Twitter Moment by simply pasting a URL
44
  * Always load Twitter ads conversion tracking JavaScript over HTTPS
@@ -56,12 +104,12 @@ Display admin notice if current PHP version does not meet minimum requirements.
56
  * Fix: save Follow button widget with no overrides
57
 
58
  = 1.0.0 =
59
- * Embedded Tweets
60
- * Embedded video
61
  * Tweet button
62
  * Twitter Cards
63
  * Follow button
64
- * Advertising tracker
65
 
66
  == Frequently Asked Questions ==
67
 
@@ -71,9 +119,9 @@ The Twitter plugin for WordPress includes a settings page with options to custom
71
 
72
  = How do I include an embedded timeline in my page? =
73
 
74
- Log in to Twitter.com and visit the [Twitter widgets settings page](https://twitter.com/settings/widgets) to create and manage [embedded timeline](https://dev.twitter.com/web/embedded-timelines) widgets for your account. Widget settings are saved to a widget identifier for the logged in account; you may want to create a widget from your site's account, not your personal account, for continuity and general organization.
75
 
76
- Copy-and-paste the HTML generated by the Twitter widgets configuration tool into a new [WordPress text widget](http://codex.wordpress.org/WordPress_Widgets#Using_Text_Widgets).
77
 
78
  = My custom link color and border color do not appear in embedded Tweets or timelines =
79
 
1
  === Plugin Name ===
2
  Contributors: Twitter, niallkennedy
3
+ Tags: twitter, embedded tweet, twitter moment, twitter video, twitter grid, vine, periscope, periscope on air, twitter cards, tweet button, follow button, twitter analytics, twitter ads
4
  Requires at least: 3.9
5
+ Tested up to: 4.4
6
+ Stable tag: 1.3.0
7
  License: MIT
8
  License URI: http://opensource.org/licenses/MIT
9
 
10
+ Official Twitter, Vine, and Periscope plugin for WordPress. Embed content and grow your audience. Requires PHP 5.4 or greater.
11
 
12
  == Description ==
13
 
14
+ The Twitter plugin for WordPress makes it easy to embed single Tweets, multiple Tweets, a Moment, or a Vine on your website. Improve the reach of your content with the Tweet button and populate rich link previews on Twitter with automatically-generated Twitter Card markup. Help your audience follow your latest updates with the Twitter follow button and Periscope On Air button.
15
+
16
+ All features are deeply integrated with WordPress APIs to make building your webpages and administrative features as easy as possible with the extensibility you expect from WordPress. The plugin is multisite-aware, supports post meta customizations through the WordPress REST API, and shortcode customizations through shortcode UI.
17
 
18
  Requires PHP version 5.4 or greater.
19
 
20
+ = Embed Twitter content =
21
+
22
+ Embed a [single Tweet](https://dev.twitter.com/web/embedded-tweets), [single Tweet with video template](https://dev.twitter.com/web/embedded-video), [Moment](https://dev.twitter.com/web/embedded-moments "Twitter Moment"), or [Twitter collection](https://dev.twitter.com/web/embedded-timelines/collection) grid template by pasting a URL into your article content. Customize advanced options using a shortcode.
23
+
24
+ Choose a light or dark theme, customize link and border colors, and configure other widget template options through your site's WordPress administrative interface.
25
+
26
+ The plugin automatically customizes embed HTML to match the locale or your site, optimally enqueues Twitter's widgets JavaScript for fast loading and extensibility, and handles advanced cases such as articles loaded asynchronously via the WordPress API.
27
+
28
+ = Embed a Vine =
29
+
30
+ Embed a Vine by pasting a URL into your article content. Customize advanced options using a shortcode.
31
+
32
+ The plugin optimally enqueues Vine's embed JavaScript to handle unpausing and unmuting videos as they become visible on the page.
33
+
34
+ = Add a Tweet button to public posts =
35
+
36
+ Add a Tweet button to public posts to encourage your visitors to share your content on Twitter. The Tweet button automatically constructs share text, URLs, and shares your site's Twitter account in the Tweet. Visitors may see recommended accounts to follow after posting your content, including your site's specified accounts.
37
+
38
+ Customize the pre-populated share text and hashtags shown in a Tweet composer for each post from your site's post editor.
39
+
40
+ = Enable link previews and Twitter bylines with Twitter Cards =
41
+
42
+ The plugin automatically generates [Twitter Card](https://dev.twitter.com/cards/overview) markup to populate link previews on Twitter and attribute articles to a site and author Twitter account. Increase engagement with your content and related Twitter accounts.
43
 
44
+ Twitter Cards with site attribution provides access to [Twitter Analytics](https://analytics.twitter.com/) for detailed information about your site's Twitter audience including top sharers and engagement data.
45
+
46
+ Provide a custom link preview title and description for each post from your site's post editor.
47
+
48
+ = Twitter follow button =
49
+
50
+ Add a [Twitter follow button](https://dev.twitter.com/web/follow-button) with a WordPress widget or shortcode.
51
+
52
+ = Periscope On Air button =
53
+
54
+ Display a [Periscope On Air button](https://www.periscope.tv/embed) by pasting a Periscope profile URL into article content. Customize advanced options using a shortcode.
55
+
56
+ = Associate WordPress accounts with Twitter and Periscope identities =
57
+
58
+ Add a Twitter or Periscope username to a WordPress profile page for easy reference to your authors' external accounts. The plugin includes author attribution for posts and can dynamically include Twitter follow or Periscope On Air buttons through a shortcode when account information exists.
59
+
60
+ = Add an advertising pixel with a shortcode =
61
+
62
+ Add a Twitter audience pixel or [track advertising conversions](https://support.twitter.com/articles/20170807-conversion-tracking-for-websites "Twitter advertising conversion tracking") by adding an advertising pixel through a simple shortcode.
63
+
64
+ > <strong>Docs and active development</strong><br>
65
+ >Contribute to the plugin, submit pull requests, or run test suites through the [Twitter plugin for WordPress GitHub repository](https://github.com/twitter/wordpress).
66
+ > View [Twitter for WordPress documentation](https://dev.twitter.com/web/wordpress) to learn more about customization through WordPress filters.
67
 
68
  == Upgrade Notice ==
69
 
70
+ = 1.3.0 =
71
+ Adds embedded Tweets grid template, Vine embeds, and Periscope On Air buttons.
72
+
73
  = 1.2.0 =
74
  Support Twitter Moment embeds.
75
 
81
 
82
  == Changelog ==
83
 
84
+ = 1.3.0 =
85
+ * Display multiple Tweets in a [media-rich responsive grid template](https://dev.twitter.com/web/embedded-timelines/collection#template-grid) by pasting a Twitter collection URL
86
+ * Add a [Periscope On Air button](https://www.periscope.tv/embed) through a widget, shortcode, or as an embed handler for a Periscope profile URL
87
+ * Add a Vine through a URL or shortcode
88
+ * Shortcode UI integration now uses the `register_shortcode_ui` action introduced in Shortcake 0.5.0
89
+
90
  = 1.2.0 =
91
  * Embed a Twitter Moment by simply pasting a URL
92
  * Always load Twitter ads conversion tracking JavaScript over HTTPS
104
  * Fix: save Follow button widget with no overrides
105
 
106
  = 1.0.0 =
107
+ * Embedded Tweet
108
+ * Embedded Tweet with video template
109
  * Tweet button
110
  * Twitter Cards
111
  * Follow button
112
+ * Advertising tracker for Twitter custom audiences and ad conversion
113
 
114
  == Frequently Asked Questions ==
115
 
119
 
120
  = How do I include an embedded timeline in my page? =
121
 
122
+ Paste a Twitter collection URL into your post content to see a media-rich grid display on your website.
123
 
124
+ Log in to Twitter.com and visit the [Twitter widgets settings page](https://twitter.com/settings/widgets) to create and manage user, list, and search [embedded timeline](https://dev.twitter.com/web/embedded-timelines) widgets for your account. Widget settings are saved to a widget identifier for the logged in account; you may want to create a widget from your site's account, not your personal account, for continuity and general organization. Copy-and-paste the HTML generated by the Twitter widgets configuration tool into a new [WordPress text widget](http://codex.wordpress.org/WordPress_Widgets#Using_Text_Widgets).
125
 
126
  = My custom link color and border color do not appear in embedded Tweets or timelines =
127
 
src/Twitter/Helpers/Validators/PeriscopeUsername.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\Helpers\Validators;
27
+
28
+ /**
29
+ * Test for Periscope username validity
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class PeriscopeUsername extends ScreenName
34
+ {
35
+ }
src/Twitter/Widgets/Language.php CHANGED
@@ -47,9 +47,10 @@ class Language
47
  'cs' => 'Czech',
48
  'da' => 'Danish',
49
  'de' => 'German',
 
50
  'en' => 'English',
51
  'es' => 'Spanish',
52
- 'fa' => 'Farsi',
53
  'fi' => 'Finnish',
54
  'fil' => 'Filipino',
55
  'fr' => 'French',
@@ -70,6 +71,7 @@ class Language
70
  'sv' => 'Swedish',
71
  'th' => 'Thai',
72
  'tr' => 'Turkish',
 
73
  'ur' => 'Urdu',
74
  'vi' => 'Vietnamese',
75
  'zh-cn' => 'Simplified Chinese',
47
  'cs' => 'Czech',
48
  'da' => 'Danish',
49
  'de' => 'German',
50
+ 'el' => 'Greek',
51
  'en' => 'English',
52
  'es' => 'Spanish',
53
+ 'fa' => 'Persian',
54
  'fi' => 'Finnish',
55
  'fil' => 'Filipino',
56
  'fr' => 'French',
71
  'sv' => 'Swedish',
72
  'th' => 'Thai',
73
  'tr' => 'Turkish',
74
+ 'uk' => 'Ukrainian',
75
  'ur' => 'Urdu',
76
  'vi' => 'Vietnamese',
77
  'zh-cn' => 'Simplified Chinese',
src/Twitter/Widgets/PeriscopeOnAir.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\Widgets;
27
+
28
+ /**
29
+ * Periscope profile button with on-air status display
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class PeriscopeOnAir extends BaseWidget
34
+ {
35
+ /**
36
+ * HTML class expected by the Periscope widget JS
37
+ *
38
+ * @since 1.3.0
39
+ *
40
+ * @type string
41
+ */
42
+ const HTML_CLASS = 'periscope-on-air';
43
+
44
+
45
+ /**
46
+ * Default button size
47
+ *
48
+ * @since 1.3.0
49
+ *
50
+ * @type string
51
+ */
52
+ const DEFAULT_SIZE = 'small';
53
+
54
+ /**
55
+ * Periscope web profile base URL
56
+ *
57
+ * @since 1.3.0
58
+ *
59
+ * @type string
60
+ */
61
+ const BASE_URL = 'https://periscope.tv/';
62
+
63
+ /**
64
+ * Periscope username
65
+ *
66
+ * @since 1.3.0
67
+ *
68
+ * @type string
69
+ */
70
+ protected $username;
71
+
72
+ /**
73
+ * Allowed values for the size property
74
+ *
75
+ * @since 1.3.0
76
+ *
77
+ * @type array allowed sizes {
78
+ * @type string size
79
+ * @type bool exists
80
+ * }
81
+ */
82
+ public static $ALLOWED_SIZES = array( 'small' => true, 'large' => true );
83
+
84
+ /**
85
+ * Size of the button
86
+ *
87
+ * @since 1.3.0
88
+ *
89
+ * @type string
90
+ */
91
+ protected $size;
92
+
93
+ /**
94
+ * Require username
95
+ *
96
+ * @since 1.3.0
97
+ */
98
+ public function __construct($username, $validate = true)
99
+ {
100
+ $username = \Twitter\Helpers\Validators\PeriscopeUsername::trim($username);
101
+ if (false === $validate || \Twitter\Helpers\Validators\PeriscopeUsername::isValid($username)) {
102
+ $this->username = $username;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Retrieve the stored Periscope username
108
+ *
109
+ * @since 1.3.0
110
+ *
111
+ * @return string Periscope username or empty string if username not set
112
+ */
113
+ public function getUsername()
114
+ {
115
+ return $this->username ?: '';
116
+ }
117
+
118
+ /**
119
+ * Build a Periscope web profile URL
120
+ *
121
+ * @since 1.3.0
122
+ *
123
+ * @return Periscope web profile URL or empty string if username not set
124
+ */
125
+ public function getWebProfileURL()
126
+ {
127
+ return $this->username ? static::BASE_URL . $this->username : '';
128
+ }
129
+
130
+ /**
131
+ * Set the desired size of the On Air button
132
+ *
133
+ * @since 1.3.0
134
+ *
135
+ * @param string $size button size
136
+ *
137
+ * @return __CLASS__ support chaining
138
+ */
139
+ public function setSize($size)
140
+ {
141
+ if ($size && isset(static::$ALLOWED_SIZES[$size])) {
142
+ $this->size = $size;
143
+ }
144
+ return $this;
145
+ }
146
+
147
+ /**
148
+ * Build a Periscope On Air object from an associative array
149
+ *
150
+ * @since 1.3.0
151
+ *
152
+ * @param array $options associative array of options {
153
+ * @type string option name
154
+ * @type string option value
155
+ * }
156
+ *
157
+ * @return __CLASS__ support chaining
158
+ */
159
+ public static function fromArray($options)
160
+ {
161
+ if (! isset( $options['username'] ) && $options['username']) {
162
+ return;
163
+ }
164
+
165
+ $class = __CLASS__;
166
+ $on_air = new $class( $options['username'] );
167
+ unset( $class );
168
+
169
+ $on_air->setBaseOptions($options);
170
+
171
+ if (isset( $options['size'] ) && static::DEFAULT_SIZE !== $options['size']) {
172
+ $on_air->setSize($options['size']);
173
+ }
174
+
175
+ return $on_air;
176
+ }
177
+
178
+ /**
179
+ * Convert the class object into an array, removing default field values
180
+ *
181
+ * @since 1.3.0
182
+ *
183
+ * @return array properties as associative array
184
+ */
185
+ public function toArray()
186
+ {
187
+ $data = parent::toArray();
188
+
189
+ if ($this->size && static::DEFAULT_SIZE !== $this->size) {
190
+ $data['size'] = $this->size;
191
+ }
192
+
193
+ return $data;
194
+ }
195
+
196
+ /**
197
+ * Generate a link to a Periscope web profile configured for enhancement by the Twitter for Websites JavaScript
198
+ *
199
+ * @since 1.3.0
200
+ *
201
+ * @param string $html_builder_class callable HTML builder with a static anchorElement class
202
+ *
203
+ * @return string HTML markup or empty string if minimum requirements not met
204
+ */
205
+ public function toHTML($html_builder_class = '\Twitter\Helpers\HTMLBuilder')
206
+ {
207
+ // test for invalid passed class
208
+ if (! ( class_exists($html_builder_class) && method_exists($html_builder_class, 'anchorElement') )) {
209
+ return '';
210
+ }
211
+
212
+ $web_profile_url = $this->getWebProfileURL();
213
+ if (! $web_profile_url) {
214
+ return '';
215
+ }
216
+
217
+ return $html_builder_class::anchorElement(
218
+ $web_profile_url,
219
+ '@' . $this->getUsername(),
220
+ array(
221
+ 'class' => static::HTML_CLASS,
222
+ ),
223
+ $this->toArray()
224
+ );
225
+ }
226
+ }
src/Twitter/WordPress/Admin/Post/MetaBox.php CHANGED
@@ -66,9 +66,13 @@ class MetaBox
66
  */
67
  public static function load()
68
  {
69
- add_action( 'wp', array( '\Twitter\WordPress\Admin\Post\TweetIntent', 'registerPostMeta' ), 10, 0 );
70
  add_action( 'add_meta_boxes', array( __CLASS__, 'addMetaBox' ), 1, 0 );
71
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueueScripts' ) );
 
 
 
 
 
72
  }
73
 
74
  /**
@@ -163,8 +167,14 @@ class MetaBox
163
  // Use nonce for verification
164
  wp_nonce_field( plugin_basename( __FILE__ ), self::NONCE_NAME );
165
 
166
- \Twitter\WordPress\Admin\Post\TweetIntent::metaBoxContent();
167
- \Twitter\WordPress\Admin\Post\TwitterCard::metaBoxContent();
 
 
 
 
 
 
168
  }
169
 
170
  /**
66
  */
67
  public static function load()
68
  {
 
69
  add_action( 'add_meta_boxes', array( __CLASS__, 'addMetaBox' ), 1, 0 );
70
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueueScripts' ) );
71
+
72
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
73
+ if ( isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] ) ) {
74
+ add_action( 'wp', array( '\Twitter\WordPress\Admin\Post\TweetIntent', 'registerPostMeta' ), 10, 0 );
75
+ }
76
  }
77
 
78
  /**
167
  // Use nonce for verification
168
  wp_nonce_field( plugin_basename( __FILE__ ), self::NONCE_NAME );
169
 
170
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
171
+
172
+ if ( isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] ) ) {
173
+ \Twitter\WordPress\Admin\Post\TweetIntent::metaBoxContent();
174
+ }
175
+ if ( isset( $features[ \Twitter\WordPress\Features::CARDS ] ) ) {
176
+ \Twitter\WordPress\Admin\Post\TwitterCard::metaBoxContent();
177
+ }
178
  }
179
 
180
  /**
src/Twitter/WordPress/Admin/Profile/PeriscopeUser.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Profile;
27
+
28
+ /**
29
+ * Associate a WordPress account with a Periscope account
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class PeriscopeUser
34
+ {
35
+ /**
36
+ * Conditionally load features on the edit profile page.
37
+ *
38
+ * @since 1.3.0
39
+ *
40
+ * @return void
41
+ */
42
+ public static function init()
43
+ {
44
+ // only show to authors
45
+ if ( ! current_user_can( 'edit_posts' ) ) {
46
+ return;
47
+ }
48
+
49
+ // add Twitter after website
50
+ add_filter( 'user_contactmethods', array( __CLASS__, 'addContactMethod' ), 1, 2 );
51
+ // provide additional hints in label text
52
+ add_filter( 'user_periscope_label', array( __CLASS__, 'contactMethodLabel' ), 10, 1 );
53
+ // clean up user input
54
+ add_filter( 'sanitize_user_meta_periscope', array( __CLASS__, 'sanitize' ), 10, 1 );
55
+ }
56
+
57
+ /**
58
+ * Add Periscope as a contact method in the Contact Info profile section
59
+ *
60
+ * @since 1.3.0
61
+ *
62
+ * @see wp_get_user_contact_methods()
63
+ *
64
+ * @param array $methods contact methods and their labels {
65
+ * @type string contact method
66
+ * @type string label
67
+ * }
68
+ * @param WP_User $user WP_User object.
69
+ *
70
+ * @return array contact methods and their labels {
71
+ * @type string contact method
72
+ * @type string label
73
+ * }
74
+ */
75
+ public static function addContactMethod( $methods, $user )
76
+ {
77
+ $methods['periscope'] = 'Periscope';
78
+ return $methods;
79
+ }
80
+
81
+ /**
82
+ * Customize HTML display label for contact method
83
+ *
84
+ * @since 1.3.0
85
+ *
86
+ * @param string $label HTML label
87
+ *
88
+ * @return string HTML label
89
+ */
90
+ public static function contactMethodLabel( $label = '' )
91
+ {
92
+ return _x( 'Periscope username', 'Prompt requesting entry of a Periscope username', 'twitter' );
93
+ }
94
+
95
+ /**
96
+ * Clean up user inputted Periscope username value before saving the option
97
+ *
98
+ * @since 1.3.0
99
+ *
100
+ * @param string $username inputted Periscope username value
101
+ *
102
+ * @return string sanitized Periscope username value
103
+ */
104
+ public static function sanitize( $username )
105
+ {
106
+ if ( ! is_string( $username ) ) {
107
+ return '';
108
+ }
109
+ $username = trim( $username );
110
+ if ( ! $username ) {
111
+ return '';
112
+ }
113
+ $username = sanitize_text_field( $username );
114
+ if ( ! $username ) {
115
+ return '';
116
+ }
117
+
118
+ return \Twitter\Helpers\Validators\PeriscopeUsername::sanitize( $username );
119
+ }
120
+ }
src/Twitter/WordPress/Admin/Settings/SinglePage.php CHANGED
@@ -75,6 +75,32 @@ class SinglePage
75
  return __( 'Twitter settings', 'twitter' );
76
  }
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  /**
79
  * Add a submenu item to WordPress admin.
80
  *
@@ -101,8 +127,10 @@ class SinglePage
101
  }
102
  $settings->hook_suffix = $hook_suffix;
103
 
 
 
104
  // add each settings component to the single page settings page
105
- foreach ( static::$SETTINGS_COMPONENTS as $settings_component ) {
106
  $settings_component::addToSettingsPage( $hook_suffix );
107
  }
108
 
75
  return __( 'Twitter settings', 'twitter' );
76
  }
77
 
78
+ /**
79
+ * Do not display a settings section if the related feature has been disabled by the site
80
+ *
81
+ * @since 1.3.0
82
+ *
83
+ * @return array {
84
+ * Settings component full qualified class names
85
+
86
+ * @type string fully qualified class name
87
+ * }
88
+ */
89
+ public static function getSettingsComponentsForEnabledFeatures() {
90
+ $components = static::$SETTINGS_COMPONENTS;
91
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
92
+
93
+ if ( ! isset( $features[ \Twitter\WordPress\Features::EMBED_TWEET ] ) ) {
94
+ unset( $components['\Twitter\WordPress\Admin\Settings\Theme'] );
95
+ }
96
+
97
+ if ( ! isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] ) ) {
98
+ unset( $components['\Twitter\WordPress\Admin\Settings\TweetButton'] );
99
+ }
100
+
101
+ return $components;
102
+ }
103
+
104
  /**
105
  * Add a submenu item to WordPress admin.
106
  *
127
  }
128
  $settings->hook_suffix = $hook_suffix;
129
 
130
+ $settings_components = static::getSettingsComponentsForEnabledFeatures();
131
+
132
  // add each settings component to the single page settings page
133
+ foreach ( $settings_components as $settings_component ) {
134
  $settings_component::addToSettingsPage( $hook_suffix );
135
  }
136
 
src/Twitter/WordPress/Features.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress;
27
+
28
+ /**
29
+ * Customize available features
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class Features
34
+ {
35
+ /**
36
+ * Describe webpage content to Twitter
37
+ *
38
+ * Twitter Cards describes a webpage using Twitter-specific HTML meta elements. Populate a link preview for URLs shared on Twitter, attribute content to a site and author Twitter account, and deeplink into the equivalent view in your iOS or Android apps.
39
+ *
40
+ * @since 1.3.0
41
+ *
42
+ * @link https://dev.twitter.com/cards/overview Twitter Cards
43
+ *
44
+ * @type string
45
+ */
46
+ const CARDS = 'cards';
47
+
48
+ /**
49
+ * Embed a single Tweet by URL or shortcode
50
+ *
51
+ * @since 1.3.0
52
+ *
53
+ * @link https://dev.twitter.com/web/embedded-tweets Embedded Tweet
54
+ *
55
+ * @type string
56
+ */
57
+ const EMBED_TWEET = 'embed-tweet';
58
+
59
+ /**
60
+ * Embed a single Tweet with a video template using a shortcode
61
+ *
62
+ * @since 1.3.0
63
+ *
64
+ * @link https://dev.twitter.com/web/embedded-video Embedded Video
65
+ *
66
+ * @type string
67
+ */
68
+ const EMBED_TWEET_VIDEO = 'embed-tweet-video';
69
+
70
+ /**
71
+ * Embed a single Vine
72
+ *
73
+ * @since 1.3.0
74
+ *
75
+ * @link https://dev.twitter.com/web/vine Vine Embed
76
+ *
77
+ * @type string
78
+ */
79
+ const EMBED_VINE = 'embed-vine';
80
+
81
+ /**
82
+ * Embed a Twitter Moment by URL or shortcode
83
+ *
84
+ * @since 1.3.0
85
+ *
86
+ * @link https://twitter.com/i/moments Twitter Moments
87
+ *
88
+ * @type string
89
+ */
90
+ const EMBED_MOMENT = 'embed-moment';
91
+
92
+ /**
93
+ * Embed multiple Tweets in a responsive grid format by URL or shortcode
94
+ *
95
+ * @since 1.3.0
96
+ *
97
+ * @type string
98
+ */
99
+ const EMBED_TWEETS_GRID = 'embed-tweets-grid';
100
+
101
+ /**
102
+ * Display a Twitter follow button using a shortcode
103
+ *
104
+ * @since 1.3.0
105
+ *
106
+ * @link https://dev.twitter.com/web/follow-button Follow Button
107
+ *
108
+ * @type string
109
+ */
110
+ const FOLLOW_BUTTON = 'follow';
111
+
112
+ /**
113
+ * Display a Tweet button using a shortcode
114
+ *
115
+ * @since 1.3.0
116
+ *
117
+ * @link https://dev.twitter.com/web/tweet-button Tweet Button
118
+ *
119
+ * @type string
120
+ */
121
+ const TWEET_BUTTON = 'share';
122
+
123
+ /**
124
+ * Display a Periscope On Air button using a shortcode
125
+ *
126
+ * @since 1.3.0
127
+ *
128
+ * @link https://www.periscope.tv/embed Periscope On Air embed
129
+ *
130
+ * @type string
131
+ */
132
+ const PERISCOPE_ON_AIR = 'periscope-on-air';
133
+
134
+ /**
135
+ * Audience conversion pixel
136
+ *
137
+ * Track your website's audience for retargeting on Twitter
138
+ * Track the result of a Twitter targeted user visiting your webpage
139
+ *
140
+ * @since 1.3.0
141
+ *
142
+ * @link https://business.twitter.com/help/website-tag-for-remarketing Twitter website tag for remarketing
143
+ * @link https://business.twitter.com/help/conversion-tracking-for-websites Twitter conversion tracking for websites
144
+ *
145
+ * @type string
146
+ */
147
+ const TRACKING_PIXEL = 'ad-pixel';
148
+
149
+ /**
150
+ * All available features
151
+ *
152
+ * Allows a publisher to filter features available on the site
153
+ *
154
+ * @since 1.3.0
155
+ *
156
+ * @type array
157
+ */
158
+ public static $features = array(
159
+ self::CARDS => true, // Twitter Cards
160
+ self::EMBED_MOMENT => true, // Twitter Moment
161
+ self::EMBED_TWEET => true, // single Tweet
162
+ self::EMBED_TWEET_VIDEO => true, // single Tweet with video-specific display template
163
+ self::EMBED_VINE => true, // single Vine
164
+ self::EMBED_TWEETS_GRID => true, // multiple Tweets displayed in a grid layout
165
+ self::FOLLOW_BUTTON => true, // Twitter Follow button
166
+ self::TWEET_BUTTON => true, // Tweet button
167
+ self::PERISCOPE_ON_AIR => true, // Periscope On Air button
168
+ self::TRACKING_PIXEL => true, // audience and conversion pixel
169
+ );
170
+
171
+ /**
172
+ * Get a list of features enabled for the current site
173
+ *
174
+ * Allows a site owner to install the plugin but limit its features
175
+ *
176
+ * @since 1.3.0
177
+ *
178
+ * @return array list of features {
179
+ * List of features
180
+ *
181
+ * @type string $key feature name
182
+ * @type bool $value true
183
+ * }
184
+ */
185
+ public static function getEnabledFeatures()
186
+ {
187
+ /**
188
+ * Limit the features available in the Twitter plugin for WordPress
189
+ *
190
+ * Disable specific features of the Twitter plugin for WordPress
191
+ *
192
+ * @since 1.3.0
193
+ *
194
+ * @param array $features {
195
+ *
196
+ * @type string $feature feature toggle. if present, the feature is active.
197
+ * @type bool $exists used to form an associative array. a false value has no effect
198
+ * }
199
+ */
200
+ $features = apply_filters( 'twitter_features', static::$features );
201
+ if ( ! is_array( $features ) ) {
202
+ $features = array();
203
+ }
204
+
205
+ return $features;
206
+ }
207
+ }
src/Twitter/WordPress/Helpers/VineOEmbed.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Helpers;
27
+
28
+ /**
29
+ * Request oEmbeds from Twitter's publisher endpoints
30
+ *
31
+ * @since 1.2.0
32
+ */
33
+ class VineOEmbed extends TwitterOEmbed
34
+ {
35
+ /**
36
+ * Vine oEmbed host FQDN
37
+ *
38
+ * @since 1.3.0
39
+ *
40
+ * @type string
41
+ */
42
+ const HOST = 'vine.co';
43
+ }
src/Twitter/WordPress/JavaScriptLoaders/AsyncJavaScript.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\JavaScriptLoaders;
27
+
28
+ /**
29
+ * Improve BackPress JavaScript handlers for asynchronous use
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ abstract class AsyncJavaScript
34
+ {
35
+ /**
36
+ * Proactively resolve FQDN asynchronously before later use
37
+ *
38
+ * @since 1.3.0
39
+ *
40
+ * @link https://dev.chromium.org/developers/design-documents/dns-prefetching Chromium prefetch behavior
41
+ * @link https://developer.mozilla.org/en-US/docs/Controlling_DNS_prefetching Firefox prefetch behavior
42
+ *
43
+ * @return void
44
+ */
45
+ public static function dnsPrefetch()
46
+ {
47
+ if ( ! ( defined( get_called_class() . '::FQDN' ) && static::FQDN ) ) {
48
+ return;
49
+ }
50
+
51
+ echo '<link rel="dns-prefetch" href="//' . esc_attr( static::FQDN ) . '"' . \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>' . "\n";
52
+ }
53
+
54
+ /**
55
+ * Register JavaScript to be later referenced by queue handle
56
+ *
57
+ * Set up related inline JavaScript to be loaded if SCRIPT_EXTRA exists.
58
+ *
59
+ * @since 1.3.0
60
+ *
61
+ * @return bool true on a successful wp_register_script response
62
+ */
63
+ public static function register()
64
+ {
65
+ global $wp_scripts;
66
+
67
+ $classname = get_called_class();
68
+ if ( ! ( defined( $classname . '::QUEUE_HANDLE' ) && static::QUEUE_HANDLE && defined( $classname . '::URI' ) && static::URI ) ) {
69
+ return false;
70
+ }
71
+
72
+ $registered = wp_register_script(
73
+ static::QUEUE_HANDLE,
74
+ static::URI, // should be overridden during queue output by asyncScriptLoaderSrc
75
+ array(), // no dependencies
76
+ null, // do not add extra query parameters for cache busting
77
+ true // in footer
78
+ );
79
+
80
+ // treat null response as true
81
+ if ( ! is_bool( $registered ) ) {
82
+ $registered = true;
83
+ }
84
+
85
+ // replace standard script element with async script element
86
+ add_filter( 'script_loader_src', array( get_called_class(), 'asyncScriptLoaderSrc' ), 1, 2 );
87
+
88
+ if ( defined( $classname . '::SCRIPT_EXTRA' ) && is_string( static::SCRIPT_EXTRA ) && static::SCRIPT_EXTRA ) {
89
+ $script = static::SCRIPT_EXTRA;
90
+ $data = $wp_scripts->get_data( static::QUEUE_HANDLE, 'data' );
91
+ if ( $data ) {
92
+ // WP 4.3+
93
+ // do not add script data if data was possibly previously added
94
+ if ( $registered ) {
95
+ $script = $data . "\n" . $script;
96
+ }
97
+ }
98
+ $wp_scripts->add_data( static::QUEUE_HANDLE, 'data', $script );
99
+ }
100
+
101
+ return $registered;
102
+ }
103
+
104
+ /**
105
+ * Enqueue the JavaScript
106
+ *
107
+ * @since 1.3.0
108
+ *
109
+ * @uses wp_enqueue_script()
110
+ *
111
+ * @return string async JavaScript loading snippet if script queue may not be supported. empty string if enqueued
112
+ */
113
+ public static function enqueue()
114
+ {
115
+ if ( ! ( defined( get_called_class() . '::QUEUE_HANDLE' ) && static::QUEUE_HANDLE ) ) {
116
+ return '';
117
+ }
118
+
119
+ if ( ! wp_script_is( static::QUEUE_HANDLE, 'registered' ) ) {
120
+ if ( ! static::register() ) {
121
+ return '';
122
+ }
123
+ }
124
+
125
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
126
+ return static::asyncScriptLoaderInline();
127
+ }
128
+
129
+ wp_enqueue_script( static::QUEUE_HANDLE );
130
+
131
+ return '';
132
+ }
133
+
134
+ /**
135
+ * Get the script element HTML markup used to load JavaScript in a browser
136
+ *
137
+ * @since 1.3.0
138
+ *
139
+ * @return string HTML <script> element
140
+ */
141
+ public static function getScriptElement()
142
+ {
143
+ $classname = get_called_class();
144
+ if ( ! ( defined( $classname . '::QUEUE_HANDLE' ) && static::QUEUE_HANDLE && defined( $classname . '::URI' ) && static::URI ) ) {
145
+ return false;
146
+ }
147
+
148
+ // type = text/javascript to match default WP_Scripts output
149
+ // id to match on DOM getElementByID in async inline loading snippet
150
+ // async property to unlock page load, preload scanner discoverable in modern browsers
151
+ // defer property for IE 9 and older
152
+ return '<script type="text/javascript" id="' . esc_attr( static::QUEUE_HANDLE ) . '" async defer src="' . esc_url( static::URI, array( 'http', 'https' ) ) . '" charset="utf-8"></script>' . "\n";
153
+ }
154
+
155
+ /**
156
+ * Create our own script element markup, replacing WordPress default with async loading
157
+ *
158
+ * Can be used with `script_loader_tag` filter in WordPress 4.1+.
159
+ *
160
+ * @since 1.3.0
161
+ *
162
+ * @param string $tag The `<script>` tag for the enqueued script
163
+ * @param string $handle The script's registered handle
164
+ * @param string $src The script's source URL
165
+ *
166
+ * @return string The `<script>` tag for the enqueued script
167
+ */
168
+ public static function scriptLoaderTag( $tag, $handle, $src = '' )
169
+ {
170
+ if ( ! ( defined( get_called_class() . '::QUEUE_HANDLE' ) && static::QUEUE_HANDLE ) ) {
171
+ return '';
172
+ }
173
+
174
+ if ( ! ( is_string( $handle ) && $handle === static::QUEUE_HANDLE ) ) {
175
+ return $tag;
176
+ }
177
+
178
+ return static::getScriptElement();
179
+ }
180
+
181
+ /**
182
+ * Load enqueued JavaScript using async deferred JavaScript properties
183
+ *
184
+ * Called from `script_loader_src` filter.
185
+ *
186
+ * @since 1.3.0
187
+ *
188
+ * @param string $src script URL
189
+ * @param string $handle WordPress registered script handle
190
+ * @global WP_Scripts $wp_scripts match concatenation preferences
191
+ *
192
+ * @return string empty string if our queue handle requested, else give back the src variable
193
+ */
194
+ public static function asyncScriptLoaderSrc( $src, $handle )
195
+ {
196
+ global $wp_scripts;
197
+
198
+ if ( ! ( is_string( $handle ) && defined( get_called_class() . '::QUEUE_HANDLE' ) && $handle === static::QUEUE_HANDLE ) ) {
199
+ return $src;
200
+ }
201
+
202
+ $html = static::getScriptElement();
203
+
204
+ if ( isset( $wp_scripts ) && $wp_scripts->do_concat ) {
205
+ $wp_scripts->print_html .= $html;
206
+ } else {
207
+ echo $html;
208
+ }
209
+
210
+ // empty out the src response to avoid extra <script>
211
+ return '';
212
+ }
213
+
214
+ /**
215
+ * Load JavaScript using an inline script block
216
+ *
217
+ * Suitable for unknown render environments where a script block may not be included in a standard enqueue output such as the wp_print_footer_scripts action. Dynamically-inserted JavaScript src is async by default.
218
+ *
219
+ * @since 1.3.0
220
+ *
221
+ * @param bool $include_script_element_wrapper wrap the returned JavaScript string in a script element wrapper
222
+ *
223
+ * @return string HTML script element containing loader script
224
+ */
225
+ public static function asyncScriptLoaderInline( $include_script_element_wrapper = true )
226
+ {
227
+ $classname = get_called_class();
228
+ if ( ! ( defined( $classname . '::QUEUE_HANDLE' ) && static::QUEUE_HANDLE && defined( $classname . '::URI' ) && static::URI ) ) {
229
+ return '';
230
+ }
231
+
232
+ $script = '(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(d.getElementById(id))return;js=d.createElement(s);js.id=id;js.src=' . json_encode( static::URI ) . ';fjs.parentNode.insertBefore(js,fjs);}(document,"script",' . json_encode( static::QUEUE_HANDLE ) . '));';
233
+
234
+ if ( $include_script_element_wrapper ) {
235
+ return '<script>' . $script . '</script>';
236
+ }
237
+ return $script;
238
+ }
239
+ }
src/Twitter/WordPress/JavaScriptLoaders/VineEmbed.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\JavaScriptLoaders;
27
+
28
+ /**
29
+ * Load the remotely hosted Vine embed JavaScript
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class VineEmbed extends AsyncJavaScript
34
+ {
35
+ /**
36
+ * Vine embed JavaScript handle
37
+ *
38
+ * Used in WordPress JavaScript queue
39
+ *
40
+ * @since 1.3.0
41
+ *
42
+ * @type string
43
+ */
44
+ const QUEUE_HANDLE = 'vine-embed-js';
45
+
46
+ /**
47
+ * Vine embed JavaScript fully-qualified domain name
48
+ *
49
+ * Used to prefetch DNS lookup
50
+ *
51
+ * @since 1.3.0
52
+ *
53
+ * @type string
54
+ */
55
+ const FQDN = 'platform.vine.co';
56
+
57
+ /**
58
+ * Vine embed JavaScript absolute URI
59
+ *
60
+ * @since 1.3.0
61
+ *
62
+ * @type string
63
+ */
64
+ const URI = 'https://platform.vine.co/static/scripts/embed.js';
65
+ }
src/Twitter/WordPress/JavaScriptLoaders/Widgets.php CHANGED
@@ -30,7 +30,7 @@ namespace Twitter\WordPress\JavaScriptLoaders;
30
  *
31
  * @since 1.0.0
32
  */
33
- class Widgets
34
  {
35
  /**
36
  * Twitter widget JavaScript handle
@@ -64,157 +64,18 @@ class Widgets
64
  const URI = 'https://platform.twitter.com/widgets.js';
65
 
66
  /**
67
- * Proactively resolve Twitter widget JS FQDN asynchronously before later use
68
  *
69
- * @since 1.0.0
70
- *
71
- * @link http://dev.chromium.org/developers/design-documents/dns-prefetching Chromium prefetch behavior
72
- * @link https://developer.mozilla.org/en-US/docs/Controlling_DNS_prefetching Firefox prefetch behavior
73
- *
74
- * @return void
75
- */
76
- public static function dnsPrefetch()
77
- {
78
- echo '<link rel="dns-prefetch" href="//' . esc_attr( self::FQDN ) . '"';
79
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
80
- echo '>' . "\n";
81
- }
82
-
83
- /**
84
- * Register Twitter widget JavaScript
85
- *
86
- * @since 1.0.0
87
- *
88
- * @return bool true on a successful wp_register_script response
89
- */
90
- public static function register()
91
- {
92
- global $wp_scripts;
93
-
94
- $registered = wp_register_script(
95
- self::QUEUE_HANDLE,
96
- self::URI, // should be overridden during queue output by asyncScriptLoaderSrc
97
- array(), // no dependencies
98
- null, // no not add extra query parameters for cache busting
99
- true // in footer
100
- );
101
-
102
- // treat null response as true
103
- if ( ! is_bool( $registered ) ) {
104
- $registered = true;
105
- }
106
-
107
- // replace standard script element with async script element
108
- add_filter( 'script_loader_src', array( __CLASS__, 'asyncScriptLoaderSrc' ), 1, 2 );
109
-
110
- // initialize the twttr variable to attach ready events before JS loaded
111
- $script = 'window.twttr=(function(w){t=w.twttr||{};t._e=[];t.ready=function(f){t._e.push(f);};return t;}(window));';
112
- $data = $wp_scripts->get_data( self::QUEUE_HANDLE, 'data' );
113
- if ( $data ) {
114
- // WP 4.3+
115
- // do not add script data if data was possibly previously added
116
- if ( $registered ) {
117
- $script = $data . "\n" . $script;
118
- }
119
- }
120
- $wp_scripts->add_data( self::QUEUE_HANDLE, 'data', $script );
121
-
122
- return $registered;
123
- }
124
-
125
- /**
126
- * Enqueue the widgets JavaScript
127
- *
128
- * @since 1.0.0
129
- *
130
- * @uses wp_enqueue_script()
131
- *
132
- * @return string async JavaScript loading snippet if script queue may not be supported. empty string if enqueued
133
- */
134
- public static function enqueue()
135
- {
136
- if ( ! wp_script_is( self::QUEUE_HANDLE, 'registered' ) ) {
137
- static::register();
138
- }
139
-
140
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
141
- return static::asyncScriptLoaderInline();
142
- }
143
-
144
- wp_enqueue_script( self::QUEUE_HANDLE );
145
-
146
- return '';
147
- }
148
-
149
- /**
150
- * Get the script element HTML markup used to load widgets.js in a browser
151
- *
152
- * @since 1.0.0
153
- *
154
- * @return string HTML <script> element
155
- */
156
- public static function getScriptElement()
157
- {
158
- // type = text/javascript to match default WP_Scripts output
159
- // async property to unlock page load, preload scanner discoverable in modern browsers
160
- // defer property for IE 9 and older
161
- return '<script type="text/javascript" id="' . esc_attr( self::QUEUE_HANDLE ) . '" async defer src="' . esc_url( self::URI, array( 'http', 'https' ) ) . '" charset="utf-8"></script>' . "\n";
162
- }
163
-
164
- /**
165
- * Create our own script element markup, replacing WordPress default with async loading
166
  *
167
- * Can be used with `script_loader_tag` filter in WordPress 4.1+
168
- *
169
- * @since 1.0.0
170
  *
171
- * @param string $tag The `<script>` tag for the enqueued script.
172
- * @param string $handle The script's registered handle.
173
- * @param string $src The script's source URL.
174
  *
175
- * @return string The `<script>` tag for the enqueued script
176
- */
177
- public static function scriptLoaderTag( $tag, $handle, $src = '' )
178
- {
179
- if ( ! ( is_string( $handle ) && $handle === static::QUEUE_HANDLE ) ) {
180
- return $tag;
181
- }
182
-
183
- return static::getScriptElement();
184
- }
185
-
186
- /**
187
- * Load Twitter widget JS using async deferred JavaScript properties
188
- *
189
- * Called from `script_loader_src` filter.
190
- *
191
- * @since 1.0.0
192
- *
193
- * @param string $src script URL
194
- * @param string $handle WordPress registered script handle
195
- * @global WP_Scripts $wp_scripts match concatenation preferences
196
- *
197
- * @return string empty string if Twitter widget JS, else give back the src variable
198
  */
199
- public static function asyncScriptLoaderSrc( $src, $handle )
200
- {
201
- global $wp_scripts;
202
-
203
- if ( ! ( is_string( $handle ) && $handle === self::QUEUE_HANDLE ) ) {
204
- return $src;
205
- }
206
-
207
- $html = static::getScriptElement();
208
-
209
- if ( isset( $wp_scripts ) && $wp_scripts->do_concat ) {
210
- $wp_scripts->print_html .= $html;
211
- } else {
212
- echo $html;
213
- }
214
-
215
- // empty out the src response to avoid extra <script>
216
- return '';
217
- }
218
 
219
  /**
220
  * Load Twitter widget JS using an inline script block
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Widgets extends AsyncJavaScript
34
  {
35
  /**
36
  * Twitter widget JavaScript handle
64
  const URI = 'https://platform.twitter.com/widgets.js';
65
 
66
  /**
67
+ * Extra JavaScript to be loaded with external JS
68
  *
69
+ * Initialize the twttr variable to attach ready events before JS loaded
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  *
71
+ * @see WP_Scripts::print_extra_script()
72
+ * @link https://dev.twitter.com/web/javascript/loading Twitter Widgets JS async loading snippet
 
73
  *
74
+ * @since 1.3.0
 
 
75
  *
76
+ * @type string
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  */
78
+ const SCRIPT_EXTRA = 'window.twttr=(function(w){t=w.twttr||{};t._e=[];t.ready=function(f){t._e.push(f);};return t;}(window));';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  /**
81
  * Load Twitter widget JS using an inline script block
src/Twitter/WordPress/PluginLoader.php CHANGED
@@ -41,7 +41,7 @@ class PluginLoader
41
  *
42
  * @type string
43
  */
44
- const VERSION = '1.2.0';
45
 
46
  /**
47
  * Unique domain of the plugin's translated text
@@ -61,25 +61,27 @@ class PluginLoader
61
  */
62
  public static function init()
63
  {
 
 
64
  // load translated text
65
- add_action( 'init', array( __CLASS__, 'loadTranslatedText' ) );
66
 
67
  // compatibility wrappers to coexist with other popular plugins
68
- add_action( 'plugins_loaded', array( __CLASS__, 'compatibility' ) );
69
 
70
  // make widgets available on front and back end
71
- add_action( 'widgets_init', array( __CLASS__, 'widgetsInit' ) );
72
 
73
  // register Twitter JavaScript to eligible for later enqueueing
74
- add_action( 'wp_enqueue_scripts', array( __CLASS__, 'registerScripts' ), 1, 0 );
75
 
76
  if ( is_admin() ) {
77
  // admin-specific functionality
78
- add_action( 'init', array( __CLASS__, 'adminInit' ) );
79
  } else {
80
  // hooks to be executed on general execution of WordPress such as public pageviews
81
- add_action( 'init', array( __CLASS__, 'publicInit' ) );
82
- add_action( 'wp_head', array( __CLASS__, 'wpHead' ), 1, 0 );
83
  }
84
 
85
  // shortcodes
@@ -135,7 +137,15 @@ class PluginLoader
135
  */
136
  public static function widgetsInit()
137
  {
138
- register_widget( '\Twitter\WordPress\Widgets\Follow' );
 
 
 
 
 
 
 
 
139
  }
140
 
141
  /**
@@ -147,14 +157,22 @@ class PluginLoader
147
  */
148
  public static function adminInit()
149
  {
 
 
 
 
 
 
150
  // Twitter settings menu
151
  \Twitter\WordPress\Admin\Settings\Loader::init();
152
 
153
- // Edit post meta box
154
- add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Post\MetaBox', 'init' ) );
155
-
156
- // User profile fields
157
- add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Profile\User', 'init' ) );
 
 
158
  }
159
 
160
  /**
@@ -171,10 +189,16 @@ class PluginLoader
171
  return;
172
  }
173
 
 
 
174
  // load widgets JS if a Twitter widget is active
175
- if ( is_active_widget( false, false, \Twitter\WordPress\Widgets\Follow::BASE_ID, true ) ) {
 
 
 
176
  // enqueue after the script is registered in wp_enqueue_scripts action priority 1
177
  add_action( 'wp_enqueue_scripts', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'enqueue' ) );
 
178
  }
179
 
180
  // do not add content filters to HTTP 404 response
@@ -182,6 +206,10 @@ class PluginLoader
182
  return;
183
  }
184
 
 
 
 
 
185
  /**
186
  * Set the priority to apply to Twitter elements automatically added to the_content
187
  *
@@ -210,56 +238,53 @@ class PluginLoader
210
  */
211
  public static function registerShortcodeHandlers()
212
  {
 
 
 
213
  // features requiring HTTPS remote requests
214
  if ( wp_http_supports( array( 'ssl' => true ) ) ) {
215
- // Embedded Tweet
216
- add_action(
217
- 'plugins_loaded',
218
- array( '\Twitter\WordPress\Shortcodes\EmbeddedTweet', 'init' ),
219
- 5,
220
- 0
221
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
- // Twitter embedded videos
224
- add_action(
225
- 'plugins_loaded',
226
- array( '\Twitter\WordPress\Shortcodes\EmbeddedTweetVideo', 'init' ),
227
- 5,
228
- 0
229
- );
 
 
 
 
 
230
 
231
- // Twitter Moment
232
  add_action(
233
  'plugins_loaded',
234
- array( '\Twitter\WordPress\Shortcodes\Moment', 'init' ),
235
  5,
236
  0
237
  );
238
  }
239
-
240
- // Follow button
241
- add_action(
242
- 'plugins_loaded',
243
- array( '\Twitter\WordPress\Shortcodes\Follow', 'init' ),
244
- 5,
245
- 0
246
- );
247
-
248
- // Tweet button
249
- add_action(
250
- 'plugins_loaded',
251
- array( '\Twitter\WordPress\Shortcodes\Share', 'init' ),
252
- 5,
253
- 0
254
- );
255
-
256
- // Ad conversion and audience tracking
257
- add_action(
258
- 'plugins_loaded',
259
- array( '\Twitter\WordPress\Shortcodes\Tracking', 'init' ),
260
- 5,
261
- 0
262
- );
263
  }
264
 
265
  /**
@@ -271,13 +296,17 @@ class PluginLoader
271
  */
272
  public static function wpHead()
273
  {
 
 
274
  // Twitter Cards markup
275
- add_action(
276
- 'wp_head',
277
- array( '\Twitter\WordPress\Head\CardsMetaElements', 'outputMetaElements' ),
278
- 99, // late priority to override if multiple values provided
279
- 0 // expects no arguments
280
- );
 
 
281
 
282
  // page-level customizations referenced by Twitter JavaScript
283
  add_action(
@@ -307,10 +336,13 @@ class PluginLoader
307
  */
308
  public static function registerScripts()
309
  {
310
- // widgets.js
311
  \Twitter\WordPress\JavaScriptLoaders\Widgets::register();
312
 
313
- // ad tracker
 
 
 
314
  \Twitter\WordPress\JavaScriptLoaders\Tracking::register();
315
  }
316
 
@@ -323,6 +355,9 @@ class PluginLoader
323
  */
324
  public static function compatibility()
325
  {
326
- \Twitter\WordPress\Cards\Compatibility::init();
 
 
 
327
  }
328
  }
41
  *
42
  * @type string
43
  */
44
+ const VERSION = '1.3.0';
45
 
46
  /**
47
  * Unique domain of the plugin's translated text
61
  */
62
  public static function init()
63
  {
64
+ $classname = get_called_class();
65
+
66
  // load translated text
67
+ add_action( 'init', array( $classname, 'loadTranslatedText' ) );
68
 
69
  // compatibility wrappers to coexist with other popular plugins
70
+ add_action( 'plugins_loaded', array( $classname, 'compatibility' ) );
71
 
72
  // make widgets available on front and back end
73
+ add_action( 'widgets_init', array( $classname, 'widgetsInit' ) );
74
 
75
  // register Twitter JavaScript to eligible for later enqueueing
76
+ add_action( 'wp_enqueue_scripts', array( $classname, 'registerScripts' ), 1, 0 );
77
 
78
  if ( is_admin() ) {
79
  // admin-specific functionality
80
+ add_action( 'init', array( $classname, 'adminInit' ) );
81
  } else {
82
  // hooks to be executed on general execution of WordPress such as public pageviews
83
+ add_action( 'init', array( $classname, 'publicInit' ) );
84
+ add_action( 'wp_head', array( $classname, 'wpHead' ), 1, 0 );
85
  }
86
 
87
  // shortcodes
137
  */
138
  public static function widgetsInit()
139
  {
140
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
141
+
142
+ if ( isset( $features[ \Twitter\WordPress\Features::FOLLOW_BUTTON ] ) ) {
143
+ register_widget( '\Twitter\WordPress\Widgets\Follow' );
144
+ }
145
+
146
+ if ( isset( $features[ \Twitter\WordPress\Features::PERISCOPE_ON_AIR ] ) ) {
147
+ register_widget( '\Twitter\WordPress\Widgets\PeriscopeOnAir' );
148
+ }
149
  }
150
 
151
  /**
157
  */
158
  public static function adminInit()
159
  {
160
+ // User profile fields
161
+ add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Profile\User', 'init' ) );
162
+ add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Profile\PeriscopeUser', 'init' ) );
163
+
164
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
165
+
166
  // Twitter settings menu
167
  \Twitter\WordPress\Admin\Settings\Loader::init();
168
 
169
+ if (
170
+ isset( $features[ \Twitter\WordPress\Features::CARDS ] ) ||
171
+ isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] )
172
+ ) {
173
+ // Edit post meta box
174
+ add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Post\MetaBox', 'init' ) );
175
+ }
176
  }
177
 
178
  /**
189
  return;
190
  }
191
 
192
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
193
+
194
  // load widgets JS if a Twitter widget is active
195
+ if (
196
+ ( isset( $features[ \Twitter\WordPress\Features::FOLLOW_BUTTON ] ) && is_active_widget( false, false, \Twitter\WordPress\Widgets\Follow::BASE_ID, true ) ) ||
197
+ ( isset( $features[ \Twitter\WordPress\Features::PERISCOPE_ON_AIR ] ) && is_active_widget( false, false, \Twitter\WordPress\Widgets\PeriscopeOnAir::BASE_ID, true ) )
198
+ ) {
199
  // enqueue after the script is registered in wp_enqueue_scripts action priority 1
200
  add_action( 'wp_enqueue_scripts', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'enqueue' ) );
201
+ add_action( 'wp_head', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'dnsPrefetch' ) );
202
  }
203
 
204
  // do not add content filters to HTTP 404 response
206
  return;
207
  }
208
 
209
+ if ( ! isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] ) ) {
210
+ return;
211
+ }
212
+
213
  /**
214
  * Set the priority to apply to Twitter elements automatically added to the_content
215
  *
238
  */
239
  public static function registerShortcodeHandlers()
240
  {
241
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
242
+ $shortcode_namespace = '\\Twitter\\WordPress\\Shortcodes\\';
243
+
244
  // features requiring HTTPS remote requests
245
  if ( wp_http_supports( array( 'ssl' => true ) ) ) {
246
+ foreach (
247
+ array(
248
+ \Twitter\WordPress\Features::EMBED_TWEET => 'EmbeddedTweet',
249
+ \Twitter\WordPress\Features::EMBED_TWEET_VIDEO => 'EmbeddedTweetVideo',
250
+ \Twitter\WordPress\Features::EMBED_VINE => 'Vine',
251
+ \Twitter\WordPress\Features::EMBED_TWEETS_GRID => 'TweetGrid',
252
+ \Twitter\WordPress\Features::EMBED_MOMENT => 'Moment',
253
+ ) as $feature => $shortcode_class
254
+ ) {
255
+ if ( ! isset( $features[ $feature ] ) ) {
256
+ continue;
257
+ }
258
+
259
+ add_action(
260
+ 'plugins_loaded',
261
+ array( $shortcode_namespace . $shortcode_class, 'init' ),
262
+ 5,
263
+ 0
264
+ );
265
+ }
266
+ }
267
 
268
+ // initialize buttons and ad pixel if not disabled
269
+ foreach (
270
+ array(
271
+ \Twitter\WordPress\Features::FOLLOW_BUTTON => 'Follow',
272
+ \Twitter\WordPress\Features::TWEET_BUTTON => 'Share',
273
+ \Twitter\WordPress\Features::PERISCOPE_ON_AIR => 'PeriscopeOnAir',
274
+ \Twitter\WordPress\Features::TRACKING_PIXEL => 'Tracking',
275
+ ) as $feature => $shortcode_class
276
+ ) {
277
+ if ( ! isset( $features[ $feature ] ) ) {
278
+ continue;
279
+ }
280
 
 
281
  add_action(
282
  'plugins_loaded',
283
+ array( $shortcode_namespace . $shortcode_class, 'init' ),
284
  5,
285
  0
286
  );
287
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  }
289
 
290
  /**
296
  */
297
  public static function wpHead()
298
  {
299
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
300
+
301
  // Twitter Cards markup
302
+ if ( isset( $features[ \Twitter\WordPress\Features::CARDS ] ) ) {
303
+ add_action(
304
+ 'wp_head',
305
+ array( '\Twitter\WordPress\Head\CardsMetaElements', 'outputMetaElements' ),
306
+ 99, // late priority to override if multiple values provided
307
+ 0 // expects no arguments
308
+ );
309
+ }
310
 
311
  // page-level customizations referenced by Twitter JavaScript
312
  add_action(
336
  */
337
  public static function registerScripts()
338
  {
339
+ // Twitter widgets
340
  \Twitter\WordPress\JavaScriptLoaders\Widgets::register();
341
 
342
+ // Vine embed
343
+ \Twitter\WordPress\JavaScriptLoaders\VineEmbed::register();
344
+
345
+ // Twitter audience tracker and conversion
346
  \Twitter\WordPress\JavaScriptLoaders\Tracking::register();
347
  }
348
 
355
  */
356
  public static function compatibility()
357
  {
358
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
359
+ if ( isset( $features[ \Twitter\WordPress\Features::CARDS ] ) ) {
360
+ \Twitter\WordPress\Cards\Compatibility::init();
361
+ }
362
  }
363
  }
src/Twitter/WordPress/Shortcodes/EmbeddedTweet.php CHANGED
@@ -30,8 +30,9 @@ namespace Twitter\WordPress\Shortcodes;
30
  *
31
  * @since 1.0.0
32
  */
33
- class EmbeddedTweet
34
  {
 
35
 
36
  /**
37
  * Shortcode tag to be matched
@@ -42,6 +43,15 @@ class EmbeddedTweet
42
  */
43
  const SHORTCODE_TAG = 'tweet';
44
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * Relative path for the oEmbed API relative to Twitter API base path
47
  *
@@ -71,6 +81,15 @@ class EmbeddedTweet
71
  */
72
  const TWEET_URL_REGEX = '#^https?://(www\.)?twitter\.com/.+?/status(es)?/([0-9]+)#i';
73
 
 
 
 
 
 
 
 
 
 
74
  /**
75
  * Accepted values for the align parameter
76
  *
@@ -101,17 +120,15 @@ class EmbeddedTweet
101
  // register our shortcode and its handler
102
  add_shortcode( self::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
103
 
104
- if ( is_admin() ) {
105
- // Shortcake UI
106
- if ( function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
107
- add_action(
108
- 'admin_init',
109
- array( __CLASS__, 'shortcodeUI' ),
110
- 5,
111
- 0
112
- );
113
- }
114
- } else {
115
  // unhook the WordPress Core oEmbed handler
116
  wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
117
  // pass a Tweet detail URL through the Tweet shortcode handler
@@ -145,7 +162,7 @@ class EmbeddedTweet
145
  shortcode_ui_register_for_shortcode(
146
  self::SHORTCODE_TAG,
147
  array(
148
- 'label' => __( 'Embedded Tweet', 'twitter' ),
149
  'listItemImage' => 'dashicons-twitter',
150
  'attrs' => array(
151
  array(
@@ -334,40 +351,6 @@ class EmbeddedTweet
334
  return $html;
335
  }
336
 
337
- /**
338
- * Get the base set of oEmbed params before applying shortcode customizations
339
- *
340
- * @since 1.0.0
341
- *
342
- * @param string $tweet_id Tweet identifier
343
- *
344
- * @return array associative array of query parameters ready for http_build_query {
345
- * @type string query parameter name
346
- * @type string|bool query parameter value
347
- * }
348
- */
349
- public static function getBaseOEmbedParams( $tweet_id )
350
- {
351
- $tweet_id = trim( $tweet_id );
352
- if ( ! $tweet_id ) {
353
- return array();
354
- }
355
-
356
- // omit JavaScript. enqueue separately
357
- $query_parameters = array(
358
- 'id' => $tweet_id,
359
- 'omit_script' => true,
360
- );
361
-
362
- // attempt to customize text for site language
363
- $lang = \Twitter\WordPress\Language::localeToTwitterLang();
364
- if ( $lang ) {
365
- $query_parameters['lang'] = $lang;
366
- }
367
-
368
- return $query_parameters;
369
- }
370
-
371
  /**
372
  * Convert shortcode parameters into query parameters supported by the Twitter oEmbed endpoint
373
  *
@@ -464,45 +447,4 @@ class EmbeddedTweet
464
 
465
  return implode( '_', $key_pieces );
466
  }
467
-
468
- /**
469
- * Request and parse oEmbed markup from Twitter's API servers
470
- *
471
- * @since 1.0.0
472
- *
473
- * @param array $query_parameters request parameters
474
- *
475
- * @return string HTML markup returned by the oEmbed endpoint
476
- */
477
- public static function getOEmbedMarkup( array $query_parameters )
478
- {
479
- $cache_key = static::oEmbedCacheKey( $query_parameters );
480
- if ( ! $cache_key ) {
481
- return '';
482
- }
483
-
484
- // check for cached result
485
- $html = get_transient( $cache_key );
486
- if ( $html ) {
487
- return $html;
488
- }
489
-
490
- $ttl = DAY_IN_SECONDS;
491
-
492
- $oembed_response = \Twitter\WordPress\Helpers\TwitterAPI::getJSON( self::OEMBED_API_ENDPOINT, $query_parameters );
493
- if ( ! $oembed_response || ! isset( $oembed_response->type ) || 'rich' !== $oembed_response->type || ! ( isset( $oembed_response->html ) && $oembed_response->html ) ) {
494
- // do not rerequest errors with every page request
495
- set_transient( $cache_key, ' ', $ttl );
496
- return '';
497
- }
498
-
499
- $html = $oembed_response->html;
500
-
501
- if ( isset( $oembed_response->cache_age ) ) {
502
- $ttl = absint( $oembed_response->cache_age );
503
- }
504
- set_transient( $cache_key, $html, $ttl );
505
-
506
- return $html;
507
- }
508
  }
30
  *
31
  * @since 1.0.0
32
  */
33
+ class EmbeddedTweet implements ShortcodeInterface
34
  {
35
+ use OEmbedTrait;
36
 
37
  /**
38
  * Shortcode tag to be matched
43
  */
44
  const SHORTCODE_TAG = 'tweet';
45
 
46
+ /**
47
+ * PHP class to use for fetching oEmbed data
48
+ *
49
+ * @since 1.3.0
50
+ *
51
+ * @type string
52
+ */
53
+ const OEMBED_API_CLASS = '\Twitter\WordPress\Helpers\TwitterAPI';
54
+
55
  /**
56
  * Relative path for the oEmbed API relative to Twitter API base path
57
  *
81
  */
82
  const TWEET_URL_REGEX = '#^https?://(www\.)?twitter\.com/.+?/status(es)?/([0-9]+)#i';
83
 
84
+ /**
85
+ * Base URL used to reconstruct a Tweet URL
86
+ *
87
+ * @since 1.3.0
88
+ *
89
+ * @type string
90
+ */
91
+ const BASE_URL = 'https://twitter.com/_/status/';
92
+
93
  /**
94
  * Accepted values for the align parameter
95
  *
120
  // register our shortcode and its handler
121
  add_shortcode( self::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
122
 
123
+ // Shortcode UI, if supported
124
+ add_action(
125
+ 'register_shortcode_ui',
126
+ array( __CLASS__, 'shortcodeUI' ),
127
+ 5,
128
+ 0
129
+ );
130
+
131
+ if ( ! is_admin() ) {
 
 
132
  // unhook the WordPress Core oEmbed handler
133
  wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
134
  // pass a Tweet detail URL through the Tweet shortcode handler
162
  shortcode_ui_register_for_shortcode(
163
  self::SHORTCODE_TAG,
164
  array(
165
+ 'label' => esc_html( __( 'Embedded Tweet', 'twitter' ) ),
166
  'listItemImage' => 'dashicons-twitter',
167
  'attrs' => array(
168
  array(
351
  return $html;
352
  }
353
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  /**
355
  * Convert shortcode parameters into query parameters supported by the Twitter oEmbed endpoint
356
  *
447
 
448
  return implode( '_', $key_pieces );
449
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  }
src/Twitter/WordPress/Shortcodes/EmbeddedTweetVideo.php CHANGED
@@ -63,15 +63,13 @@ class EmbeddedTweetVideo extends EmbeddedTweet
63
  // register our shortcode and its handler
64
  add_shortcode( self::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
65
 
66
- // Shortcake UI
67
- if ( function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
68
- add_action(
69
- 'admin_init',
70
- array( __CLASS__, 'shortcodeUI' ),
71
- 5,
72
- 0
73
- );
74
- }
75
  }
76
 
77
  /**
63
  // register our shortcode and its handler
64
  add_shortcode( self::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
65
 
66
+ // Shortcode UI, if supported
67
+ add_action(
68
+ 'register_shortcode_ui',
69
+ array( __CLASS__, 'shortcodeUI' ),
70
+ 5,
71
+ 0
72
+ );
 
 
73
  }
74
 
75
  /**
src/Twitter/WordPress/Shortcodes/Follow.php CHANGED
@@ -30,7 +30,7 @@ namespace Twitter\WordPress\Shortcodes;
30
  *
31
  * @since 1.0.0
32
  */
33
- class Follow
34
  {
35
 
36
  /**
@@ -62,15 +62,13 @@ class Follow
62
  {
63
  add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
64
 
65
- // Shortcake UI
66
- if ( function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
67
- add_action(
68
- 'admin_init',
69
- array( __CLASS__, 'shortcodeUI' ),
70
- 5,
71
- 0
72
- );
73
- }
74
  }
75
 
76
  /**
@@ -92,12 +90,12 @@ class Follow
92
  shortcode_ui_register_for_shortcode(
93
  static::SHORTCODE_TAG,
94
  array(
95
- 'label' => __( 'Follow Button', 'twitter' ),
96
  'listItemImage' => 'dashicons-twitter',
97
  'attrs' => array(
98
  array(
99
  'attr' => 'screen_name',
100
- 'label' => __( 'Twitter @username', 'twitter' ),
101
  'type' => 'text',
102
  'meta' => array(
103
  'placeholder' => 'WordPress',
@@ -106,12 +104,12 @@ class Follow
106
  ),
107
  array(
108
  'attr' => 'size',
109
- 'label' => __( 'Button size:', 'twitter' ),
110
  'type' => 'radio',
111
- 'value' => 'medium',
112
  'options' => array(
113
- '' => _x( 'medium', 'medium size button', 'twitter' ),
114
- 'large' => _x( 'large', 'large size button', 'twitter' ),
115
  ),
116
  ),
117
  ),
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Follow implements ShortcodeInterface
34
  {
35
 
36
  /**
62
  {
63
  add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
64
 
65
+ // Shortcode UI, if supported
66
+ add_action(
67
+ 'register_shortcode_ui',
68
+ array( __CLASS__, 'shortcodeUI' ),
69
+ 5,
70
+ 0
71
+ );
 
 
72
  }
73
 
74
  /**
90
  shortcode_ui_register_for_shortcode(
91
  static::SHORTCODE_TAG,
92
  array(
93
+ 'label' => esc_html( __( 'Follow Button', 'twitter' ) ),
94
  'listItemImage' => 'dashicons-twitter',
95
  'attrs' => array(
96
  array(
97
  'attr' => 'screen_name',
98
+ 'label' => esc_html( __( 'Twitter @username', 'twitter' ) ),
99
  'type' => 'text',
100
  'meta' => array(
101
  'placeholder' => 'WordPress',
104
  ),
105
  array(
106
  'attr' => 'size',
107
+ 'label' => esc_html( __( 'Button size:', 'twitter' ) ),
108
  'type' => 'radio',
109
+ 'value' => '',
110
  'options' => array(
111
+ '' => esc_html( _x( 'medium', 'medium size button', 'twitter' ) ),
112
+ 'large' => esc_html( _x( 'large', 'large size button', 'twitter' ) ),
113
  ),
114
  ),
115
  ),
src/Twitter/WordPress/Shortcodes/Moment.php CHANGED
@@ -30,7 +30,7 @@ namespace Twitter\WordPress\Shortcodes;
30
  *
31
  * @since 1.2.0
32
  */
33
- class Moment
34
  {
35
 
36
  /**
@@ -43,13 +43,13 @@ class Moment
43
  const SHORTCODE_TAG = 'twitter_moment';
44
 
45
  /**
46
- * Relative path for the oEmbed API relative to Twitter publishers base path
47
  *
48
- * @since 1.2.0
49
  *
50
  * @type string
51
  */
52
- const OEMBED_API_ENDPOINT = 'oembed';
53
 
54
  /**
55
  * Regex used to match a Moment in text
@@ -70,252 +70,14 @@ class Moment
70
  const BASE_URL = 'https://twitter.com/i/moments/';
71
 
72
  /**
73
- * Accepted shortcode attributes and their default values
74
- *
75
- * @since 1.2.0
76
- *
77
- * @type array
78
- */
79
- public static $SHORTCODE_DEFAULTS = array( 'id' => '' );
80
-
81
- /**
82
- * Attach handlers for Twitter Moment
83
- *
84
- * @since 1.2.0
85
- *
86
- * @return void
87
- */
88
- public static function init()
89
- {
90
- // register our shortcode and its handler
91
- add_shortcode( self::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
92
-
93
- wp_embed_register_handler(
94
- self::SHORTCODE_TAG,
95
- static::URL_REGEX,
96
- array( __CLASS__, 'linkHandler' ),
97
- 1
98
- );
99
-
100
- if ( is_admin() ) {
101
- // Shortcake UI
102
- if ( function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
103
- add_action(
104
- 'admin_init',
105
- array( __CLASS__, 'shortcodeUI' ),
106
- 5,
107
- 0
108
- );
109
- }
110
- }
111
- }
112
-
113
- /**
114
- * Describe shortcode for Shortcake UI
115
- *
116
- * @since 1.2.0
117
- *
118
- * @link https://github.com/fusioneng/Shortcake Shortcake UI
119
- *
120
- * @return void
121
- */
122
- public static function shortcodeUI()
123
- {
124
- // Shortcake required
125
- if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
126
- return;
127
- }
128
-
129
- shortcode_ui_register_for_shortcode(
130
- self::SHORTCODE_TAG,
131
- array(
132
- 'label' => __( 'Twitter Moment', 'twitter' ),
133
- 'listItemImage' => 'dashicons-twitter',
134
- 'attrs' => array(
135
- array(
136
- 'attr' => 'id',
137
- 'label' => 'ID',
138
- 'type' => 'text',
139
- 'meta' => array(
140
- 'required' => true,
141
- 'pattern' => '[0-9]+',
142
- ),
143
- ),
144
- ),
145
- )
146
- );
147
- }
148
-
149
- /**
150
- * Handle a URL matched by a Moment embed handler
151
- *
152
- * @since 1.2.0
153
- *
154
- * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
155
- * @param array $attr Embed attributes. Not used.
156
- * @param string $url The original URL that was matched by the regex. Not used.
157
- * @param array $rawattr The original unmodified attributes. Not used.
158
- *
159
- * @return string HTML markup for the Tweet or an empty string if requirements not met
160
- */
161
- public static function linkHandler( $matches, $attr, $url, $rawattr )
162
- {
163
- if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
164
- return '';
165
- }
166
-
167
- return static::shortcodeHandler( array( 'id' => $matches[1] ) );
168
- }
169
-
170
- /**
171
- * Handle shortcode macro
172
- *
173
- * @since 1.2.0
174
- *
175
- * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
176
- * @type string|int attribute name or positional int
177
- * @type mixed shortcode value
178
- * }
179
- * @param string $content content inside a shortcode macro. no effect on this shortcode
180
- *
181
- * @return string HTML markup. empty string if parameter requirement not met or no Moment info found
182
- */
183
- public static function shortcodeHandler( $attributes, $content = '' )
184
- {
185
- $options = shortcode_atts(
186
- static::$SHORTCODE_DEFAULTS,
187
- $attributes,
188
- static::SHORTCODE_TAG
189
- );
190
-
191
- $moment_id = trim( $options['id'] );
192
- if ( ! $moment_id ) {
193
- return '';
194
- }
195
- $query_parameters = static::getBaseOEmbedParams( $moment_id );
196
-
197
- // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
198
- $html = trim( static::getOEmbedMarkup( $query_parameters ) );
199
- if ( ! $html ) {
200
- return '';
201
- }
202
-
203
- $html = '<div class="twitter-moment">' . $html . '</div>';
204
-
205
- $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
206
- if ( $inline_js ) {
207
- return $html . $inline_js;
208
- }
209
-
210
- return $html;
211
- }
212
-
213
- /**
214
- * Get the base set of oEmbed params before applying shortcode customizations
215
- *
216
- * @since 1.2.0
217
- *
218
- * @param string $moment_id Moment identifier
219
- *
220
- * @return array associative array of query parameters ready for http_build_query {
221
- * @type string query parameter name
222
- * @type string|bool query parameter value
223
- * }
224
- */
225
- public static function getBaseOEmbedParams( $moment_id )
226
- {
227
- $moment_id = trim( $moment_id );
228
- if ( ! $moment_id ) {
229
- return array();
230
- }
231
-
232
- // omit JavaScript. enqueue separately
233
- $query_parameters = array(
234
- 'id' => $moment_id,
235
- 'omit_script' => true,
236
- );
237
-
238
- // attempt to customize text for site language
239
- $lang = \Twitter\WordPress\Language::localeToTwitterLang();
240
- if ( $lang ) {
241
- $query_parameters['lang'] = $lang;
242
- }
243
-
244
- return $query_parameters;
245
- }
246
-
247
- /**
248
- * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
249
  *
250
- * @since 1.2.0
251
- *
252
- * @param array $query_parameters oEmbed API query parameters
253
  *
254
- * @return string cache key
255
  */
256
- public static function oEmbedCacheKey( array $query_parameters )
257
  {
258
- if ( ! ( isset( $query_parameters['id'] ) && $query_parameters['id'] ) ) {
259
- return '';
260
- }
261
-
262
- $key_pieces = array( self::SHORTCODE_TAG, $query_parameters['id'] );
263
-
264
- // separate cache for each explicitly-defined display language
265
- if ( isset( $query_parameters['lang'] ) && $query_parameters['lang'] ) {
266
- $key_pieces[] = $query_parameters['lang'];
267
- }
268
-
269
- return implode( '_', $key_pieces );
270
- }
271
-
272
- /**
273
- * Request and parse Moment oEmbed markup from Twitter's servers
274
- *
275
- * @since 1.2.0
276
- *
277
- * @param string $moment_id Moment identifier
278
- *
279
- * @todo make this not mocked
280
- * @return string HTML markup returned by the oEmbed endpoint
281
- */
282
- public static function getOEmbedMarkup( $query_parameters ) {
283
- $cache_key = static::oEmbedCacheKey( $query_parameters );
284
- if ( ! $cache_key ) {
285
- return '';
286
- }
287
-
288
- $cache_key = static::oEmbedCacheKey( $query_parameters );
289
- if ( ! $cache_key ) {
290
- return '';
291
- }
292
-
293
- // check for cached result
294
- $html = get_transient( $cache_key );
295
- if ( $html ) {
296
- return $html;
297
- }
298
-
299
- $ttl = DAY_IN_SECONDS;
300
-
301
- // convert ID to full URL to allow more flexibility for oEmbed endpoint as embed types expand
302
- $query_parameters['url'] = self::BASE_URL . $query_parameters['id'];
303
- unset( $query_parameters['id'] );
304
-
305
- $oembed_response = \Twitter\WordPress\Helpers\TwitterOEmbed::getJSON( self::OEMBED_API_ENDPOINT, $query_parameters );
306
- if ( ! $oembed_response || ! isset( $oembed_response->type ) || 'rich' !== $oembed_response->type || ! ( isset( $oembed_response->html ) && $oembed_response->html ) ) {
307
- // do not rerequest errors with every page request
308
- set_transient( $cache_key, ' ', $ttl );
309
- return '';
310
- }
311
-
312
- $html = $oembed_response->html;
313
-
314
- if ( isset( $oembed_response->cache_age ) ) {
315
- $ttl = absint( $oembed_response->cache_age );
316
- }
317
- set_transient( $cache_key, $html, $ttl );
318
-
319
- return $html;
320
  }
321
  }
30
  *
31
  * @since 1.2.0
32
  */
33
+ class Moment extends TweetGrid
34
  {
35
 
36
  /**
43
  const SHORTCODE_TAG = 'twitter_moment';
44
 
45
  /**
46
+ * HTML class to be used in div wrapper
47
  *
48
+ * @since 1.3.0
49
  *
50
  * @type string
51
  */
52
+ const HTML_CLASS = 'twitter-moment';
53
 
54
  /**
55
  * Regex used to match a Moment in text
70
  const BASE_URL = 'https://twitter.com/i/moments/';
71
 
72
  /**
73
+ * Reference the feature by name
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  *
75
+ * @since 1.3.0
 
 
76
  *
77
+ * @return string translated feature name
78
  */
79
+ public static function featureName()
80
  {
81
+ return __( 'Twitter Moment', 'twitter' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
  }
src/Twitter/WordPress/Shortcodes/OEmbedTrait.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Shortcodes;
27
+
28
+ /**
29
+ * Set up and fetch a Twitter oEmbed capable object
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ trait OEmbedTrait
34
+ {
35
+ /**
36
+ * Get the base set of oEmbed params before applying shortcode customizations
37
+ *
38
+ * @since 1.3.0
39
+ *
40
+ * @param string $snowflake_id object identifier
41
+ *
42
+ * @return array associative array of query parameters ready for http_build_query {
43
+ * @type string query parameter name
44
+ * @type string|bool query parameter value
45
+ * }
46
+ */
47
+ public static function getBaseOEmbedParams( $snowflake_id )
48
+ {
49
+ $snowflake_id = trim( $snowflake_id );
50
+ if ( ! $snowflake_id ) {
51
+ return array();
52
+ }
53
+
54
+ // omit JavaScript. enqueue separately
55
+ $query_parameters = array(
56
+ 'id' => $snowflake_id,
57
+ 'omit_script' => true,
58
+ );
59
+
60
+ // attempt to customize text for site language
61
+ $lang = \Twitter\WordPress\Language::localeToTwitterLang();
62
+ if ( $lang ) {
63
+ $query_parameters['lang'] = $lang;
64
+ }
65
+
66
+ return $query_parameters;
67
+ }
68
+
69
+ /**
70
+ * Request and parse oEmbed markup from Twitter's servers
71
+ *
72
+ * @since 1.3.0
73
+ *
74
+ * @param array $query_parameters query parameters to be passed to the oEmbed endpoint
75
+ *
76
+ * @todo make this not mocked
77
+ * @return string HTML markup returned by the oEmbed endpoint
78
+ */
79
+ public static function getOEmbedMarkup( $query_parameters )
80
+ {
81
+ $cache_key = static::oEmbedCacheKey( $query_parameters );
82
+ if ( ! $cache_key ) {
83
+ return '';
84
+ }
85
+
86
+ // check for cached result
87
+ $html = get_transient( $cache_key );
88
+ if ( $html ) {
89
+ return $html;
90
+ }
91
+
92
+ $classname = get_called_class();
93
+ if ( ! ( defined( $classname . '::OEMBED_API_CLASS' ) && static::OEMBED_API_CLASS ) ) {
94
+ return '';
95
+ }
96
+ $oembed_api_class = static::OEMBED_API_CLASS;
97
+ if ( ! ( class_exists( $oembed_api_class ) && method_exists( $oembed_api_class, 'getJSON' ) ) ) {
98
+ return '';
99
+ }
100
+
101
+ if ( defined( $classname . '::BASE_URL' ) ) {
102
+ // convert ID to full URL to allow more flexibility for oEmbed endpoint
103
+ $query_parameters['url'] = static::BASE_URL . $query_parameters['id'];
104
+ unset( $query_parameters['id'] );
105
+ }
106
+
107
+ $allowed_resource_types = array( 'rich' => true, 'video' => true );
108
+ $ttl = DAY_IN_SECONDS;
109
+
110
+ $oembed_response = $oembed_api_class::getJSON( static::OEMBED_API_ENDPOINT, $query_parameters );
111
+ if ( ! (
112
+ $oembed_response &&
113
+ isset( $oembed_response->type ) &&
114
+ isset( $allowed_resource_types[ $oembed_response->type ] ) &&
115
+ isset( $oembed_response->html ) &&
116
+ $oembed_response->html
117
+ ) ) {
118
+ // do not rerequest errors with every page request
119
+ set_transient( $cache_key, ' ', $ttl );
120
+ return '';
121
+ }
122
+
123
+ $html = $oembed_response->html;
124
+
125
+ if ( isset( $oembed_response->cache_age ) ) {
126
+ $ttl = absint( $oembed_response->cache_age );
127
+ }
128
+ set_transient( $cache_key, $html, $ttl );
129
+
130
+ return $html;
131
+ }
132
+ }
src/Twitter/WordPress/Shortcodes/PeriscopeOnAir.php ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Shortcodes;
27
+
28
+ /**
29
+ * Display a Periscope On Air button
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class PeriscopeOnAir implements ShortcodeInterface
34
+ {
35
+
36
+ /**
37
+ * Shortcode tag to be matched
38
+ *
39
+ * @since 1.3.0
40
+ *
41
+ * @type string
42
+ */
43
+ const SHORTCODE_TAG = 'periscope_on_air';
44
+
45
+ /**
46
+ * Regex used to match a Periscope profile URL in text
47
+ *
48
+ * @since 1.3.0
49
+ *
50
+ * @type string
51
+ */
52
+ const PERISCOPE_PROFILE_URL_REGEX = '#^https://(www\.)?periscope\.tv/([a-z0-9_]{1,20})#i';
53
+
54
+ /**
55
+ * Accepted shortcode attributes and their default values
56
+ *
57
+ * @since 1.3.0
58
+ *
59
+ * @type array
60
+ */
61
+ public static $SHORTCODE_DEFAULTS = array( 'username' => '', 'size' => 'small' );
62
+
63
+ /**
64
+ * Register shortcode macro and handler
65
+ *
66
+ * @since 1.3.0
67
+ *
68
+ * @return void
69
+ */
70
+ public static function init()
71
+ {
72
+ add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
73
+
74
+ // pass a Periscope profile URL through the Periscope On Air shortcode handler
75
+ wp_embed_register_handler(
76
+ self::SHORTCODE_TAG,
77
+ static::PERISCOPE_PROFILE_URL_REGEX,
78
+ array( __CLASS__, 'linkHandler' ),
79
+ 1
80
+ );
81
+
82
+ // Shortcode UI, if supported
83
+ add_action(
84
+ 'register_shortcode_ui',
85
+ array( __CLASS__, 'shortcodeUI' ),
86
+ 5,
87
+ 0
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Describe shortcode for Shortcake UI
93
+ *
94
+ * @since 1.3.0
95
+ *
96
+ * @link https://github.com/fusioneng/Shortcake Shortcake UI
97
+ *
98
+ * @return void
99
+ */
100
+ public static function shortcodeUI()
101
+ {
102
+ // Shortcake required
103
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
104
+ return;
105
+ }
106
+
107
+ shortcode_ui_register_for_shortcode(
108
+ static::SHORTCODE_TAG,
109
+ array(
110
+ 'label' => esc_html( __( 'Periscope On Air', 'twitter' ) ),
111
+ 'listItemImage' => 'dashicons-video-alt',
112
+ 'attrs' => array(
113
+ array(
114
+ 'attr' => 'username',
115
+ 'label' => esc_html( __( 'Periscope username', 'twitter' ) ),
116
+ 'type' => 'text',
117
+ 'meta' => array(
118
+ 'placeholder' => 'photomatt',
119
+ 'pattern' => '[A-Za-z0-9_]{1,20}',
120
+ ),
121
+ ),
122
+ array(
123
+ 'attr' => 'size',
124
+ 'label' => esc_html( __( 'Button size:', 'twitter' ) ),
125
+ 'type' => 'radio',
126
+ 'value' => '',
127
+ 'options' => array(
128
+ '' => esc_html( _x( 'small', 'small size button', 'twitter' ) ),
129
+ 'large' => esc_html( _x( 'large', 'large size button', 'twitter' ) ),
130
+ ),
131
+ ),
132
+ ),
133
+ )
134
+ );
135
+ }
136
+
137
+ /**
138
+ * Handle a URL matched by an embed handler
139
+ *
140
+ * @since 1.3.0
141
+ *
142
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
143
+ * @param array $attr Embed attributes. Not used.
144
+ * @param string $url The original URL that was matched by the regex. Not used.
145
+ * @param array $rawattr The original unmodified attributes. Not used.
146
+ *
147
+ * @return string HTML markup for the Tweet or an empty string if requirements not met
148
+ */
149
+ public static function linkHandler( $matches, $attr, $url, $rawattr )
150
+ {
151
+ if ( ! ( is_array( $matches ) && isset( $matches[2] ) && $matches[2] ) ) {
152
+ return '';
153
+ }
154
+
155
+ return static::shortcodeHandler( array( 'username' => $matches[2] ) );
156
+ }
157
+
158
+ /**
159
+ * Clean up provided shortcode values
160
+ *
161
+ * @since 1.3.0
162
+ *
163
+ * @param array $attributes provided shortcode attributes {
164
+ * @type string shortcode attribute name
165
+ * @type mixed shortcode attribute value
166
+ * }
167
+ *
168
+ * @return array simplified shortcode values with defaults removed {
169
+ * @type string shortcode attribute name
170
+ * @type bool|string shortcode attribute value
171
+ * }
172
+ */
173
+ public static function sanitizeShortcodeParameters( $attributes = array() )
174
+ {
175
+ if ( ! is_array( $attributes ) ) {
176
+ return array();
177
+ }
178
+
179
+ $options = array();
180
+
181
+ if ( isset( $attributes['username'] ) ) {
182
+ $username = \Twitter\Helpers\Validators\PeriscopeUsername::trim( $attributes['username'] );
183
+ if ( $username ) {
184
+ $options['username'] = $username;
185
+ }
186
+ unset( $username );
187
+ }
188
+
189
+ // large is the only option
190
+ if ( isset( $attributes['size'] ) ) {
191
+ if ( is_string( $attributes['size'] ) && in_array( strtolower( $attributes['size'] ), array( 'large', 'l' ) ) ) {
192
+ $options['size'] = 'large';
193
+ }
194
+ }
195
+
196
+ return $options;
197
+ }
198
+
199
+ /**
200
+ * Get the Periscope username of the author of the current post
201
+ *
202
+ * @since 1.3.0
203
+ *
204
+ * @return string Periscope username or empty if no screen name stored
205
+ */
206
+ public static function getUsername()
207
+ {
208
+ if ( ! in_the_loop() ) {
209
+ return '';
210
+ }
211
+
212
+ $username = \Twitter\WordPress\User\Meta::getPeriscopeUsername( get_the_author_meta( 'ID' ) );
213
+ if ( ! $username ) {
214
+ return '';
215
+ }
216
+
217
+ return $username;
218
+ }
219
+
220
+ /**
221
+ * Handle shortcode macro
222
+ *
223
+ * @since 1.3.0
224
+ *
225
+ * @param array $attributes shortcode attributes
226
+ * @param string $content shortcode content. no effect
227
+ *
228
+ * @return string Periscope On Air button HTML or empty string
229
+ */
230
+ public static function shortcodeHandler( $attributes, $content = '' )
231
+ {
232
+ // clean up attribute to shortcode option mappings before passing to filter
233
+ // apply the same filter as shortcode_atts
234
+ /** This filter is documented in wp-includes/shortcodes.php */
235
+ $options = apply_filters(
236
+ 'shortcode_atts_' . self::SHORTCODE_TAG,
237
+ array_merge(
238
+ static::$SHORTCODE_DEFAULTS,
239
+ static::sanitizeShortcodeParameters( (array) $attributes )
240
+ ),
241
+ static::$SHORTCODE_DEFAULTS,
242
+ $attributes
243
+ );
244
+
245
+ $username = '';
246
+ if ( isset( $options['username'] ) ) {
247
+ $username = $options['username'];
248
+ unset( $options['username'] );
249
+ }
250
+ if ( ! $username ) {
251
+ $username = static::getUsername();
252
+ // user target required
253
+ if ( ! $username ) {
254
+ return '';
255
+ }
256
+ }
257
+
258
+ // update the options array with the Periscope username
259
+ $options['username'] = $username;
260
+ unset( $username );
261
+
262
+ $on_air = \Twitter\Widgets\PeriscopeOnAir::fromArray( $options );
263
+ if ( ! $on_air ) {
264
+ return '';
265
+ }
266
+
267
+ $html = $on_air->toHTML( '\Twitter\WordPress\Helpers\HTMLBuilder' );
268
+ if ( ! $html ) {
269
+ return '';
270
+ }
271
+
272
+ $html = '<div class="periscope-on-air">' . $html . '</div>';
273
+
274
+ $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
275
+ if ( $inline_js ) {
276
+ return $html . $inline_js;
277
+ }
278
+
279
+ return $html;
280
+ }
281
+ }
src/Twitter/WordPress/Shortcodes/Share.php CHANGED
@@ -30,7 +30,7 @@ namespace Twitter\WordPress\Shortcodes;
30
  *
31
  * @since 1.0.0
32
  */
33
- class Share
34
  {
35
 
36
  /**
@@ -62,15 +62,13 @@ class Share
62
  {
63
  add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
64
 
65
- // Shortcake UI
66
- if ( function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
67
- add_action(
68
- 'admin_init',
69
- array( __CLASS__, 'shortcodeUI' ),
70
- 5,
71
- 0
72
- );
73
- }
74
  }
75
 
76
  /**
@@ -92,12 +90,12 @@ class Share
92
  shortcode_ui_register_for_shortcode(
93
  static::SHORTCODE_TAG,
94
  array(
95
- 'label' => __( 'Tweet Button', 'twitter' ),
96
  'listItemImage' => 'dashicons-twitter',
97
  'attrs' => array(
98
  array(
99
  'attr' => 'text',
100
- 'label' => _x( 'Text', 'Share / Tweet text', 'twitter' ),
101
  'type' => 'text',
102
  ),
103
  array(
@@ -107,12 +105,12 @@ class Share
107
  ),
108
  array(
109
  'attr' => 'size',
110
- 'label' => __( 'Button size:', 'twitter' ),
111
  'type' => 'radio',
112
- 'value' => 'medium',
113
  'options' => array(
114
- '' => _x( 'medium', 'medium size button', 'twitter' ),
115
- 'large' => _x( 'large', 'large size button', 'twitter' ),
116
  ),
117
  ),
118
  ),
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Share implements ShortcodeInterface
34
  {
35
 
36
  /**
62
  {
63
  add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
64
 
65
+ // Shortcode UI, if supported
66
+ add_action(
67
+ 'register_shortcode_ui',
68
+ array( __CLASS__, 'shortcodeUI' ),
69
+ 5,
70
+ 0
71
+ );
 
 
72
  }
73
 
74
  /**
90
  shortcode_ui_register_for_shortcode(
91
  static::SHORTCODE_TAG,
92
  array(
93
+ 'label' => esc_html( __( 'Tweet Button', 'twitter' ) ),
94
  'listItemImage' => 'dashicons-twitter',
95
  'attrs' => array(
96
  array(
97
  'attr' => 'text',
98
+ 'label' => esc_html( _x( 'Text', 'Share / Tweet text', 'twitter' ) ),
99
  'type' => 'text',
100
  ),
101
  array(
105
  ),
106
  array(
107
  'attr' => 'size',
108
+ 'label' => esc_html( __( 'Button size:', 'twitter' ) ),
109
  'type' => 'radio',
110
+ 'value' => '',
111
  'options' => array(
112
+ '' => esc_html( _x( 'medium', 'medium size button', 'twitter' ) ),
113
+ 'large' => esc_html( _x( 'large', 'large size button', 'twitter' ) ),
114
  ),
115
  ),
116
  ),
src/Twitter/WordPress/Shortcodes/ShortcodeInterface.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Shortcodes;
27
+
28
+ /**
29
+ * Common methods expected to exist in each shortcode handler
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ interface ShortcodeInterface
34
+ {
35
+ /**
36
+ * Register shortcode macro and handler
37
+ *
38
+ * @since 1.3.0
39
+ *
40
+ * @return void
41
+ */
42
+ public static function init();
43
+
44
+ /**
45
+ * Describe shortcode for Shortcake UI
46
+ *
47
+ * @since 1.3.0
48
+ *
49
+ * @link https://github.com/fusioneng/Shortcake Shortcake UI
50
+ *
51
+ * @return void
52
+ */
53
+ public static function shortcodeUI();
54
+
55
+ /**
56
+ * Handle shortcode macro
57
+ *
58
+ * @since 1.3.0
59
+ *
60
+ * @param array $attributes shortcode attributes
61
+ * @param string $content shortcode content. no effect
62
+ *
63
+ * @return string HTML result or empty string. JavaScript dependencies should be enqueued or loaded in the returned HTML
64
+ */
65
+ public static function shortcodeHandler( $attributes, $content );
66
+ }
src/Twitter/WordPress/Shortcodes/Tracking.php CHANGED
@@ -30,7 +30,7 @@ namespace Twitter\WordPress\Shortcodes;
30
  *
31
  * @since 1.0.0
32
  */
33
- class Tracking
34
  {
35
 
36
  /**
@@ -71,15 +71,13 @@ class Tracking
71
  {
72
  add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
73
 
74
- // Shortcake UI
75
- if ( function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
76
- add_action(
77
- 'admin_init',
78
- array( __CLASS__, 'shortcodeUI' ),
79
- 5,
80
- 0
81
- );
82
- }
83
  }
84
 
85
  /**
@@ -101,13 +99,13 @@ class Tracking
101
  shortcode_ui_register_for_shortcode(
102
  static::SHORTCODE_TAG,
103
  array(
104
- 'label' => __( 'Twitter Advertising Tracker', 'twitter' ),
105
  'listItemImage' => 'dashicons-twitter',
106
  'attrs' => array(
107
  array(
108
  'attr' => 'id',
109
  'label' => 'ID',
110
- 'description' => __( 'Twitter conversion or remarketing audience tracking identifier', 'twitter' ),
111
  'type' => 'text',
112
  'meta' => array(
113
  'required' => true,
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Tracking implements ShortcodeInterface
34
  {
35
 
36
  /**
71
  {
72
  add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
73
 
74
+ // Shortcode UI, if supported
75
+ add_action(
76
+ 'register_shortcode_ui',
77
+ array( __CLASS__, 'shortcodeUI' ),
78
+ 5,
79
+ 0
80
+ );
 
 
81
  }
82
 
83
  /**
99
  shortcode_ui_register_for_shortcode(
100
  static::SHORTCODE_TAG,
101
  array(
102
+ 'label' => esc_html( __( 'Twitter Advertising Tracker', 'twitter' ) ),
103
  'listItemImage' => 'dashicons-twitter',
104
  'attrs' => array(
105
  array(
106
  'attr' => 'id',
107
  'label' => 'ID',
108
+ 'description' => esc_html( __( 'Twitter conversion or remarketing audience tracking identifier', 'twitter' ) ),
109
  'type' => 'text',
110
  'meta' => array(
111
  'required' => true,
src/Twitter/WordPress/Shortcodes/TweetGrid.php ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Shortcodes;
27
+
28
+ /**
29
+ * Display a grid of Tweets
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class TweetGrid implements ShortcodeInterface
34
+ {
35
+ use OEmbedTrait;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 1.3.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'twitter_grid';
45
+
46
+ /**
47
+ * HTML class to be used in div wrapper
48
+ *
49
+ * @since 1.3.0
50
+ *
51
+ * @type string
52
+ */
53
+ const HTML_CLASS = 'twitter-grid';
54
+
55
+ /**
56
+ * Regex used to match a Collection in text
57
+ *
58
+ * @since 1.3.0
59
+ *
60
+ * @type string
61
+ */
62
+ const URL_REGEX = '#^https://twitter\.com/.+?/timelines/([0-9]+)#i';
63
+
64
+ /**
65
+ * Base URL used to reconstruct a Collection URL
66
+ *
67
+ * @since 1.3.0
68
+ *
69
+ * @type string
70
+ */
71
+ const BASE_URL = 'https://twitter.com/_/timelines/';
72
+
73
+ /**
74
+ * PHP class to use for fetching oEmbed data
75
+ *
76
+ * @since 1.3.0
77
+ *
78
+ * @type string
79
+ */
80
+ const OEMBED_API_CLASS = '\Twitter\WordPress\Helpers\TwitterOEmbed';
81
+
82
+ /**
83
+ * Relative path for the oEmbed API relative to Twitter publishers base path
84
+ *
85
+ * @since 1.3.0
86
+ *
87
+ * @type string
88
+ */
89
+ const OEMBED_API_ENDPOINT = 'oembed';
90
+
91
+ /**
92
+ * Accepted shortcode attributes and their default values
93
+ *
94
+ * @since 1.3.0
95
+ *
96
+ * @type array
97
+ */
98
+ public static $SHORTCODE_DEFAULTS = array( 'id' => '', 'limit' => 20 );
99
+
100
+ /**
101
+ * Attach handlers for Twitter Grid
102
+ *
103
+ * @since 1.3.0
104
+ *
105
+ * @return void
106
+ */
107
+ public static function init()
108
+ {
109
+ $classname = get_called_class();
110
+
111
+ // register our shortcode and its handler
112
+ add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
113
+
114
+ // convert a URL into the shortcode equivalent
115
+ wp_embed_register_handler(
116
+ static::SHORTCODE_TAG,
117
+ static::URL_REGEX,
118
+ array( $classname, 'linkHandler' ),
119
+ 1
120
+ );
121
+
122
+ // Shortcode UI, if supported
123
+ add_action(
124
+ 'register_shortcode_ui',
125
+ array( $classname, 'shortcodeUI' ),
126
+ 5,
127
+ 0
128
+ );
129
+ }
130
+
131
+ /**
132
+ * Reference the feature by name
133
+ *
134
+ * @since 1.3.0
135
+ *
136
+ * @return string translated feature name
137
+ */
138
+ public static function featureName()
139
+ {
140
+ return __( 'Tweet Grid', 'twitter' );
141
+ }
142
+
143
+ /**
144
+ * Describe shortcode for Shortcake UI
145
+ *
146
+ * @since 1.3.0
147
+ *
148
+ * @link https://github.com/fusioneng/Shortcake Shortcake UI
149
+ *
150
+ * @return void
151
+ */
152
+ public static function shortcodeUI()
153
+ {
154
+ // Shortcake required
155
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
156
+ return;
157
+ }
158
+
159
+ shortcode_ui_register_for_shortcode(
160
+ static::SHORTCODE_TAG,
161
+ array(
162
+ 'label' => esc_html( static::featureName() ),
163
+ 'listItemImage' => 'dashicons-twitter',
164
+ 'attrs' => array(
165
+ array(
166
+ 'attr' => 'id',
167
+ 'label' => 'ID',
168
+ 'type' => 'text',
169
+ 'meta' => array(
170
+ 'required' => true,
171
+ 'pattern' => '[0-9]+',
172
+ ),
173
+ ),
174
+ array(
175
+ 'attr' => 'limit',
176
+ 'label' => _x( 'Limit', 'Maximum number of items to include', 'twitter' ),
177
+ 'type' => 'number',
178
+ 'value' => static::$SHORTCODE_DEFAULTS['limit'],
179
+ 'meta' => array(
180
+ 'min' => 1,
181
+ 'max' => 20,
182
+ 'step' => 1,
183
+ ),
184
+ ),
185
+ ),
186
+ )
187
+ );
188
+ }
189
+
190
+ /**
191
+ * Handle a URL matched by a embed handler
192
+ *
193
+ * @since 1.3.0
194
+ *
195
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
196
+ * @param array $attr Embed attributes. Not used.
197
+ * @param string $url The original URL that was matched by the regex. Not used.
198
+ * @param array $rawattr The original unmodified attributes. Not used.
199
+ *
200
+ * @return string HTML markup for the Tweet or an empty string if requirements not met
201
+ */
202
+ public static function linkHandler( $matches, $attr, $url, $rawattr )
203
+ {
204
+ if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
205
+ return '';
206
+ }
207
+
208
+ return static::shortcodeHandler( array( 'id' => $matches[1] ) );
209
+ }
210
+
211
+ /**
212
+ * Handle shortcode macro
213
+ *
214
+ * @since 1.3.0
215
+ *
216
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
217
+ * @type string|int attribute name or positional int
218
+ * @type mixed shortcode value
219
+ * }
220
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
221
+ *
222
+ * @return string HTML markup. empty string if parameter requirement not met or no collection info found
223
+ */
224
+ public static function shortcodeHandler( $attributes, $content = '' )
225
+ {
226
+ $options = shortcode_atts(
227
+ static::$SHORTCODE_DEFAULTS,
228
+ $attributes,
229
+ static::SHORTCODE_TAG
230
+ );
231
+
232
+ $snowflake_id = trim( $options['id'] );
233
+ if ( ! $snowflake_id ) {
234
+ return '';
235
+ }
236
+
237
+ $query_parameters = static::getBaseOEmbedParams( $snowflake_id );
238
+ if ( isset( $options['limit'] ) ) {
239
+ $limit = 0;
240
+ try {
241
+ $limit = intval( trim( $options['limit'] ), 10 );
242
+ } catch( Exception $e ) {}
243
+ if ( $limit > 0 && $limit < 20 ) {
244
+ $query_parameters['limit'] = $limit;
245
+ }
246
+ }
247
+
248
+ // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
249
+ $html = trim( static::getOEmbedMarkup( $query_parameters ) );
250
+ if ( ! $html ) {
251
+ return '';
252
+ }
253
+
254
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
255
+
256
+ $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
257
+ if ( $inline_js ) {
258
+ return $html . $inline_js;
259
+ }
260
+
261
+ return $html;
262
+ }
263
+
264
+ /**
265
+ * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
266
+ *
267
+ * @since 1.3.0
268
+ *
269
+ * @param array $query_parameters oEmbed API query parameters
270
+ *
271
+ * @return string cache key
272
+ */
273
+ public static function oEmbedCacheKey( array $query_parameters )
274
+ {
275
+ if ( ! ( isset( $query_parameters['id'] ) && $query_parameters['id'] ) ) {
276
+ return '';
277
+ }
278
+
279
+ $key_pieces = array( static::SHORTCODE_TAG, $query_parameters['id'] );
280
+
281
+ // separate cache for each explicitly-defined display language
282
+ if ( isset( $query_parameters['lang'] ) && $query_parameters['lang'] ) {
283
+ $key_pieces[] = $query_parameters['lang'];
284
+ }
285
+
286
+ if ( isset( $query_parameters['limit'] ) && $query_parameters['limit'] ) {
287
+ $key_pieces[] = $query_parameters['limit'];
288
+ }
289
+
290
+ return implode( '_', $key_pieces );
291
+ }
292
+ }
src/Twitter/WordPress/Shortcodes/Vine.php ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Shortcodes;
27
+
28
+ /**
29
+ * Display a Vine
30
+ *
31
+ * @since 1.3.0
32
+ */
33
+ class Vine implements ShortcodeInterface
34
+ {
35
+ use OEmbedTrait;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 1.3.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'vine';
45
+
46
+ /**
47
+ * HTML class to be used in div wrapper
48
+ *
49
+ * @since 1.3.0
50
+ *
51
+ * @type string
52
+ */
53
+ const HTML_CLASS = 'vine-embed';
54
+
55
+ /**
56
+ * Regex used to match a Vine in text
57
+ *
58
+ * @since 1.3.0
59
+ *
60
+ * @type string
61
+ */
62
+ const URL_REGEX = '#https?://vine\.co/v/([a-z0-9]+)\/?#i';
63
+
64
+ /**
65
+ * oEmbed regex registered by WordPress Core since 4.1
66
+ *
67
+ * @since 1.3.0
68
+ *
69
+ * @type string
70
+ */
71
+ const OEMBED_CORE_REGEX = '#https?://vine.co/v/.*#i';
72
+
73
+ /**
74
+ * Base URL used to reconstruct a Vine URL
75
+ *
76
+ * @since 1.3.0
77
+ *
78
+ * @type string
79
+ */
80
+ const BASE_URL = 'https://vine.co/v/';
81
+
82
+ /**
83
+ * PHP class to use for fetching oEmbed data
84
+ *
85
+ * @since 1.3.0
86
+ *
87
+ * @type string
88
+ */
89
+ const OEMBED_API_CLASS = '\Twitter\WordPress\Helpers\VineOEmbed';
90
+
91
+ /**
92
+ * Relative path for the oEmbed API relative to Vine base path
93
+ *
94
+ * @since 1.3.0
95
+ *
96
+ * @type string
97
+ */
98
+ const OEMBED_API_ENDPOINT = 'oembed';
99
+
100
+ /**
101
+ * Accepted shortcode attributes and their default values
102
+ *
103
+ * @since 1.3.0
104
+ *
105
+ * @type array
106
+ */
107
+ public static $SHORTCODE_DEFAULTS = array( 'id' => '', 'width' => 0 );
108
+
109
+ /**
110
+ * Attach handlers for Vine
111
+ *
112
+ * @since 1.3.0
113
+ *
114
+ * @return void
115
+ */
116
+ public static function init()
117
+ {
118
+ $classname = get_called_class();
119
+
120
+ // register our shortcode and its handler
121
+ add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
122
+
123
+ // unhook the WordPress Core oEmbed handler
124
+ wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
125
+
126
+ // convert a URL into the shortcode equivalent
127
+ wp_embed_register_handler(
128
+ static::SHORTCODE_TAG,
129
+ static::URL_REGEX,
130
+ array( $classname, 'linkHandler' ),
131
+ 1
132
+ );
133
+
134
+ // Shortcode UI, if supported
135
+ add_action(
136
+ 'register_shortcode_ui',
137
+ array( $classname, 'shortcodeUI' ),
138
+ 5,
139
+ 0
140
+ );
141
+ }
142
+
143
+ /**
144
+ * Reference the feature by name
145
+ *
146
+ * @since 1.3.0
147
+ *
148
+ * @return string translated feature name
149
+ */
150
+ public static function featureName()
151
+ {
152
+ return 'Vine';
153
+ }
154
+
155
+ /**
156
+ * Describe shortcode for Shortcake UI
157
+ *
158
+ * @since 1.3.0
159
+ *
160
+ * @link https://github.com/fusioneng/Shortcake Shortcake UI
161
+ *
162
+ * @return void
163
+ */
164
+ public static function shortcodeUI()
165
+ {
166
+ // Shortcake required
167
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
168
+ return;
169
+ }
170
+
171
+ shortcode_ui_register_for_shortcode(
172
+ static::SHORTCODE_TAG,
173
+ array(
174
+ 'label' => esc_html( static::featureName() ),
175
+ 'listItemImage' => 'dashicons-video-alt3',
176
+ 'attrs' => array(
177
+ array(
178
+ 'attr' => 'id',
179
+ 'label' => 'ID',
180
+ 'type' => 'text',
181
+ 'meta' => array(
182
+ 'required' => true,
183
+ 'pattern' => '[A-Za-z0-9]+',
184
+ ),
185
+ ),
186
+ ),
187
+ )
188
+ );
189
+ }
190
+
191
+ /**
192
+ * Handle a URL matched by a embed handler
193
+ *
194
+ * @since 1.3.0
195
+ *
196
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
197
+ * @param array $attr Embed attributes. Not used.
198
+ * @param string $url The original URL that was matched by the regex. Not used.
199
+ * @param array $rawattr The original unmodified attributes. Not used.
200
+ *
201
+ * @return string HTML markup for the Tweet or an empty string if requirements not met
202
+ */
203
+ public static function linkHandler( $matches, $attr, $url, $rawattr )
204
+ {
205
+ if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
206
+ return '';
207
+ }
208
+
209
+ return static::shortcodeHandler( array( 'id' => $matches[1] ) );
210
+ }
211
+
212
+ /**
213
+ * Handle shortcode macro
214
+ *
215
+ * @since 1.3.0
216
+ *
217
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
218
+ * @type string|int attribute name or positional int
219
+ * @type mixed shortcode value
220
+ * }
221
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
222
+ *
223
+ * @return string HTML markup. empty string if parameter requirement not met or no collection info found
224
+ */
225
+ public static function shortcodeHandler( $attributes, $content = '' )
226
+ {
227
+ global $content_width;
228
+
229
+ $options = shortcode_atts(
230
+ static::$SHORTCODE_DEFAULTS,
231
+ $attributes,
232
+ static::SHORTCODE_TAG
233
+ );
234
+
235
+ $vine_id = trim( $options['id'] );
236
+ if ( ! $vine_id ) {
237
+ return '';
238
+ }
239
+
240
+ $query_parameters = static::getBaseOEmbedParams( $vine_id );
241
+ unset( $query_parameters['lang'] );
242
+
243
+ $width = absint( $options['width'] );
244
+ if ( $width < 100 && isset( $content_width ) ) {
245
+ $width = absint( $content_width );
246
+ }
247
+ if ( $width > 100 ) {
248
+ // reset max_width to max value supported by Vine
249
+ // collapses cache hits
250
+ if ( $width > 600 ) {
251
+ $width = 600;
252
+ }
253
+ $query_parameters['maxwidth'] = $width;
254
+ }
255
+
256
+ // fetch HTML markup from Vine oEmbed endpoint for the given parameters
257
+ $html = trim( static::getOEmbedMarkup( $query_parameters ) );
258
+ if ( ! $html ) {
259
+ return '';
260
+ }
261
+
262
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
263
+
264
+ $inline_js = \Twitter\WordPress\JavaScriptLoaders\VineEmbed::enqueue();
265
+ if ( $inline_js ) {
266
+ return $html . $inline_js;
267
+ }
268
+
269
+ return $html;
270
+ }
271
+
272
+ /**
273
+ * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
274
+ *
275
+ * @since 1.3.0
276
+ *
277
+ * @param array $query_parameters oEmbed API query parameters
278
+ *
279
+ * @return string cache key
280
+ */
281
+ public static function oEmbedCacheKey( array $query_parameters )
282
+ {
283
+ if ( ! ( isset( $query_parameters['id'] ) && $query_parameters['id'] ) ) {
284
+ return '';
285
+ }
286
+
287
+ $key_pieces = array( static::SHORTCODE_TAG, $query_parameters['id'] );
288
+
289
+ if ( isset( $query_parameters['maxwidth'] ) && $query_parameters['maxwidth'] ) {
290
+ $key_pieces[] = 'w' . $query_parameters['maxwidth'];
291
+ }
292
+
293
+ return implode( '_', $key_pieces );
294
+ }
295
+ }
src/Twitter/WordPress/User/Meta.php CHANGED
@@ -34,26 +34,28 @@ class Meta
34
  {
35
 
36
  /**
37
- * Get a Twitter @username stored for a given WordPress user identifier
38
  *
39
- * @since 1.0.0
40
  *
41
  * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
 
42
  *
43
- * @return string Twitter username value stored for the given WordPress user identifier
44
  */
45
- public static function getTwitterUsername( $user_id )
46
- {
47
  // basic test for invalid passed parameter
48
  if ( ! $user_id ) {
49
  return '';
50
  }
 
 
 
51
 
52
- $meta_key = 'twitter';
53
  if ( function_exists( 'get_user_attribute' ) ) {
54
- $username = get_user_attribute( $user_id, $meta_key );
55
  } else {
56
- $username = get_user_meta( $user_id, $meta_key, /* single */ true );
57
  }
58
 
59
  if ( ! is_string( $username ) ) {
@@ -70,9 +72,35 @@ class Meta
70
  * @param string $username Twitter username associated with a WordPress user ID
71
  * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
72
  */
73
- $username = apply_filters( 'twitter_username', $username, $user_id );
74
  }
75
 
76
  return $username;
77
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
34
  {
35
 
36
  /**
37
+ * Get a username value stored for the given WordPress user identifier
38
  *
39
+ * @since 1.3.0
40
  *
41
  * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
42
+ * @param string $key user attribute or meta key storing the username of interest
43
  *
44
+ * @return string stored username. empty string if no user_id provided or no username found
45
  */
46
+ public static function getSocialUsername( $user_id, $key ) {
 
47
  // basic test for invalid passed parameter
48
  if ( ! $user_id ) {
49
  return '';
50
  }
51
+ if ( ! is_string( $key ) || ! $key ) {
52
+ return '';
53
+ }
54
 
 
55
  if ( function_exists( 'get_user_attribute' ) ) {
56
+ $username = get_user_attribute( $user_id, $key );
57
  } else {
58
+ $username = get_user_meta( $user_id, $key, /* single */ true );
59
  }
60
 
61
  if ( ! is_string( $username ) ) {
72
  * @param string $username Twitter username associated with a WordPress user ID
73
  * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
74
  */
75
+ $username = apply_filters( $key . '_username', $username, $user_id );
76
  }
77
 
78
  return $username;
79
  }
80
+
81
+ /**
82
+ * Get a Twitter @username stored for a given WordPress user identifier
83
+ *
84
+ * @since 1.0.0
85
+ *
86
+ * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
87
+ *
88
+ * @return string Twitter username value stored for the given WordPress user identifier
89
+ */
90
+ public static function getTwitterUsername( $user_id ) {
91
+ return static::getSocialUsername( $user_id, 'twitter' );
92
+ }
93
+
94
+ /**
95
+ * Get a Periscope username stored for a given WordPress identifier
96
+ *
97
+ * @since 1.3.0
98
+ *
99
+ * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
100
+ *
101
+ * @return string Periscope username value stored for the given WordPress user identifier
102
+ */
103
+ public static function getPeriscopeUsername( $user_id ) {
104
+ return static::getSocialUsername( $user_id, 'periscope' );
105
+ }
106
  }
src/Twitter/WordPress/Widgets/PeriscopeOnAir.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Widgets;
27
+
28
+ /**
29
+ * Add Periscope On Air button as a WordPress widget
30
+ *
31
+ * @link http://codex.wordpress.org/Widgets_API WordPress widgets API
32
+ *
33
+ * @since 1.3.0
34
+ */
35
+ class PeriscopeOnAir extends \WP_Widget
36
+ {
37
+
38
+ /**
39
+ * Widget base ID
40
+ *
41
+ * Widget identifiers will derive from the base based on their positioning. e.g. periscope-on-air-1
42
+ *
43
+ * @since 1.3.0
44
+ *
45
+ * @type string
46
+ */
47
+ const BASE_ID = 'periscope-on-air';
48
+
49
+ /**
50
+ * Register widget with WordPress
51
+ *
52
+ * @since 1.3.0
53
+ *
54
+ * @return void
55
+ */
56
+ public function __construct()
57
+ {
58
+ parent::__construct(
59
+ static::BASE_ID, // Base ID
60
+ __( 'Periscope On Air', 'twitter' ), // name
61
+ array(
62
+ 'description' => __( 'Lets a viewer discover your Periscope account and on air status', 'twitter' ) // args
63
+ )
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Front-end display of widget
69
+ *
70
+ * @since 1.3.0
71
+ *
72
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
73
+ * @param array $instance The settings for the particular instance of the widget
74
+ *
75
+ * @return void
76
+ */
77
+ public function widget( $args, $instance )
78
+ {
79
+ // no Periscope username target
80
+ if ( empty( $instance['username'] ) ) {
81
+ return;
82
+ }
83
+
84
+ /** This filter is documented in wp-includes/default-widgets.php */
85
+ $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
86
+ unset( $instance['title'] );
87
+
88
+ $button_html = \Twitter\WordPress\Shortcodes\PeriscopeOnAir::shortcodeHandler( $instance );
89
+ if ( ! $button_html ) {
90
+ return;
91
+ }
92
+
93
+ echo $args['before_widget'];
94
+
95
+ if ( $title ) {
96
+ echo $args['before_title'] . $title . $args['after_title'];
97
+ }
98
+
99
+ // escaped in markup builder
100
+ // @codingStandardsIgnoreStart WordPress.XSS.EscapeOutput
101
+ echo $button_html;
102
+ // @codingStandardsIgnoreEnd WordPress.XSS.EscapeOutput
103
+
104
+ echo $args['after_widget'];
105
+ }
106
+
107
+ /**
108
+ * Settings update form
109
+ *
110
+ * @since 1.3.0
111
+ *
112
+ * @param array $instance Current settings
113
+ *
114
+ * @return void
115
+ */
116
+ public function form( $instance )
117
+ {
118
+ $instance = wp_parse_args(
119
+ (array) $instance,
120
+ array_merge(
121
+ array( 'title' => '' ),
122
+ \Twitter\WordPress\Shortcodes\PeriscopeOnAir::$SHORTCODE_DEFAULTS
123
+ )
124
+ );
125
+
126
+ $close_void_element = \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
127
+ ?>
128
+ <p><label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php echo esc_html( __( 'Title:', 'twitter' ) ); ?></label>
129
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( trim( strip_tags( $instance['title'] ) ) ); ?>"<?php echo $close_void_element; ?>></p>
130
+
131
+ <p><label for="<?php echo esc_attr( $this->get_field_id( 'username' ) ); ?>"><?php echo esc_html( __( 'Periscope username', 'twitter' ) ); ?></label>
132
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'username' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'username' ) ); ?>" type="text" pattern="[a-zA-Z0-9_]{1,20}" value="<?php echo esc_attr( $instance['username'] ); ?>"<?php echo $close_void_element; ?>></p>
133
+
134
+ <p><label for="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>"><?php echo esc_html( __( 'Button size:', 'twitter' ) ); ?></label>
135
+ <fieldset id="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>">
136
+ <label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'size' ) ); ?>" value="small"<?php checked( 'large' != $instance['size'] ); echo $close_void_element; ?>> <?php echo esc_html( _x( 'small', 'small size button', 'twitter' ) ); ?> </label>
137
+ <label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'size' ) ); ?>" value="large"<?php checked( 'large' == $instance['size'] ); echo $close_void_element; ?>> <?php echo esc_html( _x( 'large', 'large size button', 'twitter' ) ); ?> </label>
138
+ </fieldset></p>
139
+ <?php
140
+ }
141
+
142
+ /**
143
+ * Update a widget instance
144
+ *
145
+ * @since 1.3.0
146
+ *
147
+ * @param array $new_instance New settings for this instance as input by the user via form()
148
+ * @param array $old_instance Old settings for this instance
149
+ *
150
+ * @return bool|array settings to save or false to cancel saving
151
+ */
152
+ public function update( $new_instance, $old_instance )
153
+ {
154
+ $instance = array();
155
+ $new_instance = (array) $new_instance;
156
+ $title = trim( strip_tags( $new_instance['title'] ) );
157
+ if ( $title ) {
158
+ $instance['title'] = $title;
159
+ }
160
+ unset( $new_instance['title'] );
161
+
162
+ $on_air = \Twitter\Widgets\PeriscopeOnAir::fromArray( $new_instance );
163
+ if ( ! $on_air ) {
164
+ return false;
165
+ }
166
+
167
+ $filtered_options = $on_air->toArray();
168
+ $username = $on_air->getUsername();
169
+ if ( $username ) {
170
+ $filtered_options['username'] = $username;
171
+ }
172
+ unset( $username );
173
+
174
+ return array_merge( $instance, $filtered_options );
175
+ }
176
+ }
twitter.php CHANGED
@@ -24,13 +24,13 @@ THE SOFTWARE.
24
  */
25
  /**
26
  * @package twitter
27
- * @version 1.1.0
28
  */
29
  /*
30
  Plugin Name: Twitter
31
  Plugin URI: http://wordpress.org/plugins/twitter/
32
  Description: Official Twitter plugin for WordPress. Embed Twitter content and grow your audience on Twitter. Requires PHP 5.4 or greater.
33
- Version: 1.2.0
34
  Author: Twitter
35
  Author URI: https://dev.twitter.com/
36
  License: MIT
@@ -53,7 +53,7 @@ if ( ! function_exists( 'add_action' ) ) {
53
  // plugin requires PHP 5.4 or greater
54
  if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
55
  if ( ! class_exists( 'Twitter_CompatibilityNotice' ) ) {
56
- require_once( dirname(__FILE__) . '/compatibility-notice.php' );
57
  }
58
 
59
  // possibly display a notice, trigger error
24
  */
25
  /**
26
  * @package twitter
27
+ * @version 1.3.0
28
  */
29
  /*
30
  Plugin Name: Twitter
31
  Plugin URI: http://wordpress.org/plugins/twitter/
32
  Description: Official Twitter plugin for WordPress. Embed Twitter content and grow your audience on Twitter. Requires PHP 5.4 or greater.
33
+ Version: 1.3.0
34
  Author: Twitter
35
  Author URI: https://dev.twitter.com/
36
  License: MIT
53
  // plugin requires PHP 5.4 or greater
54
  if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
55
  if ( ! class_exists( 'Twitter_CompatibilityNotice' ) ) {
56
+ require_once( dirname( __FILE__ ) . '/compatibility-notice.php' );
57
  }
58
 
59
  // possibly display a notice, trigger error
uninstall.php CHANGED
@@ -65,12 +65,14 @@ if ( function_exists( 'delete_post_meta_by_key' ) ) {
65
 
66
  // user meta
67
  if ( function_exists( 'delete_metadata' ) ) {
68
- // delete Twitter customizations stored for a WordPress user account
69
- delete_metadata(
70
- 'user', // meta type
71
- 0, // user ID (ignored)
72
- 'twitter', // meta key
73
- '', // delete all values
74
- true // delete all. ignore passed user id
75
- );
 
 
76
  }
65
 
66
  // user meta
67
  if ( function_exists( 'delete_metadata' ) ) {
68
+ foreach ( array( 'twitter', 'periscope' ) as $social ) {
69
+ // delete Twitter customization stored for a WordPress user account
70
+ delete_metadata(
71
+ 'user', // meta type
72
+ 0, // user ID (ignored)
73
+ $social, // meta key
74
+ '', // delete all values
75
+ true // delete all. ignore passed user id
76
+ );
77
+ }
78
  }