Twitter - Version 2.0.0

Version Description

Adds profile, list, search, and collection timelines. Upgrade advertising pixel.

Download this release

Release Info

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

Code changes from version 1.5.0 to 2.0.0

Files changed (89) hide show
  1. autoload.php +21 -22
  2. readme.txt +47 -57
  3. src/Twitter/Cards/Card.php +6 -6
  4. src/Twitter/Cards/Components/Account.php +3 -3
  5. src/Twitter/Cards/Components/Creator.php +1 -1
  6. src/Twitter/Cards/Components/Description.php +1 -1
  7. src/Twitter/Cards/Components/Image.php +54 -10
  8. src/Twitter/Cards/Components/SingleImage.php +14 -7
  9. src/Twitter/Cards/Summary.php +5 -7
  10. src/Twitter/Helpers/HTMLBuilder.php +24 -21
  11. src/Twitter/Helpers/Validators/ScreenName.php +63 -2
  12. src/Twitter/Helpers/Validators/WebsiteTag.php +121 -0
  13. src/Twitter/Intents/Traits/Related.php +95 -0
  14. src/Twitter/Intents/Tweet.php +34 -88
  15. src/Twitter/Widgets/{BaseWidget.php → Base.php} +8 -8
  16. src/Twitter/Widgets/{FollowButton.php → Buttons/Follow.php} +15 -15
  17. src/Twitter/Widgets/{PeriscopeOnAir.php → Buttons/Periscope/OnAir.php} +27 -24
  18. src/Twitter/Widgets/{TweetButton.php → Buttons/Tweet.php} +17 -15
  19. src/Twitter/Widgets/Embeds/Moment.php +96 -0
  20. src/Twitter/Widgets/Embeds/Theme.php +321 -0
  21. src/Twitter/Widgets/Embeds/Timeline.php +678 -0
  22. src/Twitter/Widgets/Embeds/Timeline/Collection.php +317 -0
  23. src/Twitter/Widgets/Embeds/Timeline/Profile.php +192 -0
  24. src/Twitter/Widgets/Embeds/Timeline/Search.php +358 -0
  25. src/Twitter/Widgets/Embeds/Timeline/TwitterList.php +199 -0
  26. src/Twitter/Widgets/Embeds/Tweet.php +344 -0
  27. src/Twitter/Widgets/Embeds/Tweet/Base.php +215 -0
  28. src/Twitter/Widgets/Embeds/Tweet/Video.php +84 -0
  29. src/Twitter/Widgets/Embeds/Vine.php +227 -0
  30. src/Twitter/Widgets/Language.php +2 -0
  31. src/Twitter/WordPress/Admin/Post/MetaBox.php +1 -1
  32. src/Twitter/WordPress/Admin/Post/TweetIntent.php +21 -7
  33. src/Twitter/WordPress/Admin/Post/TwitterCard.php +18 -11
  34. src/Twitter/WordPress/Admin/Profile/PeriscopeUser.php +5 -5
  35. src/Twitter/WordPress/Admin/Profile/User.php +5 -5
  36. src/Twitter/WordPress/Admin/Settings/{TweetButton.php → Buttons/Tweet.php} +27 -12
  37. src/Twitter/WordPress/Admin/Settings/{SiteAttribution.php → Cards/SiteAttribution.php} +32 -10
  38. src/Twitter/WordPress/Admin/Settings/{Theme.php → Embeds/Theme.php} +35 -13
  39. src/Twitter/WordPress/Admin/Settings/Loader.php +1 -1
  40. src/Twitter/WordPress/Admin/Settings/SinglePage.php +13 -8
  41. src/Twitter/WordPress/Cards/Compatibility.php +1 -1
  42. src/Twitter/WordPress/Cards/Generator.php +21 -17
  43. src/Twitter/WordPress/Cards/ImageHandler.php +12 -6
  44. src/Twitter/WordPress/Content/{TweetButton.php → Buttons/Tweet.php} +4 -4
  45. src/Twitter/WordPress/Features.php +68 -18
  46. src/Twitter/WordPress/Head/AuthorshipLink.php +6 -1
  47. src/Twitter/WordPress/Head/CardsMetaElements.php +11 -2
  48. src/Twitter/WordPress/Head/WidgetsMetaElements.php +2 -3
  49. src/Twitter/WordPress/Helpers/HTMLBuilder.php +1 -1
  50. src/Twitter/WordPress/Helpers/TwitterAPI.php +27 -8
  51. src/Twitter/WordPress/JavaScriptLoaders/AsyncJavaScript.php +45 -8
  52. src/Twitter/WordPress/JavaScriptLoaders/Tracking.php +34 -35
  53. src/Twitter/WordPress/JavaScriptLoaders/Widgets.php +3 -2
  54. src/Twitter/WordPress/PluginLoader.php +51 -26
  55. src/Twitter/WordPress/Shortcodes/{Tracking.php → Advertising/Tracking.php} +97 -34
  56. src/Twitter/WordPress/Shortcodes/AuthorContext.php +51 -0
  57. src/Twitter/WordPress/Shortcodes/Buttons/Follow.php +244 -0
  58. src/Twitter/WordPress/Shortcodes/{PeriscopeOnAir.php → Buttons/Periscope/OnAir.php} +83 -77
  59. src/Twitter/WordPress/Shortcodes/{Share.php → Buttons/Share.php} +55 -18
  60. src/Twitter/WordPress/Shortcodes/EmbeddedTweet.php +0 -434
  61. src/Twitter/WordPress/Shortcodes/{Moment.php → Embeds/Moment.php} +32 -3
  62. src/Twitter/WordPress/Shortcodes/Embeds/Timeline.php +347 -0
  63. src/Twitter/WordPress/Shortcodes/Embeds/Timeline/Collection.php +220 -0
  64. src/Twitter/WordPress/Shortcodes/Embeds/Timeline/CollectionGrid.php +163 -0
  65. src/Twitter/WordPress/Shortcodes/Embeds/Timeline/Profile.php +220 -0
  66. src/Twitter/WordPress/Shortcodes/Embeds/Timeline/Search.php +228 -0
  67. src/Twitter/WordPress/Shortcodes/Embeds/Timeline/TwitterList.php +218 -0
  68. src/Twitter/WordPress/Shortcodes/Embeds/Tweet.php +559 -0
  69. src/Twitter/WordPress/Shortcodes/{EmbeddedTweetVideo.php → Embeds/Tweet/Video.php} +61 -92
  70. src/Twitter/WordPress/Shortcodes/{Vine.php → Embeds/Vine.php} +104 -51
  71. src/Twitter/WordPress/Shortcodes/Follow.php +0 -293
  72. src/Twitter/WordPress/Shortcodes/Helpers/Attributes.php +157 -0
  73. src/Twitter/WordPress/Shortcodes/OEmbedTrait.php +19 -25
  74. src/Twitter/WordPress/Shortcodes/PublishOEmbedEndpoint.php +14 -1
  75. src/Twitter/WordPress/Shortcodes/ShortcodeInterface.php +22 -1
  76. src/Twitter/WordPress/Shortcodes/TweetGrid.php +0 -288
  77. src/Twitter/WordPress/Site/Username.php +1 -1
  78. src/Twitter/WordPress/User/Meta.php +7 -4
  79. src/Twitter/WordPress/Widgets/Advertising/Tracking.php +181 -0
  80. src/Twitter/WordPress/Widgets/{Follow.php → Buttons/Follow.php} +67 -27
  81. src/Twitter/WordPress/Widgets/{PeriscopeOnAir.php → Buttons/Periscope/OnAir.php} +50 -23
  82. src/Twitter/WordPress/Widgets/Embeds/Timeline.php +222 -0
  83. src/Twitter/WordPress/Widgets/Embeds/Timeline/Collection.php +128 -0
  84. src/Twitter/WordPress/Widgets/Embeds/Timeline/Profile.php +128 -0
  85. src/Twitter/WordPress/Widgets/Embeds/Timeline/Search.php +139 -0
  86. src/Twitter/WordPress/Widgets/Embeds/Timeline/TwitterList.php +133 -0
  87. src/Twitter/WordPress/Widgets/Widget.php +52 -0
  88. src/Twitter/WordPress/Widgets/WidgetInterface.php +86 -0
  89. twitter.php +2 -2
autoload.php CHANGED
@@ -37,31 +37,30 @@ THE SOFTWARE.
37
  *
38
  * @return void
39
  */
40
- spl_autoload_register( function ( $class ) {
41
- // project-specific namespace prefix
42
- $prefix = 'Twitter\\';
43
 
44
- // base directory for the namespace prefix
45
- $base_dir = defined( 'TWITTER_PLUGIN_DIR' ) ? TWITTER_PLUGIN_DIR : __DIR__ . '/src/Twitter/';
46
 
47
- // does the class use the namespace prefix?
48
- $len = strlen( $prefix );
49
- if ( 0 !== strncmp( $prefix, $class, $len ) ) {
50
- // no, move to the next registered autoloader
51
- return;
52
- }
53
 
54
- // get the relative class name
55
- $relative_class = substr( $class, $len );
56
 
57
- // replace the namespace prefix with the base directory, replace namespace
58
- // separators with directory separators in the relative class name, append
59
- // with .php
60
- $file = $base_dir . str_replace( '\\', '/', $relative_class ) . '.php';
61
 
62
- // if the file exists, require it
63
- if ( file_exists( $file ) ) {
64
- require $file;
65
- }
66
  });
67
- ?>
37
  *
38
  * @return void
39
  */
40
+ spl_autoload_register(function ($class) {
41
+ // project-specific namespace prefix
42
+ $prefix = 'Twitter\\';
43
 
44
+ // base directory for the namespace prefix
45
+ $base_dir = defined('TWITTER_PLUGIN_DIR') ? TWITTER_PLUGIN_DIR : __DIR__ . '/src/Twitter/';
46
 
47
+ // does the class use the namespace prefix?
48
+ $len = strlen($prefix);
49
+ if (0 !== strncmp($prefix, $class, $len)) {
50
+ // no, move to the next registered autoloader
51
+ return;
52
+ }
53
 
54
+ // get the relative class name
55
+ $relative_class = substr($class, $len);
56
 
57
+ // replace the namespace prefix with the base directory, replace namespace
58
+ // separators with directory separators in the relative class name, append
59
+ // with .php
60
+ $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
61
 
62
+ // if the file exists, require it
63
+ if (file_exists($file)) {
64
+ require $file;
65
+ }
66
  });
 
readme.txt CHANGED
@@ -1,71 +1,58 @@
1
  === Plugin Name ===
2
  Contributors: Twitter, niallkennedy
3
- Tags: twitter, embedded tweet, twitter moment, twitter video, twitter grid, vine, periscope, twitter cards, tweet button, follow button, twitter analytics, twitter ads
4
- Requires at least: 3.9
5
  Tested up to: 4.7
6
- Stable tag: 1.5.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, shortcode, or by pasting a Twitter profile link into a post.
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
  = 1.5.0 =
70
  Update admin menu functionality for compatibility with WordPress 4.5+.
71
 
@@ -85,8 +72,21 @@ Shortcode improvements for ajax-loaded posts. Remove photo, gallery, and product
85
  Display admin notice if current PHP version does not meet minimum requirements. Do not display Tweet button in auto-generated excerpt.
86
 
87
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  = 1.5.0 =
89
- * Place Twitter administrative menu as a general menu item, not the deprecated utility menu. WordPress 4.5 compatibility feature.
90
  * Use publish.twitter.com oEmbed API endpoint for single Tweet oEmbed
91
 
92
  = 1.4.0 =
@@ -128,19 +128,13 @@ Display admin notice if current PHP version does not meet minimum requirements.
128
 
129
  = How can I change an embedded Tweet's background and link colors to match my site's theme? =
130
 
131
- The Twitter plugin for WordPress includes a settings page with options to customize the background color, link color, and border color used in Twitter embedded Tweets and embedded timelines.
132
-
133
- = How do I include an embedded timeline in my page? =
134
-
135
- Paste a Twitter collection URL into your post content to see a media-rich grid display on your website.
136
-
137
- 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).
138
 
139
  = My custom link color and border color do not appear in embedded Tweets or timelines =
140
 
141
  Your site may have a [Content Security Policy](https://developer.mozilla.org/docs/Web/Security/CSP/Introducing_Content_Security_Policy) blocking Twitter's JavaScript from inserting your custom styling into the widget.
142
 
143
- You may have configured an embedded timeline widget with a non-default link color. Your widget configuration overrides your page / theme configuration.
144
 
145
  = Does the Twitter plugin add additional tracking of my site's visitors? =
146
 
@@ -148,15 +142,11 @@ The Twitter plugin for WordPress makes it easier to explicitly include Twitter f
148
 
149
  Twitter widgets and buttons load Twitter's widgets.js library through the WordPress JavaScript queue. Read more about [how Twitter for Websites widgets respect user privacy](https://dev.twitter.com/web/overview/privacy).
150
 
151
- Twitter advertising trackers are only included on the page when invoked by the site using the `twitter_tracking` shortcode. Read more about [Twitter's policies for conversion tracking and tailored audiences products](https://support.twitter.com/articles/20171365-policies-for-conversion-tracking-and-tailored-audiences).
152
 
153
  == Screenshots ==
154
 
155
  1. Settings screen. Customize Tweet and Timeline color schemes including background, text colors, and borders. Attribute site content to a Twitter account. Automatically include Tweet buttons alongside your post content.
156
  2. Post editor meta box. Define custom Tweet text, hashtags, and Twitter Card data.
157
- 3. Embed single Tweets, Tweets with video templates, multiple Tweets in a grid format, Twitter Moments, and Vines.
158
-
159
- == Installation ==
160
-
161
- 1. Add the Twitter plugin to your WordPress installation
162
- 1. Activate the plugin through the 'Plugins' menu in WordPress
1
  === Plugin Name ===
2
  Contributors: Twitter, niallkennedy
3
+ Tags: twitter, embedded tweet, embedded timeline, twitter profile, twitter list, twitter moment, twitter video, twitter grid, vine, periscope, twitter cards, tweet button, follow button, twitter analytics, twitter ads
4
+ Requires at least: 4.1
5
  Tested up to: 4.7
6
+ Stable tag: 2.0.0
7
  License: MIT
8
+ License URI: https://opensource.org/licenses/MIT
9
 
10
+ Official Twitter, Periscope, and Vine plugin for WordPress. Embed content and grow your audience. Requires PHP 5.4 or greater.
11
 
12
  == Description ==
13
 
14
+ Embed Twitter content, improve sharing on Twitter, convert your web audience into Twitter or Periscope subscribers, and easily track visits to your website from Twitter advertising.
 
 
15
 
16
  Requires PHP version 5.4 or greater.
17
 
18
  = Embed Twitter content =
19
+ Embed Twitter content by pasting a URL, customizing a shortcode, or in a widget area.
20
 
21
+ * [single Tweet](https://dev.twitter.com/web/embedded-tweets "single Tweet embed")
22
+ * [single Tweet with video template](https://dev.twitter.com/web/embedded-video "single Tweet with video embed")
23
+ * [profile timeline](https://dev.twitter.com/web/embedded-timelines/user "Twitter embedded profile timeline")
24
+ * [list timeline](https://dev.twitter.com/web/embedded-timelines/list "Twitter embedded list timeline")
25
+ * [search timeline](https://dev.twitter.com/web/embedded-timelines/search "Twitter embedded search timeline")
26
+ * [collection](https://dev.twitter.com/web/embedded-timelines/collection "Twitter embedded collection")
27
+ * [Moment](https://dev.twitter.com/web/embedded-moments "Twitter embedded Moment")
28
+ * [Vine](https://dev.twitter.com/web/vine "Vine embed")
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ Customize embed display to match your theme. Choose a light or dark background, customize link and border colors, and customize timeline template components through your site's WordPress administrative interface.
31
 
32
+ The plugin automatically customizes an embed's template text to match the locale of your site, optimally loads Twitter's JavaScript to improve site speed and extensibility, and handles advanced use cases such as articles loaded asynchronously via the WordPress API.
33
 
34
+ = Grow your Twitter audience =
35
 
36
+ Automatically generate link previews for your site's URLs shared on Twitter using [Twitter Cards markup](https://dev.twitter.com/cards/overview) . Easily identify your site and author Twitter accounts through your site and user administrative interfaces.
37
 
38
+ Add a [Tweet button](https://dev.twitter.com/web/tweet-button) to public posts to encourage your visitors to share your content on Twitter. Visitors may see recommended accounts to follow after sharing your content including your site and its authors.
39
 
40
+ Add a [Follow button](https://dev.twitter.com/web/follow-button) to convert your site visitors into Twitter subscribers.
41
 
42
+ Add a [Periscope On Air button](https://www.periscope.tv/embed#on-air-button) to convert your site visitors into Periscope subscribers.
43
 
44
+ = Improve Twitter advertising campaigns =
45
 
46
+ Easily add a Twitter website tag to your website to track the effectiveness and [conversion rates](https://business.twitter.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites.html) of Twitter advertising campaigns or [build tailored audiences](https://business.twitter.com/en/targeting/tailored-audiences.html) to target your Twitter advertisements for your website audience.
 
 
 
 
47
 
48
  > <strong>Docs and active development</strong><br>
49
+ > Contribute to the plugin, submit pull requests, or run test suites through the [Twitter plugin for WordPress GitHub repository](https://github.com/twitter/wordpress).
50
  > View [Twitter for WordPress documentation](https://dev.twitter.com/web/wordpress) to learn more about customization through WordPress filters.
51
 
52
  == Upgrade Notice ==
53
+ = 2.0.0 =
54
+ Adds profile, list, search, and collection timelines. Upgrade advertising pixel.
55
+
56
  = 1.5.0 =
57
  Update admin menu functionality for compatibility with WordPress 4.5+.
58
 
72
  Display admin notice if current PHP version does not meet minimum requirements. Do not display Tweet button in auto-generated excerpt.
73
 
74
  == Changelog ==
75
+ = 2.0.0 =
76
+ * Embed a [profile timeline](https://dev.twitter.com/web/embedded-timelines/user "Twitter embedded profile timeline"), [list timeline](https://dev.twitter.com/web/embedded-timelines/list "Twitter embedded list timeline"), or [collection](https://dev.twitter.com/web/embedded-timelines/collection "Twitter embedded collection") by pasting a URL, customizing a shortcode, or a widget
77
+ * Embed a [search timeline](https://dev.twitter.com/web/embedded-timelines/search "Twitter embedded search timeline") by shortcode or widget using a widget ID configured on Twitter.com
78
+ * Upgrade Twitter advertising tracker to [universal website tag](https://blog.twitter.com/2016/website-conversion-tracking-and-remarketing-made-easier-and-more-flexible "Twitter blog: announcing universal website tag")
79
+ * Twitter Cards include image alternative text when available
80
+ * Follow button and Periscope On Air button use post author or site username if no username specified
81
+ * Improved compatibility with WordPress.com / Jetpack formatting of `tweet` shortcode
82
+ * Prefer wp_resource_hints API for DNS prefetch in WordPress 4.6+
83
+ * Update single Tweet with video to remove status customization no longer supported by Twitter
84
+ * Fix bug when a large featured image is highlighted in a Twitter Card
85
+ * Describe site username option for WordPress REST API in WordPress 4.7
86
+ * Bump minimum WordPress version to 4.1
87
+
88
  = 1.5.0 =
89
+ * Place Twitter administrative menu as a general menu item, not the deprecated utility menu. WordPress 4.5 compatibility feature
90
  * Use publish.twitter.com oEmbed API endpoint for single Tweet oEmbed
91
 
92
  = 1.4.0 =
128
 
129
  = How can I change an embedded Tweet's background and link colors to match my site's theme? =
130
 
131
+ The Twitter plugin for WordPress includes a settings page with options to set a light or dark theme and choose a link or border color used in embedded Tweets and timelines.
 
 
 
 
 
 
132
 
133
  = My custom link color and border color do not appear in embedded Tweets or timelines =
134
 
135
  Your site may have a [Content Security Policy](https://developer.mozilla.org/docs/Web/Security/CSP/Introducing_Content_Security_Policy) blocking Twitter's JavaScript from inserting your custom styling into the widget.
136
 
137
+ You may have configured an embedded search timeline widget with a non-default link color. Your stored widget configuration overrides your page or theme’s configuration.
138
 
139
  = Does the Twitter plugin add additional tracking of my site's visitors? =
140
 
142
 
143
  Twitter widgets and buttons load Twitter's widgets.js library through the WordPress JavaScript queue. Read more about [how Twitter for Websites widgets respect user privacy](https://dev.twitter.com/web/overview/privacy).
144
 
145
+ Twitter advertising trackers are only included on the page when invoked by the site using the `twitter_tracking` shortcode or placing the Twitter advertising shortcode in a widget area. Read more about [Twitter's policies for conversion tracking and tailored audiences products](https://support.twitter.com/articles/20171365).
146
 
147
  == Screenshots ==
148
 
149
  1. Settings screen. Customize Tweet and Timeline color schemes including background, text colors, and borders. Attribute site content to a Twitter account. Automatically include Tweet buttons alongside your post content.
150
  2. Post editor meta box. Define custom Tweet text, hashtags, and Twitter Card data.
151
+ 3. Embed single Tweets, timelines, Twitter Moments, and Vines.
152
+ 4. Embed a Twitter profile, list, collection, Moment, or search result in a theme widget area. Add a Twitter follow button or Periscope on Air button to a theme widget area to increase followers. Easily add advertising conversion tracking through a widget.
 
 
 
 
src/Twitter/Cards/Card.php CHANGED
@@ -104,7 +104,7 @@ class Card
104
  *
105
  * @param string $title content title
106
  *
107
- * @return __CLASS__ support chaining
108
  */
109
  public function setTitle($title)
110
  {
@@ -123,7 +123,7 @@ class Card
123
  *
124
  * @since 1.0.0
125
  *
126
- * @return __CLASS__ support chaining
127
  */
128
  public function setSite($site)
129
  {
@@ -146,21 +146,21 @@ class Card
146
  */
147
  public function toArray()
148
  {
149
- if (! ( isset( $this->type ) && $this->type )) {
150
  return array();
151
  }
152
 
153
  $card = array( 'card' => $this->type );
154
- if (isset( $this->title ) && $this->title) {
155
  $card['title'] = $this->title;
156
  }
157
 
158
- if (isset( $this->site ) && $this->site) {
159
  $site = $this->site->asCardProperties();
160
  if ($site) {
161
  $card['site'] = $site;
162
  }
163
- unset( $site );
164
  }
165
 
166
  return $card;
104
  *
105
  * @param string $title content title
106
  *
107
+ * @return self support chaining
108
  */
109
  public function setTitle($title)
110
  {
123
  *
124
  * @since 1.0.0
125
  *
126
+ * @return self support chaining
127
  */
128
  public function setSite($site)
129
  {
146
  */
147
  public function toArray()
148
  {
149
+ if (! ( isset($this->type) && $this->type )) {
150
  return array();
151
  }
152
 
153
  $card = array( 'card' => $this->type );
154
+ if (isset($this->title) && $this->title) {
155
  $card['title'] = $this->title;
156
  }
157
 
158
+ if (isset($this->site) && $this->site) {
159
  $site = $this->site->asCardProperties();
160
  if ($site) {
161
  $card['site'] = $site;
162
  }
163
+ unset($site);
164
  }
165
 
166
  return $card;
src/Twitter/Cards/Components/Account.php CHANGED
@@ -63,7 +63,7 @@ class Account
63
  $account->setScreenName($screen_name);
64
 
65
  if (! $account->hasScreenName()) {
66
- return;
67
  }
68
 
69
  return $account;
@@ -84,7 +84,7 @@ class Account
84
  $account->setID($id);
85
 
86
  if (! $account->hasID()) {
87
- return;
88
  }
89
 
90
  return $account;
@@ -135,7 +135,7 @@ class Account
135
  *
136
  * @param string $id Twitter user id
137
  *
138
- * @return __CLASS__ support chaining
139
  */
140
  public function setID($id)
141
  {
63
  $account->setScreenName($screen_name);
64
 
65
  if (! $account->hasScreenName()) {
66
+ return null;
67
  }
68
 
69
  return $account;
84
  $account->setID($id);
85
 
86
  if (! $account->hasID()) {
87
+ return null;
88
  }
89
 
90
  return $account;
135
  *
136
  * @param string $id Twitter user id
137
  *
138
+ * @return self support chaining
139
  */
140
  public function setID($id)
141
  {
src/Twitter/Cards/Components/Creator.php CHANGED
@@ -46,7 +46,7 @@ trait Creator
46
  *
47
  * @param \Twitter\Cards\Components\Account $creator Twitter account
48
  *
49
- * @return __CLASS__ support chaining
50
  */
51
  public function setCreator($creator)
52
  {
46
  *
47
  * @param \Twitter\Cards\Components\Account $creator Twitter account
48
  *
49
+ * @return self support chaining
50
  */
51
  public function setCreator($creator)
52
  {
src/Twitter/Cards/Components/Description.php CHANGED
@@ -71,7 +71,7 @@ trait Description
71
  *
72
  * @param string $description content description
73
  *
74
- * @return __CLASS__ support chaining
75
  */
76
  public function setDescription($description)
77
  {
71
  *
72
  * @param string $description content description
73
  *
74
+ * @return self support chaining
75
  */
76
  public function setDescription($description)
77
  {
src/Twitter/Cards/Components/Image.php CHANGED
@@ -59,10 +59,19 @@ class Image
59
  */
60
  protected $height;
61
 
 
 
 
 
 
 
 
 
 
62
  /**
63
  * @since 1.0.0
64
  *
65
- * @return void
66
  */
67
  public function __construct($src)
68
  {
@@ -103,7 +112,7 @@ class Image
103
  *
104
  * @param int $width width of the image in whole pixels
105
  *
106
- * @return __CLASS__ support chaining
107
  */
108
  public function setWidth($width)
109
  {
@@ -132,7 +141,7 @@ class Image
132
  *
133
  * @param int $height
134
  *
135
- * @return __CLASS__ support chaining
136
  */
137
  public function setHeight($height)
138
  {
@@ -142,6 +151,36 @@ class Image
142
  return $this;
143
  }
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  /**
146
  * Convert to card properties
147
  *
@@ -151,15 +190,20 @@ class Image
151
  */
152
  public function asCardProperties()
153
  {
154
- if (! ( isset( $this->src ) && $this->src )) {
155
  return '';
156
  }
157
- if (isset( $this->width ) && isset( $this->height )) {
158
- return array(
159
- 'src' => $this->src,
160
- 'width' => $this->width,
161
- 'height' => $this->height,
162
- );
 
 
 
 
 
163
  } else {
164
  return $this->src;
165
  }
59
  */
60
  protected $height;
61
 
62
+ /**
63
+ * A text description of the image
64
+ *
65
+ * @since 2.0.0
66
+ *
67
+ * @type string
68
+ */
69
+ protected $alt;
70
+
71
  /**
72
  * @since 1.0.0
73
  *
74
+ * @param string $src image URL
75
  */
76
  public function __construct($src)
77
  {
112
  *
113
  * @param int $width width of the image in whole pixels
114
  *
115
+ * @return self support chaining
116
  */
117
  public function setWidth($width)
118
  {
141
  *
142
  * @param int $height
143
  *
144
+ * @return self support chaining
145
  */
146
  public function setHeight($height)
147
  {
151
  return $this;
152
  }
153
 
154
+ /**
155
+ * Get a text description of the image
156
+ *
157
+ * @since 2.0.0
158
+ *
159
+ * @return string a text description of the image
160
+ */
161
+ public function getAlternativeText()
162
+ {
163
+ return $this->alt ?: '';
164
+ }
165
+
166
+ /**
167
+ * Set a text description of the image
168
+ *
169
+ * @since 2.0.0
170
+ *
171
+ * @return self support chaining
172
+ */
173
+ public function setAlternativeText($alt)
174
+ {
175
+ if (is_string($alt)) {
176
+ $alt = trim($alt);
177
+ if ($alt) {
178
+ $this->alt = $alt;
179
+ }
180
+ }
181
+ return $this;
182
+ }
183
+
184
  /**
185
  * Convert to card properties
186
  *
190
  */
191
  public function asCardProperties()
192
  {
193
+ if (! ( isset($this->src) && $this->src )) {
194
  return '';
195
  }
196
+ $properties = array(
197
+ 'src' => $this->src
198
+ );
199
+ $has_properties = false;
200
+ if (isset($this->alt)) {
201
+ $properties['alt'] = $this->alt;
202
+ $has_properties = true;
203
+ }
204
+
205
+ if ($has_properties) {
206
+ return $properties;
207
  } else {
208
  return $this->src;
209
  }
src/Twitter/Cards/Components/SingleImage.php CHANGED
@@ -51,9 +51,9 @@ trait SingleImage
51
  * @param int $width width of the image in whole pixels
52
  * @param int $height height of the image in whole pixels
53
  *
54
- * @return __CLASS__ support chaining
55
  */
56
- public function setImage($url, $width = 0, $height = 0)
57
  {
58
  if (! $url) {
59
  return $this;
@@ -64,6 +64,9 @@ trait SingleImage
64
  if (! ( is_int($height) && $height >= 0 )) {
65
  $height = 0;
66
  }
 
 
 
67
 
68
  $image = null;
69
  $preset = false;
@@ -72,10 +75,11 @@ trait SingleImage
72
  $image = $url;
73
  $width = $url->getWidth();
74
  $height = $url->getHeight();
 
75
  } elseif (is_string($url)) {
76
  try {
77
  $image = new \Twitter\Cards\Components\Image($url);
78
- } catch (Exception $e) {
79
  return $this;
80
  }
81
  }
@@ -104,6 +108,9 @@ trait SingleImage
104
  }
105
  }
106
  }
 
 
 
107
 
108
  $this->image = $image;
109
 
@@ -127,13 +134,13 @@ trait SingleImage
127
  */
128
  protected function imageCardProperties()
129
  {
130
- if (! isset( $this->image )) {
131
- return '';
132
  }
133
 
134
  $card_properties = $this->image->asCardProperties();
135
- if (empty( $card_properties )) {
136
- return '';
137
  }
138
 
139
  return $card_properties;
51
  * @param int $width width of the image in whole pixels
52
  * @param int $height height of the image in whole pixels
53
  *
54
+ * @return self support chaining
55
  */
56
+ public function setImage($url, $width = 0, $height = 0, $alt = '')
57
  {
58
  if (! $url) {
59
  return $this;
64
  if (! ( is_int($height) && $height >= 0 )) {
65
  $height = 0;
66
  }
67
+ if (! is_string($alt)) {
68
+ $alt = '';
69
+ }
70
 
71
  $image = null;
72
  $preset = false;
75
  $image = $url;
76
  $width = $url->getWidth();
77
  $height = $url->getHeight();
78
+ $alt = $url->getAlternativeText();
79
  } elseif (is_string($url)) {
80
  try {
81
  $image = new \Twitter\Cards\Components\Image($url);
82
+ } catch (\Exception $e) {
83
  return $this;
84
  }
85
  }
108
  }
109
  }
110
  }
111
+ if (is_string($alt) && $alt) {
112
+ $image->setAlternativeText($alt);
113
+ }
114
 
115
  $this->image = $image;
116
 
134
  */
135
  protected function imageCardProperties()
136
  {
137
+ if (! isset($this->image)) {
138
+ return array();
139
  }
140
 
141
  $card_properties = $this->image->asCardProperties();
142
+ if (empty($card_properties)) {
143
+ return array();
144
  }
145
 
146
  return $card_properties;
src/Twitter/Cards/Summary.php CHANGED
@@ -69,8 +69,6 @@ class Summary extends Card
69
  * Set the card type
70
  *
71
  * @since 1.0.0
72
- *
73
- * @return void
74
  */
75
  public function __construct()
76
  {
@@ -91,22 +89,22 @@ class Summary extends Card
91
  {
92
  $card = parent::toArray();
93
 
94
- if (isset( $this->description ) && $this->description) {
95
  $card['description'] = $this->description;
96
  }
97
 
98
  $image_properties = $this->imageCardProperties();
99
- if (! empty( $image_properties )) {
100
  $card['image'] = $image_properties;
101
  }
102
- unset( $image_properties );
103
 
104
- if (isset( $this->creator ) && $this->creator) {
105
  $creator = $this->creator->asCardProperties();
106
  if ($creator) {
107
  $card['creator'] = $creator;
108
  }
109
- unset( $creator );
110
  }
111
 
112
  return $card;
69
  * Set the card type
70
  *
71
  * @since 1.0.0
 
 
72
  */
73
  public function __construct()
74
  {
89
  {
90
  $card = parent::toArray();
91
 
92
+ if (isset($this->description) && $this->description) {
93
  $card['description'] = $this->description;
94
  }
95
 
96
  $image_properties = $this->imageCardProperties();
97
+ if (! empty($image_properties)) {
98
  $card['image'] = $image_properties;
99
  }
100
+ unset($image_properties);
101
 
102
+ if (isset($this->creator) && $this->creator) {
103
  $creator = $this->creator->asCardProperties();
104
  if ($creator) {
105
  $card['creator'] = $creator;
106
  }
107
+ unset($creator);
108
  }
109
 
110
  return $card;
src/Twitter/Helpers/HTMLBuilder.php CHANGED
@@ -40,7 +40,7 @@ class HTMLBuilder
40
  *
41
  * @param string $class possible HTML class
42
  *
43
- * @return class name stripped of invalid values or empty string
44
  */
45
  public static function escapeClassName($class)
46
  {
@@ -109,82 +109,85 @@ class HTMLBuilder
109
 
110
  $clean_attributes = array();
111
 
112
- if (is_array($attributes) && ! empty( $attributes )) {
113
  // string
114
- if (isset( $attributes['id'] )) {
115
  $id = static::escapeAttributeValue(trim($attributes['id']));
116
  if ($id) {
117
  $clean_attributes['id'] = $id;
118
  }
119
- unset( $id );
120
  }
121
 
122
  // accept array of values to be combined
123
  $tokens = array( 'class', 'rel' );
124
  foreach ($tokens as $attribute) {
125
- if (! isset( $attributes[ $attribute ] )) {
126
  continue;
127
  }
128
 
129
  $attribute_tokens = array();
130
  if (is_array($attributes[ $attribute ])) {
131
- if (! empty( $attributes[ $attribute ] )) {
132
  $cleaned_tokens = array_filter(array_map('trim', $attributes[ $attribute ]));
133
- if (! empty( $cleaned_tokens )) {
134
  $attribute_tokens = $cleaned_tokens;
135
  }
136
- unset( $cleaned_tokens );
137
  }
138
  } elseif (is_string($attributes[ $attribute ])) {
139
  $cleaned_token = trim($attributes[ $attribute ]);
140
  if ($cleaned_token) {
141
  $attribute_tokens = explode(' ', $cleaned_token);
142
  }
143
- unset( $cleaned_token );
144
  }
145
 
146
  // filter and store
147
- if (! empty( $attribute_tokens )) {
148
  $attribute_tokens = array_map(
149
- __CLASS__ . '::' . ( $attribute === 'class' ? 'escapeClassName' : 'escapeAttribute' ),
150
  $attribute_tokens
151
  );
152
- if (! empty( $attribute_tokens )) {
153
  $clean_attributes[ $attribute ] = implode(' ', $attribute_tokens);
154
  }
155
  }
156
- unset( $attribute_tokens );
157
  }
158
- unset( $tokens );
159
 
160
  // URL
161
- if (isset( $attributes['ping'] )) {
162
  $ping = static::escapeURL(trim($attributes['ping']));
163
  if ($ping) {
164
  $clean_attributes['ping'] = $ping;
165
  }
166
- unset( $ping );
167
  }
168
 
169
  // enum
170
- if (isset( $attributes['target'] )) {
171
  $target = trim('target');
172
  if ($target) {
173
  $valid_targets = array( '_blank' => true, '_self' => true, '_parent' => true, '_top' => true );
174
- if (isset( $valid_targets[ $target ] )) {
175
  $clean_attributes['target'] = $target;
176
  }
177
- unset( $valid_targets );
178
  }
179
- unset( $target );
180
  }
181
  }
182
 
183
- if (is_array($data_attributes) && ! empty( $data_attributes )) {
184
  foreach ($data_attributes as $attribute => $value) {
185
  if (! $attribute) {
186
  continue;
187
  }
 
 
 
188
 
189
  $clean_attributes[ 'data-' . $attribute ] = static::escapeAttributeValue(trim($value));
190
  }
40
  *
41
  * @param string $class possible HTML class
42
  *
43
+ * @return string class name stripped of invalid values or empty string
44
  */
45
  public static function escapeClassName($class)
46
  {
109
 
110
  $clean_attributes = array();
111
 
112
+ if (is_array($attributes) && ! empty($attributes)) {
113
  // string
114
+ if (isset($attributes['id'])) {
115
  $id = static::escapeAttributeValue(trim($attributes['id']));
116
  if ($id) {
117
  $clean_attributes['id'] = $id;
118
  }
119
+ unset($id);
120
  }
121
 
122
  // accept array of values to be combined
123
  $tokens = array( 'class', 'rel' );
124
  foreach ($tokens as $attribute) {
125
+ if (! isset($attributes[ $attribute ])) {
126
  continue;
127
  }
128
 
129
  $attribute_tokens = array();
130
  if (is_array($attributes[ $attribute ])) {
131
+ if (! empty($attributes[ $attribute ])) {
132
  $cleaned_tokens = array_filter(array_map('trim', $attributes[ $attribute ]));
133
+ if (! empty($cleaned_tokens)) {
134
  $attribute_tokens = $cleaned_tokens;
135
  }
136
+ unset($cleaned_tokens);
137
  }
138
  } elseif (is_string($attributes[ $attribute ])) {
139
  $cleaned_token = trim($attributes[ $attribute ]);
140
  if ($cleaned_token) {
141
  $attribute_tokens = explode(' ', $cleaned_token);
142
  }
143
+ unset($cleaned_token);
144
  }
145
 
146
  // filter and store
147
+ if (! empty($attribute_tokens)) {
148
  $attribute_tokens = array_map(
149
+ get_called_class() . '::' . ( $attribute === 'class' ? 'escapeClassName' : 'escapeAttribute' ),
150
  $attribute_tokens
151
  );
152
+ if (! empty($attribute_tokens)) {
153
  $clean_attributes[ $attribute ] = implode(' ', $attribute_tokens);
154
  }
155
  }
156
+ unset($attribute_tokens);
157
  }
158
+ unset($tokens);
159
 
160
  // URL
161
+ if (isset($attributes['ping'])) {
162
  $ping = static::escapeURL(trim($attributes['ping']));
163
  if ($ping) {
164
  $clean_attributes['ping'] = $ping;
165
  }
166
+ unset($ping);
167
  }
168
 
169
  // enum
170
+ if (isset($attributes['target'])) {
171
  $target = trim('target');
172
  if ($target) {
173
  $valid_targets = array( '_blank' => true, '_self' => true, '_parent' => true, '_top' => true );
174
+ if (isset($valid_targets[ $target ])) {
175
  $clean_attributes['target'] = $target;
176
  }
177
+ unset($valid_targets);
178
  }
179
+ unset($target);
180
  }
181
  }
182
 
183
+ if (is_array($data_attributes) && ! empty($data_attributes)) {
184
  foreach ($data_attributes as $attribute => $value) {
185
  if (! $attribute) {
186
  continue;
187
  }
188
+ if (is_array($value)) {
189
+ $value = implode(' ', $value);
190
+ }
191
 
192
  $clean_attributes[ 'data-' . $attribute ] = static::escapeAttributeValue(trim($value));
193
  }
src/Twitter/Helpers/Validators/ScreenName.php CHANGED
@@ -32,6 +32,67 @@ namespace Twitter\Helpers\Validators;
32
  */
33
  class ScreenName
34
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  /**
36
  * Remove possible '@' from beginning of a Twitter screen_name
37
  *
@@ -43,7 +104,7 @@ class ScreenName
43
  */
44
  public static function trim($screen_name)
45
  {
46
- return ltrim(trim($screen_name), '@@');
47
  }
48
 
49
  /**
@@ -59,7 +120,7 @@ class ScreenName
59
  */
60
  public static function isValid($screen_name)
61
  {
62
- return (bool) preg_match('/^[a-z0-9_]{1,20}$/i', $screen_name);
63
  }
64
 
65
  /**
32
  */
33
  class ScreenName
34
  {
35
+ /**
36
+ * Unicode characters possibly preceding a Twitter username in common citation syntax
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @link https://github.com/twitter/twitter-text/blob/master/java/src/com/twitter/Regex.java Twitter Text AT_SIGNS_CHARS
41
+ *
42
+ * @type string
43
+ */
44
+ const AT_SIGNS = '@@';
45
+
46
+ /**
47
+ * Characters allowed in a Twitter username
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @link https://github.com/twitter/twitter-text/blob/master/java/src/com/twitter/Regex.java Twitter Text VALID_REPLY
52
+ *
53
+ * @type string
54
+ */
55
+ const ALLOWED_CHARACTERS = 'a-zA-Z0-9_';
56
+
57
+ /**
58
+ * Maximum allowed length of a Twitter username
59
+ *
60
+ * @since 2.0.0
61
+ *
62
+ * @link https://github.com/twitter/twitter-text/blob/master/java/src/com/twitter/Regex.java Twitter Text VALID_REPLY
63
+ *
64
+ * @type int
65
+ */
66
+ const MAX_LENGTH = 20;
67
+
68
+ /**
69
+ * Combine allowed characters and max length into a pattern suitable for use in HTML form validation or inside a regex matcher
70
+ *
71
+ * @since 2.0.0
72
+ *
73
+ * @link https://tc39.github.io/ecma262/#prod-Pattern JavaScript pattern production
74
+ *
75
+ * @return string pattern string
76
+ */
77
+ public static function getPattern()
78
+ {
79
+ return '[' . static::ALLOWED_CHARACTERS . ']{1,' . static::MAX_LENGTH . '}';
80
+ }
81
+
82
+ /**
83
+ * Get a PCRE pattern suitable for use in a matcher
84
+ *
85
+ * @since 2.0.0
86
+ *
87
+ * @link http://php.net/manual/en/pcre.pattern.php PHP PCRE pattern
88
+ *
89
+ * @return string PCRE pattern
90
+ */
91
+ public static function getRegexPattern()
92
+ {
93
+ return '/^' . static::getPattern() . '$/';
94
+ }
95
+
96
  /**
97
  * Remove possible '@' from beginning of a Twitter screen_name
98
  *
104
  */
105
  public static function trim($screen_name)
106
  {
107
+ return ltrim(trim($screen_name), static::AT_SIGNS);
108
  }
109
 
110
  /**
120
  */
121
  public static function isValid($screen_name)
122
  {
123
+ return (bool) preg_match(static::getRegexPattern(), $screen_name);
124
  }
125
 
126
  /**
src/Twitter/Helpers/Validators/WebsiteTag.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 Twitter screen_name validity
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class WebsiteTag
34
+ {
35
+ /**
36
+ * Characters allowed in a Twitter website tag
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const ALLOWED_CHARACTERS = 'a-zA-Z0-9';
43
+
44
+ /**
45
+ * Maximum allowed length of a Twitter website tag
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @type int
50
+ */
51
+ const MAX_LENGTH = 5;
52
+
53
+ /**
54
+ * Combine allowed characters and max length into a pattern suitable for use in HTML form validation or inside a regex matcher
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @link https://tc39.github.io/ecma262/#prod-Pattern JavaScript pattern production
59
+ *
60
+ * @return string pattern string
61
+ */
62
+ public static function getPattern()
63
+ {
64
+ return '[' . static::ALLOWED_CHARACTERS . ']{1,' . static::MAX_LENGTH . '}';
65
+ }
66
+
67
+ /**
68
+ * Get a PCRE pattern suitable for use in a matcher
69
+ *
70
+ * @since 2.0.0
71
+ *
72
+ * @link http://php.net/manual/en/pcre.pattern.php PHP PCRE pattern
73
+ *
74
+ * @return string PCRE pattern
75
+ */
76
+ public static function getRegexPattern()
77
+ {
78
+ return '/^' . static::getPattern() . '$/';
79
+ }
80
+
81
+ /**
82
+ * Tests a supplied Twitter website tag for validity
83
+ *
84
+ * @since 2.0.0
85
+ *
86
+ * @param string $website_tag Twitter website tag
87
+ *
88
+ * @return bool true if valid Twitter website tag
89
+ */
90
+ public static function isValid($website_tag)
91
+ {
92
+ return (bool) preg_match(static::getRegexPattern(), $website_tag);
93
+ }
94
+
95
+ /**
96
+ * Sanitize a user-inputted Twitter website tag value
97
+ *
98
+ * @since 2.0.0
99
+ *
100
+ * @param string $website_tag Twitter website tag
101
+ *
102
+ * @return string Twitter website tag or empty string if invalid website provided
103
+ */
104
+ public static function sanitize($website_tag)
105
+ {
106
+ if (! is_string($website_tag)) {
107
+ return '';
108
+ }
109
+
110
+ $website_tag = strtolower(trim($website_tag));
111
+ if ( ! $website_tag ) {
112
+ return '';
113
+ }
114
+
115
+ if (! static::isValid($website_tag)) {
116
+ return '';
117
+ }
118
+
119
+ return $website_tag;
120
+ }
121
+ }
src/Twitter/Intents/Traits/Related.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Intents\Traits;
27
+
28
+ /**
29
+ * Accounts related to web intent
30
+ *
31
+ * May be used to suggest accounts to follow after completing an action
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ trait Related
36
+ {
37
+ /**
38
+ * Related Twitter usernames
39
+ *
40
+ * May be presented as a suggested account to follow after the Tweet is published
41
+ *
42
+ * @since 2.0.0
43
+ *
44
+ * @type array {
45
+ * @type string username in lowercase
46
+ * @type string description of how the username relates to Tweet content
47
+ * }
48
+ */
49
+ protected $related = array();
50
+
51
+ /**
52
+ * Add a related Twitter account
53
+ *
54
+ * @since 2.0.0
55
+ *
56
+ * @param string $username Twitter username
57
+ * @param string $label brief description of how the account relates to the Tweet content
58
+ *
59
+ * @return self support chaining
60
+ */
61
+ public function addRelated($username, $label = '')
62
+ {
63
+ $username = \Twitter\Helpers\Validators\ScreenName::trim($username);
64
+ if ($username) {
65
+ // normalize passed parameter
66
+ $comparison_username = strtolower($username);
67
+ if (! isset($this->related[ $comparison_username ])) {
68
+ if (property_exists(get_called_class(), 'validate_inputs') && $this->validate_inputs) {
69
+ if (\Twitter\Helpers\Validators\ScreenName::isValid($username)) {
70
+ $this->related[ $comparison_username ] = trim($label);
71
+ }
72
+ } else {
73
+ $this->related[ $comparison_username ] = trim($label);
74
+ }
75
+ }
76
+ }
77
+
78
+ return $this;
79
+ }
80
+
81
+ /**
82
+ * Get related Twitter usernames
83
+ *
84
+ * @since 2.0.0
85
+ *
86
+ * @return array {
87
+ * @type string username in lowercase
88
+ * @type string description of how the username relates to Tweet content
89
+ * }
90
+ */
91
+ public function getRelated()
92
+ {
93
+ return $this->related;
94
+ }
95
+ }
src/Twitter/Intents/Tweet.php CHANGED
@@ -34,6 +34,7 @@ namespace Twitter\Intents;
34
  */
35
  class Tweet
36
  {
 
37
 
38
  /**
39
  * Tweet Web Intent URL
@@ -101,20 +102,6 @@ class Tweet
101
  */
102
  protected $via;
103
 
104
- /**
105
- * Related Twitter usernames
106
- *
107
- * May be presented as a suggested account to follow after the Tweet is published
108
- *
109
- * @since 1.0.0
110
- *
111
- * @type array {
112
- * @type string username in lowercase
113
- * @type string description of how the username relates to Tweet content
114
- * }
115
- */
116
- protected $related = array();
117
-
118
  /**
119
  * Do not validate inputs
120
  *
@@ -122,7 +109,7 @@ class Tweet
122
  *
123
  * @since 1.0.0
124
  *
125
- * @return __CLASS__ support chaining
126
  */
127
  public function disableValidation()
128
  {
@@ -135,7 +122,7 @@ class Tweet
135
  *
136
  * @since 1.0.0
137
  *
138
- * @return __CLASS__ support chaining
139
  */
140
  public function enableValidation()
141
  {
@@ -162,7 +149,7 @@ class Tweet
162
  *
163
  * @param string $tweet_id Parent Tweet ID
164
  *
165
- * @return __CLASS__ support chaining
166
  */
167
  public function setInReplyTo($tweet_id)
168
  {
@@ -181,7 +168,7 @@ class Tweet
181
  *
182
  * @param string $text Tweet text
183
  *
184
- * @return __CLASS__ support chaining
185
  */
186
  public function setText($text)
187
  {
@@ -228,7 +215,7 @@ class Tweet
228
  *
229
  * @param string $url absolute URL
230
  *
231
- * @return __CLASS__ support chaining
232
  */
233
  public function setURL($url)
234
  {
@@ -253,14 +240,18 @@ class Tweet
253
  *
254
  * @param string $hashtag hashtag
255
  *
256
- * @return __CLASS__ support chaining
257
  */
258
  public function addHashtag($hashtag)
259
  {
260
  $hashtag = \Twitter\Helpers\Validators\Hashtag::trim($hashtag);
261
  if ($hashtag) {
262
- $comparison_hashtag = mb_strtolower($hashtag);
263
- if (! isset( $this->hashtags[ $comparison_hashtag ] )) {
 
 
 
 
264
  $this->hashtags[ $comparison_hashtag ] = $hashtag;
265
  }
266
  }
@@ -301,7 +292,7 @@ class Tweet
301
  *
302
  * @param string $username Twitter username
303
  *
304
- * @return __CLASS__ support chaining
305
  */
306
  public function setVia($username)
307
  {
@@ -319,51 +310,6 @@ class Tweet
319
  return $this;
320
  }
321
 
322
- /**
323
- * Add a related Twitter account
324
- *
325
- * @since 1.0.0
326
- *
327
- * @param string $username Twitter username
328
- * @param string $label brief description of how the account relates to the Tweet content
329
- *
330
- * @return __CLASS__ support chaining
331
- */
332
- public function addRelated($username, $label = '')
333
- {
334
- $username = \Twitter\Helpers\Validators\ScreenName::trim($username);
335
- if ($username) {
336
- // normalize passed parameter
337
- $comparison_username = strtolower($username);
338
- if (! isset( $this->related[ $comparison_username ] )) {
339
- if ($this->validate_inputs) {
340
- if (\Twitter\Helpers\Validators\ScreenName::isValid($username)) {
341
- $this->related[ $comparison_username ] = trim($label);
342
- }
343
- } else {
344
- $this->related[ $comparison_username ] = trim($label);
345
- }
346
- }
347
- }
348
-
349
- return $this;
350
- }
351
-
352
- /**
353
- * Get related Twitter usernames
354
- *
355
- * @since 1.0.0
356
- *
357
- * @return array {
358
- * @type string username in lowercase
359
- * @type string description of how the username relates to Tweet content
360
- * }
361
- */
362
- public function getRelated()
363
- {
364
- return $this->related;
365
- }
366
-
367
  /**
368
  * Construct a new Tweet intent object from an options array
369
  *
@@ -374,7 +320,7 @@ class Tweet
374
  * @type string|int|bool option value
375
  * }
376
  *
377
- * @return __CLASS__ object initialized based on passed array values
378
  */
379
  public static function fromArray($values)
380
  {
@@ -384,9 +330,9 @@ class Tweet
384
 
385
  $class = get_called_class();
386
  $intent = new $class;
387
- unset( $class );
388
 
389
- if (isset( $values['validate'] )) {
390
  if (false == $values['validate'] || 'false' === $values['validate'] || 0 == $values['validate']) {
391
  $intent->disableValidation();
392
  }
@@ -396,16 +342,16 @@ class Tweet
396
  $values = array_filter($values);
397
 
398
  // intent parameters
399
- if (isset( $values['in_reply_to'] )) {
400
  $intent->setInReplyTo($values['in_reply_to']);
401
  }
402
- if (isset( $values['text'] )) {
403
  $intent->setText($values['text']);
404
  }
405
- if (isset( $values['url'] )) {
406
  $intent->setURL($values['url']);
407
  }
408
- if (isset( $values['hashtags'] )) {
409
  $hashtags = array();
410
 
411
  if (is_array($values['hashtags'])) {
@@ -414,16 +360,16 @@ class Tweet
414
  $hashtags = explode(',', $values['hashtags']);
415
  }
416
 
417
- if (! empty( $hashtags )) {
418
  array_walk($hashtags, array( $intent, 'addHashtag' ));
419
  }
420
 
421
- unset( $hashtags );
422
  }
423
- if (isset( $values['via'] )) {
424
  $intent->setVia($values['via']);
425
  }
426
- if (isset( $values['related'] )) {
427
  $related = array();
428
 
429
  if (is_array($values['related'])) {
@@ -433,12 +379,12 @@ class Tweet
433
  foreach ($related_accounts as $related_account) {
434
  // extract the label
435
  $account_pieces = explode(':', $related_account, 2);
436
- $related[ $account_pieces[0] ] = ( isset( $account_pieces[1] ) ? rawurldecode($account_pieces[1]) : '' );
437
- unset( $account_pieces );
438
  }
439
  }
440
 
441
- if (! empty( $related )) {
442
  foreach ($related as $username => $label) {
443
  if (! ( is_string($username) && $username )) {
444
  continue;
@@ -448,7 +394,7 @@ class Tweet
448
  }
449
  }
450
 
451
- unset( $related );
452
  }
453
 
454
  return $intent;
@@ -479,16 +425,16 @@ class Tweet
479
  }
480
 
481
  $hashtags = $this->getHashtags();
482
- if (! empty( $hashtags )) {
483
  $data['hashtags'] = implode(',', $hashtags);
484
  }
485
- unset( $hashtags );
486
 
487
  if ($this->via) {
488
  $data['via'] = $this->via;
489
  }
490
 
491
- if (! empty( $this->related )) {
492
  $related_value = array();
493
  foreach ($this->related as $username => $label) {
494
  if ($label) {
@@ -498,7 +444,7 @@ class Tweet
498
  }
499
  }
500
  $data['related'] = implode(',', $related_value);
501
- unset( $related_value );
502
  }
503
 
504
  return $data;
@@ -515,7 +461,7 @@ class Tweet
515
  {
516
  $query_parameters = $this->toQueryParameters();
517
 
518
- if (! empty( $query_parameters )) {
519
  return self::INTENT_URL . '?' . http_build_query($query_parameters, '', '&', PHP_QUERY_RFC3986);
520
  }
521
 
34
  */
35
  class Tweet
36
  {
37
+ use \Twitter\Intents\Traits\Related;
38
 
39
  /**
40
  * Tweet Web Intent URL
102
  */
103
  protected $via;
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  /**
106
  * Do not validate inputs
107
  *
109
  *
110
  * @since 1.0.0
111
  *
112
+ * @return self support chaining
113
  */
114
  public function disableValidation()
115
  {
122
  *
123
  * @since 1.0.0
124
  *
125
+ * @return self support chaining
126
  */
127
  public function enableValidation()
128
  {
149
  *
150
  * @param string $tweet_id Parent Tweet ID
151
  *
152
+ * @return self support chaining
153
  */
154
  public function setInReplyTo($tweet_id)
155
  {
168
  *
169
  * @param string $text Tweet text
170
  *
171
+ * @return self support chaining
172
  */
173
  public function setText($text)
174
  {
215
  *
216
  * @param string $url absolute URL
217
  *
218
+ * @return self support chaining
219
  */
220
  public function setURL($url)
221
  {
240
  *
241
  * @param string $hashtag hashtag
242
  *
243
+ * @return self support chaining
244
  */
245
  public function addHashtag($hashtag)
246
  {
247
  $hashtag = \Twitter\Helpers\Validators\Hashtag::trim($hashtag);
248
  if ($hashtag) {
249
+ if (function_exists('mb_strtolower')) {
250
+ $comparison_hashtag = mb_strtolower($hashtag);
251
+ } else {
252
+ $comparison_hashtag = strtolower($hashtag);
253
+ }
254
+ if (! isset($this->hashtags[ $comparison_hashtag ])) {
255
  $this->hashtags[ $comparison_hashtag ] = $hashtag;
256
  }
257
  }
292
  *
293
  * @param string $username Twitter username
294
  *
295
+ * @return self support chaining
296
  */
297
  public function setVia($username)
298
  {
310
  return $this;
311
  }
312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  /**
314
  * Construct a new Tweet intent object from an options array
315
  *
320
  * @type string|int|bool option value
321
  * }
322
  *
323
+ * @return self object initialized based on passed array values
324
  */
325
  public static function fromArray($values)
326
  {
330
 
331
  $class = get_called_class();
332
  $intent = new $class;
333
+ unset($class);
334
 
335
+ if (isset($values['validate'])) {
336
  if (false == $values['validate'] || 'false' === $values['validate'] || 0 == $values['validate']) {
337
  $intent->disableValidation();
338
  }
342
  $values = array_filter($values);
343
 
344
  // intent parameters
345
+ if (isset($values['in_reply_to'])) {
346
  $intent->setInReplyTo($values['in_reply_to']);
347
  }
348
+ if (isset($values['text'])) {
349
  $intent->setText($values['text']);
350
  }
351
+ if (isset($values['url'])) {
352
  $intent->setURL($values['url']);
353
  }
354
+ if (isset($values['hashtags'])) {
355
  $hashtags = array();
356
 
357
  if (is_array($values['hashtags'])) {
360
  $hashtags = explode(',', $values['hashtags']);
361
  }
362
 
363
+ if (! empty($hashtags)) {
364
  array_walk($hashtags, array( $intent, 'addHashtag' ));
365
  }
366
 
367
+ unset($hashtags);
368
  }
369
+ if (isset($values['via'])) {
370
  $intent->setVia($values['via']);
371
  }
372
+ if (isset($values['related'])) {
373
  $related = array();
374
 
375
  if (is_array($values['related'])) {
379
  foreach ($related_accounts as $related_account) {
380
  // extract the label
381
  $account_pieces = explode(':', $related_account, 2);
382
+ $related[ $account_pieces[0] ] = ( isset($account_pieces[1]) ? rawurldecode($account_pieces[1]) : '' );
383
+ unset($account_pieces);
384
  }
385
  }
386
 
387
+ if (! empty($related)) {
388
  foreach ($related as $username => $label) {
389
  if (! ( is_string($username) && $username )) {
390
  continue;
394
  }
395
  }
396
 
397
+ unset($related);
398
  }
399
 
400
  return $intent;
425
  }
426
 
427
  $hashtags = $this->getHashtags();
428
+ if (! empty($hashtags)) {
429
  $data['hashtags'] = implode(',', $hashtags);
430
  }
431
+ unset($hashtags);
432
 
433
  if ($this->via) {
434
  $data['via'] = $this->via;
435
  }
436
 
437
+ if (! empty($this->related)) {
438
  $related_value = array();
439
  foreach ($this->related as $username => $label) {
440
  if ($label) {
444
  }
445
  }
446
  $data['related'] = implode(',', $related_value);
447
+ unset($related_value);
448
  }
449
 
450
  return $data;
461
  {
462
  $query_parameters = $this->toQueryParameters();
463
 
464
+ if (! empty($query_parameters)) {
465
  return self::INTENT_URL . '?' . http_build_query($query_parameters, '', '&', PHP_QUERY_RFC3986);
466
  }
467
 
src/Twitter/Widgets/{BaseWidget.php → Base.php} RENAMED
@@ -30,7 +30,7 @@ namespace Twitter\Widgets;
30
  *
31
  * @since 1.0.0
32
  */
33
- abstract class BaseWidget
34
  {
35
 
36
  /**
@@ -58,7 +58,7 @@ abstract class BaseWidget
58
  *
59
  * @since 1.0.0
60
  *
61
- * @return __CLASS__ support chaining
62
  */
63
  public function doNotTrack()
64
  {
@@ -71,7 +71,7 @@ abstract class BaseWidget
71
  *
72
  * @since 1.0.0
73
  *
74
- * @return __CLASS__ support chaining
75
  */
76
  public function allowTracking()
77
  {
@@ -88,7 +88,7 @@ abstract class BaseWidget
88
  *
89
  * @param string $lang Twitter-supported language code
90
  *
91
- * @return __CLASS__ support chaining
92
  */
93
  public function setLanguage($lang)
94
  {
@@ -100,7 +100,7 @@ abstract class BaseWidget
100
  }
101
 
102
  /**
103
- * Populate BaseWidget options from a passed associative array
104
  *
105
  * @since 1.0.0
106
  *
@@ -109,15 +109,15 @@ abstract class BaseWidget
109
  * @type string|int|bool option value
110
  * }
111
  *
112
- * @return __CLASS__ support chaining
113
  */
114
  public function setBaseOptions($options)
115
  {
116
- if (isset( $options['dnt'] ) && ( true === $options['dnt'] || 'true' === $options['dnt'] || 'on' === $options['dnt'] || 1 == $options['dnt'] )) {
117
  $this->doNotTrack();
118
  }
119
 
120
- if (isset( $options['lang'] ) && $options['lang']) {
121
  $this->setLanguage($options['lang']);
122
  }
123
 
30
  *
31
  * @since 1.0.0
32
  */
33
+ abstract class Base
34
  {
35
 
36
  /**
58
  *
59
  * @since 1.0.0
60
  *
61
+ * @return self support chaining
62
  */
63
  public function doNotTrack()
64
  {
71
  *
72
  * @since 1.0.0
73
  *
74
+ * @return self support chaining
75
  */
76
  public function allowTracking()
77
  {
88
  *
89
  * @param string $lang Twitter-supported language code
90
  *
91
+ * @return self support chaining
92
  */
93
  public function setLanguage($lang)
94
  {
100
  }
101
 
102
  /**
103
+ * Populate Base options from a passed associative array
104
  *
105
  * @since 1.0.0
106
  *
109
  * @type string|int|bool option value
110
  * }
111
  *
112
+ * @return self support chaining
113
  */
114
  public function setBaseOptions($options)
115
  {
116
+ if (isset($options['dnt']) && ( true === $options['dnt'] || 'true' === $options['dnt'] || 'on' === $options['dnt'] || 1 == $options['dnt'] )) {
117
  $this->doNotTrack();
118
  }
119
 
120
+ if (isset($options['lang']) && $options['lang']) {
121
  $this->setLanguage($options['lang']);
122
  }
123
 
src/Twitter/Widgets/{FollowButton.php → Buttons/Follow.php} RENAMED
@@ -23,7 +23,7 @@ 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
  * Follow button markup to be interpreted by Twitter's widget JavaScript
@@ -32,7 +32,7 @@ namespace Twitter\Widgets;
32
  *
33
  * @link https://dev.twitter.com/web/follow-button Follow button documentation
34
  */
35
- class FollowButton extends BaseWidget
36
  {
37
 
38
  /**
@@ -110,7 +110,7 @@ class FollowButton extends BaseWidget
110
  *
111
  * @since 1.0.0
112
  *
113
- * @return __CLASS__ support chaining
114
  */
115
  public function showCount()
116
  {
@@ -123,7 +123,7 @@ class FollowButton extends BaseWidget
123
  *
124
  * @since 1.0.0
125
  *
126
- * @return __CLASS__ support chaining
127
  */
128
  public function hideCount()
129
  {
@@ -148,7 +148,7 @@ class FollowButton extends BaseWidget
148
  *
149
  * @since 1.0.0
150
  *
151
- * @return __CLASS__ support chaining
152
  */
153
  public function showScreenName()
154
  {
@@ -161,7 +161,7 @@ class FollowButton extends BaseWidget
161
  *
162
  * @since 1.0.0
163
  *
164
- * @return __CLASS__ support chaining
165
  */
166
  public function hideScreenName()
167
  {
@@ -176,7 +176,7 @@ class FollowButton extends BaseWidget
176
  *
177
  * @param string $size button size
178
  *
179
- * @return __CLASS__ support chaining
180
  */
181
  public function setSize($size)
182
  {
@@ -197,28 +197,28 @@ class FollowButton extends BaseWidget
197
  * @type string|int|bool option value
198
  * }
199
  *
200
- * @return __CLASS__ support chaining
201
  */
202
  public static function fromArray($options)
203
  {
204
- if (! isset( $options['screen_name'] ) && $options['screen_name']) {
205
- return;
206
  }
207
 
208
- $class = __CLASS__;
209
  $follow = new $class( $options['screen_name'] );
210
- unset( $class );
211
 
212
  $follow->setBaseOptions($options);
213
 
214
- if (isset( $options['show_count'] ) && ( false === $options['show_count'] || 'false' === $options['show_count'] || 0 == $options['show_count'] )) {
215
  $follow->hideCount();
216
  }
217
- if (isset( $options['show_screen_name'] ) && ( false === $options['show_screen_name'] || 'false' === $options['show_screen_name'] || 0 == $options['show_screen_name'] )) {
218
  $follow->hideScreenName();
219
  }
220
 
221
- if (isset( $options['size'] ) && 'medium' !== $options['size']) {
222
  $follow->setSize($options['size']);
223
  }
224
 
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\Widgets\Buttons;
27
 
28
  /**
29
  * Follow button markup to be interpreted by Twitter's widget JavaScript
32
  *
33
  * @link https://dev.twitter.com/web/follow-button Follow button documentation
34
  */
35
+ class Follow extends \Twitter\Widgets\Base
36
  {
37
 
38
  /**
110
  *
111
  * @since 1.0.0
112
  *
113
+ * @return self support chaining
114
  */
115
  public function showCount()
116
  {
123
  *
124
  * @since 1.0.0
125
  *
126
+ * @return self support chaining
127
  */
128
  public function hideCount()
129
  {
148
  *
149
  * @since 1.0.0
150
  *
151
+ * @return self support chaining
152
  */
153
  public function showScreenName()
154
  {
161
  *
162
  * @since 1.0.0
163
  *
164
+ * @return self support chaining
165
  */
166
  public function hideScreenName()
167
  {
176
  *
177
  * @param string $size button size
178
  *
179
+ * @return self support chaining
180
  */
181
  public function setSize($size)
182
  {
197
  * @type string|int|bool option value
198
  * }
199
  *
200
+ * @return static|null Follow button object or null if minimum requirements not met
201
  */
202
  public static function fromArray($options)
203
  {
204
+ if (! isset($options['screen_name']) && $options['screen_name']) {
205
+ return null;
206
  }
207
 
208
+ $class = get_called_class();
209
  $follow = new $class( $options['screen_name'] );
210
+ unset($class);
211
 
212
  $follow->setBaseOptions($options);
213
 
214
+ if (isset($options['show_count']) && ( false === $options['show_count'] || 'false' === $options['show_count'] || 0 == $options['show_count'] )) {
215
  $follow->hideCount();
216
  }
217
+ if (isset($options['show_screen_name']) && ( false === $options['show_screen_name'] || 'false' === $options['show_screen_name'] || 0 == $options['show_screen_name'] )) {
218
  $follow->hideScreenName();
219
  }
220
 
221
+ if (isset($options['size']) && 'medium' !== $options['size']) {
222
  $follow->setSize($options['size']);
223
  }
224
 
src/Twitter/Widgets/{PeriscopeOnAir.php → Buttons/Periscope/OnAir.php} RENAMED
@@ -23,19 +23,19 @@ 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
  */
@@ -45,7 +45,7 @@ class PeriscopeOnAir extends BaseWidget
45
  /**
46
  * Default button size
47
  *
48
- * @since 1.3.0
49
  *
50
  * @type string
51
  */
@@ -54,7 +54,7 @@ class PeriscopeOnAir extends BaseWidget
54
  /**
55
  * Periscope web profile base URL
56
  *
57
- * @since 1.3.0
58
  *
59
  * @type string
60
  */
@@ -63,7 +63,7 @@ class PeriscopeOnAir extends BaseWidget
63
  /**
64
  * Periscope username
65
  *
66
- * @since 1.3.0
67
  *
68
  * @type string
69
  */
@@ -72,7 +72,7 @@ class PeriscopeOnAir extends BaseWidget
72
  /**
73
  * Allowed values for the size property
74
  *
75
- * @since 1.3.0
76
  *
77
  * @type array allowed sizes {
78
  * @type string size
@@ -84,7 +84,7 @@ class PeriscopeOnAir extends BaseWidget
84
  /**
85
  * Size of the button
86
  *
87
- * @since 1.3.0
88
  *
89
  * @type string
90
  */
@@ -93,7 +93,10 @@ class PeriscopeOnAir extends BaseWidget
93
  /**
94
  * Require username
95
  *
96
- * @since 1.3.0
 
 
 
97
  */
98
  public function __construct($username, $validate = true)
99
  {
@@ -106,7 +109,7 @@ class PeriscopeOnAir extends BaseWidget
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
  */
@@ -118,9 +121,9 @@ class PeriscopeOnAir extends BaseWidget
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
  {
@@ -130,11 +133,11 @@ class PeriscopeOnAir extends BaseWidget
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
  {
@@ -147,28 +150,28 @@ class PeriscopeOnAir extends BaseWidget
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
 
@@ -178,7 +181,7 @@ class PeriscopeOnAir extends BaseWidget
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
  */
@@ -196,7 +199,7 @@ class PeriscopeOnAir extends BaseWidget
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
  *
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\Widgets\Buttons\Periscope;
27
 
28
  /**
29
  * Periscope profile button with on-air status display
30
  *
31
+ * @since 2.0.0
32
  */
33
+ class OnAir extends \Twitter\Widgets\Base
34
  {
35
  /**
36
  * HTML class expected by the Periscope widget JS
37
  *
38
+ * @since 2.0.0
39
  *
40
  * @type string
41
  */
45
  /**
46
  * Default button size
47
  *
48
+ * @since 2.0.0
49
  *
50
  * @type string
51
  */
54
  /**
55
  * Periscope web profile base URL
56
  *
57
+ * @since 2.0.0
58
  *
59
  * @type string
60
  */
63
  /**
64
  * Periscope username
65
  *
66
+ * @since 2.0.0
67
  *
68
  * @type string
69
  */
72
  /**
73
  * Allowed values for the size property
74
  *
75
+ * @since 2.0.0
76
  *
77
  * @type array allowed sizes {
78
  * @type string size
84
  /**
85
  * Size of the button
86
  *
87
+ * @since 2.0.0
88
  *
89
  * @type string
90
  */
93
  /**
94
  * Require username
95
  *
96
+ * @param string $username target username
97
+ * @param bool $validate validate inputs such as username before storing
98
+ *
99
+ * @since 2.0.0
100
  */
101
  public function __construct($username, $validate = true)
102
  {
109
  /**
110
  * Retrieve the stored Periscope username
111
  *
112
+ * @since 2.0.0
113
  *
114
  * @return string Periscope username or empty string if username not set
115
  */
121
  /**
122
  * Build a Periscope web profile URL
123
  *
124
+ * @since 2.0.0
125
  *
126
+ * @return string Periscope web profile URL or empty string if username not set
127
  */
128
  public function getWebProfileURL()
129
  {
133
  /**
134
  * Set the desired size of the On Air button
135
  *
136
+ * @since 2.0.0
137
  *
138
  * @param string $size button size
139
  *
140
+ * @return self support chaining
141
  */
142
  public function setSize($size)
143
  {
150
  /**
151
  * Build a Periscope On Air object from an associative array
152
  *
153
+ * @since 2.0.0
154
  *
155
  * @param array $options associative array of options {
156
  * @type string option name
157
  * @type string option value
158
  * }
159
  *
160
+ * @return self|null new OnAir object of null if minimum requirements not met
161
  */
162
  public static function fromArray($options)
163
  {
164
+ if (! isset($options['username']) && $options['username']) {
165
+ return null;
166
  }
167
 
168
+ $class = get_called_class();
169
  $on_air = new $class( $options['username'] );
170
+ unset($class);
171
 
172
  $on_air->setBaseOptions($options);
173
 
174
+ if (isset($options['size']) && static::DEFAULT_SIZE !== $options['size']) {
175
  $on_air->setSize($options['size']);
176
  }
177
 
181
  /**
182
  * Convert the class object into an array, removing default field values
183
  *
184
+ * @since 2.0.0
185
  *
186
  * @return array properties as associative array
187
  */
199
  /**
200
  * Generate a link to a Periscope web profile configured for enhancement by the Twitter for Websites JavaScript
201
  *
202
+ * @since 2.0.0
203
  *
204
  * @param string $html_builder_class callable HTML builder with a static anchorElement class
205
  *
src/Twitter/Widgets/{TweetButton.php → Buttons/Tweet.php} RENAMED
@@ -23,7 +23,7 @@ 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
  * Tweet button markup to be interpreted by Twitter's widget JavaScript
@@ -32,7 +32,7 @@ namespace Twitter\Widgets;
32
  *
33
  * @link https://dev.twitter.com/web/tweet-button Tweet button documentation
34
  */
35
- class TweetButton extends BaseWidget
36
  {
37
 
38
  /**
@@ -96,7 +96,7 @@ class TweetButton extends BaseWidget
96
  *
97
  * @param \Twitter\Intents\Tweet $intent Tweet Intent
98
  *
99
- * @return __CLASS__ support chaining
100
  */
101
  public function setIntent($intent)
102
  {
@@ -114,7 +114,7 @@ class TweetButton extends BaseWidget
114
  *
115
  * @param string $size button size
116
  *
117
- * @return __CLASS__ support chaining
118
  */
119
  public function setSize($size)
120
  {
@@ -132,7 +132,7 @@ class TweetButton extends BaseWidget
132
  *
133
  * @param string $tweet_id Parent Tweet ID
134
  *
135
- * @return __CLASS__ support chaining
136
  */
137
  public function setInReplyTo($tweet_id)
138
  {
@@ -148,7 +148,7 @@ class TweetButton extends BaseWidget
148
  *
149
  * @param string $text Tweet text
150
  *
151
- * @return __CLASS__ support chaining
152
  */
153
  public function setText($text)
154
  {
@@ -164,7 +164,7 @@ class TweetButton extends BaseWidget
164
  *
165
  * @param string $url absolute URL
166
  *
167
- * @return __CLASS__ support chaining
168
  */
169
  public function setURL($url)
170
  {
@@ -180,7 +180,7 @@ class TweetButton extends BaseWidget
180
  *
181
  * @param string $hashtag hashtag
182
  *
183
- * @return __CLASS__ support chaining
184
  */
185
  public function addHashtag($hashtag)
186
  {
@@ -196,11 +196,13 @@ class TweetButton extends BaseWidget
196
  *
197
  * @param string $username Twitter username
198
  *
199
- * @return __CLASS__ support chaining
200
  */
201
  public function setVia($username)
202
  {
203
  $this->intent->setVia($username);
 
 
204
  }
205
 
206
  /**
@@ -211,7 +213,7 @@ class TweetButton extends BaseWidget
211
  * @param string $username Twitter username
212
  * @param string $label brief description of how the account relates to the Tweet content
213
  *
214
- * @return __CLASS__ support chaining
215
  */
216
  public function addRelated($username, $label = '')
217
  {
@@ -228,7 +230,7 @@ class TweetButton extends BaseWidget
228
  * @type string|int|bool parameter value
229
  * }
230
  *
231
- * @return __CLASS__ new button with configured parameters
232
  */
233
  public static function fromArray($options)
234
  {
@@ -239,7 +241,7 @@ class TweetButton extends BaseWidget
239
 
240
  $class = get_called_class();
241
  $button = new $class();
242
- unset( $class );
243
 
244
  // remove values which evaluate to false
245
  $options = array_filter($options);
@@ -248,10 +250,10 @@ class TweetButton extends BaseWidget
248
  // intent parameters
249
  $intent_class = static::INTENT_CLASS;
250
  $button->setIntent($intent_class::fromArray($options));
251
- unset( $intent_class );
252
 
253
  // button parameters
254
- if (isset( $options['size'] )) {
255
  $button->setSize($options['size']);
256
  }
257
 
@@ -259,7 +261,7 @@ class TweetButton extends BaseWidget
259
  }
260
 
261
  /**
262
- * Return Tweet button parameters suitable
263
  *
264
  * @since 1.0.0
265
  *
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\Widgets\Buttons;
27
 
28
  /**
29
  * Tweet button markup to be interpreted by Twitter's widget JavaScript
32
  *
33
  * @link https://dev.twitter.com/web/tweet-button Tweet button documentation
34
  */
35
+ class Tweet extends \Twitter\Widgets\Base
36
  {
37
 
38
  /**
96
  *
97
  * @param \Twitter\Intents\Tweet $intent Tweet Intent
98
  *
99
+ * @return self support chaining
100
  */
101
  public function setIntent($intent)
102
  {
114
  *
115
  * @param string $size button size
116
  *
117
+ * @return self support chaining
118
  */
119
  public function setSize($size)
120
  {
132
  *
133
  * @param string $tweet_id Parent Tweet ID
134
  *
135
+ * @return self support chaining
136
  */
137
  public function setInReplyTo($tweet_id)
138
  {
148
  *
149
  * @param string $text Tweet text
150
  *
151
+ * @return self support chaining
152
  */
153
  public function setText($text)
154
  {
164
  *
165
  * @param string $url absolute URL
166
  *
167
+ * @return self support chaining
168
  */
169
  public function setURL($url)
170
  {
180
  *
181
  * @param string $hashtag hashtag
182
  *
183
+ * @return self support chaining
184
  */
185
  public function addHashtag($hashtag)
186
  {
196
  *
197
  * @param string $username Twitter username
198
  *
199
+ * @return self support chaining
200
  */
201
  public function setVia($username)
202
  {
203
  $this->intent->setVia($username);
204
+
205
+ return $this;
206
  }
207
 
208
  /**
213
  * @param string $username Twitter username
214
  * @param string $label brief description of how the account relates to the Tweet content
215
  *
216
+ * @return self support chaining
217
  */
218
  public function addRelated($username, $label = '')
219
  {
230
  * @type string|int|bool parameter value
231
  * }
232
  *
233
+ * @return self new button with configured parameters
234
  */
235
  public static function fromArray($options)
236
  {
241
 
242
  $class = get_called_class();
243
  $button = new $class();
244
+ unset($class);
245
 
246
  // remove values which evaluate to false
247
  $options = array_filter($options);
250
  // intent parameters
251
  $intent_class = static::INTENT_CLASS;
252
  $button->setIntent($intent_class::fromArray($options));
253
+ unset($intent_class);
254
 
255
  // button parameters
256
+ if (isset($options['size'])) {
257
  $button->setSize($options['size']);
258
  }
259
 
261
  }
262
 
263
  /**
264
+ * Return Tweet button parameters suitable for conversion to data-*
265
  *
266
  * @since 1.0.0
267
  *
src/Twitter/Widgets/Embeds/Moment.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds;
27
+
28
+ /**
29
+ * Display Tweets included in a Twitter Moment
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Moment extends \Twitter\Widgets\Embeds\Timeline\Collection
34
+ {
35
+ /**
36
+ * Construct a full Twitter URI by appending to base string
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const BASE_URL = 'https://twitter.com/i/moments/';
43
+
44
+ /**
45
+ * Create a new Moment object
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @param string $id unique identifier of the Moment
50
+ */
51
+ public function __construct($id)
52
+ {
53
+ parent::__construct($id);
54
+
55
+ // all moments are grids
56
+ $this->setGridTemplate();
57
+ }
58
+
59
+ /**
60
+ * Return Moment parameters suitable for conversion to data-*
61
+ *
62
+ * @since 2.0.0
63
+ *
64
+ * @return array Moment timeline parameter array {
65
+ * @type string dashed parameter name
66
+ * @type string parameter value
67
+ * }
68
+ */
69
+ public function toArray()
70
+ {
71
+ $data = parent::toArray();
72
+
73
+ unset($data['widget-type']);
74
+
75
+ return $data;
76
+ }
77
+
78
+ /**
79
+ * Output Moment as an array suitable for use as oEmbed query parameters
80
+ *
81
+ * @since 2.0.0
82
+ *
83
+ * @return array Moment parameter array {
84
+ * @type string query parameter name
85
+ * @type string query parameter value
86
+ * }
87
+ */
88
+ public function toOEmbedParameterArray()
89
+ {
90
+ $query_parameters = parent::toOEmbedParameterArray();
91
+
92
+ unset($query_parameters['widget_type']);
93
+
94
+ return $query_parameters;
95
+ }
96
+ }
src/Twitter/Widgets/Embeds/Theme.php ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Embeds;
27
+
28
+ /**
29
+ * An embed capable of theming
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ trait Theme
34
+ {
35
+ /**
36
+ * Light background, dark text
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ public static $THEME_LIGHT = 'light';
43
+
44
+ /**
45
+ * Dark background, light text
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ public static $THEME_DARK = 'dark';
52
+
53
+ /**
54
+ * Embed display theme
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @type string
59
+ */
60
+ protected $theme = 'light';
61
+
62
+ /**
63
+ * Hexadecimal color of borders separating Tweets or Tweet elements
64
+ *
65
+ * @since 2.0.0
66
+ *
67
+ * @type string
68
+ */
69
+ protected $border_color;
70
+
71
+ /**
72
+ * Hexadecimal color of links appearing in Tweet text
73
+ *
74
+ * @since 2.0.0
75
+ *
76
+ * @type string
77
+ */
78
+ protected $link_color;
79
+
80
+ /**
81
+ * Test if the provided theme value is accepted by Twitter for Websites embeds
82
+ *
83
+ * @since 2.0.0
84
+ *
85
+ * @param string $theme dark or light theme
86
+ *
87
+ * @return bool true if valid theme value, else false
88
+ */
89
+ public static function isValidTheme($theme)
90
+ {
91
+ return ( $theme === static::$THEME_LIGHT || $theme === static::$THEME_DARK );
92
+ }
93
+
94
+ /**
95
+ * Test if a provided string is a valid hexadecimal color
96
+ *
97
+ * @param string $color hexadecimal color
98
+ *
99
+ * @return bool true if valid hexadecimal color else false
100
+ */
101
+ public static function isValidHexadecimalColor($color)
102
+ {
103
+ if ($color && is_string($color) && 6 === strlen($color)) {
104
+ if (function_exists('ctype_xdigit')) {
105
+ if (ctype_xdigit($color)) {
106
+ return true;
107
+ }
108
+ } elseif (preg_match('/^[a-f0-9]{6}$/i', $color)) {
109
+ return true;
110
+ }
111
+ }
112
+
113
+ return false;
114
+ }
115
+
116
+ /**
117
+ * Set a widget embed theme
118
+ *
119
+ * @since 2.0.0
120
+ *
121
+ * @param string $theme light or dark
122
+ *
123
+ * @return self support chaining
124
+ */
125
+ public function setTheme($theme)
126
+ {
127
+ if ($theme && is_string($theme)) {
128
+ $theme = trim(strtolower($theme));
129
+ if ($theme && static::isValidTheme($theme)) {
130
+ $this->theme = $theme;
131
+ }
132
+ }
133
+
134
+ return $this;
135
+ }
136
+
137
+ /**
138
+ * Set a light theme
139
+ *
140
+ * @since 2.0.0
141
+ *
142
+ * @return self support chaining
143
+ */
144
+ public function setThemeLight()
145
+ {
146
+ if ($this->theme !== static::$THEME_LIGHT) {
147
+ $this->theme = static::$THEME_LIGHT;
148
+ }
149
+
150
+ return $this;
151
+ }
152
+
153
+ /**
154
+ * Set a light theme
155
+ *
156
+ * @since 2.0.0
157
+ *
158
+ * @return self support chaining
159
+ */
160
+ public function setThemeDark()
161
+ {
162
+ if ($this->theme !== static::$THEME_DARK) {
163
+ $this->theme = static::$THEME_DARK;
164
+ }
165
+
166
+ return $this;
167
+ }
168
+
169
+ /**
170
+ * Clean and normalize supplied hexadecimal color
171
+ *
172
+ * @since 2.0.0
173
+ *
174
+ * @param string $color hexadecimal color
175
+ *
176
+ * @return string cleaned and normalized hexadecimal color
177
+ */
178
+ protected static function cleanHexadecimalColor($color)
179
+ {
180
+ if ($color && is_string($color)) {
181
+ // remove any leading hash
182
+ $color = ltrim(trim($color), '#');
183
+
184
+ if ($color) {
185
+ // convert abbrevated colors
186
+ if (3 === strlen($color)) {
187
+ $color .= $color;
188
+ }
189
+
190
+ $color = strtoupper($color);
191
+ if (static::isValidHexadecimalColor($color)) {
192
+ return $color;
193
+ }
194
+ }
195
+ }
196
+
197
+ return '';
198
+ }
199
+
200
+ /**
201
+ * Set the color of links in Tweet text
202
+ *
203
+ * @since 2.0.0
204
+ *
205
+ * @param string $color hexadecimal color
206
+ *
207
+ * @return self support chaining
208
+ */
209
+ public function setLinkColor($color)
210
+ {
211
+ $color = static::cleanHexadecimalColor($color);
212
+ if ($color) {
213
+ $this->link_color = $color;
214
+ }
215
+
216
+ return $this;
217
+ }
218
+
219
+ /**
220
+ * Set the color of borders separating Tweets or Tweet components
221
+ *
222
+ * @since 2.0.0
223
+ *
224
+ * @param string $color hexadecimal color
225
+ *
226
+ * @return self support chaining
227
+ */
228
+ public function setBorderColor($color)
229
+ {
230
+ $color = static::cleanHexadecimalColor($color);
231
+ if ($color) {
232
+ $this->border_color = $color;
233
+ }
234
+
235
+ return $this;
236
+ }
237
+
238
+ /**
239
+ * Set theme-related options from an associative array
240
+ *
241
+ * @since 2.0.0
242
+ *
243
+ * @param array $options associative array of options {
244
+ * @type string option name
245
+ * @type string|int|bool option value
246
+ * }
247
+ *
248
+ * @return self support chaining
249
+ */
250
+ public function setThemeOptions($options)
251
+ {
252
+ if (isset($options['theme']) && is_string($options['theme']) && static::$THEME_DARK === strtolower($options['theme'])) {
253
+ $this->setThemeDark();
254
+ }
255
+
256
+ if (isset($options['link-color']) && $options['link-color'] && is_string($options['link-color'])) {
257
+ $this->setLinkColor($options['link-color']);
258
+ }
259
+
260
+ if (isset($options['border-color']) && $options['border-color'] && is_string($options['border-color'])) {
261
+ $this->setBorderColor($options['border-color']);
262
+ }
263
+
264
+ return $this;
265
+ }
266
+
267
+ /**
268
+ * Convert trait properties into an associative array
269
+ *
270
+ * @since 2.0.0
271
+ *
272
+ * @return array properties as associative array
273
+ */
274
+ public function themeToArray()
275
+ {
276
+ $data = array();
277
+
278
+ if (isset($this->theme) && static::$THEME_LIGHT !== $this->theme) {
279
+ $data['theme'] = $this->theme;
280
+ }
281
+
282
+ if (isset($this->link_color) && is_string($this->link_color) && 6 === strlen($this->link_color)) {
283
+ $data['link-color'] = '#' . $this->link_color;
284
+ }
285
+
286
+ if (isset($this->border_color) && is_string($this->border_color) && 6 === strlen($this->border_color)) {
287
+ $data['border-color'] = '#' . $this->border_color;
288
+ }
289
+
290
+ return $data;
291
+ }
292
+
293
+ /**
294
+ * Format theme parameters as oEmbed-ready query parameters
295
+ *
296
+ * @since 2.0.0
297
+ *
298
+ * @return array parameter array {
299
+ * @type string query parameter name
300
+ * @type string query parameter value
301
+ * }
302
+ */
303
+ public function themeToOEmbedParameterArray()
304
+ {
305
+ $oembed = $this->themeToArray();
306
+
307
+ $data_to_oembed = array(
308
+ 'link-color' => 'link_color',
309
+ 'border-color' => 'border_color',
310
+ );
311
+
312
+ foreach ($data_to_oembed as $data_attribute => $oembed_parameter) {
313
+ if (isset($oembed[$data_attribute])) {
314
+ $oembed[$oembed_parameter] = $oembed[$data_attribute];
315
+ unset($oembed[$data_attribute]);
316
+ }
317
+ }
318
+
319
+ return $oembed;
320
+ }
321
+ }
src/Twitter/Widgets/Embeds/Timeline.php ADDED
@@ -0,0 +1,678 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds;
27
+
28
+ /**
29
+ * Base properties used across all embedded timelines
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ abstract class Timeline extends \Twitter\Widgets\Base
34
+ {
35
+ use \Twitter\Widgets\Embeds\Theme;
36
+
37
+ /**
38
+ * The minimum supported display height of an embedded timeline
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type int
43
+ */
44
+ const MIN_HEIGHT = 200;
45
+
46
+ /**
47
+ * The minimum supported display height of an embedded timeline
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type int
52
+ */
53
+ const MIN_WIDTH = 180;
54
+
55
+ /**
56
+ * The maximum supported display width of an embedded timeline
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type int
61
+ */
62
+ const MAX_WIDTH = 1200;
63
+
64
+ /**
65
+ * The minimum number of Tweets supported without pagination
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @type int
70
+ */
71
+ const MIN_LIMIT = 1;
72
+
73
+ /**
74
+ * The maximum number of Tweets supported without pagination
75
+ *
76
+ * @since 2.0.0
77
+ *
78
+ * @type int
79
+ */
80
+ const MAX_LIMIT = 20;
81
+
82
+ /**
83
+ * Chrome option token for no header display
84
+ *
85
+ * @since 2.0.0
86
+ *
87
+ * @type string
88
+ */
89
+ const CHROME_NOHEADER = 'noheader';
90
+
91
+ /**
92
+ * Chrome option token for no header display
93
+ *
94
+ * @since 2.0.0
95
+ *
96
+ * @type string
97
+ */
98
+ const CHROME_NOFOOTER = 'nofooter';
99
+
100
+ /**
101
+ * Chrome option token for no borders display
102
+ *
103
+ * @since 2.0.0
104
+ *
105
+ * @type string
106
+ */
107
+ const CHROME_NOBORDERS = 'noborders';
108
+
109
+ /**
110
+ * Chrome option token for no embedded timeline scrollbar
111
+ *
112
+ * @since 2.0.0
113
+ *
114
+ * @type string
115
+ */
116
+ const CHROME_NOSCROLLBAR = 'noscrollbar';
117
+
118
+ /**
119
+ * Chrome option token for transparent background
120
+ *
121
+ * @since 2.0.0
122
+ *
123
+ * @type string
124
+ */
125
+ const CHROME_TRANSPARENT = 'transparent';
126
+
127
+ /**
128
+ * Tweets added to an embedded timeline should only be announced if the user is not currently doing anything
129
+ *
130
+ * @since 2.0.0
131
+ *
132
+ * @type string
133
+ */
134
+ const ARIA_POLITE_POLITE = 'polite';
135
+
136
+ /**
137
+ * Tweets added to an embedded timeline are important enough to be announced to the user as soon as possible, but it is not necessary to immediately interrupt the user
138
+ *
139
+ * @since 2.0.0
140
+ *
141
+ * @type string
142
+ */
143
+ const ARIA_POLITE_ASSERTIVE = 'assertive';
144
+
145
+ /**
146
+ * Available values for customizing embedded timeline chrome
147
+ *
148
+ * @since 2.0.0
149
+ *
150
+ * @type array {
151
+ * @type string valid value
152
+ * @type bool unused
153
+ * }
154
+ */
155
+ public static $CHROME_OPTIONS = array(
156
+ self::CHROME_NOHEADER => true,
157
+ self::CHROME_NOFOOTER => true,
158
+ self::CHROME_NOBORDERS => true,
159
+ self::CHROME_NOSCROLLBAR => true,
160
+ self::CHROME_TRANSPARENT => true
161
+ );
162
+
163
+ /**
164
+ * Maximum display width of the widget
165
+ *
166
+ * @since 2.0.0
167
+ *
168
+ * @type int
169
+ */
170
+ protected $width;
171
+
172
+ /**
173
+ * Maximum display height of the widget
174
+ *
175
+ * @since 2.0.0
176
+ *
177
+ * @type int
178
+ */
179
+ protected $height;
180
+
181
+ /**
182
+ * Display a specific number of Tweets, disabling auto-pagination of possible results
183
+ *
184
+ * @since 2.0.0
185
+ *
186
+ * @type int
187
+ */
188
+ protected $limit;
189
+
190
+ /**
191
+ * Display customizations for the widget container
192
+ *
193
+ * @since 2.0.0
194
+ *
195
+ * @type array {
196
+ * @type string chrome customization
197
+ * @type bool unused
198
+ * }
199
+ */
200
+ protected $chrome = array();
201
+
202
+ /**
203
+ * The politeness / priority of the embedded timeline ARIA live region
204
+ *
205
+ * @since 2.0.0
206
+ *
207
+ * @link https://www.w3.org/TR/wai-aria/states_and_properties#aria-live
208
+ *
209
+ * @type string
210
+ */
211
+ protected $aria_politeness = self::ARIA_POLITE_POLITE;
212
+
213
+ /**
214
+ * Tests a supplied width for validity
215
+ *
216
+ * @since 2.0.0
217
+ *
218
+ * @param int $width the maximum display width of an embedded timeline in whole pixels
219
+ *
220
+ * @return bool true if supplied width is an integer in the range of MIN_WIDTH and MAX_WIDTH inclusive
221
+ */
222
+ public static function isValidWidth($width)
223
+ {
224
+ return (is_int($width) && $width>=static::MIN_WIDTH && $width<=static::MAX_WIDTH);
225
+ }
226
+
227
+ /**
228
+ * Set the maximum display width of an embedded timeline in whole pixels
229
+ *
230
+ * Must be an integer in the allowed range: 180-1200 inclusive
231
+ *
232
+ * @since 2.0.0
233
+ *
234
+ * @param int $width the maximum display width of an embedded timeline in whole pixels
235
+ *
236
+ * @return self allow chaining
237
+ */
238
+ public function setWidth($width)
239
+ {
240
+ if (static::isValidWidth($width)) {
241
+ $this->width = $width;
242
+ }
243
+
244
+ return $this;
245
+ }
246
+
247
+ /**
248
+ * Tests a supplied height for validity
249
+ *
250
+ * @since 2.0.0
251
+ *
252
+ * @param int $height height of the embedded timeline in whole pixels
253
+ *
254
+ * @return bool true if height is an integer greater than or equal to the minimum allowed height
255
+ */
256
+ public static function isValidHeight($height)
257
+ {
258
+ return (is_int($height) && $height>=static::MIN_HEIGHT);
259
+ }
260
+
261
+ /**
262
+ * Set the maximum display height of an embedded timeline in whole pixels
263
+ *
264
+ * Must be an integer greater than or equal to MIN_HEIGHT
265
+ *
266
+ * @since 2.0.0
267
+ *
268
+ * @param int $height the maximum display height of an embedded timeline in whole pixels
269
+ *
270
+ * @return self allow chaining
271
+ */
272
+ public function setHeight($height)
273
+ {
274
+ if (static::isValidHeight($height)) {
275
+ $this->height = $height;
276
+ }
277
+
278
+ return $this;
279
+ }
280
+
281
+ /**
282
+ * Tests a supplied limit for validity
283
+ *
284
+ * @since 2.0.0
285
+ *
286
+ * @param int $limit maximum number of Tweets to display in a timeline without pagination
287
+ *
288
+ * @return bool true if supplied limit is an integer and within the allowed range inclusive
289
+ */
290
+ public static function isValidLimit($limit)
291
+ {
292
+ return ( is_int($limit) && $limit >=static::MIN_LIMIT && $limit<=static::MAX_LIMIT );
293
+ }
294
+
295
+ /**
296
+ * Set the maximum number of Tweets to display in the embedded timeline
297
+ *
298
+ * @param int $limit maximum number of Tweets to display
299
+ *
300
+ * @return self support chaining
301
+ */
302
+ public function setLimit($limit)
303
+ {
304
+ if (static::isValidLimit($limit)) {
305
+ $this->limit = $limit;
306
+ }
307
+
308
+ return $this;
309
+ }
310
+
311
+ /**
312
+ * Is the provided chrome option supported by embedded timelines?
313
+ *
314
+ * @since 2.0.0
315
+ *
316
+ * @param string $option chrome option
317
+ *
318
+ * @return bool true if provided option is a known chrome option
319
+ */
320
+ public static function isValidChromeOption($option)
321
+ {
322
+ return ( $option && isset(static::$CHROME_OPTIONS[$option]) );
323
+ }
324
+
325
+ /**
326
+ * Set chrome preferences from a list of possible chrome tokens
327
+ *
328
+ * @since 2.0.0
329
+ *
330
+ * @param array $chrome chrome preference tokens {
331
+ * @type string chrome preference token
332
+ * @type bool not used
333
+ * }
334
+ *
335
+ * @return self support chaining
336
+ */
337
+ public function setChrome($chrome)
338
+ {
339
+ if (! is_array($chrome) || empty($chrome)) {
340
+ return $this;
341
+ }
342
+
343
+ $keys = null;
344
+ if (isset($chrome[0])) {
345
+ $keys = $chrome;
346
+ } else {
347
+ $keys = array_keys($chrome);
348
+ }
349
+ if ($keys && is_array($keys)) {
350
+ array_walk($keys, array($this, 'setChromeOption'));
351
+ }
352
+
353
+ return $this;
354
+ }
355
+
356
+ /**
357
+ * Possibly set a chrome array value from a valid passed chrome option
358
+ *
359
+ * @since 2.0.0
360
+ *
361
+ * @param string $chrome_option chrome option
362
+ *
363
+ * @return bool option set
364
+ */
365
+ protected function setChromeOption($chrome_option)
366
+ {
367
+ if (is_string($chrome_option)) {
368
+ $chrome_option = strtolower(trim($chrome_option));
369
+ if ($chrome_option && static::isValidChromeOption($chrome_option) && ! isset($this->chrome[$chrome_option])) {
370
+ $this->chrome[$chrome_option] = true;
371
+ return true;
372
+ }
373
+ }
374
+
375
+ return false;
376
+ }
377
+
378
+ /**
379
+ * Do not include a header component of the embedded timeline
380
+ *
381
+ * Sites are expected to include their own header to introduce the datasource and link to its equivalent location on Twitter.com
382
+ *
383
+ * @since 2.0.0
384
+ *
385
+ * @return self support chaining
386
+ */
387
+ public function hideHeader()
388
+ {
389
+ if (! isset($this->chrome[static::CHROME_NOHEADER])) {
390
+ $this->chrome[static::CHROME_NOHEADER] = true;
391
+ }
392
+
393
+ return $this;
394
+ }
395
+
396
+ /**
397
+ * Include a header component of the embedded timeline (default option)
398
+ *
399
+ * @since 2.0.0
400
+ *
401
+ * @return self support chaining
402
+ */
403
+ public function showHeader()
404
+ {
405
+ unset($this->chrome[static::CHROME_NOHEADER]);
406
+
407
+ return $this;
408
+ }
409
+
410
+ /**
411
+ * Do not include a footer component of the embedded timeline
412
+ *
413
+ * @since 2.0.0
414
+ *
415
+ * @return self support chaining
416
+ */
417
+ public function hideFooter()
418
+ {
419
+ if (! isset($this->chrome[static::CHROME_NOFOOTER])) {
420
+ $this->chrome[static::CHROME_NOFOOTER] = true;
421
+ }
422
+
423
+ return $this;
424
+ }
425
+
426
+ /**
427
+ * Include a footer component of the embedded timeline (default option)
428
+ *
429
+ * @since 2.0.0
430
+ *
431
+ * @return self support chaining
432
+ */
433
+ public function showFooter()
434
+ {
435
+ unset($this->chrome[static::CHROME_NOFOOTER]);
436
+
437
+ return $this;
438
+ }
439
+
440
+ /**
441
+ * Do not include a border separating Tweets displayed in the embedded timeline
442
+ *
443
+ * @since 2.0.0
444
+ *
445
+ * @return self support chaining
446
+ */
447
+ public function hideBorders()
448
+ {
449
+ if (! isset($this->chrome[static::CHROME_NOBORDERS])) {
450
+ $this->chrome[static::CHROME_NOBORDERS] = true;
451
+ }
452
+
453
+ return $this;
454
+ }
455
+
456
+ /**
457
+ * Include a border separating Tweets displayed in the embedded timeline (default option)
458
+ *
459
+ * @since 2.0.0
460
+ *
461
+ * @return self support chaining
462
+ */
463
+ public function showBorders()
464
+ {
465
+ unset($this->chrome[static::CHROME_NOBORDERS]);
466
+
467
+ return $this;
468
+ }
469
+
470
+ /**
471
+ * Do not include a footer component of the embedded timeline
472
+ *
473
+ * @since 2.0.0
474
+ *
475
+ * @return self support chaining
476
+ */
477
+ public function hideScrollbar()
478
+ {
479
+ if (! isset($this->chrome[static::CHROME_NOSCROLLBAR])) {
480
+ $this->chrome[static::CHROME_NOSCROLLBAR] = true;
481
+ }
482
+
483
+ return $this;
484
+ }
485
+
486
+ /**
487
+ * Include a visual scrollbar to assist navigating Tweets in an embedded timeline exceeding the visible widget area (default option)
488
+ *
489
+ * @since 2.0.0
490
+ *
491
+ * @return self support chaining
492
+ */
493
+ public function showScrollbar()
494
+ {
495
+ unset($this->chrome[static::CHROME_NOSCROLLBAR]);
496
+
497
+ return $this;
498
+ }
499
+
500
+ /**
501
+ * Hide the widget theme's background by setting it to transparent
502
+ *
503
+ * @since 2.0.0
504
+ *
505
+ * @return self support chaining
506
+ */
507
+ public function hideThemeBackground()
508
+ {
509
+ if (! isset($this->chrome[static::CHROME_TRANSPARENT])) {
510
+ $this->chrome[static::CHROME_TRANSPARENT] = true;
511
+ }
512
+
513
+ return $this;
514
+ }
515
+
516
+ /**
517
+ * Display an embedded timeline with the default background color for the chosen theme (default)
518
+ *
519
+ * @since 2.0.0
520
+ *
521
+ * @return self support chaining
522
+ */
523
+ public function showThemeBackground()
524
+ {
525
+ unset($this->chrome[static::CHROME_TRANSPARENT]);
526
+
527
+ return $this;
528
+ }
529
+
530
+ /**
531
+ * Set an ARIA live region politeness value for the embedded timeline
532
+ *
533
+ * @since 2.0.0
534
+ *
535
+ * @param string $politeness ARIA live region politeness setting
536
+ *
537
+ * @return self support chaining
538
+ */
539
+ public function setAriaLive($politeness)
540
+ {
541
+ if (is_string($politeness)) {
542
+ $politeness = strtolower(trim($politeness));
543
+ if ($politeness === static::ARIA_POLITE_ASSERTIVE) {
544
+ $this->setAriaLiveAssertive();
545
+ }
546
+ }
547
+
548
+ return $this;
549
+ }
550
+
551
+ /**
552
+ * New Tweets are important enough to be announced to the user as soon as possible, but it is not necessary to immediately interrupt the user
553
+ *
554
+ * @since 2.0.0
555
+ *
556
+ * @return self support chaining
557
+ */
558
+ public function setAriaLiveAssertive()
559
+ {
560
+ $this->aria_politeness = static::ARIA_POLITE_ASSERTIVE;
561
+
562
+ return $this;
563
+ }
564
+
565
+ /**
566
+ * Populate base options from a passed associative array
567
+ *
568
+ * @since 2.0.0
569
+ *
570
+ * @param array $options associative array of options {
571
+ * @type string option name
572
+ * @type string|int|bool option value
573
+ * }
574
+ *
575
+ * @return self support chaining
576
+ */
577
+ public function setBaseOptions($options)
578
+ {
579
+ parent::setBaseOptions($options);
580
+ $this->setThemeOptions($options);
581
+
582
+ if (isset($options['width']) && $options['width']) {
583
+ $this->setWidth($options['width']);
584
+ }
585
+
586
+ if (isset($options['aria-polite']) && $options['aria-polite']) {
587
+ $this->setAriaLive($options['aria-polite']);
588
+ }
589
+
590
+ if (isset($options['limit']) && $options['limit']) {
591
+ $this->setLimit($options['limit']);
592
+ } elseif (isset($options['height']) && $options['height']) {
593
+ $this->setHeight($options['height']);
594
+ }
595
+
596
+ if (isset($options['chrome']) && is_array($options['chrome']) && !empty($options['chrome'])) {
597
+ $this->setChrome($options['chrome']);
598
+ }
599
+
600
+ return $this;
601
+ }
602
+
603
+ /**
604
+ * Convert the class object into an array, removing default field values
605
+ *
606
+ * @since 2.0.0
607
+ *
608
+ * @return array properties as associative array
609
+ */
610
+ public function toArray()
611
+ {
612
+ $data = parent::toArray();
613
+ $data = array_merge($data, $this->themeToArray());
614
+
615
+ if (isset($this->width)) {
616
+ $data['width'] = $this->width;
617
+ }
618
+
619
+ // limit sets height
620
+ if (isset($this->limit)) {
621
+ $data['tweet-limit'] = $this->limit;
622
+ // scroll bar is not used on expanded timeline display triggered by limit
623
+ unset($this->chrome[static::CHROME_NOSCROLLBAR]);
624
+ $this->aria_politeness = null;
625
+ } elseif (isset($this->height)) {
626
+ $data['height'] = $this->height;
627
+ }
628
+
629
+ if (!empty($this->chrome)) {
630
+ $data['chrome'] = array_keys($this->chrome);
631
+ }
632
+
633
+ if ($this->aria_politeness && $this->aria_politeness !== static::ARIA_POLITE_POLITE) {
634
+ $data['aria-polite'] = $this->aria_politeness;
635
+ }
636
+
637
+ return $data;
638
+ }
639
+
640
+ /**
641
+ * Output timeline as an array suitable for use as oEmbed query parameters
642
+ *
643
+ * @since 2.0.0
644
+ *
645
+ * @return array profile timeline parameter array {
646
+ * @type string query parameter name
647
+ * @type string query parameter value
648
+ * }
649
+ */
650
+ public function toOEmbedParameterArray()
651
+ {
652
+ $query_parameters = parent::toArray();
653
+ $query_parameters = array_merge($query_parameters, $this->themeToOEmbedParameterArray());
654
+
655
+ if (isset($this->width)) {
656
+ $query_parameters['maxwidth'] = $this->width;
657
+ }
658
+
659
+ if (isset($this->limit)) {
660
+ $query_parameters['limit'] = $this->limit;
661
+ // scroll bar is not used on expanded timeline display triggered by limit
662
+ unset($this->chrome[static::CHROME_NOSCROLLBAR]);
663
+ $this->aria_politeness = null;
664
+ } elseif (isset($this->height)) {
665
+ $query_parameters['maxheight'] = $this->height;
666
+ }
667
+
668
+ if (isset($this->chrome)) {
669
+ $query_parameters['chrome'] = implode(' ', array_keys($this->chrome));
670
+ }
671
+
672
+ if ($this->aria_politeness && $this->aria_politeness !== static::ARIA_POLITE_POLITE) {
673
+ $query_parameters['aria_polite'] = $this->aria_politeness;
674
+ }
675
+
676
+ return $query_parameters;
677
+ }
678
+ }
src/Twitter/Widgets/Embeds/Timeline/Collection.php ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display Tweets inside a collection
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Collection extends \Twitter\Widgets\Embeds\Timeline
34
+ {
35
+ /**
36
+ * Construct a full Twitter URI by appending to base string
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const BASE_URL = 'https://twitter.com/_/timelines/';
43
+
44
+ /**
45
+ * Grid display template
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ const WIDGET_TYPE_GRID = 'grid';
52
+
53
+ /**
54
+ * Fields supported in a vertical template not supported in a grid template
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @type array data-* key, oEmbed value
59
+ */
60
+ public static $FIELDS_NOT_SUPPORTED_IN_GRID = array(
61
+ 'height' => 'maxheight', // auto-expands to limit
62
+ 'aria-polite' => 'aria_polite', // grid is not a live region
63
+ 'theme' => 'theme', // always light text on dark theme
64
+ 'link-color' => 'link_color', // text color
65
+ 'border-color' => 'border_color',
66
+ );
67
+
68
+ /**
69
+ * Collection unique identifier
70
+ *
71
+ * @since 2.0.0
72
+ *
73
+ * @type string
74
+ */
75
+ protected $id;
76
+
77
+ /**
78
+ * The display template used to display Tweets in the collection
79
+ *
80
+ * @since 2.0.0
81
+ *
82
+ * @type string
83
+ */
84
+ protected $widget_type;
85
+
86
+ /**
87
+ * Create a new collection object
88
+ *
89
+ * @since 2.0.0
90
+ *
91
+ * @param string $id unique identifier of the collection
92
+ */
93
+ public function __construct($id)
94
+ {
95
+ if (is_string($id) && $id) {
96
+ $this->setID($id);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Test ID validity
102
+ *
103
+ * @since 2.0.0
104
+ *
105
+ * @link https://dev.twitter.com/overview/api/twitter-ids-json-and-snowflake
106
+ *
107
+ * @param string $id snowflake ID
108
+ *
109
+ * @return bool true if valid snowflake ID
110
+ */
111
+ public static function isValidSnowflakeID($id)
112
+ {
113
+ if (is_string($id)) {
114
+ if (function_exists('ctype_digit')) {
115
+ return ctype_digit($id);
116
+ } else {
117
+ return (bool) (preg_match("/^[0-9]+$/", $id));
118
+ }
119
+ }
120
+
121
+ return false;
122
+ }
123
+
124
+ /**
125
+ * Get the unique collection identifier
126
+ *
127
+ * @since 2.0.0
128
+ *
129
+ * @return string the unique collection identifier or empty string if not set
130
+ */
131
+ public function getID()
132
+ {
133
+ return $this->id ?: '';
134
+ }
135
+
136
+ /**
137
+ * Set the unique identifier of the collection
138
+ *
139
+ * @since 2.0.0
140
+ *
141
+ * @param string $id unique identifier of the collection
142
+ *
143
+ * @return self support chaining
144
+ */
145
+ public function setID($id)
146
+ {
147
+ if (is_string($id)) {
148
+ $id = trim($id);
149
+ if (static::isValidSnowflakeID($id)) {
150
+ $this->id = $id;
151
+ }
152
+ }
153
+
154
+ return $this;
155
+ }
156
+
157
+ /**
158
+ * Twitter collection URL
159
+ *
160
+ * @since 2.0.0
161
+ *
162
+ * @return string Twitter collection URL or empty string if no ID set
163
+ */
164
+ public function getURL()
165
+ {
166
+ if ($this->id) {
167
+ return static::BASE_URL . $this->id;
168
+ }
169
+
170
+ return '';
171
+ }
172
+
173
+ /**
174
+ * Use the grid display template
175
+ *
176
+ * @since 2.0.0
177
+ *
178
+ * @return self support chaining
179
+ */
180
+ public function setGridTemplate()
181
+ {
182
+ $this->widget_type = static::WIDGET_TYPE_GRID;
183
+
184
+ return $this;
185
+ }
186
+
187
+ /**
188
+ * Create a collection object from an associative array
189
+ *
190
+ * @since 2.0.0
191
+ *
192
+ * @param array $options {
193
+ * @type string parameter name
194
+ * @type string|int|bool parameter value
195
+ * }
196
+ *
197
+ * @return self|null new Collection object with configured properties
198
+ */
199
+ public static function fromArray($options)
200
+ {
201
+ // id required
202
+ if (! (is_array($options) && isset($options['id']))) {
203
+ return null;
204
+ }
205
+
206
+ if (! (is_string($options['id']) && $options['id'])) {
207
+ return null;
208
+ }
209
+
210
+ $class = get_called_class();
211
+ $timeline = new $class( $options['id'] );
212
+ unset($class);
213
+ unset($options['id']);
214
+
215
+ if (method_exists($timeline, 'setBaseOptions')) {
216
+ $timeline->setBaseOptions($options);
217
+ }
218
+
219
+ if (isset($options['widget_type']) && static::WIDGET_TYPE_GRID === $options['widget_type']) {
220
+ if (method_exists($timeline, 'setGridTemplate')) {
221
+ $timeline->setGridTemplate();
222
+ }
223
+ }
224
+
225
+ return $timeline;
226
+ }
227
+
228
+ /**
229
+ * Return collection parameters suitable for conversion to data-*
230
+ *
231
+ * @since 2.0.0
232
+ *
233
+ * @return array collection parameter array {
234
+ * @type string dashed parameter name
235
+ * @type string parameter value
236
+ * }
237
+ */
238
+ public function toArray()
239
+ {
240
+ $data = parent::toArray();
241
+
242
+ if ($this->id) {
243
+ $data['id'] = $this->id;
244
+ } else {
245
+ return array();
246
+ }
247
+
248
+ if ($this->widget_type) {
249
+ if (static::WIDGET_TYPE_GRID === $this->widget_type) {
250
+ $data['widget-type'] = $this->widget_type;
251
+
252
+ $unsupported_fields = array_keys(static::$FIELDS_NOT_SUPPORTED_IN_GRID);
253
+
254
+ foreach ($unsupported_fields as $key) {
255
+ unset($data[$key]);
256
+ }
257
+
258
+ // only footer applies. footer is transparent
259
+ if (array_key_exists(static::CHROME_NOFOOTER, $this->chrome)) {
260
+ $data['chrome'] = array(static::CHROME_NOFOOTER);
261
+ } else {
262
+ unset($data['chrome']);
263
+ }
264
+
265
+ // grid parameter differs from standard timeline parameter
266
+ if (array_key_exists('tweet-limit', $data)) {
267
+ $data['limit'] = $data['tweet-limit'];
268
+ unset($data['tweet-limit']);
269
+ }
270
+ }
271
+ }
272
+
273
+ return $data;
274
+ }
275
+
276
+ /**
277
+ * Output timeline as an array suitable for use as oEmbed query parameters
278
+ *
279
+ * @since 2.0.0
280
+ *
281
+ * @return array collection parameter array {
282
+ * @type string query parameter name
283
+ * @type string query parameter value
284
+ * }
285
+ */
286
+ public function toOEmbedParameterArray()
287
+ {
288
+ $query_parameters = parent::toOEmbedParameterArray();
289
+
290
+ $url = $this->getURL();
291
+ if (! $url) {
292
+ return array();
293
+ }
294
+ $query_parameters['url'] = $url;
295
+
296
+ if ($this->widget_type) {
297
+ if (static::WIDGET_TYPE_GRID === $this->widget_type) {
298
+ $query_parameters['widget_type'] = static::WIDGET_TYPE_GRID;
299
+
300
+ $unsupported_parameters = array_values(static::$FIELDS_NOT_SUPPORTED_IN_GRID);
301
+ foreach ($unsupported_parameters as $key) {
302
+ unset($query_parameters[$key]);
303
+ }
304
+ unset($unsupported_parameters);
305
+
306
+ // only footer applies. footer is transparent
307
+ if (array_key_exists(static::CHROME_NOFOOTER, $this->chrome)) {
308
+ $query_parameters['chrome'] = array(static::CHROME_NOFOOTER);
309
+ } else {
310
+ unset($query_parameters['chrome']);
311
+ }
312
+ }
313
+ }
314
+
315
+ return $query_parameters;
316
+ }
317
+ }
src/Twitter/Widgets/Embeds/Timeline/Profile.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display the latest Tweets from a Twitter user / profile
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Profile extends \Twitter\Widgets\Embeds\Timeline
34
+ {
35
+ /**
36
+ * Construct a full Twitter URI by appending to base string
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const BASE_URL = 'https://twitter.com/';
43
+
44
+ /**
45
+ * Twitter handle
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ protected $screen_name;
52
+
53
+ /**
54
+ * Create a new profile timeline for a given screen name
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @param string $screen_name Twitter handle
59
+ */
60
+ public function __construct($screen_name)
61
+ {
62
+ $this->setScreenName($screen_name);
63
+ }
64
+
65
+ /**
66
+ * Get the Twitter handle associated with the object
67
+ *
68
+ * @since 2.0.0
69
+ *
70
+ * @return string Twitter handle or empty string if not set
71
+ */
72
+ public function getScreenName()
73
+ {
74
+ return $this->screen_name ?: '';
75
+ }
76
+
77
+ /**
78
+ * Set the Twitter handle
79
+ *
80
+ * @since 2.0.0
81
+ *
82
+ * @param string $screen_name Twitter username / screen name
83
+ *
84
+ * @return self support chaining
85
+ */
86
+ public function setScreenName($screen_name)
87
+ {
88
+ $screen_name = \Twitter\Helpers\Validators\ScreenName::sanitize($screen_name);
89
+ if ($screen_name) {
90
+ $this->screen_name = $screen_name;
91
+ }
92
+
93
+ return $this;
94
+ }
95
+
96
+ /**
97
+ * Twitter profile URL
98
+ *
99
+ * @since 2.0.0
100
+ *
101
+ * @return string Twitter profile URL or empty string if no screen_name set
102
+ */
103
+ public function getURL()
104
+ {
105
+ if ($this->screen_name) {
106
+ return static::BASE_URL . $this->screen_name;
107
+ }
108
+
109
+ return '';
110
+ }
111
+
112
+ /**
113
+ * Create a profile timeline object from an associative array
114
+ *
115
+ * @since 2.0.0
116
+ *
117
+ * @param array $options {
118
+ * @type string parameter name
119
+ * @type string|int|bool parameter value
120
+ * }
121
+ *
122
+ * @return self|null new Profile object with configured properties
123
+ */
124
+ public static function fromArray($options)
125
+ {
126
+ // screen_name required
127
+ if (! (is_array($options) && isset($options['screen_name']))) {
128
+ return null;
129
+ }
130
+
131
+ if (! (is_string($options['screen_name']) && $options['screen_name'])) {
132
+ return null;
133
+ }
134
+
135
+ $class = get_called_class();
136
+ $timeline = new $class( $options['screen_name'] );
137
+ unset($class);
138
+ unset($options['screen_name']);
139
+
140
+ if (method_exists($timeline, 'setBaseOptions')) {
141
+ $timeline->setBaseOptions($options);
142
+ }
143
+
144
+ return $timeline;
145
+ }
146
+
147
+ /**
148
+ * Return profile timeline parameters suitable for conversion to data-*
149
+ *
150
+ * @since 2.0.0
151
+ *
152
+ * @return array profile timeline parameter array {
153
+ * @type string dashed parameter name
154
+ * @type string parameter value
155
+ * }
156
+ */
157
+ public function toArray()
158
+ {
159
+ $data = parent::toArray();
160
+
161
+ if ($this->screen_name) {
162
+ $data['screen-name'] = $this->screen_name;
163
+ } else {
164
+ return array();
165
+ }
166
+
167
+ return $data;
168
+ }
169
+
170
+ /**
171
+ * Output timeline as an array suitable for use as oEmbed query parameters
172
+ *
173
+ * @since 2.0.0
174
+ *
175
+ * @return array profile timeline parameter array {
176
+ * @type string query parameter name
177
+ * @type string query parameter value
178
+ * }
179
+ */
180
+ public function toOEmbedParameterArray()
181
+ {
182
+ $data = parent::toOEmbedParameterArray();
183
+
184
+ $url = $this->getURL();
185
+ if (! $url) {
186
+ return array();
187
+ }
188
+ $data['url'] = $url;
189
+
190
+ return $data;
191
+ }
192
+ }
src/Twitter/Widgets/Embeds/Timeline/Search.php ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2017 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display Tweets published within the last ~7 days for a given widget ID
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Search extends \Twitter\Widgets\Embeds\Timeline
34
+ {
35
+ /**
36
+ * HTML class expected by the Twitter widget JS
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const HTML_CLASS = 'twitter-timeline';
43
+
44
+ /**
45
+ * Construct a full Twitter URI by appending to base string
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ const BASE_URL = 'https://twitter.com/search';
52
+
53
+ /**
54
+ * Twitter widgets setting URL including a widget ID as a path component
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @type string
59
+ */
60
+ const SETTINGS_URL_REGEX = '#^https://twitter\.com/settings/widgets/([0-9]+)/edit$#i';
61
+
62
+ /**
63
+ * Widget ID configured on Twitter.com
64
+ *
65
+ * @since 2.0.0
66
+ *
67
+ * @see https://twitter.com/settings/widgets/new/search Search widget configuration
68
+ *
69
+ * @type string
70
+ */
71
+ protected $widget_id;
72
+
73
+ /**
74
+ * Search terms set in the widget ID
75
+ *
76
+ * Used to construct a meaningful link to a search results page and relevant link text.
77
+ *
78
+ * @since 2.0.0
79
+ *
80
+ * @type string
81
+ */
82
+ protected $search_terms;
83
+
84
+ /**
85
+ * Create a new search timeline widget with a required widget ID and optional search terms
86
+ *
87
+ * @since 2.0.0
88
+ *
89
+ * @param string $widget_id widget identifier
90
+ * @param string $search_terms (optional) search terms for display in link
91
+ */
92
+ public function __construct($widget_id, $search_terms = '')
93
+ {
94
+ $this->setWidgetID($widget_id);
95
+ if ($search_terms) {
96
+ $this->setSearchTerms($search_terms);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Extract a widget ID from a Twitter.com widgets setting URL
102
+ *
103
+ * @since 2.0.0
104
+ *
105
+ * @param string $url Twitter.com widgets setting URL
106
+ *
107
+ * @return string widget ID or empty string if none found
108
+ */
109
+ public static function getWidgetIDFromSettingsURL($url)
110
+ {
111
+ if (! (is_string($url) && $url)) {
112
+ return '';
113
+ }
114
+ $url = trim($url);
115
+ $matches = array();
116
+ preg_match(static::SETTINGS_URL_REGEX, $url, $matches);
117
+ if (is_array($matches) && isset($matches[1]) && $matches[1]) {
118
+ return $matches[1];
119
+ }
120
+
121
+ return '';
122
+ }
123
+
124
+ /**
125
+ * Test widget ID validity
126
+ *
127
+ * @since 2.0.0
128
+ *
129
+ * @param string $id widget ID
130
+ *
131
+ * @return bool true if valid widget ID
132
+ */
133
+ public static function isValidWidgetID($id)
134
+ {
135
+ if (is_string($id)) {
136
+ if (function_exists('ctype_digit')) {
137
+ return ctype_digit($id);
138
+ } else {
139
+ return (bool) (preg_match("/^[0-9]+$/", $id));
140
+ }
141
+ }
142
+
143
+ return false;
144
+ }
145
+
146
+ /**
147
+ * Get a widget ID corresponding to a saved search
148
+ *
149
+ * @since 2.0.0
150
+ *
151
+ * @return string widget ID if set else empty string
152
+ */
153
+ public function getWidgetID()
154
+ {
155
+ return $this->widget_id ?: '';
156
+ }
157
+
158
+ /**
159
+ * Set a widget ID corresponding to a saved search
160
+ *
161
+ * @since 2.0.0
162
+ *
163
+ * @param string $widget_id widget identifier
164
+ *
165
+ * @return self support chaining
166
+ */
167
+ public function setWidgetID($widget_id)
168
+ {
169
+ if (is_string($widget_id)) {
170
+ $widget_id = trim($widget_id);
171
+ if (static::isValidWidgetID($widget_id)) {
172
+ $this->widget_id = $widget_id;
173
+ }
174
+ }
175
+
176
+ return $this;
177
+ }
178
+
179
+ /**
180
+ * Get search terms for more meaningful fallback display
181
+ *
182
+ * Should match the search terms stored for the widget ID. Does not affect rendered widget
183
+ *
184
+ * @since 2.0.0
185
+ *
186
+ * @return string search terms or empty string if no search terms set
187
+ */
188
+ public function getSearchTerms()
189
+ {
190
+ return $this->search_terms ?: '';
191
+ }
192
+
193
+ /**
194
+ * Set search terms used by markup builder for more meaningful fallback display
195
+ *
196
+ * Should match search terms stored for the widget ID. Does not affect rendered widget.
197
+ *
198
+ * @since 2.0.0
199
+ *
200
+ * @param string $terms search terms
201
+ *
202
+ * @return self support chaining
203
+ */
204
+ public function setSearchTerms($terms)
205
+ {
206
+ if (is_string($terms)) {
207
+ $terms = trim($terms);
208
+ if ($terms) {
209
+ $this->search_terms = $terms;
210
+ }
211
+ }
212
+
213
+ return $this;
214
+ }
215
+
216
+ /**
217
+ * Build a URL to a Twitter search page, with terms if set
218
+ *
219
+ * @since 2.0.0
220
+ *
221
+ * @return string Twitter search URL
222
+ */
223
+ public function getURL()
224
+ {
225
+ $url = static::BASE_URL;
226
+ $search_terms = $this->getSearchTerms();
227
+ if ($search_terms) {
228
+ $url .= '?' . http_build_query(
229
+ array( 'q' => $search_terms ),
230
+ '',
231
+ '&',
232
+ PHP_QUERY_RFC3986
233
+ );
234
+ }
235
+
236
+ return $url;
237
+ }
238
+
239
+ /**
240
+ * Create a search timeline object from an associative array
241
+ *
242
+ * @since 2.0.0
243
+ *
244
+ * @param array $options {
245
+ * @type string parameter name
246
+ * @type string|int|bool parameter value
247
+ * }
248
+ *
249
+ * @return self|null new Search object with configured properties or null if minimum requirements not met
250
+ */
251
+ public static function fromArray($options)
252
+ {
253
+ // widget ID required
254
+ if (! (is_array($options) && isset($options['widget_id']))) {
255
+ return null;
256
+ }
257
+
258
+ if (! (is_string($options['widget_id']) && $options['widget_id'])) {
259
+ return null;
260
+ }
261
+ // parse a widget ID from a Twitter.com widget settings URL
262
+ if (strlen($options['widget_id']) > 7 && 0 === substr_compare('https://', $options['widget_id'], 0, 8, true)) {
263
+ $options['widget_id'] = static::getWidgetIDFromSettingsURL($options['widget_id']);
264
+ if (!$options['widget_id']) {
265
+ return null;
266
+ }
267
+ }
268
+
269
+ $search_terms = '';
270
+ if (isset($options['terms']) && is_string($options['terms']) && $options['terms']) {
271
+ $search_terms = $options['terms'];
272
+ }
273
+
274
+ $class = get_called_class();
275
+ $timeline = new $class( $options['widget_id'], $search_terms );
276
+
277
+ if (method_exists($timeline, 'setBaseOptions')) {
278
+ $timeline->setBaseOptions($options);
279
+ }
280
+
281
+ return $timeline;
282
+ }
283
+
284
+ /**
285
+ * Convert the class object into an array, removing default field values
286
+ *
287
+ * @since 2.0.0
288
+ *
289
+ * @return array properties as associative array
290
+ */
291
+ public function toArray()
292
+ {
293
+ $data = parent::toArray();
294
+
295
+ if ($this->widget_id) {
296
+ $data['widget-id'] = $this->widget_id;
297
+ }
298
+
299
+ // always include theme value. may override value stored with widget
300
+ if ($this->theme) {
301
+ $data['theme'] = $this->theme;
302
+ }
303
+
304
+ return $data;
305
+ }
306
+
307
+ /**
308
+ * Disabled. oEmbed is not supported for a search timeline widget
309
+ *
310
+ * @since 2.0.0
311
+ *
312
+ * @return array empty array
313
+ */
314
+ public function toOEmbedParameterArray()
315
+ {
316
+ // not supported
317
+ return array();
318
+ }
319
+
320
+ /**
321
+ * Generate HTML as fallback markup for the Twitter for Websites JavaScript
322
+ *
323
+ * @since 2.0.0
324
+ *
325
+ * @param string $anchor_text inner text of the generated anchor element. Supports a single '%s' screen name passed through sprintf. Default: Follow %s
326
+ * @param string $html_builder_class callable HTML builder with a static anchorElement class
327
+ *
328
+ * @return string HTML markup or empty string if minimum requirements not met
329
+ */
330
+ public function toHTML($anchor_text = 'Tweets about %s', $html_builder_class = '\Twitter\Helpers\HTMLBuilder')
331
+ {
332
+ if (! ( is_string($anchor_text) && $anchor_text )) {
333
+ return '';
334
+ }
335
+
336
+ // test for invalid passed class
337
+ if (! ( class_exists($html_builder_class) && method_exists($html_builder_class, 'anchorElement') )) {
338
+ return '';
339
+ }
340
+
341
+ $search_url = $this->getURL();
342
+ $search_terms = $this->getSearchTerms();
343
+ if ($search_terms) {
344
+ $anchor_text = sprintf($anchor_text, $search_terms);
345
+ } else {
346
+ $anchor_text = 'Twitter Search';
347
+ }
348
+
349
+ return $html_builder_class::anchorElement(
350
+ $search_url,
351
+ $anchor_text,
352
+ array(
353
+ 'class' => static::HTML_CLASS,
354
+ ),
355
+ $this->toArray()
356
+ );
357
+ }
358
+ }
src/Twitter/Widgets/Embeds/Timeline/TwitterList.php ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display the latest Tweets from multiple Twitter users
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class TwitterList extends Profile
34
+ {
35
+ /**
36
+ * Short name of the list. Unique by list owner
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ protected $slug;
43
+
44
+ /**
45
+ * Create a new list timeline for a given owner screen name and list slug
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @param string $screen_name Twitter handle
50
+ * @param string $slug Slug unique to the owner's account
51
+ */
52
+ public function __construct($screen_name, $slug)
53
+ {
54
+ parent::__construct($screen_name);
55
+
56
+ if ($this->screen_name) {
57
+ $this->setSlug($slug);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Test a supplied slug string for expected characters and length
63
+ *
64
+ * @since 2.0.0
65
+ *
66
+ * @param string $slug unique text identifier
67
+ *
68
+ * @return bool true if valid list slug
69
+ */
70
+ public static function isValidSlug($slug)
71
+ {
72
+ return (bool) preg_match('/^[a-z][a-z0-9_\\-]{0,24}$/i', $slug);
73
+ }
74
+
75
+ /**
76
+ * Get the list short name
77
+ *
78
+ * @since 2.0.0
79
+ *
80
+ * @return string list slug or empty string if no slug set
81
+ */
82
+ public function getSlug()
83
+ {
84
+ return $this->slug ?: '';
85
+ }
86
+
87
+ /**
88
+ * Set the unique identifier for the list
89
+ *
90
+ * @since 2.0.0
91
+ *
92
+ * @param string $slug unique text identifier
93
+ *
94
+ * @return self support chaining
95
+ */
96
+ public function setSlug($slug)
97
+ {
98
+ if (is_string($slug) && $slug) {
99
+ $slug = trim($slug);
100
+ if (static::isValidSlug($slug)) {
101
+ $this->slug = $slug;
102
+ }
103
+ }
104
+
105
+ return $this;
106
+ }
107
+
108
+ /**
109
+ * Twitter list URL
110
+ *
111
+ * @since 2.0.0
112
+ *
113
+ * @return string Twitter list URL or empty string if no screen_name or slug set
114
+ */
115
+ public function getURL()
116
+ {
117
+ if ($this->screen_name && $this->slug) {
118
+ $url = parent::getURL();
119
+ if ($url) {
120
+ return $url . '/lists/' . $this->slug;
121
+ }
122
+ }
123
+
124
+ return '';
125
+ }
126
+
127
+ /**
128
+ * Create a list timeline object from an associative array
129
+ *
130
+ * @since 2.0.0
131
+ *
132
+ * @param array $options {
133
+ * @type string parameter name
134
+ * @type string|int|bool parameter value
135
+ * }
136
+ *
137
+ * @return self|null new List object with configured properties
138
+ */
139
+ public static function fromArray($options)
140
+ {
141
+ // screen-name required
142
+ if (! (is_array($options) && isset($options['screen_name']) && isset($options['slug']) )) {
143
+ return null;
144
+ }
145
+
146
+ $screen_name = null;
147
+ $slug = null;
148
+ if (is_string($options['screen_name']) && $options['screen_name']) {
149
+ $screen_name = $options['screen_name'];
150
+ if (is_string($options['slug']) && $options['slug']) {
151
+ $slug = $options['slug'];
152
+ }
153
+ }
154
+
155
+ if (! ($screen_name && $slug)) {
156
+ return null;
157
+ }
158
+
159
+ unset($options['screen_name']);
160
+ unset($options['slug']);
161
+
162
+ $class = __CLASS__;
163
+ $timeline = new $class( $screen_name, $slug );
164
+ unset($class);
165
+
166
+ if (method_exists($timeline, 'setBaseOptions')) {
167
+ $timeline->setBaseOptions($options);
168
+ }
169
+
170
+ return $timeline;
171
+ }
172
+
173
+ /**
174
+ * Return list timeline parameters suitable for conversion to data-*
175
+ *
176
+ * @since 2.0.0
177
+ *
178
+ * @return array list timeline parameter array {
179
+ * @type string dashed parameter name
180
+ * @type string parameter value
181
+ * }
182
+ */
183
+ public function toArray()
184
+ {
185
+ $data = parent::toArray();
186
+
187
+ if (!isset($data['screen-name'])) {
188
+ return array();
189
+ }
190
+
191
+ if ($this->slug) {
192
+ $data['slug'] = $this->slug;
193
+ } else {
194
+ return array();
195
+ }
196
+
197
+ return $data;
198
+ }
199
+ }
src/Twitter/Widgets/Embeds/Tweet.php ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds;
27
+
28
+ /**
29
+ * Display a Tweet
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Tweet extends \Twitter\Widgets\Embeds\Tweet\Base
34
+ {
35
+ use \Twitter\Widgets\Embeds\Theme;
36
+
37
+ /**
38
+ * The Tweet is not floated
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const ALIGN_NONE = 'none';
45
+
46
+ /**
47
+ * Tweet is floated to the left. Content flows on the right side of the Tweet
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const ALIGN_LEFT = 'left';
54
+
55
+ /**
56
+ * Tweet is floated to the right. Content flows on the left side of the Tweet
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type string
61
+ */
62
+ const ALIGN_RIGHT = 'right';
63
+
64
+ /**
65
+ * Tweet is aligned center
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @type string
70
+ */
71
+ const ALIGN_CENTER = 'center';
72
+
73
+ /**
74
+ * Minimum allowed width
75
+ *
76
+ * @since 2.0.0
77
+ *
78
+ * @type int
79
+ */
80
+ const MIN_WIDTH = 220;
81
+
82
+ /**
83
+ * Maximum allowed width
84
+ *
85
+ * @since 2.0.0
86
+ *
87
+ * @type int
88
+ */
89
+ const MAX_WIDTH = 550;
90
+
91
+ /**
92
+ * Maximum width of the Tweet
93
+ *
94
+ * @since 2.0.0
95
+ *
96
+ * @type int
97
+ */
98
+ protected $width;
99
+
100
+ /**
101
+ * Display photos, videos, and link previews cited in the Tweet
102
+ *
103
+ * @since 2.0.0
104
+ *
105
+ * @type string
106
+ */
107
+ protected $cards = true;
108
+
109
+ /**
110
+ * Display the parent Tweet if the Tweet is in response to another Tweet
111
+ *
112
+ * @since 2.0.0
113
+ *
114
+ * @type string
115
+ */
116
+ protected $conversation = true;
117
+
118
+ /**
119
+ * Float the Tweet relative to the parent element
120
+ *
121
+ * @since 2.0.0
122
+ *
123
+ * @link https://www.w3.org/wiki/CSS/Properties/float float CSS property
124
+ *
125
+ * @type string
126
+ */
127
+ protected $align = 'none';
128
+
129
+ /**
130
+ * Allowed values for align property
131
+ *
132
+ * @since 2.0.0
133
+ *
134
+ * @type array
135
+ */
136
+ public static $ALLOWED_ALIGN_VALUES = array(
137
+ self::ALIGN_LEFT => true,
138
+ self::ALIGN_RIGHT => true,
139
+ self::ALIGN_CENTER => true,
140
+ self::ALIGN_NONE => true,
141
+ );
142
+
143
+ /**
144
+ * Set the fixed maximum used width of the widget display area
145
+ *
146
+ * @since 2.0.0
147
+ *
148
+ * @link https://www.w3.org/wiki/CSS/Properties/max-width CSS max-width
149
+ *
150
+ * @param int $width width of the embed
151
+ *
152
+ * @return \Twitter\Widgets\Embeds\Tweet support chaining
153
+ */
154
+ public function setWidth($width)
155
+ {
156
+ if (is_int($width) && $width >= static::MIN_WIDTH && $width <= static::MAX_WIDTH) {
157
+ $this->width = $width;
158
+ }
159
+
160
+ return $this;
161
+ }
162
+
163
+ /**
164
+ * Show photos, videos, and link previews cited in the Tweet
165
+ *
166
+ * @since 2.0.0
167
+ *
168
+ * @return \Twitter\Widgets\Embeds\Tweet support chaining
169
+ */
170
+ public function showCards()
171
+ {
172
+ $this->cards = true;
173
+
174
+ return $this;
175
+ }
176
+
177
+ /**
178
+ * Hide photos, videos, and link previews cited in the Tweet
179
+ *
180
+ * @since 2.0.0
181
+ *
182
+ * @return \Twitter\Widgets\Embeds\Tweet support chaining
183
+ */
184
+ public function hideCards()
185
+ {
186
+ $this->cards = false;
187
+
188
+ return $this;
189
+ }
190
+
191
+ /**
192
+ * Show the parent Tweet if the Tweet is in response to another Tweet
193
+ *
194
+ * @since 2.0.0
195
+ *
196
+ * @return \Twitter\Widgets\Embeds\Tweet support chaining
197
+ */
198
+ public function showParentTweet()
199
+ {
200
+ $this->conversation = true;
201
+
202
+ return $this;
203
+ }
204
+
205
+ /**
206
+ * Hide the parent Tweet if the Tweet is in response to another Tweet
207
+ *
208
+ * @since 2.0.0
209
+ *
210
+ * @return \Twitter\Widgets\Embeds\Tweet support chaining
211
+ */
212
+ public function hideParentTweet()
213
+ {
214
+ $this->conversation = false;
215
+
216
+ return $this;
217
+ }
218
+
219
+ /**
220
+ * Set the float alignment of the Tweet
221
+ *
222
+ * @since 2.0.0
223
+ *
224
+ * @param string $align alignment of the Tweet embed in the container
225
+ *
226
+ * @return \Twitter\Widgets\Embeds\Tweet support chaining
227
+ */
228
+ public function setAlign($align)
229
+ {
230
+ if (is_string($align)) {
231
+ $align = strtolower(trim($align));
232
+ if ($align && isset(static::$ALLOWED_ALIGN_VALUES[$align])) {
233
+ $this->align = $align;
234
+ }
235
+ }
236
+
237
+ return $this;
238
+ }
239
+
240
+ /**
241
+ * Create a new Tweet object from an associative array of properties
242
+ *
243
+ * @since 2.0.0
244
+ *
245
+ * @param array $options associative array of options {
246
+ * @type string option key
247
+ * @type string|bool option value
248
+ * }
249
+ *
250
+ * @return static|null Tweet object or null if minimum requirements not met
251
+ */
252
+ public static function fromArray($options)
253
+ {
254
+ $tweet = parent::fromArray($options);
255
+ if (! $tweet) {
256
+ return null;
257
+ }
258
+
259
+ $tweet->setThemeOptions($options);
260
+
261
+ if (isset($options['width'])) {
262
+ $tweet->setWidth($options['width']);
263
+ }
264
+
265
+ if (isset($options['cards']) && ( false === $options['cards'] || 'false' === $options['cards'] || 0 == $options['cards'] )) {
266
+ $tweet->hideCards();
267
+ }
268
+ if (isset($options['conversation']) && ( false === $options['conversation'] || 'false' === $options['conversation'] || 0 == $options['conversation'] )) {
269
+ $tweet->hideParentTweet();
270
+ }
271
+
272
+ if (isset($options['align'])) {
273
+ $tweet->setAlign($options['align']);
274
+ }
275
+
276
+ return $tweet;
277
+ }
278
+
279
+ /**
280
+ * Convert Tweet object into an array suitable for use as data-* attributes
281
+ *
282
+ * @since 2.0.0
283
+ *
284
+ * @return array associative array of data attribute values or empty if no id set
285
+ */
286
+ public function toArray()
287
+ {
288
+ $data = parent::toArray();
289
+
290
+ if (empty($data)) {
291
+ return array();
292
+ }
293
+ $data = array_merge($data, $this->themeToArray());
294
+
295
+ if ($this->width) {
296
+ $data['width'] = $this->width;
297
+ }
298
+ if (false === $this->cards) {
299
+ $data['cards'] = 'false';
300
+ }
301
+ if (false === $this->conversation) {
302
+ $data['conversation'] = 'false';
303
+ }
304
+ if (static::ALIGN_NONE !== $this->align) {
305
+ $data['align'] = $this->align;
306
+ }
307
+
308
+ return $data;
309
+ }
310
+
311
+ /**
312
+ * Output Tweet as an array suitable for use as oEmbed query parameters
313
+ *
314
+ * @since 2.0.0
315
+ *
316
+ * @return array Tweet parameter array {
317
+ * @type string query parameter name
318
+ * @type string query parameter value
319
+ * }
320
+ */
321
+ public function toOEmbedParameterArray()
322
+ {
323
+ $oembed = parent::toOEmbedParameterArray();
324
+ if (empty($oembed)) {
325
+ return array();
326
+ }
327
+ $oembed = array_merge($oembed, $this->themeToOEmbedParameterArray());
328
+
329
+ if ($this->width) {
330
+ $oembed['maxwidth'] = $this->width;
331
+ }
332
+ if (false === $this->cards) {
333
+ $oembed['hide_media'] = false;
334
+ }
335
+ if (false === $this->conversation) {
336
+ $oembed['hide_thread'] = false;
337
+ }
338
+ if (static::ALIGN_NONE !== $this->align) {
339
+ $oembed['align'] = $this->align;
340
+ }
341
+
342
+ return $oembed;
343
+ }
344
+ }
src/Twitter/Widgets/Embeds/Tweet/Base.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Tweet;
27
+
28
+ /**
29
+ * Basics of a Tweet shared between all display templates
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ abstract class Base extends \Twitter\Widgets\Base
34
+ {
35
+ /**
36
+ * Construct a full Twitter URI by appending to base string
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const BASE_URL = 'https://twitter.com/_/status/';
43
+
44
+ /**
45
+ * Tweet ID
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ protected $id;
52
+
53
+ /**
54
+ * Initialize the Tweet object with a Tweet ID
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @param string $id Tweet identifier
59
+ */
60
+ public function __construct($id)
61
+ {
62
+ $this->setID($id);
63
+ }
64
+
65
+ /**
66
+ * Test given Tweet ID for validity
67
+ *
68
+ * @since 2.0.0
69
+ *
70
+ * @param string $id Tweet identifier
71
+ *
72
+ * @return bool true if valid, else false
73
+ */
74
+ public static function isValidID($id)
75
+ {
76
+ if (! (is_string($id) && $id)) {
77
+ return false;
78
+ }
79
+ if (function_exists('ctype_digit')) {
80
+ if (ctype_digit($id)) {
81
+ return true;
82
+ }
83
+ } else {
84
+ if (preg_match('/^[0-9]+$/', $id)) {
85
+ return true;
86
+ }
87
+ }
88
+
89
+ return false;
90
+ }
91
+
92
+ /**
93
+ * Get the Tweet ID
94
+ *
95
+ * @since 2.0.0
96
+ *
97
+ * @return string Tweet ID, or empty string if not set
98
+ */
99
+ public function getID()
100
+ {
101
+ return $this->id ?: '';
102
+ }
103
+
104
+ /**
105
+ * Set the Tweet ID. Tests passed Tweet ID for validity before saving
106
+ *
107
+ * @since 2.0.0
108
+ *
109
+ * @param string $id Tweet identifier
110
+ *
111
+ * @return self support chaining
112
+ */
113
+ public function setID($id)
114
+ {
115
+ if (is_string($id)) {
116
+ $id = trim($id);
117
+ if ($id && static::isValidID($id)) {
118
+ $this->id = $id;
119
+ }
120
+ }
121
+
122
+ return $this;
123
+ }
124
+
125
+ /**
126
+ * Tweet URL
127
+ *
128
+ * The username component is purposely left as a placeholder value: `_`.
129
+ * The URL returned by this function will redirect to a full Tweet URL page complete with the current Twitter username of the Tweet author
130
+ *
131
+ * @since 2.0.0
132
+ *
133
+ * @return string absolute URL or empty string if ID not set
134
+ */
135
+ public function getURL()
136
+ {
137
+ if ($this->id) {
138
+ return static::BASE_URL . $this->id;
139
+ } else {
140
+ return '';
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Create a new Tweet object from an associative array of properties
146
+ *
147
+ * @since 2.0.0
148
+ *
149
+ * @param array $options associative array of options {
150
+ * @type string option key
151
+ * @type string|bool option value
152
+ * }
153
+ *
154
+ * @return static|null new instance of calling class or null if minimum requirements not met
155
+ */
156
+ public static function fromArray($options)
157
+ {
158
+ // Tweet ID required
159
+ if (! (is_array($options) && isset($options['id']) && $options['id'])) {
160
+ return null;
161
+ }
162
+
163
+ $class = get_called_class();
164
+ $tweet = new $class( $options['id'] );
165
+ unset($class);
166
+ if (method_exists($tweet, 'setBaseOptions')) {
167
+ $tweet->setBaseOptions($options);
168
+ }
169
+
170
+ return $tweet;
171
+ }
172
+
173
+ /**
174
+ * Convert Tweet object into an array suitable for use as data-* attributes
175
+ *
176
+ * @since 2.0.0
177
+ *
178
+ * @return array associative array of data attribute values or empty if no id set
179
+ */
180
+ public function toArray()
181
+ {
182
+ $data = parent::toArray();
183
+
184
+ if ($this->id) {
185
+ $data['id'] = $this->id;
186
+ } else {
187
+ return array();
188
+ }
189
+
190
+ return $data;
191
+ }
192
+
193
+ /**
194
+ * Output Tweet as an array suitable for use as oEmbed query parameters
195
+ *
196
+ * @since 2.0.0
197
+ *
198
+ * @return array Tweet parameter array {
199
+ * @type string query parameter name
200
+ * @type string query parameter value
201
+ * }
202
+ */
203
+ public function toOEmbedParameterArray()
204
+ {
205
+ $oembed = parent::toArray();
206
+
207
+ $url = $this->getURL();
208
+ if (! $url) {
209
+ return array();
210
+ }
211
+ $oembed['url'] = $url;
212
+
213
+ return $oembed;
214
+ }
215
+ }
src/Twitter/Widgets/Embeds/Tweet/Video.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Tweet;
27
+
28
+ /**
29
+ * Display a Tweetn with video in a video-focused template
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Video extends \Twitter\Widgets\Embeds\Tweet\Base
34
+ {
35
+ /**
36
+ * Display template type passed to oEmbed endpoints for a Tweet URL
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const WIDGET_TYPE = 'video';
43
+
44
+ /**
45
+ * Convert Tweet object into an array suitable for use as data-* attributes
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @return array associative array of data attribute values or empty if no id set
50
+ */
51
+ public function toArray()
52
+ {
53
+ $data = parent::toArray();
54
+ if (empty($data)) {
55
+ return array();
56
+ }
57
+
58
+ $data['widget-type'] = static::WIDGET_TYPE;
59
+
60
+ return $data;
61
+ }
62
+
63
+ /**
64
+ * Output Tweet as an array suitable for use as oEmbed query parameters
65
+ *
66
+ * @since 2.0.0
67
+ *
68
+ * @return array Tweet parameter array {
69
+ * @type string query parameter name
70
+ * @type string query parameter value
71
+ * }
72
+ */
73
+ public function toOEmbedParameterArray()
74
+ {
75
+ $oembed = parent::toOEmbedParameterArray();
76
+ if (empty($oembed)) {
77
+ return array();
78
+ }
79
+
80
+ $oembed['widget_type'] = static::WIDGET_TYPE;
81
+
82
+ return $oembed;
83
+ }
84
+ }
src/Twitter/Widgets/Embeds/Vine.php ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds;
27
+
28
+ /**
29
+ * Display a Vine simple embed
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Vine
34
+ {
35
+ /**
36
+ * Construct a full Vine URI by appending to base string
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const BASE_URL = 'https://vine.co/v/';
43
+
44
+ /**
45
+ * Minimum allowed width
46
+ *
47
+ * @since 2.0.0
48
+ *
49
+ * @type int
50
+ */
51
+ const MIN_WIDTH = 100;
52
+
53
+ /**
54
+ * Maximum allowed width
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @type int
59
+ */
60
+ const MAX_WIDTH = 600;
61
+
62
+ /**
63
+ * Vine ID
64
+ *
65
+ * @since 2.0.0
66
+ *
67
+ * @type string
68
+ */
69
+ protected $id;
70
+
71
+ /**
72
+ * Width of the embed
73
+ *
74
+ * @since 2.0.0
75
+ *
76
+ * @type int
77
+ */
78
+ protected $width;
79
+
80
+ /**
81
+ * Initialize the Vine object with a Vine ID
82
+ *
83
+ * @since 2.0.0
84
+ *
85
+ * @param string $id Vine identifier
86
+ */
87
+ public function __construct($id)
88
+ {
89
+ $this->setID($id);
90
+ }
91
+
92
+ /**
93
+ * Get the Vine ID
94
+ *
95
+ * @since 2.0.0
96
+ *
97
+ * @return string Vine ID, or empty string if not set
98
+ */
99
+ public function getID()
100
+ {
101
+ return $this->id ?: '';
102
+ }
103
+
104
+ /**
105
+ * Set the Vine ID
106
+ *
107
+ * @since 2.0.0
108
+ *
109
+ * @param string $id Vine identifier
110
+ *
111
+ * @return self support chaining
112
+ */
113
+ public function setID($id)
114
+ {
115
+ if (is_string($id)) {
116
+ $id = trim($id);
117
+ if ($id) {
118
+ $this->id = $id;
119
+ }
120
+ }
121
+
122
+ return $this;
123
+ }
124
+
125
+ /**
126
+ * Vine URL
127
+ *
128
+ * @since 2.0.0
129
+ *
130
+ * @return string absolute URL or empty string if ID not set
131
+ */
132
+ public function getURL()
133
+ {
134
+ if ($this->id) {
135
+ return static::BASE_URL . $this->id;
136
+ } else {
137
+ return '';
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Get the fixed width of the widget display area
143
+ *
144
+ * @since 2.0.0
145
+ *
146
+ * @return int|null specified width of the widget or null if none set
147
+ */
148
+ public function getWidth()
149
+ {
150
+ return $this->width ?: null;
151
+ }
152
+
153
+ /**
154
+ * Set the fixed width of the widget display area
155
+ *
156
+ * @since 2.0.0
157
+ *
158
+ * @link https://www.w3.org/wiki/CSS/Properties/max-width CSS max-width
159
+ *
160
+ * @param int $width width of the embed
161
+ *
162
+ * @return self support chaining
163
+ */
164
+ public function setWidth($width)
165
+ {
166
+ if (is_int($width) && $width >= static::MIN_WIDTH && $width <= static::MAX_WIDTH) {
167
+ $this->width = $width;
168
+ }
169
+
170
+ return $this;
171
+ }
172
+
173
+ /**
174
+ * Create a new Vine object from an associative array of properties
175
+ *
176
+ * @since 2.0.0
177
+ *
178
+ * @param array $options associative array of options {
179
+ * @type string option key
180
+ * @type string|bool option value
181
+ * }
182
+ *
183
+ * @return self new instance of calling class
184
+ */
185
+ public static function fromArray($options)
186
+ {
187
+ // Vine ID required
188
+ if (! (is_array($options) && isset($options['id']) && $options['id'])) {
189
+ return null;
190
+ }
191
+
192
+ $class = get_called_class();
193
+ $vine = new $class( $options['id'] );
194
+ unset($class);
195
+
196
+ if (isset($options['width']) && method_exists($vine, 'setWidth')) {
197
+ $vine->setWidth($options['width']);
198
+ }
199
+
200
+ return $vine;
201
+ }
202
+
203
+ /**
204
+ * Output Vine as an array suitable for use as oEmbed query parameters
205
+ *
206
+ * @since 2.0.0
207
+ *
208
+ * @return array Vine parameter array {
209
+ * @type string query parameter name
210
+ * @type string query parameter value
211
+ * }
212
+ */
213
+ public function toOEmbedParameterArray()
214
+ {
215
+ $url = $this->getURL();
216
+ if (! $url) {
217
+ return array();
218
+ }
219
+ $oembed = array('url' => $url);
220
+
221
+ if ($this->width) {
222
+ $oembed['maxwidth'] = $this->width;
223
+ }
224
+
225
+ return $oembed;
226
+ }
227
+ }
src/Twitter/Widgets/Language.php CHANGED
@@ -83,6 +83,8 @@ class Language
83
  *
84
  * @since 1.0.0
85
  *
 
 
86
  * @return bool true if passed language exists in list of Twitter production language codes
87
  */
88
  public static function isSupportedLanguage($lang)
83
  *
84
  * @since 1.0.0
85
  *
86
+ * @param string $lang language code
87
+ *
88
  * @return bool true if passed language exists in list of Twitter production language codes
89
  */
90
  public static function isSupportedLanguage($lang)
src/Twitter/WordPress/Admin/Post/MetaBox.php CHANGED
@@ -158,7 +158,7 @@ class MetaBox
158
  *
159
  * @since 1.0.0
160
  *
161
- * @param WP_Post $post WordPress post object
162
  *
163
  * @return void
164
  */
158
  *
159
  * @since 1.0.0
160
  *
161
+ * @param \WP_Post $post WordPress post object
162
  *
163
  * @return void
164
  */
src/Twitter/WordPress/Admin/Post/TweetIntent.php CHANGED
@@ -65,7 +65,7 @@ class TweetIntent
65
  * Used by WordPress JSON API to expose programmatic editor beyond the post meta box display used in the HTML-based admin interface
66
  *
67
  * @since 1.0.0
68
- * @uses register_meta
69
  *
70
  * @return void
71
  */
@@ -122,7 +122,9 @@ class TweetIntent
122
  if ( 'https' === strtolower( parse_url( $post_url, PHP_URL_SCHEME ) ) ) {
123
  $is_https = true;
124
  }
125
- } catch(Exception $e) {}
 
 
126
  if ( $is_https ) {
127
  $url_length = absint( $config->short_url_length_https );
128
  }
@@ -165,21 +167,33 @@ class TweetIntent
165
 
166
  echo '<tr>';
167
  echo '<th scope="row" class="left"><label for="tweet-text">' . esc_html( _x( 'Text', 'Share / Tweet text', 'twitter' ) ) . '</label></th>';
168
- echo '<td><input id="tweet-text" name="' . esc_attr( static::META_KEY . '[' . static::TEXT_KEY . ']' ) . '" type="text" maxlength="' . $available_characters . '" autocomplete="off"';
 
 
 
169
  if ( isset( $stored_values[ static::TEXT_KEY ] ) ) {
170
  echo ' value="' . esc_attr( $stored_values[ static::TEXT_KEY ] ) . '"';
171
  } else {
172
  echo ' placeholder="' . esc_attr( get_the_title( $post ) ) . '"';
173
  }
174
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '></td>';
 
 
175
  echo '</tr>';
176
  echo '<tr>';
177
  echo '<th scope="row" class="left"><label for="tweet-hashtags">' . esc_html( __( 'Hashtags', 'twitter' ) ) . '</label></th>';
178
- echo '<td><input id="tweet-hashtags" name="' . esc_attr( static::META_KEY . '[' . static::HASHTAGS_KEY . ']' ) . '" type="text" maxlength="' . ($available_characters - 2) . '" autocomplete="off"';
 
 
 
 
 
179
  if ( isset( $stored_values[ static::HASHTAGS_KEY ] ) && is_array( $stored_values[ static::HASHTAGS_KEY ] ) ) {
180
  echo ' value="' . esc_attr( implode( ',', $stored_values[ static::HASHTAGS_KEY ] ) ) . '"';
181
  }
182
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '></td>';
 
 
183
  echo '</tr>';
184
 
185
  echo '</tbody></table>';
@@ -266,7 +280,7 @@ class TweetIntent
266
  *
267
  * @since 1.0.0
268
  *
269
- * @param WP_Post $post WordPress post object
270
  *
271
  * @return void
272
  */
65
  * Used by WordPress JSON API to expose programmatic editor beyond the post meta box display used in the HTML-based admin interface
66
  *
67
  * @since 1.0.0
68
+ * @uses register_meta
69
  *
70
  * @return void
71
  */
122
  if ( 'https' === strtolower( parse_url( $post_url, PHP_URL_SCHEME ) ) ) {
123
  $is_https = true;
124
  }
125
+ } catch (\Exception $e) {
126
+ // assume not HTTPS if parse_url throws exception
127
+ }
128
  if ( $is_https ) {
129
  $url_length = absint( $config->short_url_length_https );
130
  }
167
 
168
  echo '<tr>';
169
  echo '<th scope="row" class="left"><label for="tweet-text">' . esc_html( _x( 'Text', 'Share / Tweet text', 'twitter' ) ) . '</label></th>';
170
+ echo '<td><input id="tweet-text" name="' . esc_attr( static::META_KEY . '[' . static::TEXT_KEY . ']' ) . '" type="text" maxlength="';
171
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
172
+ echo $available_characters;
173
+ echo '" autocomplete="off"';
174
  if ( isset( $stored_values[ static::TEXT_KEY ] ) ) {
175
  echo ' value="' . esc_attr( $stored_values[ static::TEXT_KEY ] ) . '"';
176
  } else {
177
  echo ' placeholder="' . esc_attr( get_the_title( $post ) ) . '"';
178
  }
179
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
180
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
181
+ echo '></td>';
182
  echo '</tr>';
183
  echo '<tr>';
184
  echo '<th scope="row" class="left"><label for="tweet-hashtags">' . esc_html( __( 'Hashtags', 'twitter' ) ) . '</label></th>';
185
+ echo '<td><input id="tweet-hashtags" name="' . esc_attr( static::META_KEY . '[' . static::HASHTAGS_KEY . ']' ) . '" type="text" maxlength="';
186
+ // integer does not need escaping
187
+ // @codingStandardsIgnoreStart WordPress.XSS.EscapeOutput.OutputNotEscaped
188
+ echo ($available_characters - 2);
189
+ // @codingStandardsIgnoreEnd WordPress.XSS.EscapeOutput.OutputNotEscaped
190
+ echo '" autocomplete="off"';
191
  if ( isset( $stored_values[ static::HASHTAGS_KEY ] ) && is_array( $stored_values[ static::HASHTAGS_KEY ] ) ) {
192
  echo ' value="' . esc_attr( implode( ',', $stored_values[ static::HASHTAGS_KEY ] ) ) . '"';
193
  }
194
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
195
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
196
+ echo '></td>';
197
  echo '</tr>';
198
 
199
  echo '</tbody></table>';
280
  *
281
  * @since 1.0.0
282
  *
283
+ * @param \WP_Post $post WordPress post object
284
  *
285
  * @return void
286
  */
src/Twitter/WordPress/Admin/Post/TwitterCard.php CHANGED
@@ -65,7 +65,7 @@ class TwitterCard
65
  * Used by WordPress JSON API to expose programmatic editor beyond the post meta box display used in the HTML-based admin interface
66
  *
67
  * @since 1.0.0
68
- * @uses register_meta
69
  *
70
  * @return void
71
  */
@@ -86,8 +86,8 @@ class TwitterCard
86
  * @param string $post_type post type
87
  *
88
  * @return array associative array of supported fields {
89
- * @type string field name (examples: title, description)
90
- * @type bool exists boolean for easy key reference
91
  * }
92
  */
93
  public static function supportedCardFieldsByPostType( $post_type )
@@ -133,7 +133,10 @@ class TwitterCard
133
  }
134
 
135
  // separate Twitter Cards content from Intent content above
136
- echo '<hr' . \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
 
 
 
137
 
138
  echo '<h3>' . esc_html( __( 'Twitter Card', 'twitter' ) ) . '</h3>';
139
 
@@ -151,7 +154,9 @@ class TwitterCard
151
  echo ' placeholder="' . esc_attr( get_the_title( $post ) ) . '"';
152
  }
153
 
154
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '></td>';
 
 
155
  echo '</tr>';
156
  }
157
 
@@ -162,7 +167,9 @@ class TwitterCard
162
  if ( isset( $stored_values[ static::DESCRIPTION_KEY ] ) ) {
163
  echo ' value="' . esc_attr( $stored_values[ static::DESCRIPTION_KEY ] ) . '"';
164
  }
165
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '></td>';
 
 
166
  echo '</tr>';
167
  }
168
 
@@ -195,18 +202,18 @@ class TwitterCard
195
  $cleaned_fields = array();
196
 
197
  if ( isset( $fields[ static::TITLE_KEY ] ) ) {
198
- $title = \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $fields[ static::TITLE_KEY ], /* remove breaks */ true );
199
  if ( $title ) {
200
  $cleaned_fields[ static::TITLE_KEY ] = $title;
201
  }
202
- unset($title);
203
  }
204
  if ( isset( $fields[ static::DESCRIPTION_KEY ] ) ) {
205
- $description = \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $fields[ static::DESCRIPTION_KEY ], /* remove breaks */ true );
206
  if ( $description ) {
207
  $cleaned_fields[ static::DESCRIPTION_KEY ] = $description;
208
  }
209
- unset($description);
210
  }
211
 
212
  return $cleaned_fields;
@@ -219,7 +226,7 @@ class TwitterCard
219
  *
220
  * @since 1.0.0
221
  *
222
- * @param WP_Post $post WordPress post object
223
  *
224
  * @return void
225
  */
65
  * Used by WordPress JSON API to expose programmatic editor beyond the post meta box display used in the HTML-based admin interface
66
  *
67
  * @since 1.0.0
68
+ * @uses register_meta
69
  *
70
  * @return void
71
  */
86
  * @param string $post_type post type
87
  *
88
  * @return array associative array of supported fields {
89
+ * @type string field name (examples: title, description)
90
+ * @type bool exists boolean for easy key reference
91
  * }
92
  */
93
  public static function supportedCardFieldsByPostType( $post_type )
133
  }
134
 
135
  // separate Twitter Cards content from Intent content above
136
+ echo '<hr';
137
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
138
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
139
+ echo '>';
140
 
141
  echo '<h3>' . esc_html( __( 'Twitter Card', 'twitter' ) ) . '</h3>';
142
 
154
  echo ' placeholder="' . esc_attr( get_the_title( $post ) ) . '"';
155
  }
156
 
157
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
158
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
159
+ echo '></td>';
160
  echo '</tr>';
161
  }
162
 
167
  if ( isset( $stored_values[ static::DESCRIPTION_KEY ] ) ) {
168
  echo ' value="' . esc_attr( $stored_values[ static::DESCRIPTION_KEY ] ) . '"';
169
  }
170
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
171
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
172
+ echo '></td>';
173
  echo '</tr>';
174
  }
175
 
202
  $cleaned_fields = array();
203
 
204
  if ( isset( $fields[ static::TITLE_KEY ] ) ) {
205
+ $title = \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $fields[ static::TITLE_KEY ] );
206
  if ( $title ) {
207
  $cleaned_fields[ static::TITLE_KEY ] = $title;
208
  }
209
+ unset( $title );
210
  }
211
  if ( isset( $fields[ static::DESCRIPTION_KEY ] ) ) {
212
+ $description = \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $fields[ static::DESCRIPTION_KEY ] );
213
  if ( $description ) {
214
  $cleaned_fields[ static::DESCRIPTION_KEY ] = $description;
215
  }
216
+ unset( $description );
217
  }
218
 
219
  return $cleaned_fields;
226
  *
227
  * @since 1.0.0
228
  *
229
+ * @param \WP_Post $post WordPress post object
230
  *
231
  * @return void
232
  */
src/Twitter/WordPress/Admin/Profile/PeriscopeUser.php CHANGED
@@ -62,14 +62,14 @@ class PeriscopeUser
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 )
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 )
src/Twitter/WordPress/Admin/Profile/User.php CHANGED
@@ -62,14 +62,14 @@ class User
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 )
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 )
src/Twitter/WordPress/Admin/Settings/{TweetButton.php → Buttons/Tweet.php} RENAMED
@@ -23,14 +23,14 @@ 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\Settings;
27
 
28
  /**
29
  * Store a Twitter username for attribution of site content
30
  *
31
  * @since 1.0.0
32
  */
33
- class TweetButton implements SettingsSection
34
  {
35
  /**
36
  * Define our option name
@@ -61,6 +61,15 @@ class TweetButton implements SettingsSection
61
  */
62
  protected $hook_suffix;
63
 
 
 
 
 
 
 
 
 
 
64
  /**
65
  * Reference the feature by name
66
  *
@@ -122,7 +131,7 @@ class TweetButton implements SettingsSection
122
  $this->existing_options = $options;
123
 
124
  add_action(
125
- 'add-' . $this->hook_suffix . '-section',
126
  array( &$this, 'defineSection' ),
127
  static::SECTION_PRIORITY,
128
  0 // no parameters
@@ -186,8 +195,8 @@ class TweetButton implements SettingsSection
186
  * @since 1.0.0
187
  *
188
  * @return array option values and labels {
189
- * @type string option value
190
- * @type string translated option label
191
  * }
192
  */
193
  public static function getPositionOptions()
@@ -196,7 +205,7 @@ class TweetButton implements SettingsSection
196
  '' => ' ',
197
  'before' => _x( 'before', 'before another piece of content', 'twitter' ),
198
  'after' => _x( 'after', 'after another piece of content', 'twitter' ),
199
- 'both' => _x( 'before & after', 'before and after another piece of content', 'twitter' )
200
  );
201
  }
202
 
@@ -233,6 +242,8 @@ class TweetButton implements SettingsSection
233
  }
234
  $select .= '</select>';
235
 
 
 
236
  echo sprintf( esc_html( _x( 'Display Tweet Button %s post content', 'display Tweet Button relative to the content of an article', 'twitter' ) ), $select );
237
  }
238
 
@@ -255,7 +266,11 @@ class TweetButton implements SettingsSection
255
  echo '="checked"';
256
  }
257
  }
258
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '> ' . esc_html( __( 'Large button', 'twitter' ) ) . '</label>';
 
 
 
 
259
  }
260
 
261
  /**
@@ -264,13 +279,13 @@ class TweetButton implements SettingsSection
264
  * @since 1.0.0
265
  *
266
  * @param array $options submitted option {
267
- * @type string option name
268
- * @type mixed option value
269
  * }
270
  *
271
  * @return array $options cleaned option {
272
- * @type string option name
273
- * @type string option value
274
  * }
275
  */
276
  public static function sanitizeOption( $options )
@@ -289,7 +304,7 @@ class TweetButton implements SettingsSection
289
  }
290
  unset( $position_options );
291
  }
292
- unset($key);
293
 
294
  if ( isset( $options['size'] ) && 'large' === $options['size'] ) {
295
  $clean_options['size'] = 'large';
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Admin\Settings\Buttons;
27
 
28
  /**
29
  * Store a Twitter username for attribution of site content
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Tweet implements \Twitter\WordPress\Admin\Settings\SettingsSection
34
  {
35
  /**
36
  * Define our option name
61
  */
62
  protected $hook_suffix;
63
 
64
+ /**
65
+ * Store existing options, if any exist
66
+ *
67
+ * @since 1.0.0
68
+ *
69
+ * @type array
70
+ */
71
+ protected $existing_options;
72
+
73
  /**
74
  * Reference the feature by name
75
  *
131
  $this->existing_options = $options;
132
 
133
  add_action(
134
+ 'add_' . $this->hook_suffix . '_section',
135
  array( &$this, 'defineSection' ),
136
  static::SECTION_PRIORITY,
137
  0 // no parameters
195
  * @since 1.0.0
196
  *
197
  * @return array option values and labels {
198
+ * @type string option value
199
+ * @type string translated option label
200
  * }
201
  */
202
  public static function getPositionOptions()
205
  '' => ' ',
206
  'before' => _x( 'before', 'before another piece of content', 'twitter' ),
207
  'after' => _x( 'after', 'after another piece of content', 'twitter' ),
208
+ 'both' => _x( 'before & after', 'before and after another piece of content', 'twitter' ),
209
  );
210
  }
211
 
242
  }
243
  $select .= '</select>';
244
 
245
+ // <select> markup escaped when building the element
246
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
247
  echo sprintf( esc_html( _x( 'Display Tweet Button %s post content', 'display Tweet Button relative to the content of an article', 'twitter' ) ), $select );
248
  }
249
 
266
  echo '="checked"';
267
  }
268
  }
269
+
270
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
271
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
272
+
273
+ echo '> ' . esc_html( __( 'Large button', 'twitter' ) ) . '</label>';
274
  }
275
 
276
  /**
279
  * @since 1.0.0
280
  *
281
  * @param array $options submitted option {
282
+ * @type string option name
283
+ * @type mixed option value
284
  * }
285
  *
286
  * @return array $options cleaned option {
287
+ * @type string option name
288
+ * @type string option value
289
  * }
290
  */
291
  public static function sanitizeOption( $options )
304
  }
305
  unset( $position_options );
306
  }
307
+ unset( $key );
308
 
309
  if ( isset( $options['size'] ) && 'large' === $options['size'] ) {
310
  $clean_options['size'] = 'large';
src/Twitter/WordPress/Admin/Settings/{SiteAttribution.php → Cards/SiteAttribution.php} RENAMED
@@ -23,14 +23,14 @@ 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\Settings;
27
 
28
  /**
29
  * Store a Twitter username for attribution of site content
30
  *
31
  * @since 1.0.0
32
  */
33
- class SiteAttribution implements SettingsSection
34
  {
35
  /**
36
  * Define our option name
@@ -73,6 +73,18 @@ class SiteAttribution implements SettingsSection
73
  return _x( 'Site attribution', 'Attribute content on a website to a Twitter account', 'twitter' );
74
  }
75
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  /**
77
  * Add site attribution option and settings section to an existing settings page
78
  *
@@ -91,7 +103,18 @@ class SiteAttribution implements SettingsSection
91
  $settings = new static();
92
  $settings->hook_suffix = $hook_suffix;
93
 
94
- register_setting( $hook_suffix, self::OPTION_NAME, array( __CLASS__, 'sanitize' ) );
 
 
 
 
 
 
 
 
 
 
 
95
  add_action(
96
  'load-' . $hook_suffix,
97
  array( &$settings, 'onload' ),
@@ -116,7 +139,7 @@ class SiteAttribution implements SettingsSection
116
  }
117
 
118
  add_action(
119
- 'add-' . $this->hook_suffix . '-section',
120
  array( &$this, 'defineSection' ),
121
  static::SECTION_PRIORITY,
122
  0 // no parameters
@@ -124,7 +147,7 @@ class SiteAttribution implements SettingsSection
124
 
125
  // contextual help
126
  add_action(
127
- 'add-' . $this->hook_suffix . '-help-tab',
128
  array( __CLASS__, 'addHelpTab' ),
129
  static::SECTION_PRIORITY,
130
  1 // accept current screen as a parameter
@@ -172,7 +195,7 @@ class SiteAttribution implements SettingsSection
172
  public static function sectionHeader()
173
  {
174
  echo '<p>';
175
- echo esc_html( __( "Attribute shared content to your site's Twitter account", 'twitter' ) );
176
  echo '</p>';
177
  }
178
 
@@ -186,16 +209,15 @@ class SiteAttribution implements SettingsSection
186
  public function displaySiteAttributionUsername()
187
  {
188
  $html = '<input type="text" id="' . esc_attr( self::OPTION_NAME ) . '" name="' . esc_attr( self::OPTION_NAME ) . '" size="20"';
189
- $site_username = get_option( self::OPTION_NAME );
190
  if ( $site_username ) {
191
  $html .= ' value="' . esc_attr( $site_username ) . '"';
192
  }
193
  $html .= \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
194
 
195
  // escaped in markup builder
196
- // @codingStandardsIgnoreStart WordPress.XSS.EscapeOutput
197
  echo $html;
198
- // @codingStandardsIgnoreEnd WordPress.XSS.EscapeOutput
199
  }
200
 
201
  /**
@@ -203,7 +225,7 @@ class SiteAttribution implements SettingsSection
203
  *
204
  * @since 1.0.0
205
  *
206
- * @param WP_Screen $screen current screen
207
  *
208
  * @return void
209
  */
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Admin\Settings\Cards;
27
 
28
  /**
29
  * Store a Twitter username for attribution of site content
30
  *
31
  * @since 1.0.0
32
  */
33
+ class SiteAttribution implements \Twitter\WordPress\Admin\Settings\SettingsSection
34
  {
35
  /**
36
  * Define our option name
73
  return _x( 'Site attribution', 'Attribute content on a website to a Twitter account', 'twitter' );
74
  }
75
 
76
+ /**
77
+ * Describe the setting
78
+ *
79
+ * @since 2.0.0
80
+ *
81
+ * @return string description of the setting
82
+ */
83
+ public static function getDescription()
84
+ {
85
+ return __( "Attribute shared content to your site's Twitter account", 'twitter' );
86
+ }
87
+
88
  /**
89
  * Add site attribution option and settings section to an existing settings page
90
  *
103
  $settings = new static();
104
  $settings->hook_suffix = $hook_suffix;
105
 
106
+ $args = array(
107
+ 'type' => 'string',
108
+ 'description' => static::getDescription(),
109
+ 'sanitize_callback' => array( get_called_class(), 'sanitize' ),
110
+ 'default' => '',
111
+ 'show_in_rest' => true,
112
+ );
113
+ // WordPress 4.7+ registered settings
114
+ if ( ! function_exists( 'get_registered_settings' ) ) {
115
+ $args = $args['sanitize_callback'];
116
+ }
117
+ register_setting( $hook_suffix, self::OPTION_NAME, $args );
118
  add_action(
119
  'load-' . $hook_suffix,
120
  array( &$settings, 'onload' ),
139
  }
140
 
141
  add_action(
142
+ 'add_' . $this->hook_suffix . '_section',
143
  array( &$this, 'defineSection' ),
144
  static::SECTION_PRIORITY,
145
  0 // no parameters
147
 
148
  // contextual help
149
  add_action(
150
+ 'add_' . $this->hook_suffix . '_help_tab',
151
  array( __CLASS__, 'addHelpTab' ),
152
  static::SECTION_PRIORITY,
153
  1 // accept current screen as a parameter
195
  public static function sectionHeader()
196
  {
197
  echo '<p>';
198
+ echo esc_html( static::getDescription() );
199
  echo '</p>';
200
  }
201
 
209
  public function displaySiteAttributionUsername()
210
  {
211
  $html = '<input type="text" id="' . esc_attr( self::OPTION_NAME ) . '" name="' . esc_attr( self::OPTION_NAME ) . '" size="20"';
212
+ $site_username = get_option( self::OPTION_NAME, '' );
213
  if ( $site_username ) {
214
  $html .= ' value="' . esc_attr( $site_username ) . '"';
215
  }
216
  $html .= \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
217
 
218
  // escaped in markup builder
219
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput
220
  echo $html;
 
221
  }
222
 
223
  /**
225
  *
226
  * @since 1.0.0
227
  *
228
+ * @param \WP_Screen $screen current screen
229
  *
230
  * @return void
231
  */
src/Twitter/WordPress/Admin/Settings/{Theme.php → Embeds/Theme.php} RENAMED
@@ -23,14 +23,14 @@ 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\Settings;
27
 
28
  /**
29
  * Site-level settings for Twitter widget themes
30
  *
31
  * @since 1.0.0
32
  */
33
- class Theme implements SettingsSection
34
  {
35
  /**
36
  * Define our option array value.
@@ -61,6 +61,15 @@ class Theme implements SettingsSection
61
  */
62
  protected $hook_suffix;
63
 
 
 
 
 
 
 
 
 
 
64
  /**
65
  * Reference the feature by name
66
  *
@@ -84,7 +93,7 @@ class Theme implements SettingsSection
84
  {
85
  return array(
86
  'light' => _x( 'light', 'Option for content to appear with a light background and dark text', 'twitter' ),
87
- 'dark' => _x( 'dark', 'Option for content to appear with a dark background and light text', 'twitter' )
88
  );
89
  }
90
 
@@ -156,14 +165,14 @@ class Theme implements SettingsSection
156
 
157
  // theme
158
  add_action(
159
- 'add-' . $this->hook_suffix . '-section',
160
  array( &$this, 'defineSection' ),
161
  static::SECTION_PRIORITY, // top of screen
162
  0 // no parameters
163
  );
164
  // contextual help
165
  add_action(
166
- 'add-' . $this->hook_suffix . '-help-tab',
167
  array( __CLASS__, 'addHelpTab' ),
168
  static::SECTION_PRIORITY,
169
  1
@@ -292,7 +301,7 @@ class Theme implements SettingsSection
292
  echo '</p>';
293
  }
294
 
295
- /**
296
  * Choose a theme
297
  *
298
  * @since 1.0.0
@@ -314,8 +323,12 @@ class Theme implements SettingsSection
314
 
315
  echo '<fieldset id="' . esc_attr( 'twitter-' . $key ) . '">';
316
  foreach ( $choices as $value => $label ) {
317
- echo '<div><label><input type="radio" name="' . $name_attribute . '" value="' . esc_attr( $value ) . '"' . checked( $existing_value, $value, false ) . $close_void_element;
 
 
 
318
  echo ' ' . esc_html( $label );
 
319
  echo '</label></div>';
320
  }
321
  echo '</fieldset>';
@@ -336,7 +349,10 @@ class Theme implements SettingsSection
336
  if ( isset( $this->existing_options[ $key ] ) ) {
337
  echo ' value="' . esc_attr( '#' . $this->existing_options[ $key ] ) . '"';
338
  }
339
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
 
 
 
340
 
341
  echo '<p class="description">';
342
  echo esc_html( __( 'Color of link text inside a Twitter widget.', 'twitter' ) );
@@ -358,7 +374,9 @@ class Theme implements SettingsSection
358
  if ( isset( $this->existing_options[ $key ] ) ) {
359
  echo ' value="' . esc_attr( '#' . $this->existing_options[ $key ] ) . '"';
360
  }
361
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
 
 
362
 
363
  echo '<p class="description">';
364
  echo esc_html( __( 'Color of border surrounding a Twitter widget.', 'twitter' ) );
@@ -370,7 +388,7 @@ class Theme implements SettingsSection
370
  *
371
  * @since 1.0.0
372
  *
373
- * @param WP_Screen $screen current screen
374
  *
375
  * @return void
376
  */
@@ -420,13 +438,15 @@ class Theme implements SettingsSection
420
  echo '<label>';
421
  echo '<input type="checkbox" name="' . esc_attr( self::OPTION_NAME . '[' . $key . ']' ) . '" id="twitter-dnt" value="1"';
422
  checked( isset( $this->existing_options[ $key ] ) );
423
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
 
 
424
  echo esc_html( __( 'Do not use my website to tailor content and suggestions for Twitter users', 'twitter' ) );
425
  echo '</label>';
426
 
427
  echo '<p class="description">';
428
  echo esc_html( __( 'Twitter may track website visitors for advertising targeting, suggested Twitter accounts, and other purposes.', 'twitter' ) );
429
- echo ' ' . '<a href="https://dev.twitter.com/web/overview/privacy" target="_blank">' . esc_html( _x( 'Read more', 'learn more about this topic', 'twitter' ) ) . '</a>';
430
  echo '</p><p class="description">';
431
  echo esc_html( __( 'Select this option if you wish to opt-out of Twitter tracking visitors when a Twitter button or widget appears on a webpage.', 'twitter' ) );
432
  echo '</p>';
@@ -459,7 +479,9 @@ class Theme implements SettingsSection
459
 
460
  echo '<label><input type="checkbox" name="' . esc_attr( self::OPTION_NAME . '[' . $key . ']' ) . '" value="1"';
461
  checked( isset( $this->existing_options[ $key ] ) );
462
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
 
 
463
  echo esc_html( __( 'Suppress Content Security Policy warnings', 'twitter' ) );
464
  echo '</label>';
465
 
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Admin\Settings\Embeds;
27
 
28
  /**
29
  * Site-level settings for Twitter widget themes
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Theme implements \Twitter\WordPress\Admin\Settings\SettingsSection
34
  {
35
  /**
36
  * Define our option array value.
61
  */
62
  protected $hook_suffix;
63
 
64
+ /**
65
+ * Store existing options, if any exist
66
+ *
67
+ * @since 1.0.0
68
+ *
69
+ * @type array
70
+ */
71
+ protected $existing_options;
72
+
73
  /**
74
  * Reference the feature by name
75
  *
93
  {
94
  return array(
95
  'light' => _x( 'light', 'Option for content to appear with a light background and dark text', 'twitter' ),
96
+ 'dark' => _x( 'dark', 'Option for content to appear with a dark background and light text', 'twitter' ),
97
  );
98
  }
99
 
165
 
166
  // theme
167
  add_action(
168
+ 'add_' . $this->hook_suffix . '_section',
169
  array( &$this, 'defineSection' ),
170
  static::SECTION_PRIORITY, // top of screen
171
  0 // no parameters
172
  );
173
  // contextual help
174
  add_action(
175
+ 'add_' . $this->hook_suffix . '_help_tab',
176
  array( __CLASS__, 'addHelpTab' ),
177
  static::SECTION_PRIORITY,
178
  1
301
  echo '</p>';
302
  }
303
 
304
+ /**
305
  * Choose a theme
306
  *
307
  * @since 1.0.0
323
 
324
  echo '<fieldset id="' . esc_attr( 'twitter-' . $key ) . '">';
325
  foreach ( $choices as $value => $label ) {
326
+ echo '<div><label><input type="radio" name="' . $name_attribute . '" value="' . esc_attr( $value ) . '"' . checked( $existing_value, $value, false );
327
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
328
+ echo $close_void_element;
329
+
330
  echo ' ' . esc_html( $label );
331
+
332
  echo '</label></div>';
333
  }
334
  echo '</fieldset>';
349
  if ( isset( $this->existing_options[ $key ] ) ) {
350
  echo ' value="' . esc_attr( '#' . $this->existing_options[ $key ] ) . '"';
351
  }
352
+
353
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
354
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
355
+ echo '>';
356
 
357
  echo '<p class="description">';
358
  echo esc_html( __( 'Color of link text inside a Twitter widget.', 'twitter' ) );
374
  if ( isset( $this->existing_options[ $key ] ) ) {
375
  echo ' value="' . esc_attr( '#' . $this->existing_options[ $key ] ) . '"';
376
  }
377
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
378
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
379
+ echo '>';
380
 
381
  echo '<p class="description">';
382
  echo esc_html( __( 'Color of border surrounding a Twitter widget.', 'twitter' ) );
388
  *
389
  * @since 1.0.0
390
  *
391
+ * @param \WP_Screen $screen current screen
392
  *
393
  * @return void
394
  */
438
  echo '<label>';
439
  echo '<input type="checkbox" name="' . esc_attr( self::OPTION_NAME . '[' . $key . ']' ) . '" id="twitter-dnt" value="1"';
440
  checked( isset( $this->existing_options[ $key ] ) );
441
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
442
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
443
+ echo '>';
444
  echo esc_html( __( 'Do not use my website to tailor content and suggestions for Twitter users', 'twitter' ) );
445
  echo '</label>';
446
 
447
  echo '<p class="description">';
448
  echo esc_html( __( 'Twitter may track website visitors for advertising targeting, suggested Twitter accounts, and other purposes.', 'twitter' ) );
449
+ echo ' <a href="https://dev.twitter.com/web/overview/privacy" target="_blank">' . esc_html( _x( 'Read more', 'learn more about this topic', 'twitter' ) ) . '</a>';
450
  echo '</p><p class="description">';
451
  echo esc_html( __( 'Select this option if you wish to opt-out of Twitter tracking visitors when a Twitter button or widget appears on a webpage.', 'twitter' ) );
452
  echo '</p>';
479
 
480
  echo '<label><input type="checkbox" name="' . esc_attr( self::OPTION_NAME . '[' . $key . ']' ) . '" value="1"';
481
  checked( isset( $this->existing_options[ $key ] ) );
482
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
483
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
484
+ echo '>';
485
  echo esc_html( __( 'Suppress Content Security Policy warnings', 'twitter' ) );
486
  echo '</label>';
487
 
src/Twitter/WordPress/Admin/Settings/Loader.php CHANGED
@@ -57,7 +57,7 @@ class Loader
57
  */
58
  public static function pluginActionLinks( $links, $file )
59
  {
60
- if ( $file === plugin_basename( \Twitter\WordPress\PluginLoader::getPluginMainFile() ) ) {
61
  array_unshift( $links, '<a href="' . esc_url( admin_url( 'admin.php' ) . '?' . http_build_query( array( 'page' => \Twitter\WordPress\Admin\Settings\SinglePage::PAGE_SLUG ) ) ) . '">' . __( 'Settings' ) . '</a>' );
62
  }
63
 
57
  */
58
  public static function pluginActionLinks( $links, $file )
59
  {
60
+ if ( plugin_basename( \Twitter\WordPress\PluginLoader::getPluginMainFile() === $file ) ) {
61
  array_unshift( $links, '<a href="' . esc_url( admin_url( 'admin.php' ) . '?' . http_build_query( array( 'page' => \Twitter\WordPress\Admin\Settings\SinglePage::PAGE_SLUG ) ) ) . '">' . __( 'Settings' ) . '</a>' );
62
  }
63
 
src/Twitter/WordPress/Admin/Settings/SinglePage.php CHANGED
@@ -61,7 +61,11 @@ class SinglePage
61
  * @type string fully qualified class name
62
  * }
63
  */
64
- protected static $SETTINGS_COMPONENTS = array( '\Twitter\WordPress\Admin\Settings\Theme', '\Twitter\WordPress\Admin\Settings\SiteAttribution', '\Twitter\WordPress\Admin\Settings\TweetButton' );
 
 
 
 
65
 
66
  /**
67
  * Reference the feature by name
@@ -86,16 +90,17 @@ class SinglePage
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;
@@ -123,7 +128,7 @@ class SinglePage
123
 
124
  // hook_suffix may be false if current viewer does not have the manage_options capability
125
  if ( ! $hook_suffix ) {
126
- return;
127
  }
128
  $settings->hook_suffix = $hook_suffix;
129
 
@@ -157,7 +162,7 @@ class SinglePage
157
  return;
158
  }
159
 
160
- do_action( 'add-' . $this->hook_suffix . '-section' );
161
 
162
  echo '<div class="wrap">';
163
 
@@ -190,6 +195,6 @@ class SinglePage
190
  return;
191
  }
192
 
193
- do_action( 'add-' . $this->hook_suffix . '-help-tab', $screen );
194
  }
195
  }
61
  * @type string fully qualified class name
62
  * }
63
  */
64
+ protected static $SETTINGS_COMPONENTS = array(
65
+ '\Twitter\WordPress\Admin\Settings\Embeds\Theme',
66
+ '\Twitter\WordPress\Admin\Settings\Cards\SiteAttribution',
67
+ '\Twitter\WordPress\Admin\Settings\Buttons\Tweet',
68
+ );
69
 
70
  /**
71
  * Reference the feature by name
90
  * @type string fully qualified class name
91
  * }
92
  */
93
+ public static function getSettingsComponentsForEnabledFeatures()
94
+ {
95
  $components = static::$SETTINGS_COMPONENTS;
96
+ $features = \Twitter\WordPress\Features::getEnabledFeatures();
97
 
98
  if ( ! isset( $features[ \Twitter\WordPress\Features::EMBED_TWEET ] ) ) {
99
+ unset( $components['\Twitter\WordPress\Admin\Settings\Embeds\Theme'] );
100
  }
101
 
102
  if ( ! isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] ) ) {
103
+ unset( $components['\Twitter\WordPress\Admin\Settings\Buttons\Tweet'] );
104
  }
105
 
106
  return $components;
128
 
129
  // hook_suffix may be false if current viewer does not have the manage_options capability
130
  if ( ! $hook_suffix ) {
131
+ return null;
132
  }
133
  $settings->hook_suffix = $hook_suffix;
134
 
162
  return;
163
  }
164
 
165
+ do_action( 'add_' . $this->hook_suffix . '_section' );
166
 
167
  echo '<div class="wrap">';
168
 
195
  return;
196
  }
197
 
198
+ do_action( 'add_' . $this->hook_suffix . '_help_tab', $screen );
199
  }
200
  }
src/Twitter/WordPress/Cards/Compatibility.php CHANGED
@@ -26,7 +26,7 @@ THE SOFTWARE.
26
  namespace Twitter\WordPress\Cards;
27
 
28
  /**
29
- * Compatability wrappers for popular WordPress plugins with existing Twitter Cards functionality
30
  *
31
  * @since 1.0.0
32
  */
26
  namespace Twitter\WordPress\Cards;
27
 
28
  /**
29
+ * Compatibility wrappers for popular WordPress plugins with existing Twitter Cards functionality
30
  *
31
  * @since 1.0.0
32
  */
src/Twitter/WordPress/Cards/Generator.php CHANGED
@@ -32,6 +32,7 @@ namespace Twitter\WordPress\Cards;
32
  */
33
  class Generator
34
  {
 
35
  /**
36
  * Card types supported by the Twitter plugin for WordPress
37
  *
@@ -77,6 +78,8 @@ class Generator
77
  if ( static::isSupportedCardType( $card_type ) ) {
78
  return (new static::$SUPPORTED_CARDS[ $card_type ]);
79
  }
 
 
80
  }
81
 
82
  /**
@@ -94,7 +97,7 @@ class Generator
94
  public static function getCardObject( $query_type = null, $object_id = null, $card_type = 'summary' )
95
  {
96
  if ( ! static::isSupportedCardType( $card_type ) ) {
97
- return;
98
  }
99
  if ( ! ( is_string( $query_type ) && $query_type ) ) {
100
  $query_type = null;
@@ -115,10 +118,10 @@ class Generator
115
 
116
  $card = static::getCardForType( $card_type );
117
  if ( ! ( $card && is_a( $card, '\Twitter\Cards\Card' ) ) ) {
118
- return;
119
  }
120
 
121
- return static::addSiteAttribution( $card, ( $query_type === 'post' ? $object_id : null ) );
122
  }
123
 
124
  /**
@@ -129,12 +132,12 @@ class Generator
129
  * @param \Twitter\Cards\Card $card Twitter Card object
130
  * @param int|string $post_id WP_Post->ID or proprietary post identifier
131
  *
132
- * @return \Twitter\Cards\Card Twitter Card object
133
  */
134
  public static function addSiteAttribution( $card, $post_id = null )
135
  {
136
  if ( ! is_a( $card, '\Twitter\Cards\Card' ) ) {
137
- return;
138
  }
139
  if ( ! method_exists( $card, 'setSite' ) ) {
140
  return $card;
@@ -166,6 +169,8 @@ class Generator
166
  } else if ( is_archive() ) {
167
  return static::buildArchiveCard();
168
  }
 
 
169
  }
170
 
171
  /**
@@ -180,7 +185,7 @@ class Generator
180
  $query_type = 'home';
181
  $card = static::getCardObject( $query_type );
182
  if ( ! $card ) {
183
- return;
184
  }
185
 
186
  /**
@@ -237,13 +242,13 @@ class Generator
237
  {
238
  $author = get_queried_object();
239
  if ( ! ( $author && isset( $author->ID ) ) ) {
240
- return;
241
  }
242
 
243
  $query_type = 'author';
244
  $card = static::getCardObject( $query_type, $author->ID );
245
  if ( ! $card ) {
246
- return;
247
  }
248
 
249
  /** This filter is documented in ::buildHomepageCard */
@@ -284,7 +289,7 @@ class Generator
284
  $query_type = 'archive';
285
  $card = static::getCardObject( $query_type );
286
  if ( ! $card ) {
287
- return;
288
  }
289
 
290
  // WP 4.1+ functions
@@ -320,41 +325,41 @@ class Generator
320
  $post = get_post();
321
 
322
  if ( ! $post || ! isset( $post->ID ) ) {
323
- return;
324
  }
325
  setup_postdata( $post );
326
 
327
  // do not publish card markup for password-protected posts
328
  if ( ! empty( $post->post_password ) ) {
329
- return;
330
  }
331
 
332
  // only publish card markup for public posts
333
  $post_status_object = get_post_status_object( get_post_status( $post->ID ) );
334
  if ( ! ( $post_status_object && isset( $post_status_object->public ) && $post_status_object->public ) ) {
335
- return;
336
  }
337
 
338
  // only output Twitter Card markup for public post types
339
  // don't waste page generation time if the page is not meant to be consumed by TwitterBot
340
  $post_type = get_post_type( $post );
341
  if ( ! $post_type ) {
342
- return;
343
  }
344
  $post_type_object = get_post_type_object( $post_type );
345
  if ( ! ( $post_type_object && isset( $post_type_object->public ) && $post_status_object->public ) ) {
346
- return;
347
  }
348
 
349
  $query_type = 'post';
350
  $card = static::getCardObject( $query_type, $post->ID, 'summary' );
351
  if ( ! $card ) {
352
- return;
353
  }
354
 
355
  $card_class = get_class( $card );
356
  if ( ! $card_class ) {
357
- return;
358
  }
359
 
360
  // get post-specific overrides
@@ -406,7 +411,6 @@ class Generator
406
  if ( defined( $card_class . '::MIN_IMAGE_WIDTH' ) && defined( $card_class . '::MIN_IMAGE_HEIGHT' ) ) {
407
  if ( method_exists( $card, 'setImage' ) ) {
408
  // single image card type
409
-
410
  $cards_image_handler = new \Twitter\WordPress\Cards\ImageHandler();
411
  $cards_image_handler->setLimit( 1 );
412
  $cards_image_handler->setMinWidth( $card::MIN_IMAGE_WIDTH );
32
  */
33
  class Generator
34
  {
35
+
36
  /**
37
  * Card types supported by the Twitter plugin for WordPress
38
  *
78
  if ( static::isSupportedCardType( $card_type ) ) {
79
  return (new static::$SUPPORTED_CARDS[ $card_type ]);
80
  }
81
+
82
+ return null;
83
  }
84
 
85
  /**
97
  public static function getCardObject( $query_type = null, $object_id = null, $card_type = 'summary' )
98
  {
99
  if ( ! static::isSupportedCardType( $card_type ) ) {
100
+ return null;
101
  }
102
  if ( ! ( is_string( $query_type ) && $query_type ) ) {
103
  $query_type = null;
118
 
119
  $card = static::getCardForType( $card_type );
120
  if ( ! ( $card && is_a( $card, '\Twitter\Cards\Card' ) ) ) {
121
+ return null;
122
  }
123
 
124
+ return static::addSiteAttribution( $card, ( ('post' === $query_type) ? $object_id : null ) );
125
  }
126
 
127
  /**
132
  * @param \Twitter\Cards\Card $card Twitter Card object
133
  * @param int|string $post_id WP_Post->ID or proprietary post identifier
134
  *
135
+ * @return \Twitter\Cards\Card|null Twitter Card object or null if passed card object is invalid
136
  */
137
  public static function addSiteAttribution( $card, $post_id = null )
138
  {
139
  if ( ! is_a( $card, '\Twitter\Cards\Card' ) ) {
140
+ return null;
141
  }
142
  if ( ! method_exists( $card, 'setSite' ) ) {
143
  return $card;
169
  } else if ( is_archive() ) {
170
  return static::buildArchiveCard();
171
  }
172
+
173
+ return null;
174
  }
175
 
176
  /**
185
  $query_type = 'home';
186
  $card = static::getCardObject( $query_type );
187
  if ( ! $card ) {
188
+ return null;
189
  }
190
 
191
  /**
242
  {
243
  $author = get_queried_object();
244
  if ( ! ( $author && isset( $author->ID ) ) ) {
245
+ return null;
246
  }
247
 
248
  $query_type = 'author';
249
  $card = static::getCardObject( $query_type, $author->ID );
250
  if ( ! $card ) {
251
+ return null;
252
  }
253
 
254
  /** This filter is documented in ::buildHomepageCard */
289
  $query_type = 'archive';
290
  $card = static::getCardObject( $query_type );
291
  if ( ! $card ) {
292
+ return null;
293
  }
294
 
295
  // WP 4.1+ functions
325
  $post = get_post();
326
 
327
  if ( ! $post || ! isset( $post->ID ) ) {
328
+ return null;
329
  }
330
  setup_postdata( $post );
331
 
332
  // do not publish card markup for password-protected posts
333
  if ( ! empty( $post->post_password ) ) {
334
+ return null;
335
  }
336
 
337
  // only publish card markup for public posts
338
  $post_status_object = get_post_status_object( get_post_status( $post->ID ) );
339
  if ( ! ( $post_status_object && isset( $post_status_object->public ) && $post_status_object->public ) ) {
340
+ return null;
341
  }
342
 
343
  // only output Twitter Card markup for public post types
344
  // don't waste page generation time if the page is not meant to be consumed by TwitterBot
345
  $post_type = get_post_type( $post );
346
  if ( ! $post_type ) {
347
+ return null;
348
  }
349
  $post_type_object = get_post_type_object( $post_type );
350
  if ( ! ( $post_type_object && isset( $post_type_object->public ) && $post_status_object->public ) ) {
351
+ return null;
352
  }
353
 
354
  $query_type = 'post';
355
  $card = static::getCardObject( $query_type, $post->ID, 'summary' );
356
  if ( ! $card ) {
357
+ return null;
358
  }
359
 
360
  $card_class = get_class( $card );
361
  if ( ! $card_class ) {
362
+ return null;
363
  }
364
 
365
  // get post-specific overrides
411
  if ( defined( $card_class . '::MIN_IMAGE_WIDTH' ) && defined( $card_class . '::MIN_IMAGE_HEIGHT' ) ) {
412
  if ( method_exists( $card, 'setImage' ) ) {
413
  // single image card type
 
414
  $cards_image_handler = new \Twitter\WordPress\Cards\ImageHandler();
415
  $cards_image_handler->setLimit( 1 );
416
  $cards_image_handler->setMinWidth( $card::MIN_IMAGE_WIDTH );
src/Twitter/WordPress/Cards/ImageHandler.php CHANGED
@@ -215,7 +215,7 @@ class ImageHandler
215
  *
216
  * @since 1.0.0
217
  *
218
- * @param WP_Post $post post of interest
219
  *
220
  * @return void
221
  */
@@ -313,9 +313,9 @@ class ImageHandler
313
  $intermediate_size = apply_filters( 'twitter_card_intermediate_image_size', 'large', $attachment_id );
314
  // check filtered intermediate size to avoid possible infinite loop
315
  if ( ! $intermediate_size || 'full' === $intermediate_size || ! has_image_size( $intermediate_size ) ) {
316
- return;
317
  }
318
- return $handler->attachmentToTwitterImage( $attachment_id, $intermediate_size );
319
  }
320
  unset( $bytes );
321
  }
@@ -325,16 +325,22 @@ class ImageHandler
325
  list( $url, $width, $height ) = wp_get_attachment_image_src( $attachment_id, $size );
326
 
327
  if ( empty( $url ) ) {
328
- return;
329
  }
330
  $image = new \Twitter\Cards\Components\Image( $url );
331
 
 
 
 
 
 
 
332
  if ( ! empty( $width ) ) {
333
  $width = absint( $width );
334
  if ( $width ) {
335
  if ( $width < $this->min_width ) {
336
  // reject if image width below required width
337
- return;
338
  }
339
  $image->setWidth( $width );
340
  }
@@ -344,7 +350,7 @@ class ImageHandler
344
  if ( $height ) {
345
  if ( $height < $this->min_height ) {
346
  // reject if image height below required height
347
- return;
348
  }
349
  $image->setHeight( $height );
350
  }
215
  *
216
  * @since 1.0.0
217
  *
218
+ * @param \WP_Post $post post of interest
219
  *
220
  * @return void
221
  */
313
  $intermediate_size = apply_filters( 'twitter_card_intermediate_image_size', 'large', $attachment_id );
314
  // check filtered intermediate size to avoid possible infinite loop
315
  if ( ! $intermediate_size || 'full' === $intermediate_size || ! has_image_size( $intermediate_size ) ) {
316
+ return null;
317
  }
318
+ return $this->attachmentToTwitterImage( $attachment_id, $intermediate_size );
319
  }
320
  unset( $bytes );
321
  }
325
  list( $url, $width, $height ) = wp_get_attachment_image_src( $attachment_id, $size );
326
 
327
  if ( empty( $url ) ) {
328
+ return null;
329
  }
330
  $image = new \Twitter\Cards\Components\Image( $url );
331
 
332
+ $alt = trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) );
333
+ if ( $alt ) {
334
+ $image->setAlternativeText( $alt );
335
+ }
336
+ unset( $alt );
337
+
338
  if ( ! empty( $width ) ) {
339
  $width = absint( $width );
340
  if ( $width ) {
341
  if ( $width < $this->min_width ) {
342
  // reject if image width below required width
343
+ return null;
344
  }
345
  $image->setWidth( $width );
346
  }
350
  if ( $height ) {
351
  if ( $height < $this->min_height ) {
352
  // reject if image height below required height
353
+ return null;
354
  }
355
  $image->setHeight( $height );
356
  }
src/Twitter/WordPress/Content/{TweetButton.php → Buttons/Tweet.php} RENAMED
@@ -23,14 +23,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
  THE SOFTWARE.
24
  */
25
 
26
- namespace Twitter\WordPress\Content;
27
 
28
  /**
29
  * Add a Tweet button to post content
30
  *
31
  * @since 1.0.0
32
  */
33
- class TweetButton
34
  {
35
  /**
36
  * Get the stored site option for Tweet button content
@@ -44,7 +44,7 @@ class TweetButton
44
  */
45
  protected static function getOption()
46
  {
47
- return get_option( \Twitter\WordPress\Admin\Settings\TweetButton::OPTION_NAME, array() );
48
  }
49
 
50
  /**
@@ -69,7 +69,7 @@ class TweetButton
69
  $position = $options['position'];
70
  unset( $options['position'] );
71
 
72
- $tweet_button = \Twitter\WordPress\Shortcodes\Share::shortcodeHandler( $options );
73
  if ( $tweet_button ) {
74
  // wrap in newlines to preserve content scanners looking for adjacent content on its own line
75
  $tweet_button = "\n" . $tweet_button . "\n";
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Content\Buttons;
27
 
28
  /**
29
  * Add a Tweet button to post content
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Tweet
34
  {
35
  /**
36
  * Get the stored site option for Tweet button content
44
  */
45
  protected static function getOption()
46
  {
47
+ return get_option( \Twitter\WordPress\Admin\Settings\Buttons\Tweet::OPTION_NAME, array() );
48
  }
49
 
50
  /**
69
  $position = $options['position'];
70
  unset( $options['position'] );
71
 
72
+ $tweet_button = \Twitter\WordPress\Shortcodes\Buttons\Share::shortcodeHandler( $options );
73
  if ( $tweet_button ) {
74
  // wrap in newlines to preserve content scanners looking for adjacent content on its own line
75
  $tweet_button = "\n" . $tweet_button . "\n";
src/Twitter/WordPress/Features.php CHANGED
@@ -79,24 +79,70 @@ class Features
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
@@ -139,8 +185,8 @@ class Features
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
  */
@@ -156,16 +202,20 @@ class Features
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
  /**
79
  const EMBED_VINE = 'embed-vine';
80
 
81
  /**
82
+ * Embed the latest Tweets from a Twitter profile by URL or shortcode
83
  *
84
+ * @since 2.0.0
85
  *
86
+ * @link https://dev.twitter.com/web/embedded-timelines/user Embedded Profile Timeline
87
  *
88
  * @type string
89
  */
90
+ const EMBED_PROFILE = 'embed-profile';
91
+
92
+ /**
93
+ * Embed the latest Tweets from a list of Twitter accounts by URL or shortcode
94
+ *
95
+ * @since 2.0.0
96
+ *
97
+ * @link https://dev.twitter.com/web/embedded-timelines/list Embedded List Timeline
98
+ *
99
+ * @type string
100
+ */
101
+ const EMBED_LIST = 'embed-list';
102
+
103
+ /**
104
+ * Embed recent Tweets matching a search query configured at twitter.com/settings/widgets
105
+ *
106
+ * @since 2.0.0
107
+ *
108
+ * @link https://dev.twitter.com/web/embedded-timelines/search Embedded Search Timeline
109
+ *
110
+ * @type string
111
+ */
112
+ const EMBED_SEARCH = 'embed-search';
113
 
114
  /**
115
+ * Embed multiple Tweets in a vertical format by URL or shortcode
116
+ *
117
+ * @since 2.0.0
118
+ *
119
+ * @link https://dev.twitter.com/web/embedded-timelines/collection Embedded Collection
120
+ *
121
+ * @type string
122
+ */
123
+ const EMBED_COLLECTION = 'embed-collection';
124
+
125
+ /**
126
+ * Embed multiple Tweets in a vertical format by URL or shortcode
127
+ *
128
+ * @since 2.0.0
129
+ *
130
+ * @link https://dev.twitter.com/web/embedded-timelines/collection Embedded Collection
131
+ *
132
+ * @type string
133
+ */
134
+ const EMBED_COLLECTION_GRID = 'embed-collection-grid';
135
+
136
+ /**
137
+ * Embed a Twitter Moment by URL or shortcode
138
  *
139
  * @since 1.3.0
140
  *
141
+ * @link https://dev.twitter.com/web/embedded-moments Embedded Moment
142
+ *
143
  * @type string
144
  */
145
+ const EMBED_MOMENT = 'embed-moment';
146
 
147
  /**
148
  * Display a Twitter follow button using a shortcode
185
  *
186
  * @since 1.3.0
187
  *
188
+ * @link https://business.twitter.com/en/help/campaign-setup/campaign-targeting/tailored-audiences-from-web.html Twitter website tag for remarketing
189
+ * @link https://business.twitter.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites.html Twitter conversion tracking for websites
190
  *
191
  * @type string
192
  */
202
  * @type array
203
  */
204
  public static $features = array(
205
+ self::CARDS => true, // Twitter Cards
206
+ self::EMBED_TWEET => true, // single Tweet
207
+ self::EMBED_TWEET_VIDEO => true, // single Tweet with video-specific display template
208
+ self::EMBED_VINE => true, // single Vine
209
+ self::EMBED_PROFILE => true, // Twitter profile
210
+ self::EMBED_LIST => true, // Twitter List
211
+ self::EMBED_SEARCH => true, // Twitter search
212
+ self::EMBED_COLLECTION => true, // multiple Tweets organized into a collection
213
+ self::EMBED_COLLECTION_GRID => true, // multiple Tweets organized into a collection displayed in a grid format
214
+ self::EMBED_MOMENT => true, // Twitter Moment
215
+ self::FOLLOW_BUTTON => true, // Twitter Follow button
216
+ self::TWEET_BUTTON => true, // Tweet button
217
+ self::PERISCOPE_ON_AIR => true, // Periscope On Air button
218
+ self::TRACKING_PIXEL => true, // audience and conversion pixel
219
  );
220
 
221
  /**
src/Twitter/WordPress/Head/AuthorshipLink.php CHANGED
@@ -45,7 +45,12 @@ class AuthorshipLink
45
  {
46
  $site_username = \Twitter\WordPress\Site\Username::getViaAttribution( ( in_the_loop() ? get_the_ID() : null ) );
47
  if ( $site_username ) {
48
- echo '<link rel="me" href="' . esc_url( \Twitter\Helpers\TwitterURL::profile( $site_username ), array( 'https', 'http' ) ) . '"' . \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
 
 
 
 
 
49
  }
50
  }
51
  }
45
  {
46
  $site_username = \Twitter\WordPress\Site\Username::getViaAttribution( ( in_the_loop() ? get_the_ID() : null ) );
47
  if ( $site_username ) {
48
+ echo '<link rel="me" href="' . esc_url( \Twitter\Helpers\TwitterURL::profile( $site_username ), array( 'https', 'http' ) ) . '"';
49
+
50
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
51
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
52
+
53
+ echo '>';
54
  }
55
  }
56
  }
src/Twitter/WordPress/Head/CardsMetaElements.php CHANGED
@@ -70,7 +70,10 @@ class CardsMetaElements
70
  foreach ( $card_properties as $name => $content ) {
71
  if ( is_array( $content ) && $name ) {
72
  foreach ( $content as $structured_name => $structured_value ) {
73
- $html .= \Twitter\WordPress\Head\MetaElement::fromNameContentPair( ( $structured_name === 'src' ) ? $name : $name . ':' . $structured_name, $structured_value );
 
 
 
74
  }
75
  } else {
76
  $html .= \Twitter\WordPress\Head\MetaElement::fromNameContentPair( $name, $content );
@@ -88,6 +91,12 @@ class CardsMetaElements
88
  */
89
  public static function outputMetaElements()
90
  {
91
- echo "\n" . static::buildMetaElements() . "\n";
 
 
 
 
 
 
92
  }
93
  }
70
  foreach ( $card_properties as $name => $content ) {
71
  if ( is_array( $content ) && $name ) {
72
  foreach ( $content as $structured_name => $structured_value ) {
73
+ $html .= \Twitter\WordPress\Head\MetaElement::fromNameContentPair(
74
+ ( 'src' === $structured_name ) ? $name : $name . ':' . $structured_name,
75
+ $structured_value
76
+ );
77
  }
78
  } else {
79
  $html .= \Twitter\WordPress\Head\MetaElement::fromNameContentPair( $name, $content );
91
  */
92
  public static function outputMetaElements()
93
  {
94
+ echo "\n";
95
+
96
+ // Escaped when building individual elements
97
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
98
+ echo static::buildMetaElements();
99
+
100
+ echo "\n";
101
  }
102
  }
src/Twitter/WordPress/Head/WidgetsMetaElements.php CHANGED
@@ -41,7 +41,7 @@ class WidgetsMetaElements
41
  */
42
  public static function getWidgetOptions()
43
  {
44
- return get_option( \Twitter\WordPress\Admin\Settings\Theme::OPTION_NAME );
45
  }
46
 
47
  /**
@@ -157,8 +157,7 @@ class WidgetsMetaElements
157
  public static function outputMetaElements()
158
  {
159
  // escaped in markup builder
160
- // @codingStandardsIgnoreStart WordPress.XSS.EscapeOutput
161
  echo static::buildMetaElements();
162
- // @codingStandardsIgnoreEnd WordPress.XSS.EscapeOutput
163
  }
164
  }
41
  */
42
  public static function getWidgetOptions()
43
  {
44
+ return get_option( \Twitter\WordPress\Admin\Settings\Embeds\Theme::OPTION_NAME );
45
  }
46
 
47
  /**
157
  public static function outputMetaElements()
158
  {
159
  // escaped in markup builder
160
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
161
  echo static::buildMetaElements();
 
162
  }
163
  }
src/Twitter/WordPress/Helpers/HTMLBuilder.php CHANGED
@@ -41,7 +41,7 @@ class HTMLBuilder extends \Twitter\Helpers\HTMLBuilder
41
  *
42
  * @param string $class possible HTML class
43
  *
44
- * @return class name stripped of invalid values or empty string
45
  */
46
  public static function escapeClassName( $class )
47
  {
41
  *
42
  * @param string $class possible HTML class
43
  *
44
+ * @return string class name stripped of invalid values or empty string
45
  */
46
  public static function escapeClassName( $class )
47
  {
src/Twitter/WordPress/Helpers/TwitterAPI.php CHANGED
@@ -104,9 +104,26 @@ class TwitterAPI
104
  */
105
  public static function getUserAgent()
106
  {
107
- global $wp_version;
 
 
 
 
 
 
 
108
 
109
- return apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; TfWP/' . \Twitter\WordPress\PluginLoader::VERSION . '; ' . get_bloginfo( 'url' ) );
 
 
 
 
 
 
 
 
 
 
110
  }
111
 
112
  /**
@@ -117,17 +134,17 @@ class TwitterAPI
117
  * @param string $relative_path API path without the response type. e.g. statuses/show
118
  * @param array $parameters query parameters
119
  *
120
- * @return stdClass|null json decoded result or null if no JSON returned or issues with parameters
121
  */
122
  public static function getJSON( $relative_path, $parameters = null )
123
  {
124
  if ( ! $relative_path ) {
125
- return;
126
  }
127
 
128
  $request_url = static::getAPIURL( $relative_path, $parameters );
129
  if ( ! $request_url ) {
130
- return;
131
  }
132
 
133
  $response = wp_safe_remote_get(
@@ -135,15 +152,15 @@ class TwitterAPI
135
  array(
136
  'redirection' => 0,
137
  'httpversion' => '1.1',
138
- 'user-agent' => static::getUserAgent()
139
  )
140
  );
141
  if ( is_wp_error( $response ) ) {
142
- return;
143
  }
144
  $response_body = wp_remote_retrieve_body( $response );
145
  if ( ! $response_body ) {
146
- return;
147
  }
148
 
149
  $json_response = json_decode( $response_body );
@@ -152,5 +169,7 @@ class TwitterAPI
152
  if ( $json_response ) {
153
  return $json_response;
154
  }
 
 
155
  }
156
  }
104
  */
105
  public static function getUserAgent()
106
  {
107
+ $wp_version = '';
108
+ if ( isset( $GLOBALS['wp_version'] ) ) {
109
+ $wp_version = trim( $GLOBALS['wp_version'] );
110
+ }
111
+ $wordpress = 'WordPress';
112
+ if ( $wp_version ) {
113
+ $wordpress .= '/' . $wp_version;
114
+ }
115
 
116
+ return apply_filters(
117
+ 'http_headers_useragent',
118
+ implode(
119
+ '; ',
120
+ array(
121
+ $wordpress,
122
+ 'TfWP/' . \Twitter\WordPress\PluginLoader::VERSION,
123
+ get_bloginfo( 'url' ),
124
+ )
125
+ )
126
+ );
127
  }
128
 
129
  /**
134
  * @param string $relative_path API path without the response type. e.g. statuses/show
135
  * @param array $parameters query parameters
136
  *
137
+ * @return \stdClass|null json decoded result or null if no JSON returned or issues with parameters
138
  */
139
  public static function getJSON( $relative_path, $parameters = null )
140
  {
141
  if ( ! $relative_path ) {
142
+ return null;
143
  }
144
 
145
  $request_url = static::getAPIURL( $relative_path, $parameters );
146
  if ( ! $request_url ) {
147
+ return null;
148
  }
149
 
150
  $response = wp_safe_remote_get(
152
  array(
153
  'redirection' => 0,
154
  'httpversion' => '1.1',
155
+ 'user-agent' => static::getUserAgent(),
156
  )
157
  );
158
  if ( is_wp_error( $response ) ) {
159
+ return null;
160
  }
161
  $response_body = wp_remote_retrieve_body( $response );
162
  if ( ! $response_body ) {
163
+ return null;
164
  }
165
 
166
  $json_response = json_decode( $response_body );
169
  if ( $json_response ) {
170
  return $json_response;
171
  }
172
+
173
+ return null;
174
  }
175
  }
src/Twitter/WordPress/JavaScriptLoaders/AsyncJavaScript.php CHANGED
@@ -44,11 +44,42 @@ abstract class AsyncJavaScript
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
  /**
@@ -62,10 +93,12 @@ abstract class AsyncJavaScript
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
 
@@ -83,7 +116,7 @@ abstract class AsyncJavaScript
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;
@@ -171,7 +204,7 @@ abstract class AsyncJavaScript
171
  return '';
172
  }
173
 
174
- if ( ! ( is_string( $handle ) && $handle === static::QUEUE_HANDLE ) ) {
175
  return $tag;
176
  }
177
 
@@ -187,15 +220,17 @@ abstract class AsyncJavaScript
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
 
@@ -204,6 +239,8 @@ abstract class AsyncJavaScript
204
  if ( isset( $wp_scripts ) && $wp_scripts->do_concat ) {
205
  $wp_scripts->print_html .= $html;
206
  } else {
 
 
207
  echo $html;
208
  }
209
 
@@ -229,7 +266,7 @@ abstract class AsyncJavaScript
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>';
44
  */
45
  public static function dnsPrefetch()
46
  {
47
+ $classname = get_called_class();
48
+ if ( ! ( defined( $classname . '::FQDN' ) && $classname::FQDN ) ) {
49
  return;
50
  }
51
 
52
+ // Output a resource hint link element at a priority later than wp_resource_hints
53
+ add_action( 'wp_head', array( $classname, 'printDNSPrefetchElement' ) );
54
+
55
+ // Use wp_resource_hints if available
56
+ add_filter( 'wp_resource_hints',
57
+ (static function ( $urls, $relation_type ) use ( $classname ) {
58
+ if ( 'dns-prefetch' === $relation_type ) {
59
+ $urls[] = $classname::FQDN;
60
+
61
+ // WordPress will output the resource hint link
62
+ remove_action( 'wp_head', array( $classname, 'printDNSPrefetchElement' ) );
63
+ }
64
+ return $urls;
65
+ }),
66
+ 10,
67
+ 2 );
68
+ }
69
+
70
+ /**
71
+ * Proactively resolve FQDN DNS using a resource hint link
72
+ *
73
+ * @since 1.5.0
74
+ *
75
+ * @return void
76
+ */
77
+ public static function printDNSPrefetchElement()
78
+ {
79
+ echo '<link rel="dns-prefetch" href="//' . esc_attr( static::FQDN ) . '"';
80
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
81
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
82
+ echo '>' . "\n";
83
  }
84
 
85
  /**
93
  */
94
  public static function register()
95
  {
96
+ // match global use in Core
97
+ // @codingStandardsIgnoreLine Squiz.PHP.GlobalKeyword.NotAllowed
98
  global $wp_scripts;
99
 
100
  $classname = get_called_class();
101
+ if ( ! ( defined( $classname . '::QUEUE_HANDLE' ) && $classname::QUEUE_HANDLE && defined( $classname . '::URI' ) && $classname::URI ) ) {
102
  return false;
103
  }
104
 
116
  }
117
 
118
  // replace standard script element with async script element
119
+ add_filter( 'script_loader_src', array( $classname, 'asyncScriptLoaderSrc' ), 1, 2 );
120
 
121
  if ( defined( $classname . '::SCRIPT_EXTRA' ) && is_string( static::SCRIPT_EXTRA ) && static::SCRIPT_EXTRA ) {
122
  $script = static::SCRIPT_EXTRA;
204
  return '';
205
  }
206
 
207
+ if ( ! ( is_string( $handle ) && static::QUEUE_HANDLE === $handle ) ) {
208
  return $tag;
209
  }
210
 
220
  *
221
  * @param string $src script URL
222
  * @param string $handle WordPress registered script handle
223
+ * @global \WP_Scripts $wp_scripts match concatenation preferences
224
  *
225
  * @return string empty string if our queue handle requested, else give back the src variable
226
  */
227
  public static function asyncScriptLoaderSrc( $src, $handle )
228
  {
229
+ // match global use in Core
230
+ // @codingStandardsIgnoreLine Squiz.PHP.GlobalKeyword.NotAllowed
231
  global $wp_scripts;
232
 
233
+ if ( ! ( is_string( $handle ) && defined( get_called_class() . '::QUEUE_HANDLE' ) && static::QUEUE_HANDLE === $handle ) ) {
234
  return $src;
235
  }
236
 
239
  if ( isset( $wp_scripts ) && $wp_scripts->do_concat ) {
240
  $wp_scripts->print_html .= $html;
241
  } else {
242
+ // escaped when building element
243
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
244
  echo $html;
245
  }
246
 
266
  return '';
267
  }
268
 
269
+ $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=' . wp_json_encode( static::URI ) . ';fjs.parentNode.insertBefore(js,fjs);}(document,"script",' . wp_json_encode( static::QUEUE_HANDLE ) . '));';
270
 
271
  if ( $include_script_element_wrapper ) {
272
  return '<script>' . $script . '</script>';
src/Twitter/WordPress/JavaScriptLoaders/Tracking.php CHANGED
@@ -30,7 +30,7 @@ namespace Twitter\WordPress\JavaScriptLoaders;
30
  *
31
  * @since 1.0.0
32
  */
33
- class Tracking
34
  {
35
  /**
36
  * Twitter tracking JavaScript handle
@@ -44,60 +44,59 @@ class Tracking
44
  const QUEUE_HANDLE = 'twitter-tracking';
45
 
46
  /**
47
- * Twitter advertising JavaScript absolute URI
48
  *
49
- * @since 1.2.0
 
 
50
  *
51
  * @type string
52
  */
53
- const URI = 'https://platform.twitter.com/oct.js';
54
 
55
  /**
56
- * Proactively resolve Twitter advertising JS FQDN asynchronously before later use
57
- *
58
- * @since 1.0.0
59
  *
60
- * @link http://dev.chromium.org/developers/design-documents/dns-prefetching Chromium prefetch behavior
61
- * @link https://developer.mozilla.org/en-US/docs/Controlling_DNS_prefetching Firefox prefetch behavior
62
  *
63
- * @return void
64
  */
65
- public static function dnsPrefetch()
66
- {
67
- echo '<link rel="dns-prefetch" href="//platform.twitter.com"';
68
- echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
69
- echo '>' . "\n";
70
- }
71
 
72
  /**
73
- * Register Twitter advertising JavaScript
74
  *
75
- * @since 1.0.0
 
 
 
 
76
  *
77
- * @return void
78
  */
79
- public static function register()
80
- {
81
- wp_register_script(
82
- self::QUEUE_HANDLE,
83
- self::URI,
84
- array(), // no dependencies
85
- null, // no not add extra query parameters for cache busting
86
- true // in footer
87
- );
88
- }
89
 
90
  /**
91
- * Enqueue the advertising JavaScript
92
  *
93
- * @since 1.0.0
 
 
94
  *
95
- * @uses wp_enqueue_script()
96
  *
97
- * @return void
98
  */
99
- public static function enqueue()
100
  {
101
- wp_enqueue_script( self::QUEUE_HANDLE );
 
 
 
 
 
 
 
 
102
  }
103
  }
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Tracking extends AsyncJavaScript
34
  {
35
  /**
36
  * Twitter tracking JavaScript handle
44
  const QUEUE_HANDLE = 'twitter-tracking';
45
 
46
  /**
47
+ * Twitter advertising widget fully-qualified domain name
48
  *
49
+ * Used to prefetch DNS lookup
50
+ *
51
+ * @since 2.0.0
52
  *
53
  * @type string
54
  */
55
+ const FQDN = 'static.ads-twitter.com';
56
 
57
  /**
58
+ * Twitter advertising JavaScript absolute URI
 
 
59
  *
60
+ * @since 1.2.0
 
61
  *
62
+ * @type string
63
  */
64
+ const URI = 'https://static.ads-twitter.com/uwt.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
+ *
73
+ * @since 2.0.0
74
  *
75
+ * @type string
76
  */
77
+ const SCRIPT_EXTRA = 'window.twq=(function(w){t=w.twq||function(){window.twq.exe?window.twq.exe(window.twq,arguments):window.twq.queue.push(arguments)};t.version="1.1";t.queue=[];return t}(window));';
 
 
 
 
 
 
 
 
 
78
 
79
  /**
80
+ * Load Twitter ad tracking JS using an inline script block
81
  *
82
+ * 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.
83
+ *
84
+ * @since 2.0.0
85
  *
86
+ * @param bool $include_script_element_wrapper wrap the returned JavaScript string in a script element wrapper
87
  *
88
+ * @return string HTML script element containing loader script
89
  */
90
+ public static function asyncScriptLoaderInline( $include_script_element_wrapper = true )
91
  {
92
+ $script = '!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);
93
+ },s.version=\'1.1\',s.queue=[],u=t.createElement(n),u.async=!0,u.src=' . wp_json_encode( self::URI ) . ',
94
+ a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,"script");';
95
+
96
+ if ( $include_script_element_wrapper ) {
97
+ return '<script>' . $script . '</script>';
98
+ }
99
+
100
+ return $script;
101
  }
102
  }
src/Twitter/WordPress/JavaScriptLoaders/Widgets.php CHANGED
@@ -88,8 +88,9 @@ class Widgets extends AsyncJavaScript
88
  *
89
  * @return string HTML script element containing loader script
90
  */
91
- public static function asyncScriptLoaderInline( $include_script_element_wrapper = true ) {
92
- $script = 'window.twttr=(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return t;js=d.createElement(s);js.id=id;js.src=' . json_encode( self::URI ) . ';fjs.parentNode.insertBefore(js,fjs);t._e=[];t.ready=function(f){t._e.push(f);};return t;}(document,"script",' . json_encode( self::QUEUE_HANDLE ) . '));';
 
93
 
94
  if ( $include_script_element_wrapper ) {
95
  return '<script>' . $script . '</script>';
88
  *
89
  * @return string HTML script element containing loader script
90
  */
91
+ public static function asyncScriptLoaderInline( $include_script_element_wrapper = true )
92
+ {
93
+ $script = 'window.twttr=(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return t;js=d.createElement(s);js.id=id;js.src=' . wp_json_encode( self::URI ) . ';fjs.parentNode.insertBefore(js,fjs);t._e=[];t.ready=function(f){t._e.push(f);};return t;}(document,"script",' . wp_json_encode( self::QUEUE_HANDLE ) . '));';
94
 
95
  if ( $include_script_element_wrapper ) {
96
  return '<script>' . $script . '</script>';
src/Twitter/WordPress/PluginLoader.php CHANGED
@@ -41,7 +41,7 @@ class PluginLoader
41
  *
42
  * @type string
43
  */
44
- const VERSION = '1.5.0';
45
 
46
  /**
47
  * Unique domain of the plugin's translated text
@@ -124,6 +124,29 @@ class PluginLoader
124
  load_plugin_textdomain( static::TEXT_DOMAIN );
125
  }
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  /**
128
  * Register widgets
129
  *
@@ -134,13 +157,12 @@ class PluginLoader
134
  public static function widgetsInit()
135
  {
136
  $features = \Twitter\WordPress\Features::getEnabledFeatures();
 
137
 
138
- if ( isset( $features[ \Twitter\WordPress\Features::FOLLOW_BUTTON ] ) ) {
139
- register_widget( '\Twitter\WordPress\Widgets\Follow' );
140
- }
141
-
142
- if ( isset( $features[ \Twitter\WordPress\Features::PERISCOPE_ON_AIR ] ) ) {
143
- register_widget( '\Twitter\WordPress\Widgets\PeriscopeOnAir' );
144
  }
145
  }
146
 
@@ -162,9 +184,8 @@ class PluginLoader
162
  // Twitter settings menu
163
  \Twitter\WordPress\Admin\Settings\Loader::init();
164
 
165
- if (
166
- isset( $features[ \Twitter\WordPress\Features::CARDS ] ) ||
167
- isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] )
168
  ) {
169
  // Edit post meta box
170
  add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Post\MetaBox', 'init' ) );
@@ -188,13 +209,13 @@ class PluginLoader
188
  $features = \Twitter\WordPress\Features::getEnabledFeatures();
189
 
190
  // load widgets JS if a Twitter widget is active
191
- if (
192
- ( isset( $features[ \Twitter\WordPress\Features::FOLLOW_BUTTON ] ) && is_active_widget( false, false, \Twitter\WordPress\Widgets\Follow::BASE_ID, true ) ) ||
193
- ( isset( $features[ \Twitter\WordPress\Features::PERISCOPE_ON_AIR ] ) && is_active_widget( false, false, \Twitter\WordPress\Widgets\PeriscopeOnAir::BASE_ID, true ) )
194
  ) {
195
  // enqueue after the script is registered in wp_enqueue_scripts action priority 1
196
  add_action( 'wp_enqueue_scripts', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'enqueue' ) );
197
- add_action( 'wp_head', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'dnsPrefetch' ) );
 
198
  }
199
 
200
  // do not add content filters to HTTP 404 response
@@ -220,7 +241,7 @@ class PluginLoader
220
  // possibly add Tweet button(s)
221
  add_filter(
222
  'the_content',
223
- array( '\Twitter\WordPress\Content\TweetButton', 'contentFilter' ),
224
  $twitter_content_priority
225
  );
226
  }
@@ -241,11 +262,14 @@ class PluginLoader
241
  if ( wp_http_supports( array( 'ssl' => true ) ) ) {
242
  foreach (
243
  array(
244
- \Twitter\WordPress\Features::EMBED_TWEET => 'EmbeddedTweet',
245
- \Twitter\WordPress\Features::EMBED_TWEET_VIDEO => 'EmbeddedTweetVideo',
246
- \Twitter\WordPress\Features::EMBED_VINE => 'Vine',
247
- \Twitter\WordPress\Features::EMBED_TWEETS_GRID => 'TweetGrid',
248
- \Twitter\WordPress\Features::EMBED_MOMENT => 'Moment',
 
 
 
249
  ) as $feature => $shortcode_class
250
  ) {
251
  if ( ! isset( $features[ $feature ] ) ) {
@@ -254,20 +278,21 @@ class PluginLoader
254
 
255
  add_action(
256
  'plugins_loaded',
257
- array( $shortcode_namespace . $shortcode_class, 'init' ),
258
  5,
259
  0
260
  );
261
  }
262
  }
263
 
264
- // initialize buttons and ad pixel if not disabled
265
  foreach (
266
  array(
267
- \Twitter\WordPress\Features::FOLLOW_BUTTON => 'Follow',
268
- \Twitter\WordPress\Features::TWEET_BUTTON => 'Share',
269
- \Twitter\WordPress\Features::PERISCOPE_ON_AIR => 'PeriscopeOnAir',
270
- \Twitter\WordPress\Features::TRACKING_PIXEL => 'Tracking',
 
271
  ) as $feature => $shortcode_class
272
  ) {
273
  if ( ! isset( $features[ $feature ] ) ) {
41
  *
42
  * @type string
43
  */
44
+ const VERSION = '2.0.0';
45
 
46
  /**
47
  * Unique domain of the plugin's translated text
124
  load_plugin_textdomain( static::TEXT_DOMAIN );
125
  }
126
 
127
+ /**
128
+ * Plugin features and corresponding widget class
129
+ *
130
+ * @since 2.0.0
131
+ *
132
+ * @return array plugin feature and widget class{
133
+ * @type string plugin feature identifier
134
+ * @type string WP_Widget class to register
135
+ * }
136
+ */
137
+ public static function getAvailableWidgets()
138
+ {
139
+ return array(
140
+ \Twitter\WordPress\Features::FOLLOW_BUTTON => '\Twitter\WordPress\Widgets\Buttons\Follow',
141
+ \Twitter\WordPress\Features::PERISCOPE_ON_AIR => '\Twitter\WordPress\Widgets\Buttons\Periscope\OnAir',
142
+ \Twitter\WordPress\Features::EMBED_PROFILE => '\Twitter\WordPress\Widgets\Embeds\Timeline\Profile',
143
+ \Twitter\WordPress\Features::EMBED_LIST => '\Twitter\WordPress\Widgets\Embeds\Timeline\TwitterList',
144
+ \Twitter\WordPress\Features::EMBED_SEARCH => '\Twitter\WordPress\Widgets\Embeds\Timeline\Search',
145
+ \Twitter\WordPress\Features::EMBED_COLLECTION => '\Twitter\WordPress\Widgets\Embeds\Timeline\Collection',
146
+ \Twitter\WordPress\Features::TRACKING_PIXEL => '\Twitter\WordPress\Widgets\Advertising\Tracking',
147
+ );
148
+ }
149
+
150
  /**
151
  * Register widgets
152
  *
157
  public static function widgetsInit()
158
  {
159
  $features = \Twitter\WordPress\Features::getEnabledFeatures();
160
+ $widgets = static::getAvailableWidgets();
161
 
162
+ foreach ( $widgets as $feature => $widget_class ) {
163
+ if ( isset( $features[ $feature ] ) ) {
164
+ register_widget( $widget_class );
165
+ }
 
 
166
  }
167
  }
168
 
184
  // Twitter settings menu
185
  \Twitter\WordPress\Admin\Settings\Loader::init();
186
 
187
+ if ( isset( $features[ \Twitter\WordPress\Features::CARDS ] )
188
+ || isset( $features[ \Twitter\WordPress\Features::TWEET_BUTTON ] )
 
189
  ) {
190
  // Edit post meta box
191
  add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Post\MetaBox', 'init' ) );
209
  $features = \Twitter\WordPress\Features::getEnabledFeatures();
210
 
211
  // load widgets JS if a Twitter widget is active
212
+ if ( ( isset( $features[ \Twitter\WordPress\Features::FOLLOW_BUTTON ] ) && is_active_widget( false, false, \Twitter\WordPress\Widgets\Buttons\Follow::BASE_ID, true ) )
213
+ || ( isset( $features[ \Twitter\WordPress\Features::PERISCOPE_ON_AIR ] ) && is_active_widget( false, false, \Twitter\WordPress\Widgets\Buttons\Periscope\OnAir::BASE_ID, true ) )
 
214
  ) {
215
  // enqueue after the script is registered in wp_enqueue_scripts action priority 1
216
  add_action( 'wp_enqueue_scripts', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'enqueue' ) );
217
+ // register DNS prefetch before WordPress resource hints run at wp_head priority 2
218
+ add_action( 'wp_head', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'dnsPrefetch' ), 1 );
219
  }
220
 
221
  // do not add content filters to HTTP 404 response
241
  // possibly add Tweet button(s)
242
  add_filter(
243
  'the_content',
244
+ array( '\Twitter\WordPress\Content\Buttons\Tweet', 'contentFilter' ),
245
  $twitter_content_priority
246
  );
247
  }
262
  if ( wp_http_supports( array( 'ssl' => true ) ) ) {
263
  foreach (
264
  array(
265
+ \Twitter\WordPress\Features::EMBED_TWEET => 'Tweet',
266
+ \Twitter\WordPress\Features::EMBED_TWEET_VIDEO => 'Tweet\\Video',
267
+ \Twitter\WordPress\Features::EMBED_VINE => 'Vine',
268
+ \Twitter\WordPress\Features::EMBED_PROFILE => 'Timeline\\Profile',
269
+ \Twitter\WordPress\Features::EMBED_LIST => 'Timeline\\TwitterList',
270
+ \Twitter\WordPress\Features::EMBED_COLLECTION => 'Timeline\\Collection',
271
+ \Twitter\WordPress\Features::EMBED_COLLECTION_GRID => 'Timeline\\CollectionGrid',
272
+ \Twitter\WordPress\Features::EMBED_MOMENT => 'Moment',
273
  ) as $feature => $shortcode_class
274
  ) {
275
  if ( ! isset( $features[ $feature ] ) ) {
278
 
279
  add_action(
280
  'plugins_loaded',
281
+ array( $shortcode_namespace . 'Embeds\\' . $shortcode_class, 'init' ),
282
  5,
283
  0
284
  );
285
  }
286
  }
287
 
288
+ // initialize buttons, search timeline, and ad pixel if not disabled
289
  foreach (
290
  array(
291
+ \Twitter\WordPress\Features::FOLLOW_BUTTON => 'Buttons\\Follow',
292
+ \Twitter\WordPress\Features::TWEET_BUTTON => 'Buttons\\Share',
293
+ \Twitter\WordPress\Features::PERISCOPE_ON_AIR => 'Buttons\\Periscope\\OnAir',
294
+ \Twitter\WordPress\Features::EMBED_SEARCH => 'Embeds\\Timeline\\Search',
295
+ \Twitter\WordPress\Features::TRACKING_PIXEL => 'Advertising\\Tracking',
296
  ) as $feature => $shortcode_class
297
  ) {
298
  if ( ! isset( $features[ $feature ] ) ) {
src/Twitter/WordPress/Shortcodes/{Tracking.php → Advertising/Tracking.php} RENAMED
@@ -23,20 +23,20 @@ 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
  * Track a Twitter conversion and/or remarketing audience
30
  *
31
- * @since 1.0.0
32
  */
33
- class Tracking implements ShortcodeInterface
34
  {
35
 
36
  /**
37
  * Shortcode tag to be matched
38
  *
39
- * @since 1.0.0
40
  *
41
  * @type string
42
  */
@@ -45,7 +45,7 @@ class Tracking implements ShortcodeInterface
45
  /**
46
  * Accepted shortcode attributes and their default values
47
  *
48
- * @since 1.0.0
49
  *
50
  * @type array
51
  */
@@ -54,7 +54,7 @@ class Tracking implements ShortcodeInterface
54
  /**
55
  * Stored tracking ids
56
  *
57
- * @since 1.0.0
58
  *
59
  * @type array
60
  */
@@ -63,23 +63,36 @@ class Tracking implements ShortcodeInterface
63
  /**
64
  * Register shortcode macro and handler
65
  *
66
- * @since 1.0.0
67
  *
68
  * @return void
69
  */
70
  public static function init()
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
  /**
84
  * Describe shortcode for Shortcake UI
85
  *
@@ -99,7 +112,7 @@ class Tracking implements ShortcodeInterface
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(
@@ -117,34 +130,76 @@ class Tracking implements ShortcodeInterface
117
  }
118
 
119
  /**
120
- * Handle shortcode macro
121
  *
122
- * @since 1.0.0
123
  *
124
- * @param array $attributes shortcode attributes
125
- * @param string $content shortcode content. no effect
 
 
 
 
 
 
 
126
  *
127
- * @return string empty string. markup is queued for inclusion in wp_footer output
 
 
 
 
 
 
128
  */
129
- public static function shortcodeHandler( $attributes, $content = null )
130
  {
131
  $options = shortcode_atts(
132
- static::$SHORTCODE_DEFAULTS,
133
  $attributes,
134
  static::SHORTCODE_TAG
135
  );
136
 
137
- $tracking_id = trim( $options['id'] );
138
- if ( ! $tracking_id ) {
139
- return '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  }
141
 
142
  \Twitter\WordPress\JavaScriptLoaders\Tracking::enqueue();
143
- static::$tracking_ids[ $tracking_id ] = true;
 
144
 
145
- if ( false === has_action( 'wp_footer', array( __CLASS__, 'trackerJavaScript' ) ) ) {
146
  // execute script after wp_print_footer_scripts action completes at priority 20
147
- add_action( 'wp_footer', array( __CLASS__, 'trackerJavaScript' ), 25 );
 
148
  }
149
 
150
  // execute all trackers just before </body>
@@ -152,9 +207,9 @@ class Tracking implements ShortcodeInterface
152
  }
153
 
154
  /**
155
- * Track a Twitter advertising event using the twttr.conversion.trackPid JavaScript function
156
  *
157
- * @since 1.0.0
158
  *
159
  * @param string $tracking_id Twitter ads tracking ID
160
  *
@@ -162,13 +217,13 @@ class Tracking implements ShortcodeInterface
162
  */
163
  protected static function trackEventUsingJavaScript( $tracking_id )
164
  {
165
- echo 'twttr.conversion.trackPid(' . ( function_exists( 'wp_json_encode' ) ? wp_json_encode( $tracking_id ) : json_encode( $tracking_id ) ) . ');';
166
  }
167
 
168
  /**
169
  * Track a Twitter advertising event using 1x1 images
170
  *
171
- * @since 1.0.0
172
  *
173
  * @param string $tracking_id Twitter ads tracking ID
174
  *
@@ -178,14 +233,21 @@ class Tracking implements ShortcodeInterface
178
  {
179
  $query_parameters = http_build_query( array( 'txn_id' => $tracking_id, 'p_id' => 'Twitter' ), '', '&' );
180
 
181
- echo '<img height="1" width="1" alt=" " src="' . esc_url( 'https://analytics.twitter.com/i/adsct?' . $query_parameters, array( 'https', 'http' ) ) . '"' . \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
182
- echo '<img height="1" width="1" alt=" " src="' . esc_url( 'https://t.co/i/adsct?' . $query_parameters, array( 'https', 'http' ) ) . '"' . \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
 
 
 
 
 
 
 
183
  }
184
 
185
  /**
186
  * Record tracking ID actions
187
  *
188
- * @since 1.0.0
189
  *
190
  * @return void
191
  */
@@ -196,15 +258,16 @@ class Tracking implements ShortcodeInterface
196
  }
197
 
198
  $tracking_ids = array_keys( static::$tracking_ids );
 
199
 
200
  // JavaScript available
201
- echo '<script type="text/javascript">';
202
- array_walk( $tracking_ids, array( __CLASS__, 'trackEventUsingJavaScript' ) );
203
- echo '</script>';
204
 
205
  // JavaScript unavailable
206
  echo '<noscript>';
207
- array_walk( $tracking_ids, array( __CLASS__, 'trackEventUsingFallbackImages' ) );
208
  echo '</noscript>';
209
  }
210
  }
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Shortcodes\Advertising;
27
 
28
  /**
29
  * Track a Twitter conversion and/or remarketing audience
30
  *
31
+ * @since 2.0.0
32
  */
33
+ class Tracking implements \Twitter\WordPress\Shortcodes\ShortcodeInterface
34
  {
35
 
36
  /**
37
  * Shortcode tag to be matched
38
  *
39
+ * @since 2.0.0
40
  *
41
  * @type string
42
  */
45
  /**
46
  * Accepted shortcode attributes and their default values
47
  *
48
+ * @since 2.0.0
49
  *
50
  * @type array
51
  */
54
  /**
55
  * Stored tracking ids
56
  *
57
+ * @since 2.0.0
58
  *
59
  * @type array
60
  */
63
  /**
64
  * Register shortcode macro and handler
65
  *
66
+ * @since 2.0.0
67
  *
68
  * @return void
69
  */
70
  public static function init()
71
  {
72
+ $class = get_called_class();
73
+ add_shortcode( static::SHORTCODE_TAG, array( $class, 'shortcodeHandler' ) );
74
 
75
  // Shortcode UI, if supported
76
  add_action(
77
  'register_shortcode_ui',
78
+ array( $class, 'shortcodeUI' ),
79
  5,
80
  0
81
  );
82
  }
83
 
84
+ /**
85
+ * Reference the feature by name
86
+ *
87
+ * @since 2.0.0
88
+ *
89
+ * @return string translated feature name
90
+ */
91
+ public static function featureName()
92
+ {
93
+ return __( 'Twitter Advertising Tracker', 'twitter' );
94
+ }
95
+
96
  /**
97
  * Describe shortcode for Shortcake UI
98
  *
112
  shortcode_ui_register_for_shortcode(
113
  static::SHORTCODE_TAG,
114
  array(
115
+ 'label' => esc_html( static::featureName() ),
116
  'listItemImage' => 'dashicons-twitter',
117
  'attrs' => array(
118
  array(
130
  }
131
 
132
  /**
133
+ * Shortcode allowed attributes and default values
134
  *
135
+ * @since 2.0.0
136
  *
137
+ * @return array shortcode allowed attributes and default values
138
+ */
139
+ public static function getShortcodeDefaults()
140
+ {
141
+ return static::$SHORTCODE_DEFAULTS;
142
+ }
143
+
144
+ /**
145
+ * Process shortcode attributes received from the shortcode API
146
  *
147
+ * @since 2.0.0
148
+ *
149
+ * @link https://codex.wordpress.org/Shortcode_API Shortcode API
150
+ *
151
+ * @param array $attributes associative array of shortcode attributes, usually from the Shortcode API
152
+ *
153
+ * @return array array processed by shortcode_atts, prepped for Tweet object
154
  */
155
+ public static function getShortcodeAttributes( $attributes )
156
  {
157
  $options = shortcode_atts(
158
+ static::getShortcodeDefaults(),
159
  $attributes,
160
  static::SHORTCODE_TAG
161
  );
162
 
163
+ if ( ! is_array( $options ) || empty( $options ) ) {
164
+ return array();
165
+ }
166
+
167
+ if ( isset( $options['id'] ) ) {
168
+ $options['id'] = \Twitter\Helpers\Validators\WebsiteTag::sanitize( $options['id'] );
169
+ if ( ! $options['id'] ) {
170
+ unset( $options['id'] );
171
+ }
172
+ }
173
+
174
+ return $options;
175
+ }
176
+
177
+ /**
178
+ * Handle shortcode macro
179
+ *
180
+ * @since 2.0.0
181
+ *
182
+ * @param array $attributes shortcode attributes
183
+ * @param string $content shortcode content. no effect
184
+ *
185
+ * @return string empty string. markup is queued for inclusion in wp_footer output
186
+ */
187
+ public static function shortcodeHandler( $attributes, $content = '' )
188
+ {
189
+ $options = static::getShortcodeAttributes( $attributes );
190
+
191
+ if ( ! isset( $options['id'] ) ) {
192
+ return '';
193
  }
194
 
195
  \Twitter\WordPress\JavaScriptLoaders\Tracking::enqueue();
196
+ static::$tracking_ids[ $options['id'] ] = true;
197
+ $class = get_called_class();
198
 
199
+ if ( false === has_action( 'wp_footer', array( $class, 'trackerJavaScript' ) ) ) {
200
  // execute script after wp_print_footer_scripts action completes at priority 20
201
+ // async function queue should be inserted with footer scripts
202
+ add_action( 'wp_footer', array( $class, 'trackerJavaScript' ), 25 );
203
  }
204
 
205
  // execute all trackers just before </body>
207
  }
208
 
209
  /**
210
+ * Track a Twitter advertising event as a UWT PageView
211
  *
212
+ * @since 2.0.0
213
  *
214
  * @param string $tracking_id Twitter ads tracking ID
215
  *
217
  */
218
  protected static function trackEventUsingJavaScript( $tracking_id )
219
  {
220
+ echo 'twq("init",' . wp_json_encode( $tracking_id ) . ');twq("track","PageView");';
221
  }
222
 
223
  /**
224
  * Track a Twitter advertising event using 1x1 images
225
  *
226
+ * @since 2.0.0
227
  *
228
  * @param string $tracking_id Twitter ads tracking ID
229
  *
233
  {
234
  $query_parameters = http_build_query( array( 'txn_id' => $tracking_id, 'p_id' => 'Twitter' ), '', '&' );
235
 
236
+ echo '<img height="1" width="1" alt=" " src="' . esc_url( 'https://analytics.twitter.com/i/adsct?' . $query_parameters, array( 'https', 'http' ) ) . '"';
237
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
238
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
239
+
240
+ echo '><img height="1" width="1" alt=" " src="' . esc_url( 'https://t.co/i/adsct?' . $query_parameters, array( 'https', 'http' ) ) . '"';
241
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
242
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
243
+
244
+ echo '>';
245
  }
246
 
247
  /**
248
  * Record tracking ID actions
249
  *
250
+ * @since 2.0.0
251
  *
252
  * @return void
253
  */
258
  }
259
 
260
  $tracking_ids = array_keys( static::$tracking_ids );
261
+ $class = get_called_class();
262
 
263
  // JavaScript available
264
+ echo '<script type="text/javascript">if(typeof window.twq !== "undefined"){';
265
+ array_walk( $tracking_ids, array( $class, 'trackEventUsingJavaScript' ) );
266
+ echo '}</script>';
267
 
268
  // JavaScript unavailable
269
  echo '<noscript>';
270
+ array_walk( $tracking_ids, array( $class, 'trackEventUsingFallbackImages' ) );
271
  echo '</noscript>';
272
  }
273
  }
src/Twitter/WordPress/Shortcodes/AuthorContext.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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
+ * Add author context for the current post
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ trait AuthorContext
34
+ {
35
+ /**
36
+ * Get the Twitter screen name of the author of the current post
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @return string Twitter screen name or empty if no screen name stored
41
+ */
42
+ public static function getScreenName()
43
+ {
44
+ $screen_name = \Twitter\WordPress\User\Meta::getTwitterUsername( get_the_author_meta( 'ID' ) );
45
+ if ( ! $screen_name ) {
46
+ return '';
47
+ }
48
+
49
+ return $screen_name;
50
+ }
51
+ }
src/Twitter/WordPress/Shortcodes/Buttons/Follow.php ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Buttons;
27
+
28
+ /**
29
+ * Display a Follow button
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Follow implements \Twitter\WordPress\Shortcodes\ShortcodeInterface
34
+ {
35
+ use \Twitter\WordPress\Shortcodes\AuthorContext;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'twitter_follow';
45
+
46
+ /**
47
+ * HTML class to be used in div wrapper
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const HTML_CLASS = 'twitter-follow';
54
+
55
+ /**
56
+ * Accepted shortcode attributes and their default values
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @type array
61
+ */
62
+ public static $SHORTCODE_DEFAULTS = array( 'screen_name' => '', 'show_count' => true, 'show_screen_name' => true, 'size' => 'medium' );
63
+
64
+ /**
65
+ * Attach handlers for a follow button
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @return void
70
+ */
71
+ public static function init()
72
+ {
73
+ $classname = get_called_class();
74
+
75
+ // register our shortcode and its handler
76
+ add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
77
+
78
+ // Shortcode UI, if supported
79
+ add_action(
80
+ 'register_shortcode_ui',
81
+ array( $classname, 'shortcodeUI' ),
82
+ 5,
83
+ 0
84
+ );
85
+ }
86
+
87
+ /**
88
+ * Reference the feature by name
89
+ *
90
+ * @since 2.0.0
91
+ *
92
+ * @return string translated feature name
93
+ */
94
+ public static function featureName()
95
+ {
96
+ return __( 'Follow Button', 'twitter' );
97
+ }
98
+
99
+ /**
100
+ * Describe shortcode for Shortcake UI
101
+ *
102
+ * @since 1.1.0
103
+ *
104
+ * @link https://github.com/fusioneng/Shortcake Shortcake UI
105
+ *
106
+ * @return void
107
+ */
108
+ public static function shortcodeUI()
109
+ {
110
+ // Shortcake required
111
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
112
+ return;
113
+ }
114
+
115
+ shortcode_ui_register_for_shortcode(
116
+ static::SHORTCODE_TAG,
117
+ array(
118
+ 'label' => esc_html( static::featureName() ),
119
+ 'listItemImage' => 'dashicons-twitter',
120
+ 'attrs' => array(
121
+ array(
122
+ 'attr' => 'screen_name',
123
+ 'label' => esc_html( __( 'Twitter @username', 'twitter' ) ),
124
+ 'type' => 'text',
125
+ 'meta' => array(
126
+ 'placeholder' => 'WordPress',
127
+ 'pattern' => \Twitter\Helpers\Validators\ScreenName::getPattern(),
128
+ ),
129
+ ),
130
+ array(
131
+ 'attr' => 'size',
132
+ 'label' => esc_html( __( 'Button size:', 'twitter' ) ),
133
+ 'type' => 'radio',
134
+ 'value' => '',
135
+ 'options' => array(
136
+ '' => esc_html( _x( 'medium', 'medium size button', 'twitter' ) ),
137
+ 'large' => esc_html( _x( 'large', 'large size button', 'twitter' ) ),
138
+ ),
139
+ ),
140
+ ),
141
+ )
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Shortcode allowed attributes and default values
147
+ *
148
+ * @since 2.0.0
149
+ *
150
+ * @return array shortcode allowed attributes and default values
151
+ */
152
+ public static function getShortcodeDefaults()
153
+ {
154
+ return static::$SHORTCODE_DEFAULTS;
155
+ }
156
+
157
+ /**
158
+ * Process shortcode attributes received from the shortcode API
159
+ *
160
+ * @since 2.0.0
161
+ *
162
+ * @link https://codex.wordpress.org/Shortcode_API Shortcode API
163
+ *
164
+ * @param array $attributes associative array of shortcode attributes, usually from the Shortcode API
165
+ *
166
+ * @return array array processed by shortcode_atts, prepped for Tweet object
167
+ */
168
+ public static function getShortcodeAttributes( $attributes )
169
+ {
170
+ $options = shortcode_atts(
171
+ static::getShortcodeDefaults(),
172
+ $attributes,
173
+ static::SHORTCODE_TAG
174
+ );
175
+
176
+ if ( ! is_array( $options ) || empty( $options ) ) {
177
+ return array();
178
+ }
179
+
180
+ if ( isset( $attributes['screen_name'] ) ) {
181
+ $attributes['screen_name'] = \Twitter\Helpers\Validators\ScreenName::trim( $attributes['screen_name'] );
182
+ if ( ! $attributes['screen_name'] ) {
183
+ unset( $attributes['screen_name'] );
184
+ }
185
+ }
186
+
187
+ $attributes = \Twitter\WordPress\Shortcodes\Helpers\Attributes::booleanOption(
188
+ $attributes,
189
+ array( 'show_count', 'show_screen_name' )
190
+ );
191
+
192
+ // large is the only option
193
+ if ( isset( $attributes['size'] ) ) {
194
+ if ( is_string( $attributes['size'] ) && in_array( strtolower( $attributes['size'] ), array( 'large', 'l' ), /* strict */ true ) ) {
195
+ $attributes['size'] = 'large';
196
+ }
197
+ }
198
+
199
+ return $attributes;
200
+ }
201
+
202
+ /**
203
+ * Handle shortcode macro
204
+ *
205
+ * @since 1.0.0
206
+ *
207
+ * @param array $attributes shortcode attributes
208
+ * @param string $content shortcode content. no effect
209
+ *
210
+ * @return string Follow button HTML or empty string
211
+ */
212
+ public static function shortcodeHandler( $attributes, $content = '' )
213
+ {
214
+ $options = static::getShortcodeAttributes( $attributes );
215
+
216
+ if ( ! isset( $options['screen_name'] ) ) {
217
+ $options['screen_name'] = static::getScreenName();
218
+ }
219
+
220
+ // follow target required
221
+ if ( ! $options['screen_name'] ) {
222
+ return '';
223
+ }
224
+
225
+ $follow = \Twitter\Widgets\Buttons\Follow::fromArray( $options );
226
+ if ( ! $follow ) {
227
+ return '';
228
+ }
229
+
230
+ $html = $follow->toHTML( _x( 'Follow %s', 'Follow a Twitter user', 'twitter' ), '\Twitter\WordPress\Helpers\HTMLBuilder' );
231
+ if ( ! $html ) {
232
+ return '';
233
+ }
234
+
235
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
236
+
237
+ $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
238
+ if ( $inline_js ) {
239
+ return $html . $inline_js;
240
+ }
241
+
242
+ return $html;
243
+ }
244
+ }
src/Twitter/WordPress/Shortcodes/{PeriscopeOnAir.php → Buttons/Periscope/OnAir.php} RENAMED
@@ -23,14 +23,14 @@ 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
  /**
@@ -42,6 +42,15 @@ class PeriscopeOnAir implements ShortcodeInterface
42
  */
43
  const SHORTCODE_TAG = 'periscope_on_air';
44
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * Regex used to match a Periscope profile URL in text
47
  *
@@ -88,6 +97,18 @@ class PeriscopeOnAir implements ShortcodeInterface
88
  );
89
  }
90
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  /**
92
  * Describe shortcode for Shortcake UI
93
  *
@@ -107,7 +128,7 @@ class PeriscopeOnAir implements ShortcodeInterface
107
  shortcode_ui_register_for_shortcode(
108
  static::SHORTCODE_TAG,
109
  array(
110
- 'label' => esc_html( __( 'Periscope On Air Button', 'twitter' ) ),
111
  'listItemImage' => 'dashicons-video-alt',
112
  'attrs' => array(
113
  array(
@@ -126,7 +147,7 @@ class PeriscopeOnAir implements ShortcodeInterface
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
  ),
@@ -139,14 +160,11 @@ class PeriscopeOnAir implements ShortcodeInterface
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 Periscope On Air button 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 '';
@@ -156,65 +174,72 @@ class PeriscopeOnAir implements ShortcodeInterface
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
  /**
@@ -229,37 +254,18 @@ class PeriscopeOnAir implements ShortcodeInterface
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
  }
@@ -269,7 +275,7 @@ class PeriscopeOnAir implements ShortcodeInterface
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 ) {
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Shortcodes\Buttons\Periscope;
27
 
28
  /**
29
  * Display a Periscope On Air button
30
  *
31
  * @since 1.3.0
32
  */
33
+ class OnAir implements \Twitter\WordPress\Shortcodes\ShortcodeInterface
34
  {
35
 
36
  /**
42
  */
43
  const SHORTCODE_TAG = 'periscope_on_air';
44
 
45
+ /**
46
+ * HTML class to be used in div wrapper
47
+ *
48
+ * @since 2.0.0
49
+ *
50
+ * @type string
51
+ */
52
+ const HTML_CLASS = 'periscope-on-air';
53
+
54
  /**
55
  * Regex used to match a Periscope profile URL in text
56
  *
97
  );
98
  }
99
 
100
+ /**
101
+ * Reference the feature by name
102
+ *
103
+ * @since 2.0.0
104
+ *
105
+ * @return string translated feature name
106
+ */
107
+ public static function featureName()
108
+ {
109
+ return __( 'Periscope On Air Button', 'twitter' );
110
+ }
111
+
112
  /**
113
  * Describe shortcode for Shortcake UI
114
  *
128
  shortcode_ui_register_for_shortcode(
129
  static::SHORTCODE_TAG,
130
  array(
131
+ 'label' => esc_html( static::featureName() ),
132
  'listItemImage' => 'dashicons-video-alt',
133
  'attrs' => array(
134
  array(
147
  'value' => '',
148
  'options' => array(
149
  '' => esc_html( _x( 'small', 'small size button', 'twitter' ) ),
150
+ 'large' => esc_html( _x( 'large', 'large size button', 'twitter' ) ),
151
  ),
152
  ),
153
  ),
160
  *
161
  * @since 1.3.0
162
  *
163
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
 
 
 
164
  *
165
  * @return string HTML markup for the Periscope On Air button or an empty string if requirements not met
166
  */
167
+ public static function linkHandler( $matches )
168
  {
169
  if ( ! ( is_array( $matches ) && isset( $matches[2] ) && $matches[2] ) ) {
170
  return '';
174
  }
175
 
176
  /**
177
+ * Get the Periscope username of the author of the current post
178
  *
179
  * @since 1.3.0
180
  *
181
+ * @return string Periscope username or empty if no screen name stored
 
 
 
 
 
 
 
 
182
  */
183
+ public static function getUsername()
184
  {
185
+ $username = \Twitter\WordPress\User\Meta::getPeriscopeUsername( get_the_author_meta( 'ID' ) );
186
+ if ( ! $username ) {
187
+ return '';
 
 
 
 
 
 
 
 
 
188
  }
189
 
190
+ return $username;
191
+ }
 
 
 
 
192
 
193
+ /**
194
+ * Shortcode allowed attributes and default values
195
+ *
196
+ * @since 2.0.0
197
+ *
198
+ * @return array shortcode allowed attributes and default values
199
+ */
200
+ public static function getShortcodeDefaults()
201
+ {
202
+ return static::$SHORTCODE_DEFAULTS;
203
  }
204
 
205
  /**
206
+ * Process shortcode attributes received from the shortcode API
207
  *
208
+ * @since 2.0.0
209
  *
210
+ * @link https://codex.wordpress.org/Shortcode_API Shortcode API
211
+ *
212
+ * @param array $attributes associative array of shortcode attributes, usually from the Shortcode API
213
+ *
214
+ * @return array array processed by shortcode_atts, prepped for Tweet object
215
  */
216
+ public static function getShortcodeAttributes( $attributes )
217
  {
218
+ $options = shortcode_atts(
219
+ static::getShortcodeDefaults(),
220
+ $attributes,
221
+ static::SHORTCODE_TAG
222
+ );
223
+
224
+ if ( ! is_array( $options ) || empty( $options ) ) {
225
+ return array();
226
  }
227
 
228
+ if ( isset( $options['username'] ) ) {
229
+ $options['username'] = \Twitter\Helpers\Validators\PeriscopeUsername::trim( $options['username'] );
230
+ if ( ! $options['username'] ) {
231
+ unset( $options['username'] );
232
+ }
233
  }
234
 
235
+ $options = \Twitter\WordPress\Shortcodes\Helpers\Attributes::lowercaseStringOption( $options, array( 'size' ) );
236
+
237
+ // allow abbreviated size value
238
+ if ( isset( $options['size'] ) && 'l' === $options['size'] ) {
239
+ $options['size'] = 'large';
240
+ }
241
+
242
+ return $options;
243
  }
244
 
245
  /**
254
  */
255
  public static function shortcodeHandler( $attributes, $content = '' )
256
  {
257
+ $options = static::getShortcodeAttributes( $attributes );
 
 
 
 
 
 
 
 
 
 
 
258
 
259
+ if ( ! isset( $options['username'] ) ) {
260
+ $options['username'] = static::getUsername();
 
 
 
 
 
 
 
 
 
261
  }
262
 
263
+ // on air target required
264
+ if ( ! $options['username'] ) {
265
+ return '';
266
+ }
267
 
268
+ $on_air = \Twitter\Widgets\Buttons\Periscope\OnAir::fromArray( $options );
269
  if ( ! $on_air ) {
270
  return '';
271
  }
275
  return '';
276
  }
277
 
278
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
279
 
280
  $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
281
  if ( $inline_js ) {
src/Twitter/WordPress/Shortcodes/{Share.php → Buttons/Share.php} RENAMED
@@ -23,14 +23,14 @@ 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 Tweet Web Intent and queue JavaScript for conversion to a Tweet button
30
  *
31
  * @since 1.0.0
32
  */
33
- class Share implements ShortcodeInterface
34
  {
35
 
36
  /**
@@ -42,6 +42,15 @@ class Share implements ShortcodeInterface
42
  */
43
  const SHORTCODE_TAG = 'twitter_share';
44
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * Accepted shortcode attributes and their default values
47
  *
@@ -71,6 +80,18 @@ class Share implements ShortcodeInterface
71
  );
72
  }
73
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  /**
75
  * Describe shortcode for Shortcake UI
76
  *
@@ -90,7 +111,7 @@ class Share implements ShortcodeInterface
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(
@@ -110,7 +131,7 @@ class Share implements ShortcodeInterface
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
  ),
@@ -217,7 +238,7 @@ class Share implements ShortcodeInterface
217
  $options = array();
218
 
219
  if ( isset( $attributes['in_reply_to'] ) ) {
220
- $tweet_id = \Twitter\WordPress\Shortcodes\EmbeddedTweet::sanitizeTweetID( (string) $attributes['in_reply_to'] );
221
  if ( $tweet_id ) {
222
  $options['in_reply_to'] = $tweet_id;
223
  }
@@ -270,7 +291,7 @@ class Share implements ShortcodeInterface
270
 
271
  // large is the only option
272
  if ( isset( $attributes['size'] ) ) {
273
- if ( is_string( $attributes['size'] ) && in_array( strtolower( $attributes['size'] ), array( 'large', 'l' ) ) ) {
274
  $options['size'] = 'large';
275
  }
276
  }
@@ -283,11 +304,11 @@ class Share implements ShortcodeInterface
283
  *
284
  * @since 1.0.0
285
  *
286
- * @param array $options Tweet button options {
287
  * @type string option name
288
  * @type string|bool option value
289
  * }
290
- * @param WP_Post $post post of interest
291
  *
292
  * @return array Tweet button options with possible additions based on post data {
293
  * @type string option name
@@ -340,21 +361,22 @@ class Share implements ShortcodeInterface
340
  }
341
 
342
  /**
343
- * Handle shortcode macro
344
  *
345
- * @since 1.0.0
346
  *
347
- * @param array $attributes shortcode attributes
348
- * @param string $content shortcode content. no effect
349
  *
350
- * @return string Tweet button HTML or empty string
 
 
351
  */
352
- public static function shortcodeHandler( $attributes, $content = null )
353
  {
354
  // clean up attribute to shortcode option mappings before passing to filter
355
  // apply the same filter as shortcode_atts
356
  /** This filter is documented in wp-includes/shortcodes.php */
357
- $options = apply_filters(
358
  'shortcode_atts_' . self::SHORTCODE_TAG,
359
  array_merge(
360
  static::$SHORTCODE_DEFAULTS,
@@ -363,11 +385,26 @@ class Share implements ShortcodeInterface
363
  static::$SHORTCODE_DEFAULTS,
364
  $attributes
365
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
 
367
  // add options shared to post meta
368
  $options = static::addPostMetaOptions( $options );
369
 
370
- // add parameters based on per-post render context
371
  if ( in_the_loop() ) {
372
  $post = get_post();
373
 
@@ -398,7 +435,7 @@ class Share implements ShortcodeInterface
398
  unset( $via_username );
399
  }
400
 
401
- $button = \Twitter\Widgets\TweetButton::fromArray( $options );
402
  if ( ! $button ) {
403
  return '';
404
  }
@@ -408,7 +445,7 @@ class Share implements ShortcodeInterface
408
  return '';
409
  }
410
 
411
- $html = '<div class="twitter-share">' . $html . '</div>';
412
 
413
  $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
414
  if ( $inline_js ) {
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Shortcodes\Buttons;
27
 
28
  /**
29
  * Display a Tweet Web Intent and queue JavaScript for conversion to a Tweet button
30
  *
31
  * @since 1.0.0
32
  */
33
+ class Share implements \Twitter\WordPress\Shortcodes\ShortcodeInterface
34
  {
35
 
36
  /**
42
  */
43
  const SHORTCODE_TAG = 'twitter_share';
44
 
45
+ /**
46
+ * HTML class to be used in div wrapper
47
+ *
48
+ * @since 2.0.0
49
+ *
50
+ * @type string
51
+ */
52
+ const HTML_CLASS = 'twitter-share';
53
+
54
  /**
55
  * Accepted shortcode attributes and their default values
56
  *
80
  );
81
  }
82
 
83
+ /**
84
+ * Reference the feature by name
85
+ *
86
+ * @since 2.0.0
87
+ *
88
+ * @return string translated feature name
89
+ */
90
+ public static function featureName()
91
+ {
92
+ return __( 'Tweet Button', 'twitter' );
93
+ }
94
+
95
  /**
96
  * Describe shortcode for Shortcake UI
97
  *
111
  shortcode_ui_register_for_shortcode(
112
  static::SHORTCODE_TAG,
113
  array(
114
+ 'label' => esc_html( static::featureName() ),
115
  'listItemImage' => 'dashicons-twitter',
116
  'attrs' => array(
117
  array(
131
  'value' => '',
132
  'options' => array(
133
  '' => esc_html( _x( 'medium', 'medium size button', 'twitter' ) ),
134
+ 'large' => esc_html( _x( 'large', 'large size button', 'twitter' ) ),
135
  ),
136
  ),
137
  ),
238
  $options = array();
239
 
240
  if ( isset( $attributes['in_reply_to'] ) ) {
241
+ $tweet_id = \Twitter\WordPress\Shortcodes\Embeds\Tweet::sanitizeTweetID( (string) $attributes['in_reply_to'] );
242
  if ( $tweet_id ) {
243
  $options['in_reply_to'] = $tweet_id;
244
  }
291
 
292
  // large is the only option
293
  if ( isset( $attributes['size'] ) ) {
294
+ if ( is_string( $attributes['size'] ) && in_array( strtolower( $attributes['size'] ), array( 'large', 'l' ), /* strict */ true ) ) {
295
  $options['size'] = 'large';
296
  }
297
  }
304
  *
305
  * @since 1.0.0
306
  *
307
+ * @param array $options Tweet button options {
308
  * @type string option name
309
  * @type string|bool option value
310
  * }
311
+ * @param \WP_Post $post post of interest
312
  *
313
  * @return array Tweet button options with possible additions based on post data {
314
  * @type string option name
361
  }
362
 
363
  /**
364
+ * Process shortcode attributes received from the shortcode API
365
  *
366
+ * @since 2.0.0
367
  *
368
+ * @link https://codex.wordpress.org/Shortcode_API Shortcode API
 
369
  *
370
+ * @param array $attributes associative array of shortcode attributes, usually from the Shortcode API
371
+ *
372
+ * @return array array processed by shortcode_atts, prepped for Tweet object
373
  */
374
+ public static function getShortcodeAttributes( $attributes )
375
  {
376
  // clean up attribute to shortcode option mappings before passing to filter
377
  // apply the same filter as shortcode_atts
378
  /** This filter is documented in wp-includes/shortcodes.php */
379
+ return apply_filters(
380
  'shortcode_atts_' . self::SHORTCODE_TAG,
381
  array_merge(
382
  static::$SHORTCODE_DEFAULTS,
385
  static::$SHORTCODE_DEFAULTS,
386
  $attributes
387
  );
388
+ }
389
+
390
+ /**
391
+ * Handle shortcode macro
392
+ *
393
+ * @since 1.0.0
394
+ *
395
+ * @param array $attributes shortcode attributes
396
+ * @param string $content shortcode content. no effect
397
+ *
398
+ * @return string Tweet button HTML or empty string
399
+ */
400
+ public static function shortcodeHandler( $attributes, $content = null )
401
+ {
402
+ $options = static::getShortcodeAttributes( $attributes );
403
 
404
  // add options shared to post meta
405
  $options = static::addPostMetaOptions( $options );
406
 
407
+ // add parameters based on per-post render context
408
  if ( in_the_loop() ) {
409
  $post = get_post();
410
 
435
  unset( $via_username );
436
  }
437
 
438
+ $button = \Twitter\Widgets\Buttons\Tweet::fromArray( $options );
439
  if ( ! $button ) {
440
  return '';
441
  }
445
  return '';
446
  }
447
 
448
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
449
 
450
  $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
451
  if ( $inline_js ) {
src/Twitter/WordPress/Shortcodes/EmbeddedTweet.php DELETED
@@ -1,434 +0,0 @@
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 Tweet
30
- *
31
- * @since 1.0.0
32
- */
33
- class EmbeddedTweet implements ShortcodeInterface, PublishOEmbedEndpoint
34
- {
35
- use OEmbedTrait;
36
-
37
- /**
38
- * Shortcode tag to be matched
39
- *
40
- * @since 1.0.0
41
- *
42
- * @type string
43
- */
44
- const SHORTCODE_TAG = 'tweet';
45
-
46
- /**
47
- * oEmbed regex registered by WordPress Core
48
- *
49
- * @since 1.0.0
50
- *
51
- * @type string
52
- */
53
- const OEMBED_CORE_REGEX = '#https?://(www\.)?twitter\.com/.+?/status(es)?/.*#i';
54
-
55
- /**
56
- * Regex used to match a Tweet in text
57
- *
58
- * More specific than WordPress Core regex
59
- *
60
- * @since 1.0.0
61
- *
62
- * @type string
63
- */
64
- const TWEET_URL_REGEX = '#^https?://(www\.)?twitter\.com/[a-z0-9_]{1,20}/status(es)?/([0-9]+)#i';
65
-
66
- /**
67
- * Base URL used to reconstruct a Tweet URL
68
- *
69
- * @since 1.3.0
70
- *
71
- * @type string
72
- */
73
- const BASE_URL = 'https://twitter.com/_/status/';
74
-
75
- /**
76
- * Accepted values for the align parameter
77
- *
78
- * @since 1.0.0
79
- *
80
- * @type array
81
- */
82
- public static $ALIGN_OPTIONS = array( 'left' => true, 'center' => true, 'right' => true );
83
-
84
- /**
85
- * Accepted shortcode attributes and their default values
86
- *
87
- * @since 1.0.0
88
- *
89
- * @type array
90
- */
91
- public static $SHORTCODE_DEFAULTS = array( 'id' => '', 'conversation' => true, 'cards' => true, 'align' => '' );
92
-
93
- /**
94
- * Attach handlers for embedded Tweets
95
- *
96
- * @since 1.0.0
97
- *
98
- * @return void
99
- */
100
- public static function init()
101
- {
102
- $classname = get_called_class();
103
-
104
- // register our shortcode and its handler
105
- add_shortcode( self::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
106
-
107
- // Shortcode UI, if supported
108
- add_action(
109
- 'register_shortcode_ui',
110
- array( $classname, 'shortcodeUI' ),
111
- 5,
112
- 0
113
- );
114
-
115
- if ( ! is_admin() ) {
116
- // unhook the WordPress Core oEmbed handler
117
- wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
118
- // pass a Tweet detail URL through the Tweet shortcode handler
119
- wp_embed_register_handler(
120
- self::SHORTCODE_TAG,
121
- static::TWEET_URL_REGEX,
122
- array( $classname, 'linkHandler' ),
123
- 1
124
- );
125
- }
126
- }
127
-
128
- /**
129
- * Describe shortcode for Shortcake UI
130
- *
131
- * @since 1.1.0
132
- *
133
- * @link https://github.com/fusioneng/Shortcake Shortcake UI
134
- *
135
- * @return void
136
- */
137
- public static function shortcodeUI()
138
- {
139
- // Shortcake required
140
- if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
141
- return;
142
- }
143
-
144
- // id only
145
- // avoids an unchecked Shortcake input checkbox requiring a shortcode output
146
- shortcode_ui_register_for_shortcode(
147
- self::SHORTCODE_TAG,
148
- array(
149
- 'label' => esc_html( __( 'Embedded Tweet', 'twitter' ) ),
150
- 'listItemImage' => 'dashicons-twitter',
151
- 'attrs' => array(
152
- array(
153
- 'attr' => 'id',
154
- 'label' => 'ID',
155
- 'type' => 'text',
156
- 'meta' => array(
157
- 'required' => true,
158
- 'pattern' => '[0-9]+',
159
- 'placeholder' => '560070183650213889',
160
- ),
161
- ),
162
- ),
163
- )
164
- );
165
- }
166
-
167
- /**
168
- * Handle a URL matched by an embed handler
169
- *
170
- * @since 1.0.0
171
- *
172
- * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
173
- * @param array $attr Embed attributes. Not used.
174
- * @param string $url The original URL that was matched by the regex. Not used.
175
- * @param array $rawattr The original unmodified attributes. Not used.
176
- *
177
- * @return string HTML markup for the Tweet or an empty string if requirements not met
178
- */
179
- public static function linkHandler( $matches, $attr, $url, $rawattr )
180
- {
181
- if ( ! ( is_array( $matches ) && isset( $matches[3] ) && $matches[3] ) ) {
182
- return '';
183
- }
184
-
185
- return static::shortcodeHandler( array( 'id' => $matches[3] ) );
186
- }
187
-
188
- /**
189
- * Convert a Tweet ID in ID or URL form into a trimmed ID
190
- *
191
- * @since 1.0.0
192
- *
193
- * @param string $tweet_id Tweet identifier
194
- *
195
- * @return string $tweet_id Tweet identifier or empty string if minimum requirements not met
196
- */
197
- public static function sanitizeTweetID( $tweet_id )
198
- {
199
- if ( ! is_string( $tweet_id ) ) {
200
- return '';
201
- }
202
-
203
- $tweet_id = trim( $tweet_id );
204
- if ( ! $tweet_id ) {
205
- return '';
206
- }
207
- $tweet_id = trim( rtrim( trim( $tweet_id ), '/' ) );
208
- if ( ! $tweet_id ) {
209
- return '';
210
- }
211
-
212
- $last_slash = strrpos( $tweet_id, '/' );
213
- if ( false !== $last_slash ) {
214
- $tweet_id = substr( $tweet_id, $last_slash + 1 );
215
- }
216
-
217
- return $tweet_id;
218
- }
219
-
220
- /**
221
- * Convert shortcode parameters, attributes, and defaults into a clean set of Tweet parameters
222
- *
223
- * @since 1.0.0
224
- *
225
- * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
226
- * @type string|int attribute name or positional int
227
- * @type mixed shortcode value
228
- * }
229
- *
230
- * @return array cleaned up options ready for comparison {
231
- * @type string option name
232
- * @type string|bool option value
233
- * }
234
- */
235
- public static function sanitizeShortcodeParameters( $attributes = array() )
236
- {
237
- if ( ! is_array( $attributes ) ) {
238
- return array();
239
- }
240
-
241
- $options = array();
242
-
243
- if ( isset( $attributes['id'] ) ) {
244
- $tweet_id = static::sanitizeTweetID( (string) $attributes['id'] );
245
- if ( $tweet_id ) {
246
- $options['id'] = $tweet_id;
247
- }
248
- unset( $tweet_id );
249
- } else if ( isset( $attributes[0] ) ) {
250
- // compatibility with WordPress.com positional shortcode
251
- $tweet_id = static::sanitizeTweetID( (string) $attributes[0] );
252
- if ( $tweet_id ) {
253
- $options['id'] = $tweet_id;
254
- }
255
- unset( $tweet_id );
256
- }
257
-
258
- foreach ( array( 'cards' => 'hide_media', 'conversation' => 'hide_thread' ) as $falsey_option => $alternate_naming ) {
259
- // check for falsey values passed to shortcode
260
- if ( isset( $attributes[ $falsey_option ] ) ) {
261
- if ( false === $attributes[ $falsey_option ] || '0' == $attributes[ $falsey_option ] || ( is_string( $attributes[ $falsey_option ] ) && in_array( strtolower( $attributes[ $falsey_option ] ), array( 'false', 'no', 'off' ) ) ) ) {
262
- $options[ $falsey_option ] = false;
263
- }
264
- } else if ( isset( $attributes[ $alternate_naming ] ) ) {
265
- // test for an oEmbed-style parameter provided in the shortcode if the equivalent parameter was not defined
266
- if ( true === $attributes[ $alternate_naming ] || '1' == $attributes[ $alternate_naming ] || ( is_string( $attributes[ $alternate_naming ] ) && in_array( strtolower( $attributes[ $alternate_naming ] ), array( 'true', 'yes', 'on' ) ) ) ) {
267
- // test alternate attribute used by other shortcodes
268
- $options[ $falsey_option ] = false;
269
- }
270
- }
271
- }
272
-
273
- if ( isset( $attributes['align'] ) && $attributes['align'] ) {
274
- $attributes['align'] = trim( strtolower( $attributes['align'] ) );
275
- if ( array_key_exists( $attributes['align'], static::$ALIGN_OPTIONS ) ) {
276
- $options['align'] = $attributes['align'];
277
- }
278
- }
279
-
280
- return $options;
281
- }
282
-
283
- /**
284
- * Handle shortcode macro
285
- *
286
- * @since 1.0.0
287
- *
288
- * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
289
- * @type string|int attribute name or positional int
290
- * @type mixed shortcode value
291
- * }
292
- * @param string $content content inside a shortcode macro. no effect on this shortcode
293
- *
294
- * @return string HTML markup. empty string if parameter requirement not met or no Tweet info found
295
- */
296
- public static function shortcodeHandler( $attributes, $content = '' )
297
- {
298
- // clean up attribute to shortcode option mappings before passing to filter
299
- // apply the same filter as shortcode_atts
300
- /** This filter is documented in wp-includes/shortcodes.php */
301
- $options = apply_filters(
302
- 'shortcode_atts_' . self::SHORTCODE_TAG,
303
- array_merge(
304
- static::$SHORTCODE_DEFAULTS,
305
- static::sanitizeShortcodeParameters( (array) $attributes )
306
- ),
307
- static::$SHORTCODE_DEFAULTS,
308
- $attributes
309
- );
310
-
311
- if ( ! $options['id'] ) {
312
- return '';
313
- }
314
- $tweet_id = $options['id'];
315
- unset( $options['id'] );
316
-
317
- $oembed_params = static::shortcodeParamsToOEmbedParams( $tweet_id, $options );
318
- if ( empty( $oembed_params ) ) {
319
- return '';
320
- }
321
-
322
- // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
323
- $html = trim( static::getOEmbedMarkup( $oembed_params ) );
324
- if ( ! $html ) {
325
- return '';
326
- }
327
-
328
- $html = '<div class="twitter-tweet">' . $html . '</div>';
329
-
330
- $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
331
- if ( $inline_js ) {
332
- return $html . $inline_js;
333
- }
334
-
335
- return $html;
336
- }
337
-
338
- /**
339
- * Convert shortcode parameters into query parameters supported by the Twitter oEmbed endpoint
340
- *
341
- * @since 1.0.0
342
- *
343
- * @param string $tweet_id Tweet identifier
344
- * @param array $shortcode_options customizations specified in the shortcode
345
- *
346
- * @return array associative array of query parameters ready for http_build_query {
347
- * @type string query parameter name
348
- * @type string|bool query parameter value
349
- * }
350
- */
351
- public static function shortcodeParamsToOEmbedParams( $tweet_id, $shortcode_options = array() )
352
- {
353
- $query_parameters = static::getBaseOEmbedParams( $tweet_id );
354
- if ( empty( $query_parameters ) ) {
355
- return array();
356
- }
357
-
358
- // test for valid align value
359
- if ( isset( $shortcode_options['align'] ) && $shortcode_options['align'] && array_key_exists( $shortcode_options['align'], static::$ALIGN_OPTIONS ) ) {
360
- $query_parameters['align'] = $shortcode_options['align'];
361
- }
362
-
363
- // oembed parameters are the opposite of widget parameters
364
- // hide_* in oEmbed API
365
- foreach ( array( 'cards' => 'hide_media', 'conversation' => 'hide_thread' ) as $bool_option => $oembed_parameter ) {
366
- if ( isset( $shortcode_options[ $bool_option ] ) && false === $shortcode_options[ $bool_option ] ) {
367
- $query_parameters[ $oembed_parameter ] = true;
368
- }
369
- }
370
-
371
- return $query_parameters;
372
- }
373
-
374
- /**
375
- * Generate a unique string representing oEmbed result customizations set by shortcode parameters
376
- *
377
- * @since 1.0.0
378
- *
379
- * @param array $query_parameters associative array of query parameters sent to the oEmbed endpoint {
380
- * @type string query parameter name
381
- * @type string|bool query parameter value
382
- * }
383
- *
384
- * @return string cache key component
385
- */
386
- public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
387
- {
388
- $customizations = '';
389
-
390
- if ( isset( $query_parameters['hide_media'] ) && $query_parameters['hide_media'] ) {
391
- $customizations .= 'm';
392
- }
393
- if ( isset( $query_parameters['hide_thread'] ) && $query_parameters['hide_thread'] ) {
394
- $customizations .= 't';
395
- }
396
- // left, right, center
397
- if ( isset( $query_parameters['align'] ) && $query_parameters['align'] && array_key_exists( $query_parameters['align'], static::$ALIGN_OPTIONS ) ) {
398
- $customizations .= substr( $query_parameters['align'], 0, 1 );
399
- }
400
-
401
- return $customizations;
402
- }
403
-
404
- /**
405
- * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
406
- *
407
- * @since 1.0.0
408
- *
409
- * @link https://dev.twitter.com/rest/reference/get/statuses/oembed oEmbed doc
410
- * @param array $query_parameters oEmbed API query parameters
411
- *
412
- * @return string cache key
413
- */
414
- public static function oEmbedCacheKey( array $query_parameters )
415
- {
416
- if ( ! ( isset( $query_parameters['id'] ) && $query_parameters['id'] ) ) {
417
- return '';
418
- }
419
-
420
- $key_pieces = array( self::SHORTCODE_TAG, $query_parameters['id'] );
421
-
422
- // separate cache for each explicitly-defined display language
423
- if ( isset( $query_parameters['lang'] ) && $query_parameters['lang'] ) {
424
- $key_pieces[] = $query_parameters['lang'];
425
- }
426
-
427
- $customizations = static::getOEmbedCacheKeyCustomParameters( $query_parameters );
428
- if ( $customizations ) {
429
- $key_pieces[] = $customizations;
430
- }
431
-
432
- return implode( '_', $key_pieces );
433
- }
434
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/Twitter/WordPress/Shortcodes/{Moment.php → Embeds/Moment.php} RENAMED
@@ -23,14 +23,14 @@ 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 Moment
30
  *
31
  * @since 1.2.0
32
  */
33
- class Moment extends TweetGrid
34
  {
35
 
36
  /**
@@ -43,7 +43,7 @@ class Moment extends TweetGrid
43
  const SHORTCODE_TAG = 'twitter_moment';
44
 
45
  /**
46
- * oEmbed regex registered by WordPress Core
47
  *
48
  * @since 1.5.0
49
  *
@@ -89,4 +89,33 @@ class Moment extends TweetGrid
89
  {
90
  return __( 'Twitter Moment', 'twitter' );
91
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  }
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Shortcodes\Embeds;
27
 
28
  /**
29
  * Display a Moment
30
  *
31
  * @since 1.2.0
32
  */
33
+ class Moment extends Timeline\CollectionGrid
34
  {
35
 
36
  /**
43
  const SHORTCODE_TAG = 'twitter_moment';
44
 
45
  /**
46
+ * The oEmbed regex registered by WordPress Core
47
  *
48
  * @since 1.5.0
49
  *
89
  {
90
  return __( 'Twitter Moment', 'twitter' );
91
  }
92
+
93
+ /**
94
+ * Handle shortcode macro
95
+ *
96
+ * @since 2.0.0
97
+ *
98
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
99
+ * @type string|int attribute name or positional int
100
+ * @type mixed shortcode value
101
+ * }
102
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
103
+ *
104
+ * @return string HTML markup. empty string if parameter requirement not met or no collection info found
105
+ */
106
+ public static function shortcodeHandler( $attributes, $content = '' )
107
+ {
108
+ $options = static::getShortcodeAttributes( $attributes );
109
+ // Moment ID required
110
+ if ( ! $options['id'] ) {
111
+ return '';
112
+ }
113
+
114
+ $timeline = \Twitter\Widgets\Embeds\Moment::fromArray( $options );
115
+ if ( ! ( $timeline && $timeline->getID() ) ) {
116
+ return '';
117
+ }
118
+
119
+ return static::getHTMLForTimeline( $timeline );
120
+ }
121
  }
src/Twitter/WordPress/Shortcodes/Embeds/Timeline.php ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds;
27
+
28
+ /**
29
+ * Common references for a Twitter timeline
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ trait Timeline
34
+ {
35
+
36
+ /**
37
+ * Accepted timeline shortcode attributes and their default values
38
+ *
39
+ * @since 2.0.0
40
+ *
41
+ * @type array
42
+ */
43
+ public static $TIMELINE_SHORTCODE_DEFAULTS = array(
44
+ 'width' => null,
45
+ 'height' => null,
46
+ 'limit' => null,
47
+ 'chrome' => array(),
48
+ 'aria_polite' => 'polite',
49
+ 'lang' => null,
50
+ 'theme' => 'light',
51
+ 'link_color' => null,
52
+ 'border_color' => null,
53
+ );
54
+
55
+ /**
56
+ * Shortcode attributes to be converted from underscores to dashes before object initialization
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type array
61
+ */
62
+ public static $OPTIONS_KEYS_UNDERSCORE_TO_DASHES = array(
63
+ 'aria_polite' => 'aria-polite',
64
+ 'link_color' => 'link-color',
65
+ 'border_color' => 'border-color',
66
+ );
67
+
68
+ /**
69
+ * Shortcode attributes expected to be a positive integer value
70
+ *
71
+ * @since 2.0.0
72
+ *
73
+ * @type array
74
+ */
75
+ public static $OPTIONS_KEYS_INT_VALUES = array(
76
+ 'width',
77
+ 'height',
78
+ 'limit',
79
+ );
80
+
81
+ /**
82
+ * Attach handlers for shortcode, oEmbed URL, shortcode UI
83
+ *
84
+ * @since 2.0.0
85
+ *
86
+ * @return void
87
+ */
88
+ public static function init()
89
+ {
90
+ $classname = get_called_class();
91
+
92
+ // register our shortcode and its handler
93
+ add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
94
+
95
+ // Shortcode UI, if supported
96
+ add_action(
97
+ 'register_shortcode_ui',
98
+ array( $classname, 'shortcodeUI' ),
99
+ 5,
100
+ 0
101
+ );
102
+
103
+ if ( ! is_admin() ) {
104
+ // unhook the WordPress Core oEmbed handler
105
+ wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
106
+
107
+ // convert a URL into the shortcode equivalent
108
+ wp_embed_register_handler(
109
+ static::SHORTCODE_TAG,
110
+ static::URL_REGEX,
111
+ array( $classname, 'linkHandler' ),
112
+ 1
113
+ );
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Shortcode allowed attributes and default values for embedded timeline
119
+ *
120
+ * @since 2.0.0
121
+ *
122
+ * @return array shortcode allowed attributes and default values
123
+ */
124
+ public static function getShortcodeDefaults()
125
+ {
126
+ return array_merge( static::$TIMELINE_SHORTCODE_DEFAULTS, static::$SHORTCODE_DEFAULTS );
127
+ }
128
+
129
+ /**
130
+ * Process shortcode attributes received from the shortcode API
131
+ *
132
+ * @since 2.0.0
133
+ *
134
+ * @link https://codex.wordpress.org/Shortcode_API Shortcode API
135
+ *
136
+ * @param array $attributes associative array of shortcode attributes, usually from the Shortcode API
137
+ *
138
+ * @return array array processed by shortcode_atts, prepped for timeline objects
139
+ */
140
+ public static function getShortcodeAttributes( $attributes )
141
+ {
142
+ $options = shortcode_atts(
143
+ static::getShortcodeDefaults(),
144
+ $attributes,
145
+ static::SHORTCODE_TAG
146
+ );
147
+
148
+ if ( ! is_array( $options ) || empty( $options ) ) {
149
+ return array();
150
+ }
151
+
152
+ return static::shortcodeAttributesToTimelineKeys( $options );
153
+ }
154
+
155
+ /**
156
+ * Convert shortcode attributes provided as underscores to the dashed values expected by the object builder
157
+ *
158
+ * @since 2.0.0
159
+ *
160
+ * @param array $options shortcode options array {
161
+ * @type string shortcode attribute
162
+ * @type mixed shortcode attribute value
163
+ * }
164
+ *
165
+ * @return array shortcode attributes converted to keys and value types expected by timeline objects
166
+ */
167
+ public static function shortcodeAttributesToTimelineKeys( $options )
168
+ {
169
+ if ( ! is_array( $options ) ) {
170
+ return array();
171
+ }
172
+
173
+ // expected int values
174
+ $options = \Twitter\WordPress\Shortcodes\Helpers\Attributes::positiveIntegerOption(
175
+ $options,
176
+ static::$OPTIONS_KEYS_INT_VALUES
177
+ );
178
+
179
+ foreach ( static::$OPTIONS_KEYS_UNDERSCORE_TO_DASHES as $underscore => $dashes ) {
180
+ if ( ! isset( $options[ $underscore ] ) ) {
181
+ continue;
182
+ }
183
+ $options[ $dashes ] = $options[ $underscore ];
184
+ unset( $options[ $underscore ] );
185
+ }
186
+
187
+ // set multiple chrome tokens through CSV
188
+ if ( isset( $options['chrome'] ) && ! is_array( $options['chrome'] ) ) {
189
+ $options['chrome'] = explode( ',', $options['chrome'] );
190
+ }
191
+
192
+ return $options;
193
+ }
194
+
195
+ /**
196
+ * Generate a unique string representing oEmbed result customizations
197
+ *
198
+ * @since 2.0.0
199
+ *
200
+ * @param array $query_parameters associative array of query parameters sent to the oEmbed endpoint {
201
+ * @type string query parameter name
202
+ * @type string|bool query parameter value
203
+ * }
204
+ *
205
+ * @return string cache key component
206
+ */
207
+ public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
208
+ {
209
+ if ( empty( $query_parameters ) ) {
210
+ return '';
211
+ }
212
+
213
+ $key_pieces = array();
214
+
215
+ if ( isset( $query_parameters['maxwidth'] ) ) {
216
+ $key_pieces[] = 'w' . $query_parameters['maxwidth'];
217
+ }
218
+ if ( isset( $query_parameters['limit'] ) ) {
219
+ $key_pieces[] = 'l' . $query_parameters['limit'];
220
+ } else if ( isset( $query_parameters['maxheight'] ) ) {
221
+ $key_pieces[] = 'h' . $query_parameters['maxheight'];
222
+ }
223
+
224
+ if ( isset( $query_parameters['chrome'] ) && $query_parameters['chrome'] ) {
225
+ $chrome = explode( ' ', $query_parameters['chrome'] );
226
+ $chrome_piece = array();
227
+ $abbreviated_chrome_tokens = array(
228
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOHEADER => 'h',
229
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOFOOTER => 'f',
230
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOBORDERS => 'b',
231
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOSCROLLBAR => 's',
232
+ \Twitter\Widgets\Embeds\Timeline::CHROME_TRANSPARENT => 't',
233
+ );
234
+ foreach ( $chrome as $token ) {
235
+ if ( isset( $abbreviated_chrome_tokens[ $token ] ) ) {
236
+ $chrome_piece[ $abbreviated_chrome_tokens[ $token ] ] = true;
237
+ }
238
+ }
239
+ if ( ! empty( $chrome_piece ) ) {
240
+ $key_pieces[] = implode( '', array_keys( $chrome_piece ) );
241
+ }
242
+ unset( $chrome_piece );
243
+ unset( $chrome );
244
+ }
245
+
246
+ // ARIA live region override: assertive
247
+ if ( isset( $query_parameters['aria_polite'] ) ) {
248
+ $key_pieces[] = 'a';
249
+ }
250
+
251
+ // should only be set for dark theme
252
+ if ( isset( $query_parameters['theme'] ) ) {
253
+ $key_pieces[] = 'd';
254
+ }
255
+
256
+ $color_parameters = array(
257
+ 'link_color' => 'l',
258
+ 'border_color' => 'b',
259
+ );
260
+ foreach ( $color_parameters as $query_parameter => $cache_abbreviation ) {
261
+ if ( isset( $query_parameters[ $query_parameter ] ) ) {
262
+ $key_pieces[] = $cache_abbreviation . ltrim( $query_parameters[ $query_parameter ], '#' );
263
+ }
264
+ }
265
+
266
+ return implode( '_', $key_pieces );
267
+ }
268
+
269
+ /**
270
+ * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache. 172 characters or fewer
271
+ *
272
+ * @since 2.0.0
273
+ *
274
+ * @link https://dev.twitter.com/web/embedded-timelines/oembed embedded timelines oEmbed
275
+ *
276
+ * @param string $id datasource ID
277
+ * @param array $query_parameters oEmbed API query parameters
278
+ *
279
+ * @return string cache key
280
+ */
281
+ public static function getOEmbedCacheKey( $id, array $query_parameters )
282
+ {
283
+ if ( ! ( is_string( $id ) && $id ) ) {
284
+ return '';
285
+ }
286
+
287
+ $key_pieces = array( static::SHORTCODE_TAG, $id );
288
+
289
+ if ( isset( $query_parameters['lang'] ) ) {
290
+ $key_pieces[] = $query_parameters['lang'];
291
+ }
292
+
293
+ if ( isset( $query_parameters['dnt'] ) ) {
294
+ $key_pieces[] = 'p';
295
+ }
296
+
297
+ $customizations = static::getOEmbedCacheKeyCustomParameters( $query_parameters );
298
+ if ( $customizations ) {
299
+ $key_pieces[] = $customizations;
300
+ }
301
+
302
+ return implode( '_', $key_pieces );
303
+ }
304
+
305
+ /**
306
+ * Get HTML markup for a timeline
307
+ *
308
+ * @since 2.0.0
309
+ *
310
+ * @param \Twitter\Widgets\Embeds\Timeline $timeline timeline object
311
+ *
312
+ * @return string HTML markup or empty string if minimum requirements not met
313
+ */
314
+ public static function getHTMLForTimeline( $timeline )
315
+ {
316
+ // verify passed parameter
317
+ if ( ! is_a( $timeline, '\Twitter\Widgets\Embeds\Timeline' ) ) {
318
+ return '';
319
+ }
320
+
321
+ $oembed_params = $timeline->toOEmbedParameterArray();
322
+ if ( ! isset( $oembed_params['url'] ) ) {
323
+ return '';
324
+ }
325
+ $oembed_params = array_merge( static::getBaseOEmbedParams(), $oembed_params );
326
+
327
+ $cache_key = static::getOEmbedCacheKey( static::getDataSourceIdentifier( $timeline ), $oembed_params );
328
+ if ( ! $cache_key ) {
329
+ return '';
330
+ }
331
+
332
+ // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
333
+ $html = trim( static::getOEmbedMarkup( $oembed_params, $cache_key ) );
334
+ if ( ! $html ) {
335
+ return '';
336
+ }
337
+
338
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
339
+
340
+ $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
341
+ if ( $inline_js ) {
342
+ return $html . $inline_js;
343
+ }
344
+
345
+ return $html;
346
+ }
347
+ }
src/Twitter/WordPress/Shortcodes/Embeds/Timeline/Collection.php ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display a collection of Tweets
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Collection implements \Twitter\WordPress\Shortcodes\ShortcodeInterface, \Twitter\WordPress\Shortcodes\PublishOEmbedEndpoint
34
+ {
35
+ use \Twitter\WordPress\Shortcodes\OEmbedTrait, \Twitter\WordPress\Shortcodes\Embeds\Timeline;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'twitter_collection';
45
+
46
+ /**
47
+ * The oEmbed regex registered by WordPress Core
48
+ *
49
+ * @since 1.5.0
50
+ *
51
+ * @type string
52
+ */
53
+ const OEMBED_CORE_REGEX = '#https?://(www\.)?twitter\.com/\w{1,15}/timelines/.*#i';
54
+
55
+ /**
56
+ * HTML class to be used in div wrapper
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type string
61
+ */
62
+ const HTML_CLASS = 'twitter-collection';
63
+
64
+ /**
65
+ * Regex used to match a Collection in text
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @type string
70
+ */
71
+ const URL_REGEX = '#^https://twitter\.com/[a-z0-9_]{1,20}/timelines/([0-9]+)#i';
72
+
73
+ /**
74
+ * Base URL used to reconstruct a Collection URL
75
+ *
76
+ * @since 2.0.0
77
+ *
78
+ * @type string
79
+ */
80
+ const BASE_URL = 'https://twitter.com/_/timelines/';
81
+
82
+ /**
83
+ * Accepted shortcode attributes and their default values
84
+ *
85
+ * @since 2.0.0
86
+ *
87
+ * @type array
88
+ */
89
+ public static $SHORTCODE_DEFAULTS = array( 'id' => '', 'template' => '' );
90
+
91
+ /**
92
+ * Reference the feature by name
93
+ *
94
+ * @since 2.0.0
95
+ *
96
+ * @return string translated feature name
97
+ */
98
+ public static function featureName()
99
+ {
100
+ return _x( 'Twitter Collection', 'Tweets organized into a collection', 'twitter' );
101
+ }
102
+
103
+ /**
104
+ * Describe shortcode for Shortcake UI
105
+ *
106
+ * @since 2.0.0
107
+ *
108
+ * @link https://github.com/wp-shortcake/shortcake Shortcake UI
109
+ *
110
+ * @return void
111
+ */
112
+ public static function shortcodeUI()
113
+ {
114
+ // Shortcake required
115
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
116
+ return;
117
+ }
118
+
119
+ shortcode_ui_register_for_shortcode(
120
+ static::SHORTCODE_TAG,
121
+ array(
122
+ 'label' => esc_html( static::featureName() ),
123
+ 'listItemImage' => 'dashicons-twitter',
124
+ 'attrs' => array(
125
+ array(
126
+ 'attr' => 'id',
127
+ 'label' => 'ID',
128
+ 'type' => 'text',
129
+ 'meta' => array(
130
+ 'required' => true,
131
+ 'pattern' => '[0-9]+',
132
+ ),
133
+ ),
134
+ array(
135
+ 'attr' => 'limit',
136
+ 'label' => _x( 'Limit', 'Maximum number of items to include', 'twitter' ),
137
+ 'type' => 'number',
138
+ 'meta' => array(
139
+ 'min' => 1,
140
+ 'max' => 20,
141
+ 'step' => 1,
142
+ ),
143
+ ),
144
+ ),
145
+ )
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Handle a URL matched by a embed handler
151
+ *
152
+ * @since 2.0.0
153
+ *
154
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
155
+ *
156
+ * @return string HTML markup for the Twitter Collection or an empty string if requirements not met
157
+ */
158
+ public static function linkHandler( $matches )
159
+ {
160
+ if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
161
+ return '';
162
+ }
163
+
164
+ return static::shortcodeHandler( array( 'id' => $matches[1] ) );
165
+ }
166
+
167
+ /**
168
+ * Handle shortcode macro
169
+ *
170
+ * @since 2.0.0
171
+ *
172
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
173
+ * @type string|int attribute name or positional int
174
+ * @type mixed shortcode value
175
+ * }
176
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
177
+ *
178
+ * @return string HTML markup. empty string if parameter requirement not met or no collection info found
179
+ */
180
+ public static function shortcodeHandler( $attributes, $content = '' )
181
+ {
182
+ // allow grid displays to be triggered from the main collection shortcode, handled by the compatibility layer of the grid shortcode
183
+ if ( isset( $attributes['template'] ) && \Twitter\Widgets\Embeds\Timeline\Collection::WIDGET_TYPE_GRID === $attributes['template'] ) {
184
+ return \Twitter\WordPress\Shortcodes\Embeds\Timeline\CollectionGrid::shortcodeHandler( $attributes, $content );
185
+ }
186
+ $options = static::getShortcodeAttributes( $attributes );
187
+ // collection ID required
188
+ if ( ! $options['id'] ) {
189
+ return '';
190
+ }
191
+
192
+ $timeline = \Twitter\Widgets\Embeds\Timeline\Collection::fromArray( $options );
193
+ if ( ! ( $timeline && $timeline->getID() ) ) {
194
+ return '';
195
+ }
196
+
197
+ return static::getHTMLForTimeline( $timeline );
198
+ }
199
+
200
+ /**
201
+ * Get a unique identifier for the datasource to uniquely identify
202
+ *
203
+ * Used in oEmbed cache key to save a short, unique representation of the datasource
204
+ *
205
+ * @since 2.0.0
206
+ *
207
+ * @param \Twitter\Widgets\Embeds\Timeline $timeline timeline object
208
+ *
209
+ * @return string unique identifier or empty string if minimum requirements not met
210
+ */
211
+ public static function getDataSourceIdentifier( $timeline )
212
+ {
213
+ // verify passed parameter
214
+ if ( ! ( is_a( $timeline, '\Twitter\Widgets\Embeds\Timeline' ) && method_exists( $timeline, 'getID' ) ) ) {
215
+ return '';
216
+ }
217
+
218
+ return $timeline->getID();
219
+ }
220
+ }
src/Twitter/WordPress/Shortcodes/Embeds/Timeline/CollectionGrid.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display a collection of Tweets in a grid template
30
+ *
31
+ * Maintains compatibility with the twitter_grid shortcode added in 1.3.0
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ class CollectionGrid extends \Twitter\WordPress\Shortcodes\Embeds\Timeline\Collection
36
+ {
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 2.0.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 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const HTML_CLASS = 'twitter-collection-grid';
54
+
55
+ /**
56
+ * Accepted shortcode attributes and their default values
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type array
61
+ */
62
+ public static $SHORTCODE_DEFAULTS = array( 'id' => '' );
63
+
64
+ /**
65
+ * Collection shortcode defaults not supported in a grid template
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @type string[] shortcode defaults to remove if seen
70
+ */
71
+ public static $UNSUPPORTED_SHORTCODE_DEFAULTS = array(
72
+ 'height',
73
+ 'aria_polite',
74
+ 'theme',
75
+ 'link_color',
76
+ 'border_color',
77
+ );
78
+
79
+ /**
80
+ * Reference the feature by name
81
+ *
82
+ * @since 2.0.0
83
+ *
84
+ * @return string translated feature name
85
+ */
86
+ public static function featureName()
87
+ {
88
+ return _x( 'Twitter Collection Grid', 'Tweets organized into a collection displayed in a grid template', 'twitter' );
89
+ }
90
+
91
+ /**
92
+ * Override the shortcode initialization and URL handler init with only shortcode logic
93
+ *
94
+ * @since 2.0.0
95
+ *
96
+ * @return void
97
+ */
98
+ public static function init()
99
+ {
100
+ $classname = get_called_class();
101
+
102
+ // register our shortcode and its handler
103
+ add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
104
+
105
+ // Shortcode UI, if supported
106
+ add_action(
107
+ 'register_shortcode_ui',
108
+ array( $classname, 'shortcodeUI' ),
109
+ 5,
110
+ 0
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Shortcode allowed attributes and default values for grid template
116
+ *
117
+ * Remove attributes only applicable to standard timelines, allowing developers acting on the shortcode_atts_twitter_grid filter to have only valid parameters
118
+ *
119
+ * @since 2.0.0
120
+ *
121
+ * @return array shortcode allowed attributes and default values
122
+ */
123
+ public static function getShortcodeDefaults()
124
+ {
125
+ $attributes = array_merge( static::$TIMELINE_SHORTCODE_DEFAULTS, static::$SHORTCODE_DEFAULTS );
126
+
127
+ foreach ( static::$UNSUPPORTED_SHORTCODE_DEFAULTS as $key ) {
128
+ unset( $attributes[ $key ] );
129
+ }
130
+
131
+ return $attributes;
132
+ }
133
+
134
+ /**
135
+ * Handle shortcode macro
136
+ *
137
+ * @since 2.0.0
138
+ *
139
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
140
+ * @type string|int attribute name or positional int
141
+ * @type mixed shortcode value
142
+ * }
143
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
144
+ *
145
+ * @return string HTML markup. empty string if parameter requirement not met or no collection info found
146
+ */
147
+ public static function shortcodeHandler( $attributes, $content = '' )
148
+ {
149
+ $options = static::getShortcodeAttributes( $attributes );
150
+ // collection ID required
151
+ if ( ! $options['id'] ) {
152
+ return '';
153
+ }
154
+ $options['widget_type'] = \Twitter\Widgets\Embeds\Timeline\Collection::WIDGET_TYPE_GRID;
155
+
156
+ $timeline = \Twitter\Widgets\Embeds\Timeline\Collection::fromArray( $options );
157
+ if ( ! ( $timeline && $timeline->getID() ) ) {
158
+ return '';
159
+ }
160
+
161
+ return static::getHTMLForTimeline( $timeline );
162
+ }
163
+ }
src/Twitter/WordPress/Shortcodes/Embeds/Timeline/Profile.php ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display the latest Tweets from a Twitter account
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Profile implements \Twitter\WordPress\Shortcodes\ShortcodeInterface, \Twitter\WordPress\Shortcodes\PublishOEmbedEndpoint
34
+ {
35
+ use \Twitter\WordPress\Shortcodes\OEmbedTrait, \Twitter\WordPress\Shortcodes\AuthorContext, \Twitter\WordPress\Shortcodes\Embeds\Timeline;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'twitter_profile';
45
+
46
+ /**
47
+ * The oEmbed regex registered by WordPress Core
48
+ *
49
+ * @since 1.5.0
50
+ *
51
+ * @type string
52
+ */
53
+ const OEMBED_CORE_REGEX = '#https?://(www\.)?twitter\.com/\w{1,15}$#i';
54
+
55
+ /**
56
+ * Regex used to match a Profile in text
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type string
61
+ */
62
+ const URL_REGEX = '#^https://twitter\.com/([a-z0-9_]{1,20})$#i';
63
+
64
+ /**
65
+ * HTML class to be used in div wrapper
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @type string
70
+ */
71
+ const HTML_CLASS = 'twitter-timeline-profile';
72
+
73
+ /**
74
+ * Base URL used to reconstruct a Profile URL
75
+ *
76
+ * @since 2.0.0
77
+ *
78
+ * @type string
79
+ */
80
+ const BASE_URL = 'https://twitter.com/';
81
+
82
+ /**
83
+ * Accepted shortcode attributes and their default values
84
+ *
85
+ * @since 2.0.0
86
+ *
87
+ * @type array
88
+ */
89
+ public static $SHORTCODE_DEFAULTS = array( 'screen_name' => '' );
90
+
91
+ /**
92
+ * Reference the feature by name
93
+ *
94
+ * @since 2.0.0
95
+ *
96
+ * @return string translated feature name
97
+ */
98
+ public static function featureName()
99
+ {
100
+ return _x( 'Twitter Profile', 'The latest Tweets authored by a Twitter account', 'twitter' );
101
+ }
102
+
103
+ /**
104
+ * Describe shortcode for Shortcake UI
105
+ *
106
+ * @since 2.0.0
107
+ *
108
+ * @link https://github.com/wp-shortcake/shortcake Shortcode UI
109
+ *
110
+ * @return void
111
+ */
112
+ public static function shortcodeUI()
113
+ {
114
+ // Shortcake required
115
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
116
+ return;
117
+ }
118
+
119
+ shortcode_ui_register_for_shortcode(
120
+ static::SHORTCODE_TAG,
121
+ array(
122
+ 'label' => esc_html( static::featureName() ),
123
+ 'listItemImage' => 'dashicons-twitter',
124
+ 'attrs' => array(
125
+ array(
126
+ 'attr' => 'screen_name',
127
+ 'label' => esc_html( __( 'Twitter @username', 'twitter' ) ),
128
+ 'type' => 'text',
129
+ 'meta' => array(
130
+ 'placeholder' => 'WordPress',
131
+ 'pattern' => '[A-Za-z0-9_]{1,20}',
132
+ ),
133
+ ),
134
+ ),
135
+ )
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Handle a URL matched by a embed handler
141
+ *
142
+ * @since 2.0.0
143
+ *
144
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
145
+ * @param array $attr Embed attributes. Not used.
146
+ * @param string $url The original URL that was matched by the regex. Not used.
147
+ * @param array $rawattr The original unmodified attributes. Not used.
148
+ *
149
+ * @return string HTML markup for the profile timeline or an empty string if requirements not met
150
+ */
151
+ public static function linkHandler( $matches, $attr, $url, $rawattr )
152
+ {
153
+ if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
154
+ return '';
155
+ }
156
+
157
+ return static::shortcodeHandler( array( 'screen_name' => $matches[1] ) );
158
+ }
159
+
160
+ /**
161
+ * Handle shortcode macro
162
+ *
163
+ * @since 2.0.0
164
+ *
165
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
166
+ * @type string|int attribute name or positional int
167
+ * @type mixed shortcode value
168
+ * }
169
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
170
+ *
171
+ * @return string HTML markup. empty string if parameter requirement not met or no profile info found
172
+ */
173
+ public static function shortcodeHandler( $attributes, $content = '' )
174
+ {
175
+ $options = static::getShortcodeAttributes( $attributes );
176
+
177
+ $screen_name = '';
178
+ if ( isset( $options['screen_name'] ) ) {
179
+ $screen_name = $options['screen_name'];
180
+ unset( $options['screen_name'] );
181
+ }
182
+ if ( ! $screen_name ) {
183
+ // set screen name from rendering context
184
+ $screen_name = static::getScreenName();
185
+ // profile data source required
186
+ if ( ! $screen_name ) {
187
+ return '';
188
+ }
189
+ }
190
+ $options['screen_name'] = $screen_name;
191
+
192
+ $timeline = \Twitter\Widgets\Embeds\Timeline\Profile::fromArray( $options );
193
+ if ( ! ( $timeline && $timeline->getScreenName() ) ) {
194
+ return '';
195
+ }
196
+
197
+ return static::getHTMLForTimeline( $timeline );
198
+ }
199
+
200
+ /**
201
+ * Get a unique identifier for the datasource to uniquely identify
202
+ *
203
+ * Used in oEmbed cache key to save a short, unique representation of the datasource
204
+ *
205
+ * @since 2.0.0
206
+ *
207
+ * @param \Twitter\Widgets\Embeds\Timeline $timeline timeline object
208
+ *
209
+ * @return string unique identifier or empty string if minimum requirements not met
210
+ */
211
+ public static function getDataSourceIdentifier( $timeline )
212
+ {
213
+ // verify passed parameter
214
+ if ( ! is_a( $timeline, '\Twitter\Widgets\Embeds\Timeline' ) ) {
215
+ return '';
216
+ }
217
+
218
+ return $timeline->getScreenName();
219
+ }
220
+ }
src/Twitter/WordPress/Shortcodes/Embeds/Timeline/Search.php ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2017 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display recent search results in a timeline
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Search implements \Twitter\WordPress\Shortcodes\ShortcodeInterface
34
+ {
35
+ use \Twitter\WordPress\Shortcodes\Embeds\Timeline;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'twitter_search';
45
+
46
+ /**
47
+ * HTML class to be used in div wrapper
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const HTML_CLASS = 'twitter-timeline-search';
54
+
55
+ /**
56
+ * Accepted shortcode attributes and their default values
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type array
61
+ */
62
+ public static $SHORTCODE_DEFAULTS = array( 'widget_id' => '', 'terms' => '' );
63
+
64
+ /**
65
+ * Reference the feature by name
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @return string translated feature name
70
+ */
71
+ public static function featureName()
72
+ {
73
+ return _x( 'Twitter Search', 'Tweets matching a search result query', 'twitter' );
74
+ }
75
+
76
+ /**
77
+ * Attach handlers for shortcode, shortcode UI
78
+ *
79
+ * @since 2.0.0
80
+ *
81
+ * @return void
82
+ */
83
+ public static function init()
84
+ {
85
+ $classname = get_called_class();
86
+
87
+ // register our shortcode and its handler
88
+ add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
89
+
90
+ // Shortcode UI, if supported
91
+ add_action(
92
+ 'register_shortcode_ui',
93
+ array( $classname, 'shortcodeUI' ),
94
+ 5,
95
+ 0
96
+ );
97
+ }
98
+
99
+ /**
100
+ * Describe shortcode for Shortcake UI
101
+ *
102
+ * @since 2.0.0
103
+ *
104
+ * @link https://github.com/wp-shortcake/shortcake Shortcode UI
105
+ *
106
+ * @return void
107
+ */
108
+ public static function shortcodeUI()
109
+ {
110
+ // Shortcake required
111
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
112
+ return;
113
+ }
114
+
115
+ shortcode_ui_register_for_shortcode(
116
+ static::SHORTCODE_TAG,
117
+ array(
118
+ 'label' => esc_html( static::featureName() ),
119
+ 'listItemImage' => 'dashicons-twitter',
120
+ 'attrs' => array(
121
+ array(
122
+ 'attr' => 'widget_id',
123
+ 'label' => esc_html( __( 'Widget ID', 'twitter' ) ),
124
+ 'type' => 'text',
125
+ 'meta' => array(
126
+ 'pattern' => '[0-9]+',
127
+ ),
128
+ ),
129
+ ),
130
+ )
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Handle shortcode macro
136
+ *
137
+ * @since 2.0.0
138
+ *
139
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
140
+ * @type string|int attribute name or positional int
141
+ * @type mixed shortcode value
142
+ * }
143
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
144
+ *
145
+ * @return string HTML markup. empty string if parameter requirement not met or no profile info found
146
+ */
147
+ public static function shortcodeHandler( $attributes, $content = '' )
148
+ {
149
+ $options = static::getShortcodeAttributes( $attributes );
150
+ if ( isset( $options['widget_id'] ) ) {
151
+ $options['widget_id'] = trim( $options['widget_id'] );
152
+ } else {
153
+ return '';
154
+ }
155
+
156
+ $timeline = \Twitter\Widgets\Embeds\Timeline\Search::fromArray( $options );
157
+ if ( ! ( $timeline && $timeline->getWidgetID() ) ) {
158
+ return '';
159
+ }
160
+
161
+ return static::getHTMLForTimeline( $timeline );
162
+ }
163
+
164
+ /**
165
+ * Get HTML markup for a timeline
166
+ *
167
+ * @since 2.0.0
168
+ *
169
+ * @param \Twitter\Widgets\Embeds\Timeline\Search $timeline timeline object
170
+ *
171
+ * @return string HTML markup or empty string if minimum requirements not met
172
+ */
173
+ public static function getHTMLForTimeline( $timeline )
174
+ {
175
+ // verify passed parameter
176
+ if ( ! is_a( $timeline, '\Twitter\Widgets\Embeds\Timeline\Search' ) ) {
177
+ return '';
178
+ }
179
+
180
+ if ( $timeline->getSearchTerms() ) {
181
+ $link_text = _x( 'Tweets about %s', 'Tweets about a term or topic', 'twitter' );
182
+ } else {
183
+ $link_text = static::featureName();
184
+ }
185
+
186
+ $html = $timeline->toHTML( $link_text, '\Twitter\WordPress\Helpers\HTMLBuilder' );
187
+ if ( ! $html ) {
188
+ return '';
189
+ }
190
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
191
+
192
+ $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
193
+ if ( $inline_js ) {
194
+ return $html . $inline_js;
195
+ }
196
+
197
+ return $html;
198
+ }
199
+
200
+ /**
201
+ * oEmbed not supported for search timeline
202
+ *
203
+ * @since 2.0.0
204
+ *
205
+ * @param array $query_parameters not used
206
+ *
207
+ * @return string empty string
208
+ */
209
+ public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
210
+ {
211
+ return '';
212
+ }
213
+
214
+ /**
215
+ * oEmbed not supported for search timeline
216
+ *
217
+ * @since 2.0.0
218
+ *
219
+ * @param string $id not used
220
+ * @param array $query_parameters not used
221
+ *
222
+ * @return string empty string
223
+ */
224
+ public static function getOEmbedCacheKey( $id, array $query_parameters )
225
+ {
226
+ return '';
227
+ }
228
+ }
src/Twitter/WordPress/Shortcodes/Embeds/Timeline/TwitterList.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Display the latest Tweets from a list of Twitter accounts
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class TwitterList implements \Twitter\WordPress\Shortcodes\ShortcodeInterface, \Twitter\WordPress\Shortcodes\PublishOEmbedEndpoint
34
+ {
35
+ use \Twitter\WordPress\Shortcodes\OEmbedTrait, \Twitter\WordPress\Shortcodes\Embeds\Timeline;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'twitter_list';
45
+
46
+ /**
47
+ * The oEmbed regex registered by WordPress Core
48
+ *
49
+ * @since 1.5.0
50
+ *
51
+ * @type string
52
+ */
53
+ const OEMBED_CORE_REGEX = '#https?://(www\.)?twitter\.com/\w{1,15}/lists/.*#i';
54
+
55
+ /**
56
+ * Regex used to match a List in text
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @type string
61
+ */
62
+ const URL_REGEX = '#^https://twitter\.com/([a-z0-9_]{1,20})/lists/([a-z][a-z0-9_\\-]{0,24})$#i';
63
+
64
+ /**
65
+ * HTML class to be used in div wrapper
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @type string
70
+ */
71
+ const HTML_CLASS = 'twitter-timeline-list';
72
+
73
+ /**
74
+ * Accepted shortcode attributes and their default values
75
+ *
76
+ * @since 2.0.0
77
+ *
78
+ * @type array
79
+ */
80
+ public static $SHORTCODE_DEFAULTS = array( 'screen_name' => '', 'slug' => '' );
81
+
82
+ /**
83
+ * Reference the feature by name
84
+ *
85
+ * @since 2.0.0
86
+ *
87
+ * @return string translated feature name
88
+ */
89
+ public static function featureName()
90
+ {
91
+ return _x( 'Twitter List', 'The latest Tweets authored by members of a Twitter list', 'twitter' );
92
+ }
93
+
94
+ /**
95
+ * Describe shortcode for Shortcake UI
96
+ *
97
+ * @since 2.0.0
98
+ *
99
+ * @link https://github.com/wp-shortcake/shortcake Shortcode UI
100
+ *
101
+ * @return void
102
+ */
103
+ public static function shortcodeUI()
104
+ {
105
+ // Shortcode required
106
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
107
+ return;
108
+ }
109
+
110
+ shortcode_ui_register_for_shortcode(
111
+ static::SHORTCODE_TAG,
112
+ array(
113
+ 'label' => esc_html( static::featureName() ),
114
+ 'listItemImage' => 'dashicons-twitter',
115
+ 'attrs' => array(
116
+ array(
117
+ 'attr' => 'screen_name',
118
+ 'label' => esc_html( __( 'Twitter @username', 'twitter' ) ),
119
+ 'type' => 'text',
120
+ 'meta' => array(
121
+ 'placeholder' => 'UN',
122
+ 'pattern' => '[A-Za-z0-9_]{1,20}',
123
+ ),
124
+ ),
125
+ array(
126
+ 'attr' => 'slug',
127
+ 'label' => esc_html( _x( 'List slug', 'Unique identifier for a Twitter List', 'twitter' ) ),
128
+ 'type' => 'text',
129
+ 'meta' => array(
130
+ 'placeholder' => 'security-council',
131
+ 'pattern' => '[a-z][a-z0-9_\\-]{0,24}',
132
+ ),
133
+ ),
134
+ ),
135
+ )
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Handle a URL matched by a embed handler
141
+ *
142
+ * @since 2.0.0
143
+ *
144
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
145
+ * @param array $attr Embed attributes. Not used.
146
+ * @param string $url The original URL that was matched by the regex. Not used.
147
+ * @param array $rawattr The original unmodified attributes. Not used.
148
+ *
149
+ * @return string HTML markup for the profile timeline or an empty string if requirements not met
150
+ */
151
+ public static function linkHandler( $matches, $attr, $url, $rawattr )
152
+ {
153
+ if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] && isset( $matches[2] ) && $matches[2] ) ) {
154
+ return '';
155
+ }
156
+
157
+ return static::shortcodeHandler( array( 'screen_name' => $matches[1], 'slug' => $matches[2] ) );
158
+ }
159
+
160
+ /**
161
+ * Handle shortcode macro
162
+ *
163
+ * @since 2.0.0
164
+ *
165
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
166
+ * @type string|int attribute name or positional int
167
+ * @type mixed shortcode value
168
+ * }
169
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
170
+ *
171
+ * @return string HTML markup. empty string if parameter requirement not met or no profile info found
172
+ */
173
+ public static function shortcodeHandler( $attributes, $content = '' )
174
+ {
175
+ $options = static::getShortcodeAttributes( $attributes );
176
+
177
+ if ( ! ($options['screen_name'] && $options['slug']) ) {
178
+ return '';
179
+ }
180
+
181
+ $timeline = \Twitter\Widgets\Embeds\Timeline\TwitterList::fromArray( $options );
182
+ if ( ! ( $timeline && $timeline->getScreenName() && $timeline->getSlug() ) ) {
183
+ return '';
184
+ }
185
+
186
+ return static::getHTMLForTimeline( $timeline );
187
+ }
188
+
189
+ /**
190
+ * Get a unique identifier for the datasource to uniquely identify
191
+ *
192
+ * Used in oEmbed cache key to save a short, unique representation of the datasource
193
+ *
194
+ * @since 2.0.0
195
+ *
196
+ * @param \Twitter\Widgets\Embeds\Timeline $timeline timeline object
197
+ *
198
+ * @return string unique identifier or empty string if minimum requirements not met
199
+ */
200
+ public static function getDataSourceIdentifier( $timeline )
201
+ {
202
+ // verify passed parameter
203
+ if ( ! is_a( $timeline, '\Twitter\Widgets\Embeds\Timeline' ) ) {
204
+ return '';
205
+ }
206
+
207
+ $screen_name = $timeline->getScreenName();
208
+ if ( ! $screen_name ) {
209
+ return '';
210
+ }
211
+ $slug = $timeline->getSlug();
212
+ if ( ! $slug ) {
213
+ return '';
214
+ }
215
+
216
+ return $screen_name . '/' . $slug;
217
+ }
218
+ }
src/Twitter/WordPress/Shortcodes/Embeds/Tweet.php ADDED
@@ -0,0 +1,559 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Embeds;
27
+
28
+ /**
29
+ * Display a Tweet
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Tweet implements \Twitter\WordPress\Shortcodes\ShortcodeInterface, \Twitter\WordPress\Shortcodes\PublishOEmbedEndpoint
34
+ {
35
+ use \Twitter\WordPress\Shortcodes\OEmbedTrait;
36
+
37
+ /**
38
+ * Shortcode tag to be matched
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_TAG = 'tweet';
45
+
46
+ /**
47
+ * The oEmbed regex registered by WordPress Core
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const OEMBED_CORE_REGEX = '#https?://(www\.)?twitter\.com/.+?/status(es)?/.*#i';
54
+
55
+ /**
56
+ * Regex used to match a Tweet in text
57
+ *
58
+ * More specific than WordPress Core regex
59
+ *
60
+ * @since 2.0.0
61
+ *
62
+ * @type string
63
+ */
64
+ const URL_REGEX = '#^https?://(www\.)?twitter\.com/[a-z0-9_]{1,20}/status(es)?/([0-9]+)#i';
65
+
66
+ /**
67
+ * HTML class to be used in div wrapper
68
+ *
69
+ * @since 2.0.0
70
+ *
71
+ * @type string
72
+ */
73
+ const HTML_CLASS = 'twitter-tweet';
74
+
75
+ /**
76
+ * Tweet object class
77
+ *
78
+ * @since 2.0.0
79
+ *
80
+ * @type string
81
+ */
82
+ const TWEET_CLASS = '\Twitter\Widgets\Embeds\Tweet';
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 shortcode attributes and their default values
95
+ *
96
+ * @since 2.0.0
97
+ *
98
+ * @type array
99
+ */
100
+ public static $SHORTCODE_DEFAULTS = array(
101
+ 'id' => null,
102
+ 'conversation' => true,
103
+ 'cards' => true,
104
+ 'width' => null,
105
+ 'align' => null,
106
+ 'theme' => null,
107
+ 'link_color' => null,
108
+ 'border_color' => null,
109
+ );
110
+
111
+ /**
112
+ * Extra parameters accepted by Jetpack included for compatibility
113
+ *
114
+ * @since 2.0.0
115
+ *
116
+ * @link https://github.com/Automattic/jetpack/blob/master/modules/shortcodes/tweet.php Jetpack tweet shortcode
117
+ *
118
+ * @type array
119
+ */
120
+ public static $JETPACK_SHORTCODE_EXTRAS = array(
121
+ 'tweet' => null,
122
+ 'hide_thread' => false,
123
+ 'hide_media' => false,
124
+ );
125
+
126
+ /**
127
+ * Data attribute keys used in a Tweet object mapped to oEmbed keys used by Jetpack
128
+ *
129
+ * @since 2.0.0
130
+ *
131
+ * @type array
132
+ */
133
+ public static $DATA_ATTRIBUTES_TO_OEMBED_KEYS = array(
134
+ 'cards' => 'hide_media',
135
+ 'conversation' => 'hide_thread',
136
+ );
137
+
138
+ /**
139
+ * Shortcode attributes to be converted from underscores to dashes before object initialization
140
+ *
141
+ * @since 2.0.0
142
+ *
143
+ * @type array
144
+ */
145
+ public static $OPTIONS_KEYS_UNDERSCORE_TO_DASHES = array(
146
+ 'link_color' => 'link-color',
147
+ 'border_color' => 'border-color',
148
+ );
149
+
150
+ /**
151
+ * Attach handlers for embedded Tweets
152
+ *
153
+ * @since 2.0.0
154
+ *
155
+ * @return void
156
+ */
157
+ public static function init()
158
+ {
159
+ $classname = get_called_class();
160
+
161
+ // register our shortcode and its handler
162
+ add_shortcode( self::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
163
+
164
+ // Shortcode UI, if supported
165
+ add_action(
166
+ 'register_shortcode_ui',
167
+ array( $classname, 'shortcodeUI' ),
168
+ 5,
169
+ 0
170
+ );
171
+
172
+ if ( ! is_admin() ) {
173
+ // unhook the WordPress Core oEmbed handler
174
+ wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
175
+ // pass a Tweet detail URL through the Tweet shortcode handler
176
+ wp_embed_register_handler(
177
+ self::SHORTCODE_TAG,
178
+ static::URL_REGEX,
179
+ array( $classname, 'linkHandler' ),
180
+ 1
181
+ );
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Reference the feature by name
187
+ *
188
+ * @since 2.0.0
189
+ *
190
+ * @return string translated feature name
191
+ */
192
+ public static function featureName()
193
+ {
194
+ return __( 'Embedded Tweet', 'twitter' );
195
+ }
196
+
197
+ /**
198
+ * Describe shortcode for Shortcake UI
199
+ *
200
+ * @since 2.0.0
201
+ *
202
+ * @link https://github.com/wp-shortcake/shortcake Shortcake UI
203
+ *
204
+ * @return void
205
+ */
206
+ public static function shortcodeUI()
207
+ {
208
+ // Shortcake required
209
+ if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
210
+ return;
211
+ }
212
+
213
+ // id only
214
+ // avoids an unchecked Shortcake input checkbox requiring a shortcode output
215
+ shortcode_ui_register_for_shortcode(
216
+ self::SHORTCODE_TAG,
217
+ array(
218
+ 'label' => esc_html( static::featureName() ),
219
+ 'listItemImage' => 'dashicons-twitter',
220
+ 'attrs' => array(
221
+ array(
222
+ 'attr' => 'id',
223
+ 'label' => 'ID',
224
+ 'type' => 'text',
225
+ 'meta' => array(
226
+ 'required' => true,
227
+ 'pattern' => '[0-9]+',
228
+ 'placeholder' => '560070183650213889',
229
+ ),
230
+ ),
231
+ ),
232
+ )
233
+ );
234
+ }
235
+
236
+ /**
237
+ * Handle a URL matched by an embed handler
238
+ *
239
+ * @since 2.0.0
240
+ *
241
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
242
+ *
243
+ * @return string HTML markup for the Tweet or an empty string if requirements not met
244
+ */
245
+ public static function linkHandler( $matches )
246
+ {
247
+ if ( ! ( is_array( $matches ) && isset( $matches[3] ) && $matches[3] ) ) {
248
+ return '';
249
+ }
250
+
251
+ return static::shortcodeHandler( array( 'id' => $matches[3] ) );
252
+ }
253
+
254
+ /**
255
+ * Convert a Tweet ID in ID or URL form into a trimmed ID
256
+ *
257
+ * @since 2.0.0
258
+ *
259
+ * @param string $tweet_id Tweet identifier
260
+ *
261
+ * @return string $tweet_id Tweet identifier or empty string if minimum requirements not met
262
+ */
263
+ public static function sanitizeTweetID( $tweet_id )
264
+ {
265
+ if ( ! is_string( $tweet_id ) ) {
266
+ return '';
267
+ }
268
+
269
+ $tweet_id = trim( $tweet_id );
270
+ if ( ! $tweet_id ) {
271
+ return '';
272
+ }
273
+ $tweet_id = trim( rtrim( trim( $tweet_id ), '/' ) );
274
+ if ( ! $tweet_id ) {
275
+ return '';
276
+ }
277
+
278
+ $last_slash = strrpos( $tweet_id, '/' );
279
+ if ( false !== $last_slash ) {
280
+ $tweet_id = substr( $tweet_id, $last_slash + 1 );
281
+ }
282
+
283
+ return $tweet_id;
284
+ }
285
+
286
+ /**
287
+ * Shortcode allowed attributes and default values Tweet
288
+ *
289
+ * Jetpack compatibility mixed in
290
+ *
291
+ * @since 2.0.0
292
+ *
293
+ * @return array shortcode allowed attributes and default values
294
+ */
295
+ public static function getShortcodeDefaults()
296
+ {
297
+ return array_merge( static::$JETPACK_SHORTCODE_EXTRAS, static::$SHORTCODE_DEFAULTS );
298
+ }
299
+
300
+ /**
301
+ * Process shortcode attributes received from the shortcode API
302
+ *
303
+ * @since 2.0.0
304
+ *
305
+ * @link https://codex.wordpress.org/Shortcode_API Shortcode API
306
+ *
307
+ * @param array $attributes associative array of shortcode attributes, usually from the Shortcode API
308
+ *
309
+ * @return array array processed by shortcode_atts, prepped for Tweet object
310
+ */
311
+ public static function getShortcodeAttributes( $attributes )
312
+ {
313
+ $options = shortcode_atts(
314
+ static::getShortcodeDefaults(),
315
+ $attributes,
316
+ static::SHORTCODE_TAG
317
+ );
318
+
319
+ if ( ! is_array( $options ) || empty( $options ) ) {
320
+ return array();
321
+ }
322
+
323
+ // support a Tweet ID or URL in the first position for compatibility with WordPress.com / Jetpack
324
+ if ( empty( $options['id'] ) ) {
325
+ // support tweet attribute for WordPress.com / Jetpack compatibility
326
+ if ( ! empty( $options['tweet'] ) ) {
327
+ $options['id'] = $options['tweet'];
328
+ } else {
329
+ // support Tweet ID or URL in the first position for compatibility with WordPress.com / Jetpack
330
+ if ( ! empty( $attributes[0] ) ) {
331
+ $options['id'] = $attributes[0];
332
+ unset( $options['tweet'] );
333
+ }
334
+ }
335
+ }
336
+
337
+ return static::shortcodeAttributesToTweetKeys( $options );
338
+ }
339
+
340
+ /**
341
+ * Convert shortcode parameters, attributes, and defaults into a clean set of Tweet parameters
342
+ *
343
+ * @since 2.0.0
344
+ *
345
+ * @param array $attributes set of shortcode attribute-value pairs content matching the WordPress shortcode regex {
346
+ * @type string|int attribute name or positional int
347
+ * @type mixed shortcode value
348
+ * }
349
+ *
350
+ * @return array cleaned up options ready to be passed into a Tweet object {
351
+ * @type string option name
352
+ * @type string|bool option value
353
+ * }
354
+ */
355
+ public static function shortcodeAttributesToTweetKeys( $attributes )
356
+ {
357
+ if ( ! is_array( $attributes ) || empty( $attributes ) ) {
358
+ return array();
359
+ }
360
+
361
+ if ( isset( $attributes['id'] ) ) {
362
+ $attributes['id'] = static::sanitizeTweetID( (string) $attributes['id'] );
363
+ } else {
364
+ return array();
365
+ }
366
+ if ( ! $attributes['id'] ) {
367
+ return array();
368
+ }
369
+
370
+ $attributes = \Twitter\WordPress\Shortcodes\Helpers\Attributes::booleanOption(
371
+ $attributes,
372
+ array( 'cards', 'hide_media', 'conversation', 'hide_thread' )
373
+ );
374
+ $attributes = \Twitter\WordPress\Shortcodes\Helpers\Attributes::positiveIntegerOption(
375
+ $attributes,
376
+ array( 'width' )
377
+ );
378
+ $attributes = \Twitter\WordPress\Shortcodes\Helpers\Attributes::lowercaseStringOption(
379
+ $attributes,
380
+ array( 'align', 'theme', 'link_color', 'border_color' )
381
+ );
382
+
383
+ // test for oEmbed-style parameter used by WordPress.com / Jetpack
384
+ // if both attributes are used and have values prefer cards and conversation
385
+ foreach ( static::$DATA_ATTRIBUTES_TO_OEMBED_KEYS as $option => $alternate_naming ) {
386
+ if ( isset( $attributes[ $alternate_naming ] ) ) {
387
+ if ( ! isset( $attributes[ $option ] ) && $attributes[ $alternate_naming ] !== static::$SHORTCODE_DEFAULTS[ $option ] ) {
388
+ $attributes[ $option ] = $attributes[ $alternate_naming ];
389
+ }
390
+ unset( $attributes[ $alternate_naming ] );
391
+ }
392
+ }
393
+
394
+ foreach ( static::$OPTIONS_KEYS_UNDERSCORE_TO_DASHES as $underscore => $dashes ) {
395
+ if ( ! isset( $attributes[ $underscore ] ) ) {
396
+ continue;
397
+ }
398
+ $attributes[ $dashes ] = $attributes[ $underscore ];
399
+ unset( $attributes[ $underscore ] );
400
+ }
401
+
402
+ return $attributes;
403
+ }
404
+
405
+ /**
406
+ * Handle shortcode macro
407
+ *
408
+ * @since 2.0.0
409
+ *
410
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
411
+ * @type string|int attribute name or positional int
412
+ * @type mixed shortcode value
413
+ * }
414
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
415
+ *
416
+ * @return string HTML markup. empty string if parameter requirement not met or no Tweet info found
417
+ */
418
+ public static function shortcodeHandler( $attributes, $content = '' )
419
+ {
420
+ $options = static::getShortcodeAttributes( $attributes );
421
+
422
+ $tweet_id = '';
423
+ if ( isset( $options['id'] ) ) {
424
+ $tweet_id = $options['id'];
425
+ } else {
426
+ // allow shortcode use in enclosed form
427
+ $content = trim( $content );
428
+ if ( $content ) {
429
+ $tweet_id = static::sanitizeTweetID( $content );
430
+ }
431
+ }
432
+ if ( ! $tweet_id ) {
433
+ return '';
434
+ }
435
+ $options['id'] = $tweet_id;
436
+
437
+ $object_class = static::TWEET_CLASS;
438
+ if ( ! method_exists( $object_class, 'fromArray' ) ) {
439
+ return '';
440
+ }
441
+ $tweet = $object_class::fromArray( $options );
442
+ unset( $object_class );
443
+ if ( ! ($tweet && method_exists( $tweet, 'getID' ) && $tweet->getID() && method_exists( $tweet, 'toOEmbedParameterArray' ) ) ) {
444
+ return '';
445
+ }
446
+
447
+ $oembed_params = $tweet->toOEmbedParameterArray();
448
+ if ( empty( $oembed_params ) || ! isset( $oembed_params['url'] ) ) {
449
+ return '';
450
+ }
451
+ $oembed_params = array_merge( static::getBaseOEmbedParams(), $oembed_params );
452
+
453
+ $cache_key = static::getOEmbedCacheKey( $tweet_id, $oembed_params );
454
+ if ( ! $cache_key ) {
455
+ return '';
456
+ }
457
+
458
+ // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
459
+ $html = trim( static::getOEmbedMarkup( $oembed_params, $cache_key ) );
460
+ if ( ! $html ) {
461
+ return '';
462
+ }
463
+
464
+ $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
465
+
466
+ $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
467
+ if ( $inline_js ) {
468
+ return $html . $inline_js;
469
+ }
470
+
471
+ return $html;
472
+ }
473
+
474
+ /**
475
+ * Generate a unique string representing oEmbed result customizations set by shortcode parameters
476
+ *
477
+ * @since 2.0.0
478
+ *
479
+ * @param array $query_parameters associative array of query parameters sent to the oEmbed endpoint {
480
+ * @type string query parameter name
481
+ * @type string|bool query parameter value
482
+ * }
483
+ *
484
+ * @return string cache key component
485
+ */
486
+ public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
487
+ {
488
+ $key_pieces = array();
489
+
490
+ if ( isset( $query_parameters['maxwidth'] ) ) {
491
+ $key_pieces[] = 'w' . $query_parameters['maxwidth'];
492
+ }
493
+
494
+ // one letter customizations
495
+ $customizations = '';
496
+ if ( isset( $query_parameters['hide_media'] ) && $query_parameters['hide_media'] ) {
497
+ $customizations .= 'm';
498
+ }
499
+ if ( isset( $query_parameters['hide_thread'] ) && $query_parameters['hide_thread'] ) {
500
+ $customizations .= 't';
501
+ }
502
+ // should only be set for dark theme
503
+ if ( isset( $query_parameters['theme'] ) ) {
504
+ $customizations .= 'd';
505
+ }
506
+ // left, right, center
507
+ if ( isset( $query_parameters['align'] ) && $query_parameters['align'] ) {
508
+ $customizations .= substr( $query_parameters['align'], 0, 1 );
509
+ }
510
+ if ( $customizations ) {
511
+ $key_pieces[] = $customizations;
512
+ }
513
+ unset( $customizations );
514
+
515
+ $color_parameters = array(
516
+ 'link_color' => 'l',
517
+ 'border_color' => 'b',
518
+ );
519
+ foreach ( $color_parameters as $query_parameter => $cache_abbreviation ) {
520
+ if ( isset( $query_parameters[ $query_parameter ] ) ) {
521
+ $key_pieces[] = $cache_abbreviation . ltrim( $query_parameters[ $query_parameter ], '#' );
522
+ }
523
+ }
524
+
525
+ return implode( '_', $key_pieces );
526
+ }
527
+
528
+ /**
529
+ * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
530
+ *
531
+ * @since 2.0.0
532
+ *
533
+ * @link https://dev.twitter.com/rest/reference/get/statuses/oembed oEmbed doc
534
+ *
535
+ * @param string $id Tweet ID
536
+ * @param array $query_parameters oEmbed API query parameters
537
+ *
538
+ * @return string cache key
539
+ */
540
+ public static function getOEmbedCacheKey( $id, array $query_parameters )
541
+ {
542
+ if ( ! ( is_string( $id ) && $id ) ) {
543
+ return '';
544
+ }
545
+ $key_pieces = array( static::SHORTCODE_TAG, $id );
546
+
547
+ // separate cache for each explicitly-defined display language
548
+ if ( isset( $query_parameters['lang'] ) && $query_parameters['lang'] ) {
549
+ $key_pieces[] = $query_parameters['lang'];
550
+ }
551
+
552
+ $customizations = static::getOEmbedCacheKeyCustomParameters( $query_parameters );
553
+ if ( $customizations ) {
554
+ $key_pieces[] = $customizations;
555
+ }
556
+
557
+ return implode( '_', $key_pieces );
558
+ }
559
+ }
src/Twitter/WordPress/Shortcodes/{EmbeddedTweetVideo.php → Embeds/Tweet/Video.php} RENAMED
@@ -23,20 +23,20 @@ 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 video attached to a Tweet
30
  *
31
- * @since 1.0.0
32
  */
33
- class EmbeddedTweetVideo extends EmbeddedTweet
34
  {
35
 
36
  /**
37
  * Shortcode tag to be matched
38
  *
39
- * @since 1.0.0
40
  *
41
  * @type string
42
  */
@@ -45,16 +45,34 @@ class EmbeddedTweetVideo extends EmbeddedTweet
45
  /**
46
  * Accepted shortcode attributes and their default values
47
  *
48
- * @since 1.0.0
49
  *
50
  * @type array
51
  */
52
- public static $SHORTCODE_DEFAULTS = array( 'id' => '', 'status' => true );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  /**
55
  * Attach handlers for Twitter embedded video
56
  *
57
- * @since 1.0.0
58
  *
59
  * @return void
60
  */
@@ -74,12 +92,24 @@ class EmbeddedTweetVideo extends EmbeddedTweet
74
  );
75
  }
76
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  /**
78
  * Describe shortcode for Shortcake UI
79
  *
80
  * @since 1.1.0
81
  *
82
- * @link https://github.com/fusioneng/Shortcake Shortcake UI
83
  *
84
  * @return void
85
  */
@@ -95,7 +125,7 @@ class EmbeddedTweetVideo extends EmbeddedTweet
95
  shortcode_ui_register_for_shortcode(
96
  self::SHORTCODE_TAG,
97
  array(
98
- 'label' => __( 'Embedded Tweet Video', 'twitter' ),
99
  'listItemImage' => 'dashicons-twitter',
100
  'attrs' => array(
101
  array(
@@ -116,7 +146,7 @@ class EmbeddedTweetVideo extends EmbeddedTweet
116
  /**
117
  * Convert shortcode parameters into a clean set of Twitter embedded video options parameters
118
  *
119
- * @since 1.0.0
120
  *
121
  * @param array $attributes set of shortcode attribute-value pairs matching the WordPress shortcode regex {
122
  * @type string attribute name
@@ -145,101 +175,46 @@ class EmbeddedTweetVideo extends EmbeddedTweet
145
  unset( $tweet_id );
146
  }
147
 
148
- // allow option style or oEmbed style parameter
149
- if ( isset( $attributes['status'] ) ) {
150
- if ( false === $attributes['status'] || '0' == $attributes['status'] || ( is_string( $attributes['status'] ) && in_array( strtolower( $attributes['status'] ), array( 'false', 'no', 'off' ) ) ) ) {
151
- $options['status'] = false;
152
- }
153
- } else if ( isset( $attributes['hide_tweet'] ) ) {
154
- if ( true === $attributes['hide_tweet'] || '1' == $attributes['hide_tweet'] || ( is_string( $attributes['hide_tweet'] ) && in_array( strtolower( $attributes['hide_tweet'] ), array( 'true', 'yes', 'on' ) ) ) ) {
155
- $options['status'] = false;
156
- }
157
- }
158
-
159
  return $options;
160
  }
161
 
162
  /**
163
- * Handle shortcode macro
164
  *
165
- * @since 1.0.0
166
  *
167
- * @param array $attributes shortcode attributes
168
- * @param string $content shortcode content. no effect
 
 
169
  *
170
- * @return string HTML markup
 
 
 
171
  */
172
- public static function shortcodeHandler( $attributes, $content = '' )
173
  {
174
- // clean up attribute to shortcode option mappings before passing to filter
175
- // apply the same filter as shortcode_atts
176
- /** This filter is documented in wp-includes/shortcodes.php */
177
- $options = apply_filters(
178
- 'shortcode_atts_' . self::SHORTCODE_TAG,
179
- array_merge(
180
- static::$SHORTCODE_DEFAULTS,
181
- static::sanitizeShortcodeParameters( (array) $attributes )
182
- ),
183
- static::$SHORTCODE_DEFAULTS,
184
- $attributes
185
- );
186
-
187
- if ( ! $options['id'] ) {
188
- return '';
189
- }
190
- $tweet_id = $options['id'];
191
- unset( $options['id'] );
192
-
193
- $oembed_params = static::shortcodeParamsToOEmbedParams( $tweet_id, $options );
194
- if ( empty( $oembed_params ) ) {
195
- return '';
196
- }
197
-
198
- // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
199
- $html = trim( static::getOEmbedMarkup( $oembed_params ) );
200
- if ( ! $html ) {
201
- return '';
202
- }
203
-
204
- $html = '<div class="twitter-video">' . $html . '</div>';
205
-
206
- $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
207
- if ( $inline_js ) {
208
- return $html . $inline_js;
209
  }
210
 
211
- return $html;
212
- }
213
-
214
- /**
215
- * Convert shortcode parameters into query parameters supported by the Twitter oEmbed endpoint
216
- *
217
- * @since 1.0.0
218
- *
219
- * @param string $tweet_id Tweet identifier
220
- * @param array $shortcode_options customizations specified in the shortcode
221
- *
222
- * @return array associative array of query parameters ready for http_build_query
223
- */
224
- public static function shortcodeParamsToOEmbedParams( $tweet_id, $shortcode_options = array() )
225
- {
226
- $query_parameters = static::getBaseOEmbedParams( $tweet_id );
227
- if ( empty( $query_parameters ) ) {
228
  return array();
229
  }
230
- $query_parameters['widget_type'] = 'video';
231
-
232
- if ( isset( $shortcode_options['status'] ) && false === $shortcode_options['status'] ) {
233
- $query_parameters['hide_tweet'] = true;
234
  }
235
 
236
- return $query_parameters;
237
  }
238
 
239
  /**
240
  * Generate a unique string representing oEmbed result customizations set by shortcode parameters
241
  *
242
- * @since 1.0.0
243
  *
244
  * @param array $query_parameters associative array of query parameters sent to the oEmbed endpoint {
245
  * @type string query parameter name
@@ -250,12 +225,6 @@ class EmbeddedTweetVideo extends EmbeddedTweet
250
  */
251
  public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
252
  {
253
- $customizations = '';
254
-
255
- if ( isset( $query_parameters['hide_tweet'] ) && $query_parameters['hide_tweet'] ) {
256
- $customizations .= 'h';
257
- }
258
-
259
- return $customizations;
260
  }
261
  }
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Shortcodes\Embeds\Tweet;
27
 
28
  /**
29
  * Display a video attached to a Tweet
30
  *
31
+ * @since 2.0.0
32
  */
33
+ class Video extends \Twitter\WordPress\Shortcodes\Embeds\Tweet
34
  {
35
 
36
  /**
37
  * Shortcode tag to be matched
38
  *
39
+ * @since 2.0.0
40
  *
41
  * @type string
42
  */
45
  /**
46
  * Accepted shortcode attributes and their default values
47
  *
48
+ * @since 2.0.0
49
  *
50
  * @type array
51
  */
52
+ public static $SHORTCODE_DEFAULTS = array( 'id' => '' );
53
+
54
+ /**
55
+ * HTML class to be used in div wrapper
56
+ *
57
+ * @since 2.0.0
58
+ *
59
+ * @type string
60
+ */
61
+ const HTML_CLASS = 'twitter-video';
62
+
63
+ /**
64
+ * Tweet object class
65
+ *
66
+ * @since 2.0.0
67
+ *
68
+ * @type string
69
+ */
70
+ const TWEET_CLASS = '\Twitter\Widgets\Embeds\Tweet\Video';
71
 
72
  /**
73
  * Attach handlers for Twitter embedded video
74
  *
75
+ * @since 2.0.0
76
  *
77
  * @return void
78
  */
92
  );
93
  }
94
 
95
+ /**
96
+ * Reference the feature by name
97
+ *
98
+ * @since 2.0.0
99
+ *
100
+ * @return string translated feature name
101
+ */
102
+ public static function featureName()
103
+ {
104
+ return _x( 'Twitter Video', 'A single embedded Tweet shown with a video-specific template', 'twitter' );
105
+ }
106
+
107
  /**
108
  * Describe shortcode for Shortcake UI
109
  *
110
  * @since 1.1.0
111
  *
112
+ * @link https://github.com/wp-shortcake/shortcake Shortcake UI
113
  *
114
  * @return void
115
  */
125
  shortcode_ui_register_for_shortcode(
126
  self::SHORTCODE_TAG,
127
  array(
128
+ 'label' => esc_html( static::featureName() ),
129
  'listItemImage' => 'dashicons-twitter',
130
  'attrs' => array(
131
  array(
146
  /**
147
  * Convert shortcode parameters into a clean set of Twitter embedded video options parameters
148
  *
149
+ * @since 2.0.0
150
  *
151
  * @param array $attributes set of shortcode attribute-value pairs matching the WordPress shortcode regex {
152
  * @type string attribute name
175
  unset( $tweet_id );
176
  }
177
 
 
 
 
 
 
 
 
 
 
 
 
178
  return $options;
179
  }
180
 
181
  /**
182
+ * Convert shortcode parameters, attributes, and defaults into a clean set of Tweet parameters
183
  *
184
+ * @since 2.0.0
185
  *
186
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
187
+ * @type string|int attribute name or positional int
188
+ * @type mixed shortcode value
189
+ * }
190
  *
191
+ * @return array cleaned up options ready to be passed into a Tweet Video object {
192
+ * @type string option name
193
+ * @type string|bool option value
194
+ * }
195
  */
196
+ public static function shortcodeAttributesToTweetKeys( $attributes )
197
  {
198
+ if ( ! is_array( $attributes ) || empty( $attributes ) ) {
199
+ return array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  }
201
 
202
+ if ( isset( $attributes['id'] ) ) {
203
+ $attributes['id'] = static::sanitizeTweetID( (string) $attributes['id'] );
204
+ } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  return array();
206
  }
207
+ if ( ! $attributes['id'] ) {
208
+ return array();
 
 
209
  }
210
 
211
+ return $attributes;
212
  }
213
 
214
  /**
215
  * Generate a unique string representing oEmbed result customizations set by shortcode parameters
216
  *
217
+ * @since 2.0.0
218
  *
219
  * @param array $query_parameters associative array of query parameters sent to the oEmbed endpoint {
220
  * @type string query parameter name
225
  */
226
  public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
227
  {
228
+ return '';
 
 
 
 
 
 
229
  }
230
  }
src/Twitter/WordPress/Shortcodes/{Vine.php → Embeds/Vine.php} RENAMED
@@ -23,21 +23,21 @@ 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
  */
@@ -46,7 +46,7 @@ class Vine implements ShortcodeInterface
46
  /**
47
  * HTML class to be used in div wrapper
48
  *
49
- * @since 1.3.0
50
  *
51
  * @type string
52
  */
@@ -55,16 +55,16 @@ class Vine implements ShortcodeInterface
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
  */
@@ -73,7 +73,7 @@ class Vine implements ShortcodeInterface
73
  /**
74
  * Base URL used to reconstruct a Vine URL
75
  *
76
- * @since 1.3.0
77
  *
78
  * @type string
79
  */
@@ -82,7 +82,7 @@ class Vine implements ShortcodeInterface
82
  /**
83
  * PHP class to use for fetching oEmbed data
84
  *
85
- * @since 1.3.0
86
  *
87
  * @type string
88
  */
@@ -91,7 +91,7 @@ class Vine implements ShortcodeInterface
91
  /**
92
  * Relative path for the oEmbed API relative to Vine base path
93
  *
94
- * @since 1.3.0
95
  *
96
  * @type string
97
  */
@@ -100,7 +100,7 @@ class Vine implements ShortcodeInterface
100
  /**
101
  * Accepted shortcode attributes and their default values
102
  *
103
- * @since 1.3.0
104
  *
105
  * @type array
106
  */
@@ -109,7 +109,7 @@ class Vine implements ShortcodeInterface
109
  /**
110
  * Attach handlers for Vine
111
  *
112
- * @since 1.3.0
113
  *
114
  * @return void
115
  */
@@ -143,7 +143,7 @@ class Vine implements ShortcodeInterface
143
  /**
144
  * Reference the feature by name
145
  *
146
- * @since 1.3.0
147
  *
148
  * @return string translated feature name
149
  */
@@ -155,9 +155,9 @@ class Vine implements ShortcodeInterface
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
  */
@@ -191,16 +191,13 @@ class Vine implements ShortcodeInterface
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 Vine 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 '';
@@ -210,39 +207,55 @@ class Vine implements ShortcodeInterface
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
@@ -250,11 +263,51 @@ class Vine implements ShortcodeInterface
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
  }
@@ -272,19 +325,19 @@ class Vine implements ShortcodeInterface
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'];
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Shortcodes\Embeds;
27
 
28
  /**
29
  * Display a Vine
30
  *
31
+ * @since 2.0.0
32
  */
33
+ class Vine implements \Twitter\WordPress\Shortcodes\ShortcodeInterface
34
  {
35
+ use \Twitter\WordPress\Shortcodes\OEmbedTrait;
36
 
37
  /**
38
  * Shortcode tag to be matched
39
  *
40
+ * @since 2.0.0
41
  *
42
  * @type string
43
  */
46
  /**
47
  * HTML class to be used in div wrapper
48
  *
49
+ * @since 2.0.0
50
  *
51
  * @type string
52
  */
55
  /**
56
  * Regex used to match a Vine in text
57
  *
58
+ * @since 2.0.0
59
  *
60
  * @type string
61
  */
62
  const URL_REGEX = '#https?://vine\.co/v/([a-z0-9]+)\/?#i';
63
 
64
  /**
65
+ * The oEmbed regex registered by WordPress Core since 4.1
66
  *
67
+ * @since 2.0.0
68
  *
69
  * @type string
70
  */
73
  /**
74
  * Base URL used to reconstruct a Vine URL
75
  *
76
+ * @since 2.0.0
77
  *
78
  * @type string
79
  */
82
  /**
83
  * PHP class to use for fetching oEmbed data
84
  *
85
+ * @since 2.0.0
86
  *
87
  * @type string
88
  */
91
  /**
92
  * Relative path for the oEmbed API relative to Vine base path
93
  *
94
+ * @since 2.0.0
95
  *
96
  * @type string
97
  */
100
  /**
101
  * Accepted shortcode attributes and their default values
102
  *
103
+ * @since 2.0.0
104
  *
105
  * @type array
106
  */
109
  /**
110
  * Attach handlers for Vine
111
  *
112
+ * @since 2.0.0
113
  *
114
  * @return void
115
  */
143
  /**
144
  * Reference the feature by name
145
  *
146
+ * @since 2.0.0
147
  *
148
  * @return string translated feature name
149
  */
155
  /**
156
  * Describe shortcode for Shortcake UI
157
  *
158
+ * @since 2.0.0
159
  *
160
+ * @link https://github.com/wp-shortcake/shortcake Shortcake UI
161
  *
162
  * @return void
163
  */
191
  /**
192
  * Handle a URL matched by a embed handler
193
  *
194
+ * @since 2.0.0
195
  *
196
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
 
 
 
197
  *
198
  * @return string HTML markup for the Vine or an empty string if requirements not met
199
  */
200
+ public static function linkHandler( $matches )
201
  {
202
  if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
203
  return '';
207
  }
208
 
209
  /**
210
+ * Shortcode allowed attributes and default values
 
 
211
  *
212
+ * @since 2.0.0
 
 
 
 
213
  *
214
+ * @return array shortcode allowed attributes and default values
215
  */
216
+ public static function getShortcodeDefaults()
217
  {
218
+ return static::$SHORTCODE_DEFAULTS;
219
+ }
220
 
221
+ /**
222
+ * Process shortcode attributes received from the shortcode API
223
+ *
224
+ * @since 2.0.0
225
+ *
226
+ * @link https://codex.wordpress.org/Shortcode_API Shortcode API
227
+ *
228
+ * @param array $attributes associative array of shortcode attributes, usually from the Shortcode API
229
+ *
230
+ * @return array array processed by shortcode_atts, prepped for Tweet object
231
+ */
232
+ public static function getShortcodeAttributes( $attributes )
233
+ {
234
  $options = shortcode_atts(
235
+ static::getShortcodeDefaults(),
236
  $attributes,
237
  static::SHORTCODE_TAG
238
  );
239
 
240
+ if ( ! is_array( $options ) || empty( $options ) ) {
241
+ return array();
 
242
  }
243
 
244
+ if ( isset( $options['id'] ) ) {
245
+ $options['id'] = trim( $options['id'] );
246
+ }
247
 
248
+ $options = \Twitter\WordPress\Shortcodes\Helpers\Attributes::positiveIntegerOption(
249
+ $options,
250
+ array( 'width' )
251
+ );
252
+
253
+ $width = 0;
254
+ if ( isset( $options['width'] ) ) {
255
+ $width = absint( $options['width'] );
256
+ if ( $width < 100 && isset( $content_width ) ) {
257
+ $width = absint( $content_width );
258
+ }
259
  }
260
  if ( $width > 100 ) {
261
  // reset max_width to max value supported by Vine
263
  if ( $width > 600 ) {
264
  $width = 600;
265
  }
266
+ $options['width'] = $width;
267
  }
268
 
269
+ return $options;
270
+ }
271
+
272
+ /**
273
+ * Handle shortcode macro
274
+ *
275
+ * @since 2.0.0
276
+ *
277
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
278
+ * @type string|int attribute name or positional int
279
+ * @type mixed shortcode value
280
+ * }
281
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
282
+ *
283
+ * @return string HTML markup. empty string if parameter requirement not met or no collection info found
284
+ */
285
+ public static function shortcodeHandler( $attributes, $content = '' )
286
+ {
287
+ $options = static::getShortcodeAttributes( $attributes );
288
+
289
+ if ( ! ( isset( $options['id'] ) && $options['id'] ) ) {
290
+ return '';
291
+ }
292
+
293
+ $vine = \Twitter\Widgets\Embeds\Vine::fromArray( $options );
294
+
295
+ if ( ! $vine->getWidth() ) {
296
+ // set to content width of the current theme
297
+ // @see https://codex.wordpress.org/Content_Width
298
+ if ( isset( $GLOBALS['content_width'] ) && $GLOBALS['content_width'] ) {
299
+ $vine->setWidth( absint( $GLOBALS['content_width'] ) );
300
+ }
301
+ }
302
+
303
+ $query_parameters = $vine->toOEmbedParameterArray();
304
+ $query_parameters = array_merge( static::getBaseOEmbedParams(), $query_parameters );
305
+
306
+ // not used by Vine
307
+ unset( $query_parameters['lang'] );
308
+
309
  // fetch HTML markup from Vine oEmbed endpoint for the given parameters
310
+ $html = trim( static::getOEmbedMarkup( $query_parameters, static::getOEmbedCacheKey( $options['id'], $query_parameters ) ) );
311
  if ( ! $html ) {
312
  return '';
313
  }
325
  /**
326
  * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
327
  *
328
+ * @since 2.0.0
329
  *
330
+ * @param string $id Vine ID
331
+ * @param array $query_parameters oEmbed API query parameters
332
  *
333
  * @return string cache key
334
  */
335
+ public static function getOEmbedCacheKey( $id, array $query_parameters )
336
  {
337
+ if ( ! ( is_string( $id ) && $id ) ) {
338
  return '';
339
  }
340
+ $key_pieces = array( static::SHORTCODE_TAG, $id );
 
341
 
342
  if ( isset( $query_parameters['maxwidth'] ) && $query_parameters['maxwidth'] ) {
343
  $key_pieces[] = 'w' . $query_parameters['maxwidth'];
src/Twitter/WordPress/Shortcodes/Follow.php DELETED
@@ -1,293 +0,0 @@
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 Follow button
30
- *
31
- * @since 1.0.0
32
- */
33
- class Follow implements ShortcodeInterface
34
- {
35
-
36
- /**
37
- * Shortcode tag to be matched
38
- *
39
- * @since 1.0.0
40
- *
41
- * @type string
42
- */
43
- const SHORTCODE_TAG = 'twitter_follow';
44
-
45
- /**
46
- * Regex used to match a Twitter profile URL in text
47
- *
48
- * @since 1.3.0
49
- *
50
- * @type string
51
- */
52
- const URL_REGEX = '#^https://twitter\.com/([a-z0-9_]{1,20})$#i';
53
-
54
- /**
55
- * Accepted shortcode attributes and their default values
56
- *
57
- * @since 1.0.0
58
- *
59
- * @type array
60
- */
61
- public static $SHORTCODE_DEFAULTS = array( 'screen_name' => '', 'show_count' => true, 'show_screen_name' => true, 'size' => 'medium' );
62
-
63
- /**
64
- * Register shortcode macro and handler
65
- *
66
- * @since 1.0.0
67
- *
68
- * @return void
69
- */
70
- public static function init()
71
- {
72
- $classname = get_called_class();
73
-
74
- add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
75
-
76
- // convert a URL into the shortcode equivalent
77
- wp_embed_register_handler(
78
- static::SHORTCODE_TAG,
79
- static::URL_REGEX,
80
- array( $classname, 'linkHandler' ),
81
- 1
82
- );
83
-
84
- // Shortcode UI, if supported
85
- add_action(
86
- 'register_shortcode_ui',
87
- array( $classname, 'shortcodeUI' ),
88
- 5,
89
- 0
90
- );
91
- }
92
-
93
- /**
94
- * Describe shortcode for Shortcake UI
95
- *
96
- * @since 1.1.0
97
- *
98
- * @link https://github.com/fusioneng/Shortcake Shortcake UI
99
- *
100
- * @return void
101
- */
102
- public static function shortcodeUI()
103
- {
104
- // Shortcake required
105
- if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
106
- return;
107
- }
108
-
109
- shortcode_ui_register_for_shortcode(
110
- static::SHORTCODE_TAG,
111
- array(
112
- 'label' => esc_html( __( 'Follow Button', 'twitter' ) ),
113
- 'listItemImage' => 'dashicons-twitter',
114
- 'attrs' => array(
115
- array(
116
- 'attr' => 'screen_name',
117
- 'label' => esc_html( __( 'Twitter @username', 'twitter' ) ),
118
- 'type' => 'text',
119
- 'meta' => array(
120
- 'placeholder' => 'WordPress',
121
- 'pattern' => '[A-Za-z0-9_]{1,20}',
122
- ),
123
- ),
124
- array(
125
- 'attr' => 'size',
126
- 'label' => esc_html( __( 'Button size:', 'twitter' ) ),
127
- 'type' => 'radio',
128
- 'value' => '',
129
- 'options' => array(
130
- '' => esc_html( _x( 'medium', 'medium size button', 'twitter' ) ),
131
- 'large' => esc_html( _x( 'large', 'large size button', 'twitter' ) ),
132
- ),
133
- ),
134
- ),
135
- )
136
- );
137
- }
138
-
139
- /**
140
- * Handle a URL matched by a embed handler
141
- *
142
- * @since 1.3.1
143
- *
144
- * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
145
- * @param array $attr Embed attributes. Not used.
146
- * @param string $url The original URL that was matched by the regex. Not used.
147
- * @param array $rawattr The original unmodified attributes. Not used.
148
- *
149
- * @return string HTML markup for the follow button or an empty string if requirements not met
150
- */
151
- public static function linkHandler( $matches, $attr, $url, $rawattr )
152
- {
153
- if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
154
- return '';
155
- }
156
-
157
- return static::shortcodeHandler( array( 'screen_name' => $matches[1] ) );
158
- }
159
-
160
- /**
161
- * Clean up provided shortcode values
162
- *
163
- * Be liberal in what we accept in shortcode syntax before constructing a Follow button
164
- *
165
- * @since 1.0.0
166
- *
167
- * @param array $attributes provided shortcode attributes {
168
- * @type string shortcode attribute name
169
- * @type mixed shortcode attribute value
170
- * }
171
- *
172
- * @return array simplified shortcode values with defaults removed {
173
- * @type string shortcode attribute name
174
- * @type bool|string shortcode attribute value
175
- * }
176
- */
177
- public static function sanitizeShortcodeParameters( $attributes = array() )
178
- {
179
- if ( ! is_array( $attributes ) ) {
180
- return array();
181
- }
182
-
183
- $options = array();
184
-
185
- if ( isset( $attributes['screen_name'] ) ) {
186
- $screen_name = \Twitter\Helpers\Validators\ScreenName::trim( $attributes['screen_name'] );
187
- if ( $screen_name ) {
188
- $options['screen_name'] = $screen_name;
189
- }
190
- unset( $screen_name );
191
- }
192
-
193
- foreach ( array( 'show_count', 'show_screen_name' ) as $falsey_option ) {
194
- // check for falsey values passed to shortcode
195
- if ( isset( $attributes[ $falsey_option ] ) ) {
196
- if ( false === $attributes[ $falsey_option ] || '0' == $attributes[ $falsey_option ] || ( is_string( $attributes[ $falsey_option ] ) && in_array( strtolower( $attributes[ $falsey_option ] ), array( 'false', 'no', 'off' ) ) ) ) {
197
- $options[ $falsey_option ] = false;
198
- }
199
- }
200
- }
201
-
202
- // large is the only option
203
- if ( isset( $attributes['size'] ) ) {
204
- if ( is_string( $attributes['size'] ) && in_array( strtolower( $attributes['size'] ), array( 'large', 'l' ) ) ) {
205
- $options['size'] = 'large';
206
- }
207
- }
208
-
209
- return $options;
210
- }
211
-
212
- /**
213
- * Get the Twitter screen name of the author of the current post
214
- *
215
- * @since 1.0.0
216
- *
217
- * @return string Twitter screen name or empty if no screen name stored
218
- */
219
- public static function getScreenName()
220
- {
221
- if ( ! in_the_loop() ) {
222
- return '';
223
- }
224
-
225
- $screen_name = \Twitter\WordPress\User\Meta::getTwitterUsername( get_the_author_meta( 'ID' ) );
226
- if ( ! $screen_name ) {
227
- return '';
228
- }
229
-
230
- return $screen_name;
231
- }
232
-
233
- /**
234
- * Handle shortcode macro
235
- *
236
- * @since 1.0.0
237
- *
238
- * @param array $attributes shortcode attributes
239
- * @param string $content shortcode content. no effect
240
- *
241
- * @return string Follow button HTML or empty string
242
- */
243
- public static function shortcodeHandler( $attributes, $content = '' )
244
- {
245
- // clean up attribute to shortcode option mappings before passing to filter
246
- // apply the same filter as shortcode_atts
247
- /** This filter is documented in wp-includes/shortcodes.php */
248
- $options = apply_filters(
249
- 'shortcode_atts_' . self::SHORTCODE_TAG,
250
- array_merge(
251
- static::$SHORTCODE_DEFAULTS,
252
- static::sanitizeShortcodeParameters( (array) $attributes )
253
- ),
254
- static::$SHORTCODE_DEFAULTS,
255
- $attributes
256
- );
257
-
258
- $screen_name = '';
259
- if ( isset( $options['screen_name'] ) ) {
260
- $screen_name = $options['screen_name'];
261
- unset( $options['screen_name'] );
262
- }
263
- if ( ! $screen_name ) {
264
- $screen_name = static::getScreenName();
265
- // follow target required
266
- if ( ! $screen_name ) {
267
- return '';
268
- }
269
- }
270
-
271
- // update the options array with the Follow screen name
272
- $options['screen_name'] = $screen_name;
273
-
274
- $follow = \Twitter\Widgets\FollowButton::fromArray( $options );
275
- if ( ! $follow ) {
276
- return '';
277
- }
278
-
279
- $html = $follow->toHTML( _x( 'Follow %s', 'Follow a Twitter user', 'twitter' ), '\Twitter\WordPress\Helpers\HTMLBuilder' );
280
- if ( ! $html ) {
281
- return '';
282
- }
283
-
284
- $html = '<div class="twitter-follow">' . $html . '</div>';
285
-
286
- $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
287
- if ( $inline_js ) {
288
- return $html . $inline_js;
289
- }
290
-
291
- return $html;
292
- }
293
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/Twitter/WordPress/Shortcodes/Helpers/Attributes.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Helpers;
27
+
28
+ /**
29
+ * Process and convert shortcode attributes into their expected PHP types
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ class Attributes
34
+ {
35
+
36
+ /**
37
+ * Accepted string attribute values for a true boolean
38
+ *
39
+ * @since 2.0.0
40
+ *
41
+ * type array
42
+ */
43
+ public static $TRUTHY_STRING_VALUES = array(
44
+ 'true' => true,
45
+ 'yes' => true,
46
+ 'on' => true,
47
+ );
48
+
49
+ /**
50
+ * Accepted string attribute values for a false boolean
51
+ *
52
+ * @since 2.0.0
53
+ *
54
+ * type array
55
+ */
56
+ public static $FALSEY_STRING_VALUES = array(
57
+ 'false' => false,
58
+ 'no' => false,
59
+ 'off' => false,
60
+ );
61
+
62
+ /**
63
+ * Convert shortcode attribute value to boolean
64
+ *
65
+ * @since 2.0.0
66
+ *
67
+ * @param array $attributes parsed shortcode attributes
68
+ * @param array $bool_keys array keys of expected truthy or falsey values
69
+ *
70
+ * @return array $attributes shortcode attributes with possible type conversions or removed keys
71
+ */
72
+ public static function booleanOption( array $attributes, $bool_keys )
73
+ {
74
+ if ( ! is_array( $bool_keys ) || empty( $bool_keys ) ) {
75
+ return $attributes;
76
+ }
77
+
78
+ foreach ( $bool_keys as $bool_key ) {
79
+ if ( ! isset( $attributes[ $bool_key ] ) || is_bool( $attributes[ $bool_key ] ) ) {
80
+ continue;
81
+ }
82
+
83
+ // purposely allow int, string, or truthy/falsey loose match
84
+ // @codingStandardsIgnoreStart WordPress.PHP.StrictComparisons.LooseComparison
85
+ if ( '1' == $attributes[ $bool_key ] || (is_string( $attributes[ $bool_key ] ) && isset( static::$TRUTHY_STRING_VALUES[ strtolower( $attributes[ $bool_key ] ) ] ) ) ) {
86
+ $attributes[ $bool_key ] = true;
87
+ } else if ( '0' == $attributes[ $bool_key ] || (is_string( $attributes[ $bool_key ] ) && isset( static::$FALSEY_STRING_VALUES[ strtolower( $attributes[ $bool_key ] ) ] )) ) {
88
+ $attributes[ $bool_key ] = false;
89
+ } else {
90
+ unset( $attributes[ $bool_key ] );
91
+ }
92
+ // @codingStandardsIgnoreEnd WordPress.PHP.StrictComparisons.LooseComparison
93
+ }
94
+
95
+ return $attributes;
96
+ }
97
+
98
+ /**
99
+ * Convert shortcode attribute value to positive integer
100
+ *
101
+ * @since 2.0.0
102
+ *
103
+ * @param array $attributes parsed shortcode attributes
104
+ * @param array $int_keys array keys of expected positive integer values
105
+ *
106
+ * @return array $attributes shortcode attributes with possible type conversions or removed keys
107
+ */
108
+ public static function positiveIntegerOption( array $attributes, $int_keys )
109
+ {
110
+ if ( ! is_array( $int_keys ) || empty( $int_keys ) ) {
111
+ return $attributes;
112
+ }
113
+
114
+ foreach ( $int_keys as $int_key ) {
115
+ if ( ! isset( $attributes[ $int_key ] ) ) {
116
+ continue;
117
+ }
118
+
119
+ $attributes[ $int_key ] = absint( $attributes[ $int_key ] );
120
+ if ( 0 === $attributes[ $int_key ] ) {
121
+ unset( $attributes[ $int_key ] );
122
+ }
123
+ }
124
+
125
+ return $attributes;
126
+ }
127
+
128
+ /**
129
+ * Remove whitespace and convert string attribute to lowercase ASCII characters
130
+ *
131
+ * @since 2.0.0
132
+ *
133
+ * @param array $attributes parsed shortcode attributes
134
+ * @param array $string_keys array keys of expected positive integer values
135
+ *
136
+ * @return array $attributes shortcode attributes with possible type conversions, values to lowercase, or removed keys
137
+ */
138
+ public static function lowercaseStringOption( array $attributes, $string_keys )
139
+ {
140
+ if ( ! is_array( $string_keys ) || empty( $string_keys ) ) {
141
+ return $attributes;
142
+ }
143
+
144
+ foreach ( $string_keys as $string_key ) {
145
+ if ( ! isset( $attributes[ $string_key ] ) ) {
146
+ continue;
147
+ }
148
+
149
+ $attributes[ $string_key ] = strtolower( trim( $attributes[ $string_key ] ) );
150
+ if ( ! $attributes[ $string_key ] ) {
151
+ unset( $attributes[ $string_key ] );
152
+ }
153
+ }
154
+
155
+ return $attributes;
156
+ }
157
+ }
src/Twitter/WordPress/Shortcodes/OEmbedTrait.php CHANGED
@@ -37,23 +37,15 @@ trait OEmbedTrait
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
 
@@ -71,15 +63,14 @@ trait OEmbedTrait
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
 
@@ -98,23 +89,26 @@ trait OEmbedTrait
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 '';
37
  *
38
  * @since 1.3.0
39
  *
 
 
40
  * @return array associative array of query parameters ready for http_build_query {
41
  * @type string query parameter name
42
  * @type string|bool query parameter value
43
  * }
44
  */
45
+ public static function getBaseOEmbedParams()
46
  {
 
 
 
 
 
47
  // omit JavaScript. enqueue separately
48
  $query_parameters = array(
 
49
  'omit_script' => true,
50
  );
51
 
63
  *
64
  * @since 1.3.0
65
  *
66
+ * @param array $query_parameters query parameters to be passed to the oEmbed endpoint
67
+ * @param string $cache_key key to retrieve and store HTML values for the given configuration
68
  *
 
69
  * @return string HTML markup returned by the oEmbed endpoint
70
  */
71
+ public static function getOEmbedMarkup( $query_parameters, $cache_key )
72
  {
73
+ if ( ! (is_string( $cache_key ) && $cache_key ) ) {
 
74
  return '';
75
  }
76
 
89
  return '';
90
  }
91
 
92
+ if ( ! isset( $query_parameters['url'] ) ) {
93
+ if ( defined( $classname . '::BASE_URL' ) && isset( $query_parameters['id'] ) ) {
94
+ // convert ID to full URL to allow more flexibility for oEmbed endpoint
95
+ $query_parameters['url'] = static::BASE_URL . $query_parameters['id'];
96
+ unset( $query_parameters['id'] );
97
+ } else {
98
+ return '';
99
+ }
100
  }
101
 
102
  $allowed_resource_types = array( 'rich' => true, 'video' => true );
103
  $ttl = DAY_IN_SECONDS;
104
 
105
  $oembed_response = $oembed_api_class::getJSON( static::OEMBED_API_ENDPOINT, $query_parameters );
106
+ if ( ! ($oembed_response
107
+ && isset( $oembed_response->type )
108
+ && isset( $allowed_resource_types[ $oembed_response->type ] )
109
+ && isset( $oembed_response->html )
110
+ && $oembed_response->html )
111
+ ) {
 
112
  // do not rerequest errors with every page request
113
  set_transient( $cache_key, ' ', $ttl );
114
  return '';
src/Twitter/WordPress/Shortcodes/PublishOEmbedEndpoint.php CHANGED
@@ -30,7 +30,8 @@ namespace Twitter\WordPress\Shortcodes;
30
  *
31
  * @since 1.5.0
32
  */
33
- interface PublishOEmbedEndpoint {
 
34
  /**
35
  * PHP class to use for fetching oEmbed data
36
  *
@@ -48,4 +49,16 @@ interface PublishOEmbedEndpoint {
48
  * @type string
49
  */
50
  const OEMBED_API_ENDPOINT = 'oembed';
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
30
  *
31
  * @since 1.5.0
32
  */
33
+ interface PublishOEmbedEndpoint
34
+ {
35
  /**
36
  * PHP class to use for fetching oEmbed data
37
  *
49
  * @type string
50
  */
51
  const OEMBED_API_ENDPOINT = 'oembed';
52
+
53
+ /**
54
+ * Create a unique cache key to represent the requested object
55
+ *
56
+ * @since 2.0.0
57
+ *
58
+ * @param string $id datasource identifier
59
+ * @param array $query_parameters oEmbed-compatible API parameters possibly affecting display
60
+ *
61
+ * @return string cache key
62
+ */
63
+ public static function getOEmbedCacheKey( $id, array $query_parameters );
64
  }
src/Twitter/WordPress/Shortcodes/ShortcodeInterface.php CHANGED
@@ -46,7 +46,7 @@ interface ShortcodeInterface
46
  *
47
  * @since 1.3.0
48
  *
49
- * @link https://github.com/fusioneng/Shortcake Shortcake UI
50
  *
51
  * @return void
52
  */
@@ -63,4 +63,25 @@ interface ShortcodeInterface
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
  }
46
  *
47
  * @since 1.3.0
48
  *
49
+ * @see https://github.com/wp-shortcake/shortcake Shortcake UI
50
  *
51
  * @return void
52
  */
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
+
67
+ /**
68
+ * Process and clean supplied attributes
69
+ *
70
+ * @since 2.0.0
71
+ *
72
+ * @param array $attributes array of attributes passed by WordPress shortcode API
73
+ *
74
+ * @return array processed attriutes prepped
75
+ */
76
+ public static function getShortcodeAttributes( $attributes );
77
+
78
+ /**
79
+ * Describe the shortcode
80
+ *
81
+ * Used by shortcake UI to list available shortcodes.
82
+ * May be used by a widget or other visuals to describe a feature
83
+ *
84
+ * @return string Translated feature name
85
+ */
86
+ public static function featureName();
87
  }
src/Twitter/WordPress/Shortcodes/TweetGrid.php DELETED
@@ -1,288 +0,0 @@
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, PublishOEmbedEndpoint
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
- * oEmbed regex registered by WordPress Core
48
- *
49
- * @since 1.5.0
50
- *
51
- * @type string
52
- */
53
- const OEMBED_CORE_REGEX = '#https?://(www\.)?twitter\.com/.+?/timelines/.*#i';
54
-
55
- /**
56
- * HTML class to be used in div wrapper
57
- *
58
- * @since 1.3.0
59
- *
60
- * @type string
61
- */
62
- const HTML_CLASS = 'twitter-grid';
63
-
64
- /**
65
- * Regex used to match a Collection in text
66
- *
67
- * @since 1.3.0
68
- *
69
- * @type string
70
- */
71
- const URL_REGEX = '#^https://twitter\.com/[a-z0-9_]{1,20}/timelines/([0-9]+)#i';
72
-
73
- /**
74
- * Base URL used to reconstruct a Collection URL
75
- *
76
- * @since 1.3.0
77
- *
78
- * @type string
79
- */
80
- const BASE_URL = 'https://twitter.com/_/timelines/';
81
-
82
- /**
83
- * Accepted shortcode attributes and their default values
84
- *
85
- * @since 1.3.0
86
- *
87
- * @type array
88
- */
89
- public static $SHORTCODE_DEFAULTS = array( 'id' => '', 'limit' => 20 );
90
-
91
- /**
92
- * Attach handlers for Twitter Grid
93
- *
94
- * @since 1.3.0
95
- *
96
- * @return void
97
- */
98
- public static function init()
99
- {
100
- $classname = get_called_class();
101
-
102
- // register our shortcode and its handler
103
- add_shortcode( static::SHORTCODE_TAG, array( $classname, 'shortcodeHandler' ) );
104
-
105
- // Shortcode UI, if supported
106
- add_action(
107
- 'register_shortcode_ui',
108
- array( $classname, 'shortcodeUI' ),
109
- 5,
110
- 0
111
- );
112
-
113
- if ( ! is_admin() ) {
114
- // unhook the WordPress Core oEmbed handler
115
- wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
116
-
117
- // convert a URL into the shortcode equivalent
118
- wp_embed_register_handler(
119
- static::SHORTCODE_TAG,
120
- static::URL_REGEX,
121
- array( $classname, 'linkHandler' ),
122
- 1
123
- );
124
- }
125
- }
126
-
127
- /**
128
- * Reference the feature by name
129
- *
130
- * @since 1.3.0
131
- *
132
- * @return string translated feature name
133
- */
134
- public static function featureName()
135
- {
136
- return _x( 'Tweet Grid', 'Tweets displayed in a grid layout', 'twitter' );
137
- }
138
-
139
- /**
140
- * Describe shortcode for Shortcake UI
141
- *
142
- * @since 1.3.0
143
- *
144
- * @link https://github.com/fusioneng/Shortcake Shortcake UI
145
- *
146
- * @return void
147
- */
148
- public static function shortcodeUI()
149
- {
150
- // Shortcake required
151
- if ( ! function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
152
- return;
153
- }
154
-
155
- shortcode_ui_register_for_shortcode(
156
- static::SHORTCODE_TAG,
157
- array(
158
- 'label' => esc_html( static::featureName() ),
159
- 'listItemImage' => 'dashicons-twitter',
160
- 'attrs' => array(
161
- array(
162
- 'attr' => 'id',
163
- 'label' => 'ID',
164
- 'type' => 'text',
165
- 'meta' => array(
166
- 'required' => true,
167
- 'pattern' => '[0-9]+',
168
- ),
169
- ),
170
- array(
171
- 'attr' => 'limit',
172
- 'label' => _x( 'Limit', 'Maximum number of items to include', 'twitter' ),
173
- 'type' => 'number',
174
- 'value' => static::$SHORTCODE_DEFAULTS['limit'],
175
- 'meta' => array(
176
- 'min' => 1,
177
- 'max' => 20,
178
- 'step' => 1,
179
- ),
180
- ),
181
- ),
182
- )
183
- );
184
- }
185
-
186
- /**
187
- * Handle a URL matched by a embed handler
188
- *
189
- * @since 1.3.0
190
- *
191
- * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
192
- * @param array $attr Embed attributes. Not used.
193
- * @param string $url The original URL that was matched by the regex. Not used.
194
- * @param array $rawattr The original unmodified attributes. Not used.
195
- *
196
- * @return string HTML markup for the Tweet grid or an empty string if requirements not met
197
- */
198
- public static function linkHandler( $matches, $attr, $url, $rawattr )
199
- {
200
- if ( ! ( is_array( $matches ) && isset( $matches[1] ) && $matches[1] ) ) {
201
- return '';
202
- }
203
-
204
- return static::shortcodeHandler( array( 'id' => $matches[1] ) );
205
- }
206
-
207
- /**
208
- * Handle shortcode macro
209
- *
210
- * @since 1.3.0
211
- *
212
- * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
213
- * @type string|int attribute name or positional int
214
- * @type mixed shortcode value
215
- * }
216
- * @param string $content content inside a shortcode macro. no effect on this shortcode
217
- *
218
- * @return string HTML markup. empty string if parameter requirement not met or no collection info found
219
- */
220
- public static function shortcodeHandler( $attributes, $content = '' )
221
- {
222
- $options = shortcode_atts(
223
- static::$SHORTCODE_DEFAULTS,
224
- $attributes,
225
- static::SHORTCODE_TAG
226
- );
227
-
228
- $snowflake_id = trim( $options['id'] );
229
- if ( ! $snowflake_id ) {
230
- return '';
231
- }
232
-
233
- $query_parameters = static::getBaseOEmbedParams( $snowflake_id );
234
- if ( isset( $options['limit'] ) ) {
235
- $limit = 0;
236
- try {
237
- $limit = intval( trim( $options['limit'] ), 10 );
238
- } catch( Exception $e ) {}
239
- if ( $limit > 0 && $limit < 20 ) {
240
- $query_parameters['limit'] = $limit;
241
- }
242
- }
243
-
244
- // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
245
- $html = trim( static::getOEmbedMarkup( $query_parameters ) );
246
- if ( ! $html ) {
247
- return '';
248
- }
249
-
250
- $html = '<div class="' . sanitize_html_class( static::HTML_CLASS ) . '">' . $html . '</div>';
251
-
252
- $inline_js = \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
253
- if ( $inline_js ) {
254
- return $html . $inline_js;
255
- }
256
-
257
- return $html;
258
- }
259
-
260
- /**
261
- * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
262
- *
263
- * @since 1.3.0
264
- *
265
- * @param array $query_parameters oEmbed API query parameters
266
- *
267
- * @return string cache key
268
- */
269
- public static function oEmbedCacheKey( array $query_parameters )
270
- {
271
- if ( ! ( isset( $query_parameters['id'] ) && $query_parameters['id'] ) ) {
272
- return '';
273
- }
274
-
275
- $key_pieces = array( static::SHORTCODE_TAG, $query_parameters['id'] );
276
-
277
- // separate cache for each explicitly-defined display language
278
- if ( isset( $query_parameters['lang'] ) && $query_parameters['lang'] ) {
279
- $key_pieces[] = $query_parameters['lang'];
280
- }
281
-
282
- if ( isset( $query_parameters['limit'] ) && $query_parameters['limit'] ) {
283
- $key_pieces[] = $query_parameters['limit'];
284
- }
285
-
286
- return implode( '_', $key_pieces );
287
- }
288
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/Twitter/WordPress/Site/Username.php CHANGED
@@ -55,7 +55,7 @@ class Username
55
  $post_id = null;
56
  }
57
 
58
- $username = get_option( \Twitter\WordPress\Admin\Settings\SiteAttribution::OPTION_NAME, '' );
59
 
60
  if ( ! is_string( $username ) ) {
61
  $username = '';
55
  $post_id = null;
56
  }
57
 
58
+ $username = get_option( \Twitter\WordPress\Admin\Settings\Cards\SiteAttribution::OPTION_NAME, '' );
59
 
60
  if ( ! is_string( $username ) ) {
61
  $username = '';
src/Twitter/WordPress/User/Meta.php CHANGED
@@ -39,11 +39,12 @@ class Meta
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 '';
@@ -87,7 +88,8 @@ class Meta
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
 
@@ -100,7 +102,8 @@ class Meta
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
  }
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
+ {
48
  // basic test for invalid passed parameter
49
  if ( ! $user_id ) {
50
  return '';
88
  *
89
  * @return string Twitter username value stored for the given WordPress user identifier
90
  */
91
+ public static function getTwitterUsername( $user_id )
92
+ {
93
  return static::getSocialUsername( $user_id, 'twitter' );
94
  }
95
 
102
  *
103
  * @return string Periscope username value stored for the given WordPress user identifier
104
  */
105
+ public static function getPeriscopeUsername( $user_id )
106
+ {
107
  return static::getSocialUsername( $user_id, 'periscope' );
108
  }
109
  }
src/Twitter/WordPress/Widgets/Advertising/Tracking.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2017 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\Advertising;
27
+
28
+ /**
29
+ * Add embedded profile timeline as a WordPress widget
30
+ *
31
+ * @see http://codex.wordpress.org/Widgets_API WordPress widgets API
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ class Tracking extends \Twitter\WordPress\Widgets\Widget
36
+ {
37
+ /**
38
+ * Class of the related shortcode handler
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const SHORTCODE_CLASS = '\Twitter\WordPress\Shortcodes\Advertising\Tracking';
45
+
46
+ /**
47
+ * Register widget with WordPress
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @return void
52
+ */
53
+ public function __construct()
54
+ {
55
+ $shortcode_class = static::SHORTCODE_CLASS;
56
+ parent::__construct(
57
+ 'twitter-tracking', // Base ID
58
+ $shortcode_class::featureName(), // name
59
+ array(
60
+ 'description' => static::getDescription(),
61
+ )
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Describe the functionality offered by the widget
67
+ *
68
+ * @since 2.0.0
69
+ *
70
+ * @return string description of the widget functionality
71
+ */
72
+ public static function getDescription()
73
+ {
74
+ return __( 'Track Twitter advertising conversion or build a custom audience for ad targeting', 'twitter' );
75
+ }
76
+
77
+ /**
78
+ * Front-end display of widget
79
+ *
80
+ * @since 2.0.0
81
+ *
82
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
83
+ * @param array $instance The settings for the particular instance of the widget
84
+ *
85
+ * @return void
86
+ */
87
+ public function widget( $args, $instance )
88
+ {
89
+ $shortcode_class = static::SHORTCODE_CLASS;
90
+ if ( ! method_exists( $shortcode_class, 'shortcodeHandler' ) ) {
91
+ return;
92
+ }
93
+ $html = $shortcode_class::shortcodeHandler( $instance );
94
+ if ( ! $html ) {
95
+ return;
96
+ }
97
+
98
+ // Allow HTML markup set by author, site
99
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
100
+ echo $args['before_widget'];
101
+
102
+ /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
103
+ $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
104
+ if ( $title ) {
105
+ // Allow HTML markup set by author, site
106
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
107
+ echo $args['before_title'];
108
+
109
+ // Allow HTML in title. Link to Twitter datasource might be common use
110
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
111
+ echo $title;
112
+
113
+ // Allow HTML markup set by author, site
114
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
115
+ echo $args['after_title'];
116
+ }
117
+
118
+ // escaped in markup builder
119
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput
120
+ echo $html;
121
+
122
+ // Allow HTML markup set by author, site
123
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
124
+ echo $args['after_widget'];
125
+ }
126
+
127
+ /**
128
+ * Settings update form
129
+ *
130
+ * @since 2.0.0
131
+ *
132
+ * @param array $instance Current settings
133
+ *
134
+ * @return void
135
+ */
136
+ public function form( $instance )
137
+ {
138
+ $shortcode_class = static::SHORTCODE_CLASS;
139
+ if ( ! method_exists( $shortcode_class, 'getShortcodeDefaults' ) ) {
140
+ return;
141
+ }
142
+ $instance = wp_parse_args(
143
+ (array) $instance,
144
+ $shortcode_class::getShortcodeDefaults()
145
+ );
146
+
147
+ ?><p><label for="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>"><?php echo esc_html( _x( 'Website tag ID', 'Identifier used to track an advertising campaign including conversion and custom audiences', 'twitter' ) . ':' ); ?></label>
148
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'id' ) ); ?>" type="text" inputmode="verbatim" spellcheck="false" pattern="<?php echo esc_attr( \Twitter\Helpers\Validators\WebsiteTag::getPattern() ); ?>" maxlength="<?php echo esc_attr( \Twitter\Helpers\Validators\WebsiteTag::MAX_LENGTH ); ?>" value="<?php echo esc_attr( trim( $instance['id'] ) ); ?>" required<?php
149
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
150
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
151
+ ?>></p><?php
152
+ }
153
+
154
+ /**
155
+ * Update a widget instance
156
+ *
157
+ * @since 2.0.0
158
+ *
159
+ * @param array $new_instance New settings for this instance as input by the user via form()
160
+ * @param array $old_instance Old settings for this instance
161
+ *
162
+ * @return bool|array settings to save or false to cancel saving
163
+ */
164
+ public function update( $new_instance, $old_instance )
165
+ {
166
+ $new_instance = (array) $new_instance;
167
+
168
+ $id = '';
169
+ if ( isset( $new_instance['id'] ) ) {
170
+ $id = \Twitter\Helpers\Validators\WebsiteTag::sanitize( $new_instance['id'] );
171
+ }
172
+ // ID required, otherwise nothing to track
173
+ if ( ! $id ) {
174
+ return false;
175
+ }
176
+
177
+ return array(
178
+ 'id' => $id,
179
+ );
180
+ }
181
+ }
src/Twitter/WordPress/Widgets/{Follow.php → Buttons/Follow.php} RENAMED
@@ -23,16 +23,16 @@ 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 Twitter Follow Button as a WordPress widget
30
  *
31
  * @link http://codex.wordpress.org/Widgets_API WordPress widgets API
32
  *
33
  * @since 1.0.0
34
  */
35
- class Follow extends \WP_Widget
36
  {
37
 
38
  /**
@@ -50,8 +50,6 @@ class Follow extends \WP_Widget
50
  * Register widget with WordPress
51
  *
52
  * @since 1.0.0
53
- *
54
- * @return void
55
  */
56
  public function __construct()
57
  {
@@ -59,11 +57,23 @@ class Follow extends \WP_Widget
59
  static::BASE_ID, // Base ID
60
  __( 'Twitter Follow Button', 'twitter' ), // name
61
  array(
62
- 'description' => __( 'Lets a viewer follow your Twitter account', 'twitter' ) // args
63
  )
64
  );
65
  }
66
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  /**
68
  * Front-end display of widget
69
  *
@@ -76,29 +86,37 @@ class Follow extends \WP_Widget
76
  */
77
  public function widget( $args, $instance )
78
  {
79
- // no follow target
80
- if ( empty( $instance['screen_name'] ) ) {
81
- return;
82
- }
83
-
84
- $follow_button_html = \Twitter\WordPress\Shortcodes\Follow::shortcodeHandler( $instance );
85
  if ( ! $follow_button_html ) {
86
  return;
87
  }
88
 
 
 
89
  echo $args['before_widget'];
90
 
91
  /** This filter is documented in wp-includes/default-widgets.php */
92
  $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
93
  if ( $title ) {
94
- echo $args['before_title'] . $title . $args['after_title'];
 
 
 
 
 
 
 
 
 
 
95
  }
96
 
97
  // escaped in markup builder
98
- // @codingStandardsIgnoreStart WordPress.XSS.EscapeOutput
99
  echo $follow_button_html;
100
- // @codingStandardsIgnoreEnd WordPress.XSS.EscapeOutput
101
 
 
 
102
  echo $args['after_widget'];
103
  }
104
 
@@ -117,28 +135,50 @@ class Follow extends \WP_Widget
117
  (array) $instance,
118
  array_merge(
119
  array( 'title' => '' ),
120
- \Twitter\WordPress\Shortcodes\Follow::$SHORTCODE_DEFAULTS
121
  )
122
  );
123
 
 
124
  $close_void_element = \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
125
- ?>
126
- <p><label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php echo esc_html( __( 'Title:' ) ); ?></label>
127
- <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>
128
 
 
129
  <p><label for="<?php echo esc_attr( $this->get_field_id( 'screen_name' ) ); ?>"><?php echo esc_html( __( '@username:', 'twitter' ) ); ?></label>
130
- <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'screen_name' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'screen_name' ) ); ?>" type="text" pattern="[a-zA-Z0-9_]{1,20}" value="<?php echo esc_attr( $instance['screen_name'] ); ?>"<?php echo $close_void_element; ?>></p>
131
-
132
- <p><input class="checkbox" type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'show_screen_name' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'show_screen_name' ) ); ?>" value="1"<?php checked( $instance['show_screen_name'] ); echo $close_void_element; ?>>
 
 
 
 
 
 
 
133
  <label for="<?php echo esc_attr( $this->get_field_id( 'show_screen_name' ) ); ?>"><?php echo esc_html( __( 'Show username?', 'twitter' ) ); ?></label></p>
134
 
135
- <p><input class="checkbox" type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'show_count' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'show_count' ) ); ?>" value="1"<?php checked( $instance['show_count'] ); echo $close_void_element; ?>>
 
 
 
 
136
  <label for="<?php echo esc_attr( $this->get_field_id( 'show_count' ) ); ?>"><?php echo esc_html( __( 'Show number of followers?', 'twitter' ) ); ?></label></p>
137
 
138
  <p><label for="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>"><?php echo esc_html( __( 'Button size:', 'twitter' ) ); ?></label>
139
  <fieldset id="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>">
140
- <label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'size' ) ); ?>" value="medium"<?php checked( 'large' != $instance['size'] ); echo $close_void_element; ?>> <?php echo esc_html( _x( 'medium', 'medium size button', 'twitter' ) ); ?> </label>
141
- <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>
 
 
 
 
 
 
 
 
 
 
 
 
142
  </fieldset></p>
143
  <?php
144
  }
@@ -171,12 +211,12 @@ class Follow extends \WP_Widget
171
  }
172
  }
173
 
174
- $follow_button = \Twitter\Widgets\FollowButton::fromArray( $new_instance );
175
  if ( ! $follow_button ) {
176
  return false;
177
  }
178
 
179
- $filtered_options = $follow_button->toArray( /* dashed_keys */ false );
180
  $screen_name = $follow_button->getScreenName();
181
  if ( $screen_name ) {
182
  $filtered_options['screen_name'] = $screen_name;
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Widgets\Buttons;
27
 
28
  /**
29
+ * Add Twitter Follow button as a WordPress widget
30
  *
31
  * @link http://codex.wordpress.org/Widgets_API WordPress widgets API
32
  *
33
  * @since 1.0.0
34
  */
35
+ class Follow extends \Twitter\WordPress\Widgets\Widget implements \Twitter\WordPress\Widgets\WidgetInterface
36
  {
37
 
38
  /**
50
  * Register widget with WordPress
51
  *
52
  * @since 1.0.0
 
 
53
  */
54
  public function __construct()
55
  {
57
  static::BASE_ID, // Base ID
58
  __( 'Twitter Follow Button', 'twitter' ), // name
59
  array(
60
+ 'description' => static::getDescription(), // args
61
  )
62
  );
63
  }
64
 
65
+ /**
66
+ * Describe the functionality offered by the widget
67
+ *
68
+ * @since 2.0.0
69
+ *
70
+ * @return string description of the widget functionality
71
+ */
72
+ public static function getDescription()
73
+ {
74
+ return __( 'Lets a viewer follow your Twitter account', 'twitter' );
75
+ }
76
+
77
  /**
78
  * Front-end display of widget
79
  *
86
  */
87
  public function widget( $args, $instance )
88
  {
89
+ $follow_button_html = \Twitter\WordPress\Shortcodes\Buttons\Follow::shortcodeHandler( $instance );
 
 
 
 
 
90
  if ( ! $follow_button_html ) {
91
  return;
92
  }
93
 
94
+ // Allow HTML markup set by author, site
95
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
96
  echo $args['before_widget'];
97
 
98
  /** This filter is documented in wp-includes/default-widgets.php */
99
  $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
100
  if ( $title ) {
101
+ // Allow HTML markup set by author, site
102
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
103
+ echo $args['before_title'];
104
+
105
+ // Allow HTML in title. Link to Twitter profile might be common use
106
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
107
+ echo $title;
108
+
109
+ // Allow HTML markup set by author, site
110
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
111
+ echo $args['after_title'];
112
  }
113
 
114
  // escaped in markup builder
115
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
116
  echo $follow_button_html;
 
117
 
118
+ // Allow HTML markup set by author, site
119
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
120
  echo $args['after_widget'];
121
  }
122
 
135
  (array) $instance,
136
  array_merge(
137
  array( 'title' => '' ),
138
+ \Twitter\WordPress\Shortcodes\Buttons\Follow::$SHORTCODE_DEFAULTS
139
  )
140
  );
141
 
142
+ $this->titleFormElements( $instance );
143
  $close_void_element = \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
 
 
 
144
 
145
+ ?>
146
  <p><label for="<?php echo esc_attr( $this->get_field_id( 'screen_name' ) ); ?>"><?php echo esc_html( __( '@username:', 'twitter' ) ); ?></label>
147
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'screen_name' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'screen_name' ) ); ?>" type="text" pattern="<?php echo esc_attr( \Twitter\Helpers\Validators\ScreenName::getPattern() ); ?>" inputmode="verbatim" spellcheck="false" maxlength="<?php echo esc_attr( \Twitter\Helpers\Validators\ScreenName::MAX_LENGTH ); ?>" value="<?php echo esc_attr( $instance['screen_name'] ); ?>"<?php
148
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
149
+ echo $close_void_element;
150
+ ?>></p>
151
+
152
+ <p><input class="checkbox" type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'show_screen_name' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'show_screen_name' ) ); ?>" value="1"<?php
153
+ checked( $instance['show_screen_name'] );
154
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
155
+ echo $close_void_element;
156
+ ?>>
157
  <label for="<?php echo esc_attr( $this->get_field_id( 'show_screen_name' ) ); ?>"><?php echo esc_html( __( 'Show username?', 'twitter' ) ); ?></label></p>
158
 
159
+ <p><input class="checkbox" type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'show_count' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'show_count' ) ); ?>" value="1"<?php
160
+ checked( $instance['show_count'] );
161
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
162
+ echo $close_void_element;
163
+ ?>>
164
  <label for="<?php echo esc_attr( $this->get_field_id( 'show_count' ) ); ?>"><?php echo esc_html( __( 'Show number of followers?', 'twitter' ) ); ?></label></p>
165
 
166
  <p><label for="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>"><?php echo esc_html( __( 'Button size:', 'twitter' ) ); ?></label>
167
  <fieldset id="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>">
168
+ <label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'size' ) ); ?>" value="medium"
169
+ <?php
170
+ checked( 'large' !== $instance['size'] );
171
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
172
+ echo $close_void_element;
173
+ ?>>
174
+ <?php echo esc_html( _x( 'medium', 'medium size button', 'twitter' ) ); ?> </label>
175
+ <label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'size' ) ); ?>" value="large"
176
+ <?php
177
+ checked( 'large' === $instance['size'] );
178
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
179
+ echo $close_void_element;
180
+ ?>>
181
+ <?php echo esc_html( _x( 'large', 'large size button', 'twitter' ) ); ?> </label>
182
  </fieldset></p>
183
  <?php
184
  }
211
  }
212
  }
213
 
214
+ $follow_button = \Twitter\Widgets\Buttons\Follow::fromArray( $new_instance );
215
  if ( ! $follow_button ) {
216
  return false;
217
  }
218
 
219
+ $filtered_options = $follow_button->toArray();
220
  $screen_name = $follow_button->getScreenName();
221
  if ( $screen_name ) {
222
  $filtered_options['screen_name'] = $screen_name;
src/Twitter/WordPress/Widgets/{PeriscopeOnAir.php → Buttons/Periscope/OnAir.php} RENAMED
@@ -23,7 +23,7 @@ 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
@@ -32,7 +32,7 @@ namespace Twitter\WordPress\Widgets;
32
  *
33
  * @since 1.3.0
34
  */
35
- class PeriscopeOnAir extends \WP_Widget
36
  {
37
 
38
  /**
@@ -50,8 +50,6 @@ class PeriscopeOnAir extends \WP_Widget
50
  * Register widget with WordPress
51
  *
52
  * @since 1.3.0
53
- *
54
- * @return void
55
  */
56
  public function __construct()
57
  {
@@ -59,11 +57,23 @@ class PeriscopeOnAir extends \WP_Widget
59
  static::BASE_ID, // Base ID
60
  __( 'Periscope On Air Button', '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
  *
@@ -76,8 +86,8 @@ class PeriscopeOnAir extends \WP_Widget
76
  */
77
  public function widget( $args, $instance )
78
  {
79
- // no Periscope username target
80
- if ( empty( $instance['username'] ) ) {
81
  return;
82
  }
83
 
@@ -85,22 +95,30 @@ class PeriscopeOnAir extends \WP_Widget
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
 
@@ -119,22 +137,31 @@ class PeriscopeOnAir extends \WP_Widget
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( _x( 'Periscope username', 'Prompt requesting entry of a 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
  }
@@ -159,7 +186,7 @@ class PeriscopeOnAir extends \WP_Widget
159
  }
160
  unset( $new_instance['title'] );
161
 
162
- $on_air = \Twitter\Widgets\PeriscopeOnAir::fromArray( $new_instance );
163
  if ( ! $on_air ) {
164
  return false;
165
  }
23
  THE SOFTWARE.
24
  */
25
 
26
+ namespace Twitter\WordPress\Widgets\Buttons\Periscope;
27
 
28
  /**
29
  * Add Periscope On Air button as a WordPress widget
32
  *
33
  * @since 1.3.0
34
  */
35
+ class OnAir extends \Twitter\WordPress\Widgets\Widget implements \Twitter\WordPress\Widgets\WidgetInterface
36
  {
37
 
38
  /**
50
  * Register widget with WordPress
51
  *
52
  * @since 1.3.0
 
 
53
  */
54
  public function __construct()
55
  {
57
  static::BASE_ID, // Base ID
58
  __( 'Periscope On Air Button', 'twitter' ), // name
59
  array(
60
+ 'description' => static::getDescription(),
61
  )
62
  );
63
  }
64
 
65
+ /**
66
+ * Describe the functionality offered by the widget
67
+ *
68
+ * @since 2.0.0
69
+ *
70
+ * @return string description of the widget functionality
71
+ */
72
+ public static function getDescription()
73
+ {
74
+ return __( 'Lets a viewer discover your Periscope account and on air status', 'twitter' );
75
+ }
76
+
77
  /**
78
  * Front-end display of widget
79
  *
86
  */
87
  public function widget( $args, $instance )
88
  {
89
+ $button_html = \Twitter\WordPress\Shortcodes\Buttons\Periscope\OnAir::shortcodeHandler( $instance );
90
+ if ( ! $button_html ) {
91
  return;
92
  }
93
 
95
  $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
96
  unset( $instance['title'] );
97
 
98
+ // Allow HTML markup set by author, site
99
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
 
 
 
100
  echo $args['before_widget'];
101
 
102
  if ( $title ) {
103
+ // Allow HTML markup set by author, site
104
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
105
+ echo $args['before_title'];
106
+
107
+ // Allow HTML in title. Link to Periscope profile might be common use
108
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
109
+ echo $title;
110
+
111
+ // Allow HTML markup set by author, site
112
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
113
+ echo $args['after_title'];
114
  }
115
 
116
  // escaped in markup builder
117
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
118
  echo $button_html;
 
119
 
120
+ // Allow HTML markup set by author, site
121
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
122
  echo $args['after_widget'];
123
  }
124
 
137
  (array) $instance,
138
  array_merge(
139
  array( 'title' => '' ),
140
+ \Twitter\WordPress\Shortcodes\Buttons\Periscope\OnAir::$SHORTCODE_DEFAULTS
141
  )
142
  );
143
 
144
  $close_void_element = \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
145
+ $this->titleFormElements( $instance );
146
  ?>
 
 
 
147
  <p><label for="<?php echo esc_attr( $this->get_field_id( 'username' ) ); ?>"><?php echo esc_html( _x( 'Periscope username', 'Prompt requesting entry of a Periscope username', 'twitter' ) ); ?></label>
148
+ <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="<?php echo esc_attr( \Twitter\Helpers\Validators\PeriscopeUsername::getPattern() ); ?>" inputmode="verbatim" spellcheck="false" maxlength="<?php echo esc_attr( \Twitter\Helpers\Validators\PeriscopeUsername::MAX_LENGTH ); ?>" value="<?php echo esc_attr( $instance['username'] ); ?>"<?php
149
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
150
+ echo $close_void_element;
151
+ ?>></p>
152
 
153
  <p><label for="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>"><?php echo esc_html( __( 'Button size:', 'twitter' ) ); ?></label>
154
  <fieldset id="<?php echo esc_attr( $this->get_field_id( 'size' ) ); ?>">
155
+ <label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'size' ) ); ?>" value="small"<?php
156
+ checked( 'large' !== $instance['size'] );
157
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
158
+ echo $close_void_element;
159
+ ?>> <?php echo esc_html( _x( 'small', 'small size button', 'twitter' ) ); ?> </label>
160
+ <label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'size' ) ); ?>" value="large"<?php
161
+ checked( 'large' === $instance['size'] );
162
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
163
+ echo $close_void_element;
164
+ ?>> <?php echo esc_html( _x( 'large', 'large size button', 'twitter' ) ); ?> </label>
165
  </fieldset></p>
166
  <?php
167
  }
186
  }
187
  unset( $new_instance['title'] );
188
 
189
+ $on_air = \Twitter\Widgets\Buttons\Periscope\OnAir::fromArray( $new_instance );
190
  if ( ! $on_air ) {
191
  return false;
192
  }
src/Twitter/WordPress/Widgets/Embeds/Timeline.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds;
27
+
28
+ /**
29
+ * Add embedded profile timeline as a WordPress widget
30
+ *
31
+ * @link http://codex.wordpress.org/Widgets_API WordPress widgets API
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ abstract class Timeline extends \Twitter\WordPress\Widgets\Widget implements \Twitter\WordPress\Widgets\WidgetInterface
36
+ {
37
+ /**
38
+ * Register widget with WordPress
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @return void
43
+ */
44
+ public function __construct()
45
+ {
46
+ $shortcode_class = static::SHORTCODE_CLASS;
47
+ parent::__construct(
48
+ $shortcode_class::HTML_CLASS, // Base ID
49
+ $shortcode_class::featureName(), // name
50
+ array(
51
+ 'description' => static::getDescription(),
52
+ )
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Get displayed options for chrome configuration with label
58
+ *
59
+ * @since 2.0.0
60
+ *
61
+ * @return array chrome options
62
+ */
63
+ public static function getChromeFormOptions()
64
+ {
65
+ return array(
66
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOHEADER => _x( 'Hide header', 'Hide introductory section before the timeline', 'twitter' ),
67
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOFOOTER => _x( 'Hide footer', 'Hide summary section after the timeline', 'twitter' ),
68
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOBORDERS => _x( 'Hide borders', 'Hide the visual line between two sections', 'twitter' ),
69
+ \Twitter\Widgets\Embeds\Timeline::CHROME_NOSCROLLBAR => _x( 'Hide scroll bar', 'Hide the visual indicator of additional content available above or below the current position', 'twitter' ),
70
+ \Twitter\Widgets\Embeds\Timeline::CHROME_TRANSPARENT => _x( 'Transparent background', 'Remove color shown behind the widget', 'twitter' ),
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Front-end display of widget
76
+ *
77
+ * @since 2.0.0
78
+ *
79
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
80
+ * @param array $instance The settings for the particular instance of the widget
81
+ *
82
+ * @return void
83
+ */
84
+ public function widget( $args, $instance )
85
+ {
86
+ $shortcode_class = static::SHORTCODE_CLASS;
87
+ if ( ! method_exists( $shortcode_class, 'shortcodeHandler' ) ) {
88
+ return;
89
+ }
90
+ $timeline_html = $shortcode_class::shortcodeHandler( $instance );
91
+ if ( ! $timeline_html ) {
92
+ return;
93
+ }
94
+
95
+ // Allow HTML markup set by author, site
96
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
97
+ echo $args['before_widget'];
98
+
99
+ /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
100
+ $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
101
+ if ( $title ) {
102
+ // Allow HTML markup set by author, site
103
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
104
+ echo $args['before_title'];
105
+
106
+ // Allow HTML in title. Link to Twitter datasource might be common use
107
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
108
+ echo $title;
109
+
110
+ // Allow HTML markup set by author, site
111
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
112
+ echo $args['after_title'];
113
+ }
114
+
115
+ // escaped in markup builder
116
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput
117
+ echo $timeline_html;
118
+
119
+ // Allow HTML markup set by author, site
120
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
121
+ echo $args['after_widget'];
122
+ }
123
+
124
+ /**
125
+ * Settings update form
126
+ *
127
+ * @since 2.0.0
128
+ *
129
+ * @param array $instance Current settings
130
+ *
131
+ * @return void
132
+ */
133
+ public function form( $instance )
134
+ {
135
+ $shortcode_class = static::SHORTCODE_CLASS;
136
+ if ( ! method_exists( $shortcode_class, 'getShortcodeDefaults' ) ) {
137
+ return;
138
+ }
139
+ $instance = wp_parse_args(
140
+ (array) $instance,
141
+ array_merge(
142
+ array( 'title' => '' ),
143
+ $shortcode_class::getShortcodeDefaults()
144
+ )
145
+ );
146
+
147
+ $this->titleFormElements( $instance );
148
+ $this->dataSourceFormElements( $instance );
149
+ $this->timelineFormElements( $instance );
150
+ }
151
+
152
+ /**
153
+ * Widget form applicable to all timelines
154
+ *
155
+ * @since 2.0.0
156
+ *
157
+ * @param array $instance widget instance
158
+ *
159
+ * @return void
160
+ */
161
+ protected function timelineFormElements( $instance )
162
+ {
163
+ $close_void_element = \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
164
+ ?><p><label for="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>"><?php echo esc_html( \Twitter\WordPress\Admin\Settings\Embeds\Theme::featureName() . ':' ); ?></label></p><?php
165
+ $theme_choices = \Twitter\WordPress\Admin\Settings\Embeds\Theme::themeChoices();
166
+ ?><fieldset id="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>"><?php
167
+ foreach ( $theme_choices as $value => $label ) {
168
+ ?>
169
+ <p><label><input type="radio" name="<?php echo esc_attr( $this->get_field_name( 'theme' ) ); ?>" value="<?php echo esc_attr( $value ); ?>"<?php
170
+ checked( $value, $instance['theme'] );
171
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
172
+ echo $close_void_element;
173
+ ?>><?php echo esc_html( $label ); ?></label></p>
174
+ <?php } ?>
175
+ </fieldset>
176
+
177
+ <?php
178
+ $chrome = static::getChromeFormOptions();
179
+ if ( ! ( isset( $instance['chrome'] ) && is_array( $instance['chrome'] ) ) ) {
180
+ $instance['chrome'] = array();
181
+ }
182
+ ?><label for="<?php echo esc_attr( $this->get_field_id( 'chrome' ) ); ?>"><?php echo esc_html( _x( 'Layout options', 'Visual layout choices', 'twitter' ) . ':' ); ?></label>
183
+ <fieldset id="<?php echo esc_attr( $this->get_field_id( 'chrome' ) ); ?>"><?php
184
+ foreach ( $chrome as $value => $label ) {
185
+ ?><p><label><input type="checkbox" class="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'chrome' ) ); ?>[]" value="<?php echo esc_attr( $value ); ?>"<?php
186
+ checked( in_array( $value, $instance['chrome'], /* strict */ true ) );
187
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
188
+ echo $close_void_element;
189
+ ?>><?php echo esc_html( $label ); ?></label></p><?php
190
+ }
191
+ ?></fieldset>
192
+
193
+ <p>
194
+ <label for="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>"><?php echo esc_html( _x( 'Limit', 'Limit the number of Tweets displayed', 'twitter' ) . ':' ); ?></label>
195
+ <input type="number" class="tiny-text" id="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'limit' ) ); ?>" min="<?php echo esc_attr( \Twitter\Widgets\Embeds\Timeline::MIN_LIMIT ); ?>" max="<?php echo esc_attr( \Twitter\Widgets\Embeds\Timeline::MAX_LIMIT ); ?>" step="1" value="<?php echo esc_attr( $instance['limit'] ); ?>" size="3"<?php
196
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
197
+ echo $close_void_element;
198
+ ?>>
199
+ </p>
200
+
201
+ <p><label for="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>"><?php echo esc_html( _x( 'Width', 'Horizontal dimension measured in whole pixels', 'twitter' ) . ':' ); ?></label><input type="number" id="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>" class="small-text" name="<?php echo esc_attr( $this->get_field_name( 'width' ) ); ?>" step="1" min="<?php echo esc_attr( \Twitter\Widgets\Embeds\Timeline::MIN_WIDTH ); ?>" max="<?php echo esc_attr( \Twitter\Widgets\Embeds\Timeline::MAX_WIDTH ); ?>" step="10" value="<?php echo esc_attr( $instance['width'] ); ?>" size="4"<?php
202
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
203
+ echo $close_void_element;
204
+ ?>></p>
205
+
206
+ <p><label for="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>"><?php echo esc_html( _x( 'Height', 'Vertical dimension measured in whole pixels', 'twitter' ) . ':' ); ?></label><input type="number" id="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>" class="small-text" name="<?php echo esc_attr( $this->get_field_name( 'height' ) ); ?>" step="1" min="<?php echo esc_attr( \Twitter\Widgets\Embeds\Timeline::MIN_HEIGHT ); ?>" step="10" value="<?php echo esc_attr( $instance['height'] ); ?>" size="4"<?php
207
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
208
+ echo $close_void_element;
209
+ ?>></p><?php
210
+ }
211
+
212
+ /**
213
+ * Fields specific to the timeline datasource configured in the widget
214
+ *
215
+ * @since 2.0.0
216
+ *
217
+ * @param array $instance widget instance
218
+ *
219
+ * @return void
220
+ */
221
+ abstract protected function dataSourceFormElements( $instance );
222
+ }
src/Twitter/WordPress/Widgets/Embeds/Timeline/Collection.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Add embedded collection as a WordPress widget
30
+ *
31
+ * @link http://codex.wordpress.org/Widgets_API WordPress widgets API
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ class Collection extends \Twitter\WordPress\Widgets\Embeds\Timeline
36
+ {
37
+ /**
38
+ * Class of the related PHP object builder and validator
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const TIMELINE_CLASS = '\Twitter\Widgets\Embeds\Timeline\Collection';
45
+
46
+ /**
47
+ * Class of the related shortcode handler
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const SHORTCODE_CLASS = '\Twitter\WordPress\Shortcodes\Embeds\Timeline\Collection';
54
+
55
+ /**
56
+ * Describe the functionality offered by the widget
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @return string description of the widget functionality
61
+ */
62
+ public static function getDescription()
63
+ {
64
+ return __( 'Tweets organized into a collection', 'twitter' );
65
+ }
66
+
67
+ /**
68
+ * Fields specific to the timeline datasource configured in the widget
69
+ *
70
+ * @since 2.0.0
71
+ *
72
+ * @param array $instance widget instance
73
+ *
74
+ * @return void
75
+ */
76
+ protected function dataSourceFormElements( $instance )
77
+ {
78
+ ?><p><label for="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>"><?php echo esc_html( _x( 'Collection ID', 'Unique identifier for a Twitter collection', 'twitter' ) . ':' ); ?></label><input id="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>" class="widefat" name="<?php echo esc_attr( $this->get_field_name( 'id' ) ); ?>" type="text" pattern="[0-9]{1,}" inputmode="verbatim" spellcheck="false" value="<?php echo esc_attr( $instance['id'] ); ?>"<?php
79
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
80
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
81
+ ?>></p><?php
82
+ }
83
+
84
+ /**
85
+ * Update a widget instance
86
+ *
87
+ * @since 2.0.0
88
+ *
89
+ * @param array $new_instance New settings for this instance as input by the user via form()
90
+ * @param array $old_instance Old settings for this instance
91
+ *
92
+ * @return bool|array settings to save or false to cancel saving
93
+ */
94
+ public function update( $new_instance, $old_instance )
95
+ {
96
+ $new_instance = (array) $new_instance;
97
+
98
+ // collection ID required
99
+ if ( ! ( isset( $new_instance['id'] ) && $new_instance['id'] ) ) {
100
+ return false;
101
+ }
102
+
103
+ $instance = array();
104
+ $title = sanitize_text_field( $new_instance['title'] );
105
+ if ( $title ) {
106
+ $instance['title'] = $title;
107
+ }
108
+ unset( $new_instance['title'] );
109
+
110
+ // process widget parameters as if they were parsed shortcode attributes
111
+ $shortcode_class = static::SHORTCODE_CLASS;
112
+ $timeline_class = static::TIMELINE_CLASS;
113
+ if ( ! ( method_exists( $timeline_class, 'fromArray' ) && method_exists( $shortcode_class, 'shortcodeAttributesToTimelineKeys' ) ) ) {
114
+ return false;
115
+ }
116
+ $timeline = $timeline_class::fromArray( $shortcode_class::shortcodeAttributesToTimelineKeys( $new_instance ) );
117
+ if ( ! ( $timeline && method_exists( $timeline, 'getID' ) && $timeline->getID() && method_exists( $timeline, 'toArray' ) ) ) {
118
+ return false;
119
+ }
120
+
121
+ $data_attributes = $timeline->toArray();
122
+ if ( isset( $data_attributes['tweet-limit'] ) ) {
123
+ $data_attributes['limit'] = $data_attributes['tweet-limit'];
124
+ }
125
+
126
+ return array_merge( $instance, $data_attributes );
127
+ }
128
+ }
src/Twitter/WordPress/Widgets/Embeds/Timeline/Profile.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Add embedded profile timeline as a WordPress widget
30
+ *
31
+ * @see http://codex.wordpress.org/Widgets_API WordPress widgets API
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ class Profile extends \Twitter\WordPress\Widgets\Embeds\Timeline
36
+ {
37
+ /**
38
+ * Class of the related PHP object builder and validator
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const TIMELINE_CLASS = '\Twitter\Widgets\Embeds\Timeline\Profile';
45
+
46
+ /**
47
+ * Class of the related shortcode handler
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const SHORTCODE_CLASS = '\Twitter\WordPress\Shortcodes\Embeds\Timeline\Profile';
54
+
55
+ /**
56
+ * Describe the functionality offered by the widget
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @return string description of the widget functionality
61
+ */
62
+ public static function getDescription()
63
+ {
64
+ return __( 'The latest Tweets from a Twitter account', 'twitter' );
65
+ }
66
+
67
+ /**
68
+ * Fields specific to the timeline datasource configured in the widget
69
+ *
70
+ * @since 2.0.0
71
+ *
72
+ * @param array $instance widget instance
73
+ *
74
+ * @return void
75
+ */
76
+ protected function dataSourceFormElements( $instance )
77
+ {
78
+ ?><p><label for="<?php echo esc_attr( $this->get_field_id( 'screen_name' ) ); ?>"><?php echo esc_html( __( 'Twitter @username', 'twitter' ) . ':' ); ?></label>
79
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'screen_name' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'screen_name' ) ); ?>" type="text" pattern="<?php echo esc_attr( \Twitter\Helpers\Validators\ScreenName::getPattern() ); ?>" inputmode="verbatim" spellcheck="false" maxlength="<?php echo esc_attr( \Twitter\Helpers\Validators\ScreenName::MAX_LENGTH ); ?>" value="<?php echo esc_attr( $instance['screen_name'] ); ?>"<?php
80
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
81
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
82
+ ?>></p><?php
83
+ }
84
+
85
+ /**
86
+ * Update a widget instance
87
+ *
88
+ * @since 2.0.0
89
+ *
90
+ * @param array $new_instance New settings for this instance as input by the user via form()
91
+ * @param array $old_instance Old settings for this instance
92
+ *
93
+ * @return bool|array settings to save or false to cancel saving
94
+ */
95
+ public function update( $new_instance, $old_instance )
96
+ {
97
+ $instance = array();
98
+ $new_instance = (array) $new_instance;
99
+ $title = sanitize_text_field( $new_instance['title'] );
100
+ if ( $title ) {
101
+ $instance['title'] = $title;
102
+ }
103
+ unset( $new_instance['title'] );
104
+
105
+ // process widget parameters as if they were parsed shortcode attributes
106
+ $shortcode_class = static::SHORTCODE_CLASS;
107
+ $timeline_class = static::TIMELINE_CLASS;
108
+ if ( ! ( method_exists( $timeline_class, 'fromArray' ) && method_exists( $shortcode_class, 'shortcodeAttributesToTimelineKeys' ) ) ) {
109
+ return false;
110
+ }
111
+ $timeline = $timeline_class::fromArray( $shortcode_class::shortcodeAttributesToTimelineKeys( $new_instance ) );
112
+ if ( ! ($timeline && method_exists( $timeline, 'toArray' ) ) ) {
113
+ return false;
114
+ }
115
+
116
+ $data_attributes = $timeline->toArray();
117
+ // convert data-* dashes to shortcode underscores
118
+ if ( isset( $data_attributes['screen-name'] ) ) {
119
+ $data_attributes['screen_name'] = $data_attributes['screen-name'];
120
+ unset( $data_attributes['screen-name'] );
121
+ }
122
+ if ( isset( $data_attributes['tweet-limit'] ) ) {
123
+ $data_attributes['limit'] = $data_attributes['tweet-limit'];
124
+ }
125
+
126
+ return array_merge( $instance, $data_attributes );
127
+ }
128
+ }
src/Twitter/WordPress/Widgets/Embeds/Timeline/Search.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2017 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Add embedded search timeline as a WordPress widget
30
+ *
31
+ * @see http://codex.wordpress.org/Widgets_API WordPress widgets API
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ class Search extends \Twitter\WordPress\Widgets\Embeds\Timeline
36
+ {
37
+ /**
38
+ * Class of the related PHP object builder and validator
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const TIMELINE_CLASS = '\Twitter\Widgets\Embeds\Timeline\Search';
45
+
46
+ /**
47
+ * Class of the related shortcode handler
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const SHORTCODE_CLASS = '\Twitter\WordPress\Shortcodes\Embeds\Timeline\Search';
54
+
55
+ /**
56
+ * Describe the functionality offered by the widget
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @return string description of the widget functionality
61
+ */
62
+ public static function getDescription()
63
+ {
64
+ return __( 'Recent Tweets matching a Twitter search', 'twitter' );
65
+ }
66
+
67
+ /**
68
+ * Fields specific to the timeline datasource configured in the widget
69
+ *
70
+ * @since 2.0.0
71
+ *
72
+ * @param array $instance widget instance
73
+ *
74
+ * @return void
75
+ */
76
+ protected function dataSourceFormElements( $instance )
77
+ {
78
+ $close_html_element = \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
79
+ ?><p><label for="<?php echo esc_attr( $this->get_field_id( 'widget_id' ) ); ?>"><?php echo esc_html( __( 'Twitter widget ID', 'twitter' ) . ':' ); ?></label>
80
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'widget_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'widget_id' ) ); ?>" type="text" pattern="[0-9]+" inputmode="verbatim" spellcheck="false" value="<?php echo esc_attr( $instance['widget_id'] ); ?>" required<?php
81
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
82
+ echo $close_html_element;
83
+ ?>><br<?php
84
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
85
+ echo $close_html_element;
86
+ ?>><small><?php
87
+ printf(
88
+ esc_html( _x( 'Create a new widget ID at %s', 'Create a widget identifier at a URL', 'twitter' ) ),
89
+ '<a href="' . esc_url( 'https://twitter.com/settings/widgets/new/search', array( 'https', 'http' ) ) . '">' . esc_html( 'twitter.com/settings/widgets' ) . '</a>'
90
+ );
91
+ ?></small></p><p><label for="<?php echo esc_attr( $this->get_field_id( 'terms' ) ); ?>"><?php echo esc_html( __( 'Search terms', 'twitter' ) . ':' ); ?></label><input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'terms' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'terms' ) ); ?>" type="text" maxlength="450" value="<?php echo esc_attr( $instance['terms'] ); ?>"<?php
92
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
93
+ echo $close_html_element;
94
+ ?>></p>
95
+ <?php
96
+ }
97
+
98
+ /**
99
+ * Update a widget instance
100
+ *
101
+ * @since 2.0.0
102
+ *
103
+ * @param array $new_instance New settings for this instance as input by the user via form()
104
+ * @param array $old_instance Old settings for this instance
105
+ *
106
+ * @return bool|array settings to save or false to cancel saving
107
+ */
108
+ public function update( $new_instance, $old_instance )
109
+ {
110
+ $instance = array();
111
+ $new_instance = (array) $new_instance;
112
+ $title = sanitize_text_field( $new_instance['title'] );
113
+ if ( $title ) {
114
+ $instance['title'] = $title;
115
+ }
116
+ unset( $new_instance['title'] );
117
+
118
+ // process widget parameters as if they were parsed shortcode attributes
119
+ $shortcode_class = static::SHORTCODE_CLASS;
120
+ $timeline_class = static::TIMELINE_CLASS;
121
+ if ( ! ( method_exists( $timeline_class, 'fromArray' ) && method_exists( $shortcode_class, 'shortcodeAttributesToTimelineKeys' ) ) ) {
122
+ return false;
123
+ }
124
+ $timeline = $timeline_class::fromArray( $shortcode_class::shortcodeAttributesToTimelineKeys( $new_instance ) );
125
+ if ( ! ($timeline && $timeline->getWidgetID() && method_exists( $timeline, 'toArray' ) ) ) {
126
+ return false;
127
+ }
128
+
129
+ $data_attributes = $timeline->toArray();
130
+ // convert data-* dashes to shortcode underscores
131
+ if ( isset( $data_attributes['widget-id'] ) ) {
132
+ $data_attributes['widget_id'] = $data_attributes['widget-id'];
133
+ unset( $data_attributes['widget-id'] );
134
+ }
135
+ $data_attributes['terms'] = $timeline->getSearchTerms();
136
+
137
+ return array_merge( $instance, $data_attributes );
138
+ }
139
+ }
src/Twitter/WordPress/Widgets/Embeds/Timeline/TwitterList.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016 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\Embeds\Timeline;
27
+
28
+ /**
29
+ * Add embedded list timeline as a WordPress widget
30
+ *
31
+ * @link http://codex.wordpress.org/Widgets_API WordPress widgets API
32
+ *
33
+ * @since 2.0.0
34
+ */
35
+ class TwitterList extends Profile
36
+ {
37
+ /**
38
+ * Class of the related PHP object builder and validator
39
+ *
40
+ * @since 2.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const TIMELINE_CLASS = '\Twitter\Widgets\Embeds\Timeline\TwitterList';
45
+
46
+ /**
47
+ * Class of the related shortcode handler
48
+ *
49
+ * @since 2.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const SHORTCODE_CLASS = '\Twitter\WordPress\Shortcodes\Embeds\Timeline\TwitterList';
54
+
55
+ /**
56
+ * Describe the functionality offered by the widget
57
+ *
58
+ * @since 2.0.0
59
+ *
60
+ * @return string description of the widget functionality
61
+ */
62
+ public static function getDescription()
63
+ {
64
+ return __( 'The latest Tweets from a list of Twitter accounts', 'twitter' );
65
+ }
66
+
67
+ /**
68
+ * Fields specific to the timeline datasource configured in the widget
69
+ *
70
+ * @since 2.0.0
71
+ *
72
+ * @param array $instance widget instance
73
+ *
74
+ * @return void
75
+ */
76
+ protected function dataSourceFormElements( $instance )
77
+ {
78
+ parent::dataSourceFormElements( $instance );
79
+ ?><p><label for="<?php echo esc_attr( $this->get_field_id( 'slug' ) ); ?>"><?php echo esc_html( _x( 'List slug', 'Unique identifier for Twitter user list', 'twitter' ) . ':' ); ?></label><input id="<?php echo esc_attr( $this->get_field_id( 'slug' ) ); ?>" class="widefat" name="<?php echo esc_attr( $this->get_field_name( 'slug' ) ); ?>" type="text" pattern="[a-z][a-z0-9_\\-]{0,24}" inputmode="verbatim" spellcheck="false" maxlength="24" value="<?php echo esc_attr( $instance['slug'] ); ?>"<?php
80
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
81
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
82
+ ?>></p><?php
83
+ }
84
+
85
+ /**
86
+ * Update a widget instance
87
+ *
88
+ * @since 2.0.0
89
+ *
90
+ * @param array $new_instance New settings for this instance as input by the user via form()
91
+ * @param array $old_instance Old settings for this instance
92
+ *
93
+ * @return bool|array settings to save or false to cancel saving
94
+ */
95
+ public function update( $new_instance, $old_instance )
96
+ {
97
+ $new_instance = (array) $new_instance;
98
+ // a list must have a creator and a slug
99
+ if ( ! (isset( $new_instance['screen_name'] ) && $new_instance['screen_name'] && isset( $new_instance['slug'] ) && $new_instance['slug']) ) {
100
+ return false;
101
+ }
102
+
103
+ $instance = array();
104
+ $title = sanitize_text_field( $new_instance['title'] );
105
+ if ( $title ) {
106
+ $instance['title'] = $title;
107
+ }
108
+ unset( $new_instance['title'] );
109
+
110
+ // process widget parameters as if they were parsed shortcode attributes
111
+ $shortcode_class = static::SHORTCODE_CLASS;
112
+ $timeline_class = static::TIMELINE_CLASS;
113
+ if ( ! ( method_exists( $timeline_class, 'fromArray' ) && method_exists( $shortcode_class, 'shortcodeAttributesToTimelineKeys' ) ) ) {
114
+ return false;
115
+ }
116
+ $timeline = $timeline_class::fromArray( $shortcode_class::shortcodeAttributesToTimelineKeys( $new_instance ) );
117
+ if ( ! ($timeline && method_exists( $timeline, 'getScreenName' ) && $timeline->getScreenName() && method_exists( $timeline, 'getSlug' ) && $timeline->getSlug() && method_exists( $timeline, 'toArray' )) ) {
118
+ return false;
119
+ }
120
+
121
+ $data_attributes = $timeline->toArray();
122
+ // convert data-* dashes to shortcode underscores
123
+ if ( isset( $data_attributes['screen-name'] ) ) {
124
+ $data_attributes['screen_name'] = $data_attributes['screen-name'];
125
+ unset( $data_attributes['screen-name'] );
126
+ }
127
+ if ( isset( $data_attributes['tweet-limit'] ) ) {
128
+ $data_attributes['limit'] = $data_attributes['tweet-limit'];
129
+ }
130
+
131
+ return array_merge( $instance, $data_attributes );
132
+ }
133
+ }
src/Twitter/WordPress/Widgets/Widget.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2017 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
+ * Describe functions expected to be implemented by every widget class
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ abstract class Widget extends \WP_Widget
34
+ {
35
+ /**
36
+ * Allow a site author to include a title before the widget area
37
+ *
38
+ * @since 2.0.0
39
+ *
40
+ * @param array $instance settings for a particular instance of the widget
41
+ *
42
+ * @return void
43
+ */
44
+ protected function titleFormElements( $instance )
45
+ {
46
+ ?><p><label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php echo esc_html( __( 'Title', 'twitter' ) . ':' ); ?></label>
47
+ <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
48
+ // @codingStandardsIgnoreLine WordPress.XSS.EscapeOutput.OutputNotEscaped
49
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
50
+ ?>></p><?php
51
+ }
52
+ }
src/Twitter/WordPress/Widgets/WidgetInterface.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2017 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
+ * Describe functions expected to be implemented by every widget class
30
+ *
31
+ * @since 2.0.0
32
+ */
33
+ interface WidgetInterface
34
+ {
35
+
36
+ /**
37
+ * An individual widget constructor should call the \WP_Widget constructor
38
+ *
39
+ * @since 2.0.0
40
+ */
41
+ function __construct();
42
+
43
+ /**
44
+ * Describe the functionality offered by the widget
45
+ *
46
+ * @since 2.0.0
47
+ *
48
+ * @return string description of the widget functionality
49
+ */
50
+ public static function getDescription();
51
+
52
+ /**
53
+ * Front-end display of widget
54
+ *
55
+ * @since 2.0.0
56
+ *
57
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
58
+ * @param array $instance The settings for the particular instance of the widget
59
+ *
60
+ * @return void
61
+ */
62
+ function widget( $args, $instance );
63
+
64
+ /**
65
+ * Settings update form
66
+ *
67
+ * @since 2.0.0
68
+ *
69
+ * @param array $instance Current settings
70
+ *
71
+ * @return void
72
+ */
73
+ public function form( $instance );
74
+
75
+ /**
76
+ * Update a widget instance
77
+ *
78
+ * @since 2.0.0
79
+ *
80
+ * @param array $new_instance New settings for this instance as input by the user via form()
81
+ * @param array $old_instance Old settings for this instance
82
+ *
83
+ * @return bool|array settings to save or false to cancel saving
84
+ */
85
+ public function update( $new_instance, $old_instance );
86
+ }
twitter.php CHANGED
@@ -24,13 +24,13 @@ THE SOFTWARE.
24
  */
25
  /**
26
  * @package twitter
27
- * @version 1.5.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.5.0
34
  Author: Twitter
35
  Author URI: https://dev.twitter.com/
36
  License: MIT
24
  */
25
  /**
26
  * @package twitter
27
+ * @version 2.0.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: 2.0.0
34
  Author: Twitter
35
  Author URI: https://dev.twitter.com/
36
  License: MIT