Twitter - Version 1.0.0

Version Description

  • Embedded Tweets
  • Embedded video
  • Tweet button
  • Twitter Cards
  • Follow button
  • Advertising tracker

=

Download this release

Release Info

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

Version 1.0.0

Files changed (90) hide show
  1. LICENSE +22 -0
  2. autoload.php +67 -0
  3. composer.json +30 -0
  4. index.php +2 -0
  5. languages/twitter-ar.mo +0 -0
  6. languages/twitter-da_DK.mo +0 -0
  7. languages/twitter-de_DE.mo +0 -0
  8. languages/twitter-el.mo +0 -0
  9. languages/twitter-es_MX.mo +0 -0
  10. languages/twitter-fa_IR.mo +0 -0
  11. languages/twitter-fi.mo +0 -0
  12. languages/twitter-fr_FR.mo +0 -0
  13. languages/twitter-he_IL.mo +0 -0
  14. languages/twitter-hi_IN.mo +0 -0
  15. languages/twitter-hu_HU.mo +0 -0
  16. languages/twitter-id_ID.mo +0 -0
  17. languages/twitter-it_IT.mo +0 -0
  18. languages/twitter-ja.mo +0 -0
  19. languages/twitter-ko_KR.mo +0 -0
  20. languages/twitter-ms_MY.mo +0 -0
  21. languages/twitter-nb_NO.mo +0 -0
  22. languages/twitter-nl_NL.mo +0 -0
  23. languages/twitter-pl_PL.mo +0 -0
  24. languages/twitter-pt_BR.mo +0 -0
  25. languages/twitter-ru_RU.mo +0 -0
  26. languages/twitter-sv_SE.mo +0 -0
  27. languages/twitter-th.mo +0 -0
  28. languages/twitter-tl.mo +0 -0
  29. languages/twitter-tr_TR.mo +0 -0
  30. languages/twitter-zh_CN.mo +0 -0
  31. languages/twitter-zh_TW.mo +0 -0
  32. readme.txt +77 -0
  33. src/Twitter/Cards/Card.php +168 -0
  34. src/Twitter/Cards/Components/Account.php +182 -0
  35. src/Twitter/Cards/Components/Creator.php +59 -0
  36. src/Twitter/Cards/Components/Description.php +85 -0
  37. src/Twitter/Cards/Components/Image.php +167 -0
  38. src/Twitter/Cards/Components/MultipleImages.php +158 -0
  39. src/Twitter/Cards/Components/SingleImage.php +141 -0
  40. src/Twitter/Cards/Gallery.php +125 -0
  41. src/Twitter/Cards/Photo.php +109 -0
  42. src/Twitter/Cards/Product.php +187 -0
  43. src/Twitter/Cards/Summary.php +114 -0
  44. src/Twitter/Cards/SummaryLargeImage.php +63 -0
  45. src/Twitter/Helpers/HTMLBuilder.php +207 -0
  46. src/Twitter/Helpers/TwitterURL.php +108 -0
  47. src/Twitter/Helpers/Validators/Hashtag.php +48 -0
  48. src/Twitter/Helpers/Validators/ScreenName.php +106 -0
  49. src/Twitter/Intents/Follow.php +113 -0
  50. src/Twitter/Intents/Tweet.php +512 -0
  51. src/Twitter/Widgets/BaseWidget.php +148 -0
  52. src/Twitter/Widgets/FollowButton.php +288 -0
  53. src/Twitter/Widgets/Language.php +90 -0
  54. src/Twitter/Widgets/TweetButton.php +460 -0
  55. src/Twitter/WordPress/Admin/Post/MetaBox.php +242 -0
  56. src/Twitter/WordPress/Admin/Post/TweetIntent.php +296 -0
  57. src/Twitter/WordPress/Admin/Post/TwitterCard.php +247 -0
  58. src/Twitter/WordPress/Admin/Profile/User.php +120 -0
  59. src/Twitter/WordPress/Admin/Settings/Loader.php +78 -0
  60. src/Twitter/WordPress/Admin/Settings/SinglePage.php +137 -0
  61. src/Twitter/WordPress/Admin/Settings/SiteAttribution.php +251 -0
  62. src/Twitter/WordPress/Admin/Settings/Template.php +67 -0
  63. src/Twitter/WordPress/Admin/Settings/Theme.php +507 -0
  64. src/Twitter/WordPress/Admin/Settings/TweetButton.php +334 -0
  65. src/Twitter/WordPress/Cards/Compatibility.php +60 -0
  66. src/Twitter/WordPress/Cards/Generator.php +465 -0
  67. src/Twitter/WordPress/Cards/ImageHandler.php +356 -0
  68. src/Twitter/WordPress/Cards/Sanitize.php +128 -0
  69. src/Twitter/WordPress/Content/TweetButton.php +83 -0
  70. src/Twitter/WordPress/Head/AuthorshipLink.php +51 -0
  71. src/Twitter/WordPress/Head/CardsMetaElements.php +93 -0
  72. src/Twitter/WordPress/Head/MetaElement.php +64 -0
  73. src/Twitter/WordPress/Head/WidgetsMetaElements.php +164 -0
  74. src/Twitter/WordPress/Helpers/HTMLBuilder.php +104 -0
  75. src/Twitter/WordPress/Helpers/TwitterAPI.php +156 -0
  76. src/Twitter/WordPress/JavaScriptLoaders/Tracking.php +108 -0
  77. src/Twitter/WordPress/JavaScriptLoaders/Widgets.php +194 -0
  78. src/Twitter/WordPress/Language.php +91 -0
  79. src/Twitter/WordPress/PluginLoader.php +306 -0
  80. src/Twitter/WordPress/Shortcodes/EmbeddedTweet.php +451 -0
  81. src/Twitter/WordPress/Shortcodes/EmbeddedTweetVideo.php +206 -0
  82. src/Twitter/WordPress/Shortcodes/Follow.php +193 -0
  83. src/Twitter/WordPress/Shortcodes/Share.php +372 -0
  84. src/Twitter/WordPress/Shortcodes/Tracking.php +166 -0
  85. src/Twitter/WordPress/Site/Username.php +103 -0
  86. src/Twitter/WordPress/User/Meta.php +78 -0
  87. src/Twitter/WordPress/Widgets/Follow.php +205 -0
  88. static/css/admin/post/edit.min.css +1 -0
  89. twitter.php +70 -0
  90. uninstall.php +76 -0
LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Twitter, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
autoload.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
27
+ * Autoloader for the Twitter plugin for WordPress
28
+ */
29
+
30
+ /**
31
+ * Register the autoloader for the Twitter plugin classes.
32
+ *
33
+ * Based off the official PSR-4 autoloader example found here:
34
+ * https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md
35
+ *
36
+ * @param string $class The fully-qualified class name
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
+ ?>
composer.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "twitter/wordpress",
3
+ "type": "wordpress-plugin",
4
+ "description": "Official Twitter plugin for WordPress",
5
+ "homepage": "https://dev.twitter.com/web/wordpress",
6
+ "license": "MIT",
7
+ "keywords": ["twitter","wordpress","plugin"],
8
+ "require": {
9
+ "php": ">=5.4.0"
10
+ },
11
+ "authors": [
12
+ {
13
+ "name": "Twitter",
14
+ "homepage": "https://github.com/twitter/wordpress/contributors"
15
+ }
16
+ ],
17
+ "support": {
18
+ "forum": "https://wordpress.org/support/plugin/twitter"
19
+ },
20
+ "autoload": {
21
+ "psr-4": {
22
+ "Twitter\\": "src/Twitter/"
23
+ }
24
+ },
25
+ "autoload-dev": {
26
+ "psr-4": {
27
+ "Twitter\\Tests\\": "tests/phpunit/"
28
+ }
29
+ }
30
+ }
index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
languages/twitter-ar.mo ADDED
Binary file
languages/twitter-da_DK.mo ADDED
Binary file
languages/twitter-de_DE.mo ADDED
Binary file
languages/twitter-el.mo ADDED
Binary file
languages/twitter-es_MX.mo ADDED
Binary file
languages/twitter-fa_IR.mo ADDED
Binary file
languages/twitter-fi.mo ADDED
Binary file
languages/twitter-fr_FR.mo ADDED
Binary file
languages/twitter-he_IL.mo ADDED
Binary file
languages/twitter-hi_IN.mo ADDED
Binary file
languages/twitter-hu_HU.mo ADDED
Binary file
languages/twitter-id_ID.mo ADDED
Binary file
languages/twitter-it_IT.mo ADDED
Binary file
languages/twitter-ja.mo ADDED
Binary file
languages/twitter-ko_KR.mo ADDED
Binary file
languages/twitter-ms_MY.mo ADDED
Binary file
languages/twitter-nb_NO.mo ADDED
Binary file
languages/twitter-nl_NL.mo ADDED
Binary file
languages/twitter-pl_PL.mo ADDED
Binary file
languages/twitter-pt_BR.mo ADDED
Binary file
languages/twitter-ru_RU.mo ADDED
Binary file
languages/twitter-sv_SE.mo ADDED
Binary file
languages/twitter-th.mo ADDED
Binary file
languages/twitter-tl.mo ADDED
Binary file
languages/twitter-tr_TR.mo ADDED
Binary file
languages/twitter-zh_CN.mo ADDED
Binary file
languages/twitter-zh_TW.mo ADDED
Binary file
readme.txt ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Plugin Name ===
2
+ Contributors: Twitter, niallkennedy
3
+ Tags: twitter, embedded tweet, twitter video, twitter cards, tweet button, follow button, twitter analytics, twitter ads
4
+ Requires at least: 3.9
5
+ Tested up to: 4.1
6
+ Stable tag: 1.0.0
7
+ License: MIT
8
+ License URI: http://opensource.org/licenses/MIT
9
+
10
+ Official Twitter plugin for WordPress. Embed Twitter content and grow your audience on Twitter. Requires PHP 5.4 or newer.
11
+
12
+ == Description ==
13
+
14
+ The Twitter plugin for WordPress optimizes your website for a Twitter audience through easy to use sharing buttons, embedded Tweets, auto-generated markup indexed by Twitter, and Follow buttons to help grow your Twitter audience. All features are deeply integrated with WordPress APIs to make building your webpages and administrative features as easy as possible with the extensibility you expect from WordPress.
15
+
16
+ Requires PHP 5.4 or later.
17
+
18
+ * [Embedded Tweet](https://dev.twitter.com/web/embedded-tweets) - customize backgrounds and color schemes to match your site's theme
19
+ * [Embedded Twitter video](https://dev.twitter.com/web/embedded-video) - showcase video uploaded to Twitter
20
+ * [Tweet button](https://dev.twitter.com/web/tweet-button) - simple sharing of your site's content on Twitter
21
+ * [Twitter Cards](https://dev.twitter.com/cards/overview) - highlight your site's content when shared on Twitter
22
+ * [Twitter Analytics](https://analytics.twitter.com/) - track impressions and top distributors of your site's content on Twitter
23
+ * [Follow button](https://dev.twitter.com/web/follow-button) - grow your Twitter audience
24
+ * [Twitter ads conversion tracking](https://support.twitter.com/articles/20170807-conversion-tracking-for-websites) - easily track actions on your WordPress site triggered by a Twitter ad or build a custom targeting audience
25
+
26
+ Contribute to the plugin, submit pull requests, or run test suites through the [Twitter plugin for WordPress GitHub repository](https://github.com/twitter/wordpress). View [Twitter for WordPress documentation](https://dev.twitter.com/web/wordpress) to learn more about customization through filters.
27
+
28
+ == Changelog ==
29
+
30
+ = 1.0.0 =
31
+ * Embedded Tweets
32
+ * Embedded video
33
+ * Tweet button
34
+ * Twitter Cards
35
+ * Follow button
36
+ * Advertising tracker
37
+
38
+ == Frequently Asked Questions ==
39
+
40
+ = Twitter Cards do not appear for my shared links =
41
+
42
+ Twitter Cards must be enabled for your domain before they will appear alongside a Tweet. Submit a URL to the [Twitter Cards validator](https://cards-dev.twitter.com/validator) and request Twitter add the card type for your domain.
43
+
44
+ = How can I change an embedded Tweet's background and link colors to match my site's theme? =
45
+
46
+ 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.
47
+
48
+ = How do I include an embedded timeline in my page? =
49
+
50
+ Log in to Twitter.com and visit the [Twitter widgets settings page](https://twitter.com/settings/widgets) to create and manage [embedded timeline](https://dev.twitter.com/web/embedded-timelines) widgets for your account. Widget settings are saved to a widget identifier for the logged in account; you may want to create a widget from your site's account, not your personal account, for continuity and general organization.
51
+
52
+ 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).
53
+
54
+ = My custom link color and border color do not appear in embedded Tweets or timelines =
55
+
56
+ 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.
57
+
58
+ You may have configured an embedded timeline widget with a non-default link color. Your widget configuration overrides your page / theme configuration.
59
+
60
+ = Does the Twitter plugin add additional tracking of my site's visitors? =
61
+
62
+ The Twitter plugin for WordPress makes it easier to explicitly include Twitter features and functionality on your WordPress site. No additional tracking is added as a result of our plugin code's execution on your server(s).
63
+
64
+ 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).
65
+
66
+ 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).
67
+
68
+ == Screenshots ==
69
+
70
+ 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.
71
+ 2. Post editor meta box. Define custom Tweet text, hashtags, and Twitter Card data.
72
+ 3. Twitter widgets and buttons in action.
73
+
74
+ == Installation ==
75
+
76
+ 1. Add the Twitter plugin to your WordPress installation
77
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
src/Twitter/Cards/Card.php ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Properties common to all Twitter Cards
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Card
34
+ {
35
+
36
+ /**
37
+ * Twitter Card type. summary, photo, etc.
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @type string
42
+ */
43
+ protected $type;
44
+
45
+ /**
46
+ * Title of the content
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @type string
51
+ */
52
+ protected $title;
53
+
54
+ /**
55
+ * Twitter account responsible for site content
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @type \Twitter\Cards\Components\Account
60
+ */
61
+ protected $site;
62
+
63
+ /**
64
+ * @since 1.0.0
65
+ *
66
+ * @param string $type card type
67
+ */
68
+ public function __construct($type)
69
+ {
70
+ if (! ( is_string($type) && $type )) {
71
+ // summary default
72
+ $type = 'summary';
73
+ }
74
+ $this->type = $type;
75
+ }
76
+
77
+ /**
78
+ * Prepare a passed description for the requirements of a Twitter Card description
79
+ *
80
+ * @since 1.0.0
81
+ *
82
+ * @param string $title title unique to the page
83
+ *
84
+ * @return string sanitized title, or empty string of minimum requirements not met
85
+ */
86
+ public static function sanitizeTitle($title)
87
+ {
88
+ if (! ( is_string($title) && $title )) {
89
+ return '';
90
+ }
91
+
92
+ $title = trim($title);
93
+ if (! $title) {
94
+ return '';
95
+ }
96
+
97
+ return $title;
98
+ }
99
+
100
+ /**
101
+ * Set the title of the content
102
+ *
103
+ * @since 1.0.0
104
+ *
105
+ * @param string $title content title
106
+ *
107
+ * @return __CLASS__ support chaining
108
+ */
109
+ public function setTitle($title)
110
+ {
111
+ $title = static::sanitizeTitle($title);
112
+ if ($title) {
113
+ $this->title = $title;
114
+ }
115
+
116
+ return $this;
117
+ }
118
+
119
+ /**
120
+ * Set the site associated with content
121
+ *
122
+ * @param \Twitter\Cards\Components\Account $site Twitter account
123
+ *
124
+ * @since 1.0.0
125
+ *
126
+ * @return __CLASS__ support chaining
127
+ */
128
+ public function setSite($site)
129
+ {
130
+ if ($site && is_a($site, '\Twitter\Cards\Components\Account')) {
131
+ $this->site = $site;
132
+ }
133
+
134
+ return $this;
135
+ }
136
+
137
+ /**
138
+ * Convert class properties to an array
139
+ *
140
+ * @since 1.0.0
141
+ *
142
+ * @return array class properties as an array {
143
+ * @type string property name
144
+ * @type string|int|array property value or an array for nested properties
145
+ * }
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;
167
+ }
168
+ }
src/Twitter/Cards/Components/Account.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards\Components;
27
+
28
+ /**
29
+ * Bundle information about a Twitter account as used in Cards markup
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Account
34
+ {
35
+ /**
36
+ * Twitter account screen name. Example: ev
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ public $screen_name;
43
+
44
+ /**
45
+ * Twitter account identifier. Example: '20'
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ public $id;
52
+
53
+ /**
54
+ * Create a new account object from a Twitter screen name
55
+ *
56
+ * @since 1.0.0
57
+ *
58
+ * @return self|null
59
+ */
60
+ public static function fromScreenName($screen_name)
61
+ {
62
+ $account = new self();
63
+ $account->setScreenName($screen_name);
64
+
65
+ if (! $account->hasScreenName()) {
66
+ return;
67
+ }
68
+
69
+ return $account;
70
+ }
71
+
72
+ /**
73
+ * Create a new account object from a Twitter id
74
+ *
75
+ * @since 1.0.0
76
+ *
77
+ * @param string $id Twitter account identifier
78
+ *
79
+ * @return self|null
80
+ */
81
+ public static function fromID($id)
82
+ {
83
+ $account = new self();
84
+ $account->setID($id);
85
+
86
+ if (! $account->hasID()) {
87
+ return;
88
+ }
89
+
90
+ return $account;
91
+ }
92
+
93
+ /**
94
+ * Set a Twitter screen_name for a Twitter account
95
+ *
96
+ * @since 1.0.0
97
+ *
98
+ * @param string $screen_name Twitter screen name
99
+ *
100
+ * @return self support chaining
101
+ */
102
+ public function setScreenName($screen_name)
103
+ {
104
+ if (! is_string($screen_name)) {
105
+ return $this;
106
+ }
107
+
108
+ // remove any preceding @
109
+ $screen_name = \Twitter\Helpers\Validators\ScreenName::trim($screen_name);
110
+ if (! $screen_name) {
111
+ return $this;
112
+ }
113
+
114
+ $this->screen_name = $screen_name;
115
+
116
+ return $this;
117
+ }
118
+
119
+ /**
120
+ * Test if account has screen_name set
121
+ *
122
+ * @since 1.0.0
123
+ *
124
+ * @return bool true if screen_name set and not blank
125
+ */
126
+ public function hasScreenName()
127
+ {
128
+ return (bool) $this->screen_name;
129
+ }
130
+
131
+ /**
132
+ * Set a Twitter ID for a Twitter account
133
+ *
134
+ * @since 1.0.0
135
+ *
136
+ * @param string $id Twitter user id
137
+ *
138
+ * @return __CLASS__ support chaining
139
+ */
140
+ public function setID($id)
141
+ {
142
+ $id = trim((string) $id);
143
+ if (! $id) {
144
+ return $this;
145
+ }
146
+ if (function_exists('ctype_digit') && ! ctype_digit($id)) {
147
+ return $this;
148
+ }
149
+
150
+ $this->id = $id;
151
+
152
+ return $this;
153
+ }
154
+
155
+ /**
156
+ * Test if account has ID set
157
+ *
158
+ * @since 1.0.0
159
+ *
160
+ * @return bool true if ID set and not blank
161
+ */
162
+ public function hasID()
163
+ {
164
+ return (bool) $this->id;
165
+ }
166
+
167
+ /**
168
+ * Convert the account into a Twitter Card property
169
+ *
170
+ * @since 1.0.0
171
+ *
172
+ * @return string|array site username or id as structured property
173
+ */
174
+ public function asCardProperties()
175
+ {
176
+ if ($this->hasID()) {
177
+ return array( 'id' => $this->id );
178
+ } elseif ($this->hasScreenName()) {
179
+ return '@' . $this->screen_name;
180
+ }
181
+ }
182
+ }
src/Twitter/Cards/Components/Creator.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards\Components;
27
+
28
+ /**
29
+ * Twitter account of the content creator
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ trait Creator
34
+ {
35
+ /**
36
+ * Twitter account of the content creator
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type \Twitter\Cards\Components\Account
41
+ */
42
+ protected $creator;
43
+
44
+ /**
45
+ * Set the author associated with content
46
+ *
47
+ * @param \Twitter\Cards\Components\Account $creator Twitter account
48
+ *
49
+ * @return __CLASS__ support chaining
50
+ */
51
+ public function setCreator($creator)
52
+ {
53
+ if ($creator && is_a($creator, '\Twitter\Cards\Components\Account')) {
54
+ $this->creator = $creator;
55
+ }
56
+
57
+ return $this;
58
+ }
59
+ }
src/Twitter/Cards/Components/Description.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards\Components;
27
+
28
+ /**
29
+ * A card with a description
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ trait Description
34
+ {
35
+ /**
36
+ * A description of the content. 200 characters max
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ protected $description;
43
+
44
+ /**
45
+ * Prepare a passed description for the requirements of a Twitter Card description
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @param string $description short description of the page
50
+ *
51
+ * @return string sanitized description, or empty string of minimum requirements not met
52
+ */
53
+ public static function sanitizeDescription($description)
54
+ {
55
+ if (! ( is_string($description) && $description )) {
56
+ return '';
57
+ }
58
+
59
+ $description = trim($description);
60
+ if (! $description) {
61
+ return '';
62
+ }
63
+
64
+ return $description;
65
+ }
66
+
67
+ /**
68
+ * Set the description of the content
69
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @param string $description content description
73
+ *
74
+ * @return __CLASS__ support chaining
75
+ */
76
+ public function setDescription($description)
77
+ {
78
+ $description = static::sanitizeDescription($description);
79
+ if ($description) {
80
+ $this->description = $description;
81
+ }
82
+
83
+ return $this;
84
+ }
85
+ }
src/Twitter/Cards/Components/Image.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards\Components;
27
+
28
+ /**
29
+ * Twitter Card image representation
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Image
34
+ {
35
+ /**
36
+ * Image URL. Must be less than 1 MB in size.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ protected $src;
43
+
44
+ /**
45
+ * Height of the image in whole pixels
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @type int
50
+ */
51
+ protected $width;
52
+
53
+ /**
54
+ * Width of the image in whole pixels
55
+ *
56
+ * @since 1.0.0
57
+ *
58
+ * @type int
59
+ */
60
+ protected $height;
61
+
62
+ /**
63
+ * @since 1.0.0
64
+ *
65
+ * @return void
66
+ */
67
+ public function __construct($src)
68
+ {
69
+ if (! ( is_string($src) && $src )) {
70
+ return;
71
+ }
72
+ $this->src = $src;
73
+ }
74
+
75
+ /**
76
+ * Image URL
77
+ *
78
+ * @since 1.0.0
79
+ *
80
+ * @return string absolute URL
81
+ */
82
+ public function getURL()
83
+ {
84
+ return $this->src ?: '';
85
+ }
86
+
87
+ /**
88
+ * Get the width of the image
89
+ *
90
+ * @since 1.0.0
91
+ *
92
+ * @return int width of the image in whole pixels
93
+ */
94
+ public function getWidth()
95
+ {
96
+ return $this->width ?: 0;
97
+ }
98
+
99
+ /**
100
+ * Set the width of the image
101
+ *
102
+ * @since 1.0.0
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
+ {
110
+ if (is_int($width) && $width >= 0) {
111
+ $this->width = $width;
112
+ }
113
+ return $this;
114
+ }
115
+
116
+ /**
117
+ * Get the height of the image
118
+ *
119
+ * @since 1.0.0
120
+ *
121
+ * @return int height of the image in whole pixels
122
+ */
123
+ public function getHeight()
124
+ {
125
+ return $this->height ?: 0;
126
+ }
127
+
128
+ /**
129
+ * Set the height of the image
130
+ *
131
+ * @since 1.0.0
132
+ *
133
+ * @param int $height
134
+ *
135
+ * @return __CLASS__ support chaining
136
+ */
137
+ public function setHeight($height)
138
+ {
139
+ if (is_int($height) && $height >= 0) {
140
+ $this->height = $height;
141
+ }
142
+ return $this;
143
+ }
144
+
145
+ /**
146
+ * Convert to card properties
147
+ *
148
+ * @since 1.0.0
149
+ *
150
+ * @return array|string image property as shorthand or full properties
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
+ }
166
+ }
167
+ }
src/Twitter/Cards/Components/MultipleImages.php ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards\Components;
27
+
28
+ /**
29
+ * A card with multiple images
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ trait MultipleImages
34
+ {
35
+
36
+ /**
37
+ * Images representing the content of the page
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @type array {
42
+ * Images stored for the card
43
+ *
44
+ * @type string image uri
45
+ * @type \Twitter\Cards\Components\Image card image
46
+ * }
47
+ */
48
+ protected $images = array();
49
+
50
+ /**
51
+ * Keep track of the total number of images stored for the card
52
+ *
53
+ * @since 1.0.0
54
+ *
55
+ * @type int
56
+ */
57
+ protected $image_count = 0;
58
+
59
+ /**
60
+ * Get the images array
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * @return array {
65
+ * Images stored for the card
66
+ *
67
+ * @type string image uri
68
+ * @type \Twitter\Cards\Components\Image card image
69
+ * }
70
+ */
71
+ public function getImages()
72
+ {
73
+ return $this->images;
74
+ }
75
+
76
+ /**
77
+ * Add an image representing the content of the page
78
+ *
79
+ * @since 1.0.0
80
+ *
81
+ * @param string|\Twitter\Cards\Components\Image $url absolute URL of an image file
82
+ * @param int $width width of the image in whole pixels
83
+ * @param int $height height of the image in whole pixels
84
+ *
85
+ * @return __CLASS__ support chaining
86
+ */
87
+ public function addImage($url, $width = 0, $height = 0)
88
+ {
89
+ // URL required
90
+ if (! $url) {
91
+ return $this;
92
+ }
93
+ if (! ( is_int($width) && $width >= 0 )) {
94
+ $width = 0;
95
+ }
96
+ if (! ( is_int($height) && $height >= 0 )) {
97
+ $height = 0;
98
+ }
99
+
100
+ // have we already filled the image allotment?
101
+ if (defined(__CLASS__ . '::MAX_IMAGES') && $this->image_count === self::MAX_IMAGES) {
102
+ return $this;
103
+ }
104
+
105
+ $image = null;
106
+ $preset = false;
107
+ if (is_a($url, '\Twitter\Cards\Components\Image')) {
108
+ // support overloading the function
109
+ $image = $url;
110
+ $url = $url->getURL();
111
+ if (isset( $this->images[ $url ] )) {
112
+ return $this;
113
+ }
114
+ $preset = true;
115
+ $width = $image->getWidth();
116
+ $height = $image->getHeight();
117
+ } elseif (is_string($url)) {
118
+ if (isset( $this->images[ $url ] )) {
119
+ return $this;
120
+ }
121
+ try {
122
+ $image = new \Twitter\Cards\Components\Image($url);
123
+ } catch (Exception $e) {
124
+ return $this;
125
+ }
126
+ }
127
+
128
+ if (! $image) {
129
+ return $this;
130
+ }
131
+
132
+ // only set dimensions if both width and height exist
133
+ if (is_int($width) && $width && is_int($height) && $height) {
134
+ // test if minimum width and height requirements are met for the card type
135
+ if (defined(__CLASS__ . '::MIN_IMAGE_WIDTH') && defined(__CLASS__ . '::MIN_IMAGE_HEIGHT')) {
136
+ if ($width >= self::MIN_IMAGE_WIDTH && $height >= self::MIN_IMAGE_HEIGHT) {
137
+ if (! $preset) {
138
+ $image->setWidth($width);
139
+ $image->setHeight($height);
140
+ }
141
+ } else {
142
+ // do not store image if minimum requirements not met
143
+ return $this;
144
+ }
145
+ } else {
146
+ if (! $preset) {
147
+ $image->setWidth($width);
148
+ $image->setHeight($height);
149
+ }
150
+ }
151
+ }
152
+
153
+ $this->images[ $url ] = $image;
154
+ $this->image_count = count($this->images);
155
+
156
+ return $this;
157
+ }
158
+ }
src/Twitter/Cards/Components/SingleImage.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards\Components;
27
+
28
+ /**
29
+ * Card image store
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ trait SingleImage
34
+ {
35
+
36
+ /**
37
+ * Image representing the content of the page
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @type \Twitter\Cards\Components\Image card image
42
+ */
43
+ protected $image;
44
+
45
+ /**
46
+ * Set an image representing the content of the page
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @param string|\Twitter\Cards\Components\Image $url absolute URL of an image file
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;
60
+ }
61
+ if (! ( is_int($width) && $width >= 0 )) {
62
+ $width = 0;
63
+ }
64
+ if (! ( is_int($height) && $height >= 0 )) {
65
+ $height = 0;
66
+ }
67
+
68
+ $image = null;
69
+ $preset = false;
70
+ if (is_a($url, '\Twitter\Cards\Components\Image')) {
71
+ $preset = true;
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
+ }
82
+
83
+ if (! $image) {
84
+ return $this;
85
+ }
86
+
87
+ // only set dimensions if both width and height exist
88
+ if (is_int($width) && $width && is_int($height) && $height) {
89
+ // test if minimum width and height requirements are met for the card type
90
+ if (defined(__CLASS__ . '::MIN_IMAGE_WIDTH') && defined(__CLASS__ . '::MIN_IMAGE_HEIGHT')) {
91
+ if ($width >= self::MIN_IMAGE_WIDTH && $height >= self::MIN_IMAGE_HEIGHT) {
92
+ if (! $preset) {
93
+ $image->setWidth($width);
94
+ $image->setHeight($height);
95
+ }
96
+ } else {
97
+ // do not store image if minimum requirements not met
98
+ return $this;
99
+ }
100
+ } else {
101
+ if (! $preset) {
102
+ $image->setWidth($width);
103
+ $image->setHeight($height);
104
+ }
105
+ }
106
+ }
107
+
108
+ $this->image = $image;
109
+
110
+ return $this;
111
+ }
112
+
113
+ /**
114
+ * Output the image properties of a Twitter card
115
+ *
116
+ * Suitable for merging with a larger Twitter Card property array
117
+ *
118
+ * @since 1.0.0
119
+ *
120
+ * @return array {
121
+ * @type string image property
122
+ * @type array Twitter Card image values {
123
+ * @type string structured property: src, width, height
124
+ * @type string|int URL and dimensions
125
+ * }
126
+ * }
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;
140
+ }
141
+ }
src/Twitter/Cards/Gallery.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Gallery card
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/cards/types/gallery
34
+ */
35
+ class Gallery extends Card
36
+ {
37
+ use \Twitter\Cards\Components\Creator;
38
+ use \Twitter\Cards\Components\Description;
39
+ use \Twitter\Cards\Components\MultipleImages;
40
+
41
+ /**
42
+ * Twitter Card type value
43
+ *
44
+ * @since 1.0.0
45
+ *
46
+ * @type string
47
+ */
48
+ const TYPE = 'gallery';
49
+
50
+ /**
51
+ * Minimum width of an image in whole pixels
52
+ *
53
+ * @since 1.0.0
54
+ *
55
+ * @type int
56
+ */
57
+ const MIN_IMAGE_WIDTH = 280;
58
+
59
+ /**
60
+ * Minimum height of an image in whole pixels
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * @type int
65
+ */
66
+ const MIN_IMAGE_HEIGHT = 150;
67
+
68
+ /**
69
+ * Maximum number of images used in a gallery card template
70
+ *
71
+ * @since 1.0.0
72
+ *
73
+ * @type int
74
+ */
75
+ const MAX_IMAGES = 4;
76
+
77
+ /**
78
+ * Set the card type
79
+ *
80
+ * @since 1.0.0
81
+ *
82
+ * @return void
83
+ */
84
+ public function __construct()
85
+ {
86
+ parent::__construct(static::TYPE);
87
+ }
88
+
89
+ /**
90
+ * Convert to an array suitable for use as Twitter Card structured properties
91
+ *
92
+ * @since 1.0.0
93
+ *
94
+ * @return array {
95
+ * @type string Twitter card property
96
+ * @type mixed property value
97
+ * }
98
+ */
99
+ public function toArray()
100
+ {
101
+ $card = parent::toArray();
102
+
103
+ $images = $this->getImages();
104
+ if (! empty( $images )) {
105
+ $image_count = count($images);
106
+ // flatten to just \Twitter\Cards\Components\Image
107
+ $images = array_values($images);
108
+ for ($i = 0; $i < $image_count; $i++) {
109
+ $card[ 'image' . $i ] = $images[ $i ]->asCardProperties();
110
+ }
111
+ unset( $image_count );
112
+ }
113
+ unset( $images );
114
+
115
+ if (isset( $this->creator ) && $this->creator) {
116
+ $creator = $this->creator->asCardProperties();
117
+ if ($creator) {
118
+ $card['creator'] = $creator;
119
+ }
120
+ unset( $creator );
121
+ }
122
+
123
+ return $card;
124
+ }
125
+ }
src/Twitter/Cards/Photo.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Photo card
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/cards/types/photo
34
+ */
35
+ class Photo extends Card
36
+ {
37
+ use \Twitter\Cards\Components\Creator;
38
+ use \Twitter\Cards\Components\SingleImage;
39
+
40
+ /**
41
+ * Twitter Card type value
42
+ *
43
+ * @since 1.0.0
44
+ *
45
+ * @type string
46
+ */
47
+ const TYPE = 'photo';
48
+
49
+ /**
50
+ * Minimum width of the image in whole pixels
51
+ *
52
+ * @since 1.0.0
53
+ *
54
+ * @type int
55
+ */
56
+ const MIN_IMAGE_WIDTH = 280;
57
+
58
+ /**
59
+ * Minimum height of the image in whole pixels
60
+ *
61
+ * @since 1.0.0
62
+ *
63
+ * @type int
64
+ */
65
+ const MIN_IMAGE_HEIGHT = 150;
66
+
67
+ /**
68
+ * Set the card type
69
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @return void
73
+ */
74
+ public function __construct()
75
+ {
76
+ parent::__construct(static::TYPE);
77
+ }
78
+
79
+ /**
80
+ * Convert to an array suitable for use as Twitter Card structured properties
81
+ *
82
+ * @since 1.0.0
83
+ *
84
+ * @return array {
85
+ * @type string Twitter card property
86
+ * @type mixed property value
87
+ * }
88
+ */
89
+ public function toArray()
90
+ {
91
+ $card = parent::toArray();
92
+
93
+ $image_properties = $this->imageCardProperties();
94
+ if (! empty( $image_properties )) {
95
+ $card['image'] = $image_properties;
96
+ }
97
+ unset( $image_properties );
98
+
99
+ if (isset( $this->creator ) && $this->creator) {
100
+ $creator = $this->creator->asCardProperties();
101
+ if ($creator) {
102
+ $card['creator'] = $creator;
103
+ }
104
+ unset( $creator );
105
+ }
106
+
107
+ return $card;
108
+ }
109
+ }
src/Twitter/Cards/Product.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Product card
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/cards/types/product
34
+ */
35
+ class Product extends Card
36
+ {
37
+ use \Twitter\Cards\Components\Creator;
38
+ use \Twitter\Cards\Components\Description;
39
+ use \Twitter\Cards\Components\SingleImage;
40
+
41
+ /**
42
+ * Twitter Card type value
43
+ *
44
+ * @since 1.0.0
45
+ *
46
+ * @type string
47
+ */
48
+ const TYPE = 'product';
49
+
50
+ /**
51
+ * Minimum width of the image in whole pixels
52
+ *
53
+ * @since 1.0.0
54
+ *
55
+ * @type int
56
+ */
57
+ const MIN_IMAGE_WIDTH = 160;
58
+
59
+ /**
60
+ * Minimum height of the image in whole pixels
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * @type int
65
+ */
66
+ const MIN_IMAGE_HEIGHT = 160;
67
+
68
+ /**
69
+ * Maximum number of displayed product details
70
+ *
71
+ * @since 1.0.0
72
+ *
73
+ * @type int
74
+ */
75
+ const MAX_DETAILS = 2;
76
+
77
+ /**
78
+ * Tabular data for display in the card template
79
+ *
80
+ * @since 1.0.0
81
+ *
82
+ * @type array label value pairs {
83
+ * @type string label
84
+ * @type string data
85
+ * }
86
+ */
87
+ protected $details = array();
88
+
89
+ /**
90
+ * Number of stored details for the product
91
+ *
92
+ * @since 1.0.0
93
+ *
94
+ * @type int
95
+ */
96
+ protected $details_count = 0;
97
+
98
+ /**
99
+ * Set the card type
100
+ *
101
+ * @since 1.0.0
102
+ *
103
+ * @return void
104
+ */
105
+ public function __construct()
106
+ {
107
+ parent::__construct(static::TYPE);
108
+ }
109
+
110
+ /**
111
+ * Add a detail
112
+ *
113
+ * @since 1.0.0
114
+ *
115
+ * @param string $label product detail label
116
+ * @param string $value product detail value
117
+ *
118
+ * @return __CLASS__ support chaining
119
+ */
120
+ public function addDetail($label, $value)
121
+ {
122
+ // maximum allowed details already reached
123
+ if ($this->details_count === static::MAX_DETAILS) {
124
+ return $this;
125
+ }
126
+
127
+ try {
128
+ $label = trim((string) $label);
129
+ $value = trim((string) $value);
130
+ } catch (Exception $e) {
131
+ return $this;
132
+ }
133
+
134
+ if ($label && $value && ! isset( $this->details[ $label ] )) {
135
+ $this->details[ $label ] = $value;
136
+ $this->details_count = count($this->details);
137
+ }
138
+
139
+ return $this;
140
+ }
141
+
142
+ /**
143
+ * Convert to an array suitable for use as Twitter Card structured properties
144
+ *
145
+ * @since 1.0.0
146
+ *
147
+ * @return array {
148
+ * @type string Twitter card property
149
+ * @type string|array property value
150
+ * }
151
+ */
152
+ public function toArray()
153
+ {
154
+ $card = parent::toArray();
155
+
156
+ if (isset( $this->description ) && $this->description) {
157
+ $card['description'] = $this->description;
158
+ }
159
+
160
+ $image_properties = $this->imageCardProperties();
161
+ if (! empty( $image_properties )) {
162
+ $card['image'] = $image_properties;
163
+ }
164
+ unset( $image_properties );
165
+
166
+ if (! empty( $this->details )) {
167
+ // product card table is 1-based
168
+ $details_position = 1;
169
+ foreach ($this->details as $label => $data) {
170
+ $card[ 'label' . $details_position ] = $label;
171
+ $card[ 'data' . $details_position ] = $data;
172
+ $details_position++;
173
+ }
174
+ unset( $details_position );
175
+ }
176
+
177
+ if (isset( $this->creator ) && $this->creator) {
178
+ $creator = $this->creator->asCardProperties();
179
+ if ($creator) {
180
+ $card['creator'] = $creator;
181
+ }
182
+ unset( $creator );
183
+ }
184
+
185
+ return $card;
186
+ }
187
+ }
src/Twitter/Cards/Summary.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Twitter summary card
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/docs/cards/types/summary-card Twitter Summary Card
34
+ */
35
+ class Summary extends Card
36
+ {
37
+ use \Twitter\Cards\Components\Creator;
38
+ use \Twitter\Cards\Components\Description;
39
+ use \Twitter\Cards\Components\SingleImage;
40
+
41
+ /**
42
+ * Twitter Card type value
43
+ *
44
+ * @since 1.0.0
45
+ *
46
+ * @type string
47
+ */
48
+ const TYPE = 'summary';
49
+
50
+ /**
51
+ * Minimum width of the image in whole pixels
52
+ *
53
+ * @since 1.0.0
54
+ *
55
+ * @type int
56
+ */
57
+ const MIN_IMAGE_WIDTH = 120;
58
+
59
+ /**
60
+ * Minimum height of the image in whole pixels
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * @type int
65
+ */
66
+ const MIN_IMAGE_HEIGHT = 120;
67
+
68
+ /**
69
+ * Set the card type
70
+ *
71
+ * @since 1.0.0
72
+ *
73
+ * @return void
74
+ */
75
+ public function __construct()
76
+ {
77
+ parent::__construct(static::TYPE);
78
+ }
79
+
80
+ /**
81
+ * Convert to an array suitable for use as Twitter Card structured properties
82
+ *
83
+ * @since 1.0.0
84
+ *
85
+ * @return array {
86
+ * @type string Twitter card property
87
+ * @type mixed property value
88
+ * }
89
+ */
90
+ public function toArray()
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;
113
+ }
114
+ }
src/Twitter/Cards/SummaryLargeImage.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Summary large image card
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/cards/types/summary-large-image
34
+ */
35
+ class SummaryLargeImage extends Summary
36
+ {
37
+ /**
38
+ * Twitter Card type value
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const TYPE = 'summary_large_image';
45
+
46
+ /**
47
+ * Minimum width of the image in whole pixels
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @type int
52
+ */
53
+ const MIN_IMAGE_WIDTH = 280;
54
+
55
+ /**
56
+ * Minimum height of the image in whole pixels
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @type int
61
+ */
62
+ const MIN_IMAGE_HEIGHT = 150;
63
+ }
src/Twitter/Helpers/HTMLBuilder.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\Helpers;
27
+
28
+ /**
29
+ * String builder for HTML elements
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class HTMLBuilder
34
+ {
35
+
36
+ /**
37
+ * Allow extensibility of allowed class name values
38
+ *
39
+ * @since 1.0.0
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
+ {
47
+ return static::escapeAttributeValue($class);
48
+ }
49
+
50
+ /**
51
+ * Escape an element's inner text
52
+ *
53
+ * @since 1.0.0
54
+ *
55
+ * @param string $inner_text inner text of a DOM element
56
+ *
57
+ * @return string escaped string or empty string if passed string failed to parse
58
+ */
59
+ public static function escapeInnerText($inner_text)
60
+ {
61
+ return htmlspecialchars($inner_text, defined('ENT_HTML5') ? ENT_HTML5 : ENT_COMPAT);
62
+ }
63
+
64
+ /**
65
+ * Escape an element attribute value including double quotes
66
+ *
67
+ * @since 1.0.0
68
+ *
69
+ * @param string $value element attribute value
70
+ *
71
+ * @return string escaped string or empty string if passed string failed to parse
72
+ */
73
+ public static function escapeAttributeValue($value)
74
+ {
75
+ return htmlspecialchars($value, ENT_COMPAT);
76
+ }
77
+
78
+ /**
79
+ * Escape a URL
80
+ *
81
+ * @since 1.0.0
82
+ *
83
+ * @param string $url web URL
84
+ *
85
+ * @return string escaped string or empty string if passed string failed to parse
86
+ */
87
+ public static function escapeURL($url)
88
+ {
89
+ return htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
90
+ }
91
+
92
+ /**
93
+ * Build a HTML anchor element for the given URL and data attributes
94
+ *
95
+ * @since 1.0.0
96
+ *
97
+ * @param string $href intent URL
98
+ * @param string $inner_text anchor element innerText
99
+ * @param array $attributes anchor attributes to be added. limited to a whitelist of: id, class, rel, ping, target
100
+ * @param array $data_attributes data attributes to be interpreted by Twitter's widget JS
101
+ *
102
+ * @return string HTML anchor element string or empty string if no URL passed
103
+ */
104
+ public static function anchorElement($href, $inner_text, $attributes = array(), $data_attributes = array())
105
+ {
106
+ if (! is_string($href) && $href && is_string($inner_text) && $inner_text) {
107
+ return '';
108
+ }
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
+ }
191
+ }
192
+
193
+ $html = '<a href="' . static::escapeURL($href) . '"';
194
+ foreach ($clean_attributes as $attribute => $value) {
195
+ $html .= ' ' . $attribute;
196
+
197
+ // escaped during per-attribute scrub
198
+ // allow properties without a value
199
+ if ($value) {
200
+ $html .= '="' . $value . '"';
201
+ }
202
+ }
203
+ $html .= '>' . static::escapeInnerText($inner_text) . '</a>';
204
+
205
+ return $html;
206
+ }
207
+ }
src/Twitter/Helpers/TwitterURL.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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;
27
+
28
+ /**
29
+ * Reference Twitter URLs
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class TwitterURL
34
+ {
35
+ /**
36
+ * Scheme, FQDN, and path to Twitter web
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const BASE_URL = 'https://twitter.com/';
43
+
44
+ /**
45
+ * A Twitter user profile page
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @param string $screen_name Twitter screen name
50
+ *
51
+ * @return string absolute URI of a Twitter profile page
52
+ */
53
+ public static function profile($screen_name)
54
+ {
55
+ if (! ( is_string($screen_name) && $screen_name )) {
56
+ return '';
57
+ }
58
+ return self::BASE_URL . $screen_name;
59
+ }
60
+
61
+ /**
62
+ * Individual Tweet / status URI
63
+ *
64
+ * @since 1.0.0
65
+ *
66
+ * @param string $screen_name Twitter account screen name
67
+ * @param string|int $status_id status ID
68
+ *
69
+ * @return string Tweet detail page absolute URI
70
+ */
71
+ public static function tweet($screen_name, $status_id)
72
+ {
73
+ $profile_url = static::profile($screen_name);
74
+ if (! $profile_url) {
75
+ return '';
76
+ }
77
+ $status_id = (string) $status_id;
78
+ if (! $status_id) {
79
+ return '';
80
+ }
81
+
82
+ return $profile_url . '/status/' . $status_id;
83
+ }
84
+
85
+ /**
86
+ * Twitter Collection / Custom Timeline
87
+ *
88
+ * @since 1.0.0
89
+ *
90
+ * @param string $screen_name Twitter account screen name
91
+ * @param string $collection_id Twitter collection numeric identifier
92
+ *
93
+ * @return string Twitter collection absolute URI
94
+ */
95
+ public static function collection($screen_name, $collection_id)
96
+ {
97
+ if (! ( $screen_name && $collection_id )) {
98
+ return '';
99
+ }
100
+
101
+ $profile_url = static::profile($screen_name);
102
+ if (! $profile_url) {
103
+ return '';
104
+ }
105
+
106
+ return $profile_url . '/timelines/' . $collection_id;
107
+ }
108
+ }
src/Twitter/Helpers/Validators/Hashtag.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 hashtag validity
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Hashtag
34
+ {
35
+ /**
36
+ * Remove possible '#' from beginning of a Twitter hashtag
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @param string $hashtag Twitter hashtag
41
+ *
42
+ * @return string Twitter hashtag
43
+ */
44
+ public static function trim($hashtag)
45
+ {
46
+ return ltrim(trim($hashtag), '##');
47
+ }
48
+ }
src/Twitter/Helpers/Validators/ScreenName.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ScreenName
34
+ {
35
+ /**
36
+ * Remove possible '@' from beginning of a Twitter screen_name
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @param string $screen_name Twitter screen name
41
+ *
42
+ * @return string Twitter screen name
43
+ */
44
+ public static function trim($screen_name)
45
+ {
46
+ return ltrim(trim($screen_name), '@@');
47
+ }
48
+
49
+ /**
50
+ * Tests a supplied Twitter screen_name for validity
51
+ *
52
+ * @since 1.0.0
53
+ *
54
+ * @link https://github.com/twitter/twitter-text/blob/master/java/src/com/twitter/Regex.java Twitter text
55
+ *
56
+ * @param string $screen_name Twitter screen name
57
+ *
58
+ * @return bool true if valid screen name
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
+ /**
66
+ * Sanitize a user-inputted screen name value
67
+ *
68
+ * Account for a leading @, extra spaces, or a Twitter.com URL
69
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @param string $screen_name Twitter screen name
73
+ *
74
+ * @return string Twitter screen name or empty string if invalid screen name provided
75
+ */
76
+ public static function sanitize($screen_name)
77
+ {
78
+ if (! is_string($screen_name)) {
79
+ return '';
80
+ }
81
+
82
+ $screen_name = trim($screen_name);
83
+ if (! $screen_name) {
84
+ return '';
85
+ }
86
+ $screen_name = trim(rtrim(trim($screen_name), '/'));
87
+ if (! $screen_name) {
88
+ return '';
89
+ }
90
+
91
+ $last_slash = strrpos($screen_name, '/');
92
+ if (false !== $last_slash) {
93
+ $screen_name = substr($screen_name, $last_slash + 1);
94
+ }
95
+ $screen_name = static::trim($screen_name);
96
+ if (! $screen_name) {
97
+ return '';
98
+ }
99
+
100
+ if (! static::isValid($screen_name)) {
101
+ return '';
102
+ }
103
+
104
+ return $screen_name;
105
+ }
106
+ }
src/Twitter/Intents/Follow.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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;
27
+
28
+ /**
29
+ * Link to a follow web intent page
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/web/follow-button/web-intent
34
+ */
35
+ class Follow
36
+ {
37
+
38
+ /**
39
+ * Follow Web Intent URL
40
+ *
41
+ * @since 1.0.0
42
+ *
43
+ * @type string
44
+ */
45
+ const INTENT_URL = 'https://twitter.com/intent/follow';
46
+
47
+ /**
48
+ * Twitter handle
49
+ *
50
+ * @since 1.0.0
51
+ *
52
+ * @type string
53
+ */
54
+ protected $screen_name;
55
+
56
+ /**
57
+ * Construct a new follow intent for the given Twitter screen name
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @param string $screen_name Twitter screen name
62
+ * @param bool $validate validate screen name matches Twitter username allowed characters and length before saving
63
+ */
64
+ public function __construct($screen_name, $validate = true)
65
+ {
66
+ $screen_name = \Twitter\Helpers\Validators\ScreenName::trim($screen_name);
67
+ if ($screen_name) {
68
+ if (false === $validate || \Twitter\Helpers\Validators\ScreenName::isValid($screen_name)) {
69
+ $this->screen_name = $screen_name;
70
+ }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Retrieve the stored Twitter screen name
76
+ *
77
+ * @since 1.0.0
78
+ *
79
+ * @return string Twitter screen name or empty string if none set
80
+ */
81
+ public function getScreenName()
82
+ {
83
+ return $this->screen_name ?: '';
84
+ }
85
+
86
+ /**
87
+ * Return the follow intent URL
88
+ *
89
+ * @since 1.0.0
90
+ *
91
+ * @return string Follow intent URL or empty string if no valid screen name
92
+ */
93
+ public function __toString()
94
+ {
95
+ return $this->getIntentURL();
96
+ }
97
+
98
+ /**
99
+ * Follow intent URL
100
+ *
101
+ * @since 1.0.0
102
+ *
103
+ * @return string Follow intent URL or empty string if no valid screen name
104
+ */
105
+ public function getIntentURL()
106
+ {
107
+ if (! $this->screen_name) {
108
+ return '';
109
+ }
110
+
111
+ return static::INTENT_URL . '?' . http_build_query(array( 'screen_name' => $this->screen_name ), '', '&', PHP_QUERY_RFC3986);
112
+ }
113
+ }
src/Twitter/Intents/Tweet.php ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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;
27
+
28
+ /**
29
+ * Influence the Tweet creation flow
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/web/tweet-button/web-intent
34
+ */
35
+ class Tweet
36
+ {
37
+
38
+ /**
39
+ * Tweet Web Intent URL
40
+ *
41
+ * @since 1.0.0
42
+ *
43
+ * @type string
44
+ */
45
+ const INTENT_URL = 'https://twitter.com/intent/tweet';
46
+
47
+ /**
48
+ * Validate passed variables before storing
49
+ *
50
+ * @since 1.0.0
51
+ *
52
+ * @type bool
53
+ */
54
+ protected $validate_inputs = true;
55
+
56
+ /**
57
+ * Parent Tweet identifier
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @type string
62
+ */
63
+ protected $in_reply_to;
64
+
65
+ /**
66
+ * Pre-populated Tweet text
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * @type string
71
+ */
72
+ protected $text;
73
+
74
+ /**
75
+ * Share a URL
76
+ *
77
+ * @since 1.0.0
78
+ *
79
+ * @type string
80
+ */
81
+ protected $url;
82
+
83
+ /**
84
+ * Hashtags to include in the Tweet
85
+ *
86
+ * @since 1.0.0
87
+ *
88
+ * @type array {
89
+ * @type string comparison hashtag in lowercase
90
+ * @type string passed hashtag
91
+ * }
92
+ */
93
+ protected $hashtags = array();
94
+
95
+ /**
96
+ * Associate a Tweet with an source Twitter account such as the username of your website
97
+ *
98
+ * @since 1.0.0
99
+ *
100
+ * @type string
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
+ *
121
+ * Disabling validation may speed up Web Intent generation but may also cause user-facing issues
122
+ *
123
+ * @since 1.0.0
124
+ *
125
+ * @return __CLASS__ support chaining
126
+ */
127
+ public function disableValidation()
128
+ {
129
+ $this->validate_inputs = false;
130
+ return $this;
131
+ }
132
+
133
+ /**
134
+ * Validate inputs
135
+ *
136
+ * @since 1.0.0
137
+ *
138
+ * @return __CLASS__ support chaining
139
+ */
140
+ public function enableValidation()
141
+ {
142
+ $this->validate_inputs = true;
143
+ return $this;
144
+ }
145
+
146
+ /**
147
+ * Should data be validated before setting?
148
+ *
149
+ * @since 1.0.0
150
+ *
151
+ * @return bool validate inputs
152
+ */
153
+ public function shouldValidate()
154
+ {
155
+ return $this->validate_inputs;
156
+ }
157
+
158
+ /**
159
+ * Define a parent Tweet by ID
160
+ *
161
+ * @since 1.0.0
162
+ *
163
+ * @param string $tweet_id Parent Tweet ID
164
+ *
165
+ * @return __CLASS__ support chaining
166
+ */
167
+ public function setInReplyTo($tweet_id)
168
+ {
169
+ $tweet_id = trim($tweet_id);
170
+ if ($tweet_id) {
171
+ $this->in_reply_to = $tweet_id;
172
+ }
173
+
174
+ return $this;
175
+ }
176
+
177
+ /**
178
+ * Pre-populate Tweet text
179
+ *
180
+ * @since 1.0.0
181
+ *
182
+ * @param string $text Tweet text
183
+ *
184
+ * @return __CLASS__ support chaining
185
+ */
186
+ public function setText($text)
187
+ {
188
+ $text = trim($text);
189
+ if ($text) {
190
+ $this->text = $text;
191
+ }
192
+
193
+ return $this;
194
+ }
195
+
196
+ /**
197
+ * Is the passed URL an absolute URL using the HTTP or HTTPS scheme?
198
+ *
199
+ * @since 1.0.0
200
+ *
201
+ * @param string $url URL to test
202
+ *
203
+ * @return bool true if URL was parsed and contains a HTTP or HTTPs scheme
204
+ */
205
+ public static function isHTTPURL($url)
206
+ {
207
+ if (! ( is_string($url) && $url )) {
208
+ return false;
209
+ }
210
+
211
+ try {
212
+ $scheme = parse_url($url, PHP_URL_SCHEME);
213
+ } catch (Exception $e) {
214
+ return false;
215
+ }
216
+
217
+ if ('http' === $scheme || 'https' === $scheme) {
218
+ return true;
219
+ }
220
+
221
+ return false;
222
+ }
223
+
224
+ /**
225
+ * Share a URL
226
+ *
227
+ * @since 1.0.0
228
+ *
229
+ * @param string $url absolute URL
230
+ *
231
+ * @return __CLASS__ support chaining
232
+ */
233
+ public function setURL($url)
234
+ {
235
+ $url = trim($url);
236
+ if ($url) {
237
+ if ($this->validate_inputs) {
238
+ if (static::isHTTPURL($url)) {
239
+ $this->url = $url;
240
+ }
241
+ } else {
242
+ $this->url = $url;
243
+ }
244
+ }
245
+
246
+ return $this;
247
+ }
248
+
249
+ /**
250
+ * Add a hashtag
251
+ *
252
+ * @since 1.0.0
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
+ }
267
+
268
+ return $this;
269
+ }
270
+
271
+ /**
272
+ * Get a list of hashtags stored for the Tweet
273
+ *
274
+ * @since 1.0.0
275
+ *
276
+ * @return array hashtags {
277
+ * @type string hashtag
278
+ * }
279
+ */
280
+ public function getHashtags()
281
+ {
282
+ return array_values($this->hashtags);
283
+ }
284
+
285
+ /**
286
+ * Associate Tweet with a source account
287
+ *
288
+ * @since 1.0.0
289
+ *
290
+ * @param string $username Twitter username
291
+ *
292
+ * @return __CLASS__ support chaining
293
+ */
294
+ public function setVia($username)
295
+ {
296
+ $username = \Twitter\Helpers\Validators\ScreenName::trim($username);
297
+ if ($username) {
298
+ if ($this->validate_inputs) {
299
+ if (\Twitter\Helpers\Validators\ScreenName::isValid($username)) {
300
+ $this->via = $username;
301
+ }
302
+ } else {
303
+ $this->via = $username;
304
+ }
305
+ }
306
+
307
+ return $this;
308
+ }
309
+
310
+ /**
311
+ * Add a related Twitter account
312
+ *
313
+ * @since 1.0.0
314
+ *
315
+ * @param string $username Twitter username
316
+ * @param string $label brief description of how the account relates to the Tweet content
317
+ *
318
+ * @return __CLASS__ support chaining
319
+ */
320
+ public function addRelated($username, $label = '')
321
+ {
322
+ $username = \Twitter\Helpers\Validators\ScreenName::trim($username);
323
+ if ($username) {
324
+ // normalize passed parameter
325
+ $comparison_username = strtolower($username);
326
+ if (! isset( $this->related[ $comparison_username ] )) {
327
+ if ($this->validate_inputs) {
328
+ if (\Twitter\Helpers\Validators\ScreenName::isValid($username)) {
329
+ $this->related[ $comparison_username ] = trim($label);
330
+ }
331
+ } else {
332
+ $this->related[ $comparison_username ] = trim($label);
333
+ }
334
+ }
335
+ }
336
+
337
+ return $this;
338
+ }
339
+
340
+ /**
341
+ * Get related Twitter usernames
342
+ *
343
+ * @since 1.0.0
344
+ *
345
+ * @return array {
346
+ * @type string username in lowercase
347
+ * @type string description of how the username relates to Tweet content
348
+ * }
349
+ */
350
+ public function getRelated()
351
+ {
352
+ return $this->related;
353
+ }
354
+
355
+ /**
356
+ * Construct a new Tweet intent object from an options array
357
+ *
358
+ * @since 1.0.0
359
+ *
360
+ * @param array $values options array {
361
+ * @type string option name
362
+ * @type string|int|bool option value
363
+ * }
364
+ *
365
+ * @return __CLASS__ object initialized based on passed array values
366
+ */
367
+ public static function fromArray($values)
368
+ {
369
+ if (! is_array($values)) {
370
+ $values = array();
371
+ }
372
+
373
+ $class = __CLASS__;
374
+ $intent = new $class;
375
+ unset( $class );
376
+
377
+ if (isset( $values['validate'] )) {
378
+ if (false == $values['validate'] || 'false' === $values['validate'] || 0 == $values['validate']) {
379
+ $intent->disableValidation();
380
+ }
381
+ }
382
+
383
+ // remove values which evaluate to false
384
+ $values = array_filter($values);
385
+
386
+ // intent parameters
387
+ if (isset( $values['in_reply_to'] )) {
388
+ $intent->setInReplyTo($values['in_reply_to']);
389
+ }
390
+ if (isset( $values['text'] )) {
391
+ $intent->setText($values['text']);
392
+ }
393
+ if (isset( $values['url'] )) {
394
+ $intent->setURL($values['url']);
395
+ }
396
+ if (isset( $values['hashtags'] )) {
397
+ $hashtags = array();
398
+
399
+ if (is_array($values['hashtags'])) {
400
+ $hashtags = $values['hashtags'];
401
+ } else {
402
+ $hashtags = explode(',', $values['hashtags']);
403
+ }
404
+
405
+ if (! empty( $hashtags )) {
406
+ array_walk($hashtags, array( $intent, 'addHashtag' ));
407
+ }
408
+
409
+ unset( $hashtags );
410
+ }
411
+ if (isset( $values['via'] )) {
412
+ $intent->setVia($values['via']);
413
+ }
414
+ if (isset( $values['related'] )) {
415
+ $related = array();
416
+
417
+ if (is_array($values['related'])) {
418
+ $related = $values['related'];
419
+ } elseif (is_string($values['related'])) {
420
+ $related_accounts = explode(',', $values['related']);
421
+ foreach ($related_accounts as $related_account) {
422
+ // extract the label
423
+ $account_pieces = explode(':', $related_account, 2);
424
+ $related[ $account_pieces[0] ] = ( isset( $account_pieces[1] ) ? rawurldecode($account_pieces[1]) : '' );
425
+ unset( $account_pieces );
426
+ }
427
+ }
428
+
429
+ if (! empty( $related )) {
430
+ foreach ($related as $username => $label) {
431
+ if (! ( is_string($username) && $username )) {
432
+ continue;
433
+ }
434
+
435
+ $intent->addRelated($username, $label);
436
+ }
437
+ }
438
+
439
+ unset( $related );
440
+ }
441
+
442
+ return $intent;
443
+ }
444
+
445
+ /**
446
+ * Convert parameters into an array prepped for use in query parameters (underscores) or data-* attributes (dashed)
447
+ *
448
+ * @since 1.0.0
449
+ *
450
+ * @return array Tweet parameters {
451
+ * @type string Tweet parameter
452
+ * @type string parameter value
453
+ * }
454
+ */
455
+ public function toQueryParameters()
456
+ {
457
+ $data = array();
458
+
459
+ if ($this->in_reply_to) {
460
+ $data['in_reply_to'] = $this->in_reply_to;
461
+ }
462
+ if ($this->text) {
463
+ $data['text'] = $this->text;
464
+ }
465
+ if ($this->url) {
466
+ $data['url'] = $this->url;
467
+ }
468
+
469
+ $hashtags = $this->getHashtags();
470
+ if (! empty( $hashtags )) {
471
+ $data['hashtags'] = implode(',', $hashtags);
472
+ }
473
+ unset( $hashtags );
474
+
475
+ if ($this->via) {
476
+ $data['via'] = $this->via;
477
+ }
478
+
479
+ if (! empty( $this->related )) {
480
+ $related_value = array();
481
+ foreach ($this->related as $username => $label) {
482
+ if ($label) {
483
+ $related_value[] = $username . ':' . $label;
484
+ } else {
485
+ $related_value[] = $username;
486
+ }
487
+ }
488
+ $data['related'] = implode(',', $related_value);
489
+ unset( $related_value );
490
+ }
491
+
492
+ return $data;
493
+ }
494
+
495
+ /**
496
+ * Tweet intent URL
497
+ *
498
+ * @since 1.0.0
499
+ *
500
+ * @return string Tweet intent URL with query parameters
501
+ */
502
+ public function getIntentURL()
503
+ {
504
+ $query_parameters = $this->toQueryParameters();
505
+
506
+ if (! empty( $query_parameters )) {
507
+ return self::INTENT_URL . '?' . http_build_query($query_parameters, '', '&', PHP_QUERY_RFC3986);
508
+ }
509
+
510
+ return self::INTENT_URL;
511
+ }
512
+ }
src/Twitter/Widgets/BaseWidget.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\Widgets;
27
+
28
+ /**
29
+ * Base properties used across all Twitter widgets
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ abstract class BaseWidget
34
+ {
35
+
36
+ /**
37
+ * Opt out of tailoring content and suggestions for Twitter users
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @link https://support.twitter.com/articles/20169421 About tailored suggestions
42
+ *
43
+ * @type bool
44
+ */
45
+ protected $dnt = false;
46
+
47
+ /**
48
+ * Requested language for translatable strings in the widget
49
+ *
50
+ * @since 1.0.0
51
+ *
52
+ * @type string
53
+ */
54
+ protected $lang;
55
+
56
+ /**
57
+ * Opt-out of tailoring content and suggestions for Twitter users
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @return __CLASS__ support chaining
62
+ */
63
+ public function doNotTrack()
64
+ {
65
+ $this->dnt = true;
66
+ return $this;
67
+ }
68
+
69
+ /**
70
+ * Reset the do not track preference back to its default state: false
71
+ *
72
+ * @since 1.0.0
73
+ *
74
+ * @return __CLASS__ support chaining
75
+ */
76
+ public function allowTracking()
77
+ {
78
+ $this->dnt = false;
79
+ return $this;
80
+ }
81
+
82
+ /**
83
+ * Explicitly set a Twitter-supported language code for translatable strings in a button or widget
84
+ *
85
+ * @since 1.0.0
86
+ *
87
+ * @uses \Twitter\Widgets\Language::isSupportedLanguage verify language parameter before saving
88
+ *
89
+ * @param string $lang Twitter-supported language code
90
+ *
91
+ * @return __CLASS__ support chaining
92
+ */
93
+ public function setLanguage($lang)
94
+ {
95
+ if ($lang && \Twitter\Widgets\Language::isSupportedLanguage($lang)) {
96
+ $this->lang = $lang;
97
+ }
98
+
99
+ return $this;
100
+ }
101
+
102
+ /**
103
+ * Populate BaseWidget options from a passed associative array
104
+ *
105
+ * @since 1.0.0
106
+ *
107
+ * @param array $options associative array of options {
108
+ * @type string option name
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
+
124
+ return $this;
125
+ }
126
+
127
+ /**
128
+ * Convert the class object into an array, removing default field values
129
+ *
130
+ * @since 1.0.0
131
+ *
132
+ * @return array properties as associative array
133
+ */
134
+ public function toArray()
135
+ {
136
+ $data = array();
137
+
138
+ if (true === $this->dnt) {
139
+ $data['dnt'] = 'true';
140
+ }
141
+
142
+ if ($this->lang) {
143
+ $data['lang'] = $this->lang;
144
+ }
145
+
146
+ return $data;
147
+ }
148
+ }
src/Twitter/Widgets/FollowButton.php ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\Widgets;
27
+
28
+ /**
29
+ * Follow button markup to be interpreted by Twitter's widget JavaScript
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/web/follow-button Follow button documentation
34
+ */
35
+ class FollowButton extends BaseWidget
36
+ {
37
+
38
+ /**
39
+ * HTML class expected by the Twitter widget JS
40
+ *
41
+ * @since 1.0.0
42
+ *
43
+ * @type string
44
+ */
45
+ const HTML_CLASS = 'twitter-follow-button';
46
+
47
+ /**
48
+ * Allowed values for the size property
49
+ *
50
+ * @since 1.0.0
51
+ *
52
+ * @type array allowed sizes {
53
+ * @type string size
54
+ * @type bool exists
55
+ * }
56
+ */
57
+ public static $ALLOWED_SIZES = array( 'medium' => true, 'large' => true );
58
+
59
+ /**
60
+ * Show the current number of followers alongside the button
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * @type bool
65
+ */
66
+ protected $show_count = true;
67
+
68
+ /**
69
+ * Follow Web Intent
70
+ *
71
+ * @since 1.0.0
72
+ *
73
+ * @type \Twitter\Intents\Follow
74
+ */
75
+ protected $intent;
76
+
77
+ /**
78
+ * Show or hide the screen name of the account
79
+ *
80
+ * @since 1.0.0
81
+ *
82
+ * @type bool
83
+ */
84
+ protected $show_screen_name = true;
85
+
86
+ /**
87
+ * Size of the button
88
+ *
89
+ * @since 1.0.0
90
+ *
91
+ * @type string
92
+ */
93
+ protected $size;
94
+
95
+ /**
96
+ * Require screen name. Initialize Follow Web Intent
97
+ *
98
+ * @since 1.0.0
99
+ *
100
+ * @param string $screen_name Twitter account name
101
+ * @param bool $validate validate screen name matches Twitter username allowed characters and length before saving
102
+ */
103
+ public function __construct($screen_name, $validate = true)
104
+ {
105
+ $this->intent = new \Twitter\Intents\Follow($screen_name, $validate);
106
+ }
107
+
108
+ /**
109
+ * Show the number of followers alongside the Follow button
110
+ *
111
+ * @since 1.0.0
112
+ *
113
+ * @return __CLASS__ support chaining
114
+ */
115
+ public function showCount()
116
+ {
117
+ $this->show_count = true;
118
+ return $this;
119
+ }
120
+
121
+ /**
122
+ * Show only the Follow button, without a follower count
123
+ *
124
+ * @since 1.0.0
125
+ *
126
+ * @return __CLASS__ support chaining
127
+ */
128
+ public function hideCount()
129
+ {
130
+ $this->show_count = false;
131
+ return $this;
132
+ }
133
+
134
+ /**
135
+ * Return the screen name of the Twitter user
136
+ *
137
+ * @since 1.0.0
138
+ *
139
+ * @return string Twitter screen name, or blank string if no screen name stored
140
+ */
141
+ public function getScreenName()
142
+ {
143
+ return $this->intent->getScreenName();
144
+ }
145
+
146
+ /**
147
+ * Show the screen name to follow inside the Follow button
148
+ *
149
+ * @since 1.0.0
150
+ *
151
+ * @return __CLASS__ support chaining
152
+ */
153
+ public function showScreenName()
154
+ {
155
+ $this->show_screen_name = true;
156
+ return $this;
157
+ }
158
+
159
+ /**
160
+ * Hide the screen name from display inside the Follow button
161
+ *
162
+ * @since 1.0.0
163
+ *
164
+ * @return __CLASS__ support chaining
165
+ */
166
+ public function hideScreenName()
167
+ {
168
+ $this->show_screen_name = false;
169
+ return $this;
170
+ }
171
+
172
+ /**
173
+ * Set the desired size of the Follow button
174
+ *
175
+ * @since 1.0.0
176
+ *
177
+ * @param string $size button size
178
+ *
179
+ * @return __CLASS__ support chaining
180
+ */
181
+ public function setSize($size)
182
+ {
183
+ if ($size && isset(static::$ALLOWED_SIZES[$size])) {
184
+ $this->size = $size;
185
+ }
186
+ return $this;
187
+ }
188
+
189
+
190
+ /**
191
+ * Build a Follow button object from an associative array
192
+ *
193
+ * @since 1.0.0
194
+ *
195
+ * @param array $options associative array of options {
196
+ * @type string option name
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
+
225
+ return $follow;
226
+ }
227
+
228
+ /**
229
+ * Convert the class object into an array, removing default field values
230
+ *
231
+ * @since 1.0.0
232
+ *
233
+ * @return array properties as associative array
234
+ */
235
+ public function toArray()
236
+ {
237
+ $data = parent::toArray();
238
+
239
+ if (false === $this->show_screen_name) {
240
+ $data['show-screen-name'] = 'false';
241
+ }
242
+ if (false === $this->show_count) {
243
+ $data['show-count'] = 'false';
244
+ }
245
+ if ($this->size && 'medium' !== $this->size) {
246
+ $data['size'] = $this->size;
247
+ }
248
+
249
+ return $data;
250
+ }
251
+
252
+ /**
253
+ * Generate HTML to encourage follow behavior and expose data to the Twitter for Websites JavaScript
254
+ *
255
+ * @since 1.0.0
256
+ *
257
+ * @param string $anchor_text inner text of the generated anchor element. Supports a single '%s' screen name passed through sprintf. Default: Follow %s
258
+ * @param string $html_builder_class callable HTML builder with a static anchorElement class
259
+ *
260
+ * @return string HTML markup or empty string if minimum requirements not met
261
+ */
262
+ public function toHTML($anchor_text = 'Follow %s', $html_builder_class = '\Twitter\Helpers\HTMLBuilder')
263
+ {
264
+ if (! ( is_string($anchor_text) && $anchor_text )) {
265
+ return '';
266
+ }
267
+
268
+ // test for invalid passed class
269
+ if (! ( class_exists($html_builder_class) && method_exists($html_builder_class, 'anchorElement') )) {
270
+ return '';
271
+ }
272
+
273
+ $intent_url = $this->intent->getIntentURL();
274
+ // no screen name stored
275
+ if (! $intent_url) {
276
+ return '';
277
+ }
278
+
279
+ return $html_builder_class::anchorElement(
280
+ $intent_url,
281
+ sprintf($anchor_text, '@' . $this->getScreenName()),
282
+ array(
283
+ 'class' => static::HTML_CLASS,
284
+ ),
285
+ $this->toArray()
286
+ );
287
+ }
288
+ }
src/Twitter/Widgets/Language.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\Widgets;
27
+
28
+ /**
29
+ * Languages supported by Twitter for Websites widgets
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/rest/reference/get/help/languages list of supported languages
34
+ */
35
+ class Language
36
+ {
37
+ /**
38
+ * A list of languages supported by Twitter in production
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type array language code => english name
43
+ */
44
+ public static $SUPPORTED_LANGUAGES = array(
45
+ 'ar' => 'Arabic',
46
+ 'bn' => 'Bengali',
47
+ 'cs' => 'Czech',
48
+ 'da' => 'Danish',
49
+ 'de' => 'German',
50
+ 'en' => 'English',
51
+ 'es' => 'Spanish',
52
+ 'fa' => 'Farsi',
53
+ 'fi' => 'Finnish',
54
+ 'fil' => 'Filipino',
55
+ 'fr' => 'French',
56
+ 'he' => 'Hebrew',
57
+ 'hi' => 'Hindi',
58
+ 'hu' => 'Hungarian',
59
+ 'id' => 'Indonesian',
60
+ 'it' => 'Italian',
61
+ 'ja' => 'Japanese',
62
+ 'ko' => 'Korean',
63
+ 'msa' => 'Malay',
64
+ 'nl' => 'Dutch',
65
+ 'no' => 'Norwegian',
66
+ 'pl' => 'Polish',
67
+ 'pt' => 'Portuguese',
68
+ 'ro' => 'Romanian',
69
+ 'ru' => 'Russian',
70
+ 'sv' => 'Swedish',
71
+ 'th' => 'Thai',
72
+ 'tr' => 'Turkish',
73
+ 'ur' => 'Urdu',
74
+ 'vi' => 'Vietnamese',
75
+ 'zh-cn' => 'Simplified Chinese',
76
+ 'zh-tw' => 'Traditional Chinese',
77
+ );
78
+
79
+ /**
80
+ * Is the passed language a valid Twitter production language code?
81
+ *
82
+ * @since 1.0.0
83
+ *
84
+ * @return bool true if passed language exists in list of Twitter production language codes
85
+ */
86
+ public static function isSupportedLanguage($lang)
87
+ {
88
+ return ( is_string($lang) && $lang && isset(static::$SUPPORTED_LANGUAGES[$lang]) );
89
+ }
90
+ }
src/Twitter/Widgets/TweetButton.php ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\Widgets;
27
+
28
+ /**
29
+ * Tweet button markup to be interpreted by Twitter's widget JavaScript
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link https://dev.twitter.com/web/tweet-button Tweet button documentation
34
+ */
35
+ class TweetButton extends BaseWidget
36
+ {
37
+
38
+ /**
39
+ * HTML class expected by the Twitter widget JS
40
+ *
41
+ * @since 1.0.0
42
+ *
43
+ * @type string
44
+ */
45
+ const HTML_CLASS = 'twitter-share-button';
46
+
47
+ /**
48
+ * Class name of the stored web intent
49
+ *
50
+ * @since 1.0.0
51
+ *
52
+ * @type string
53
+ */
54
+ const INTENT_CLASS = '\Twitter\Intents\Tweet';
55
+
56
+ /**
57
+ * Allowed values for the count variable
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @type array allowed values {
62
+ * @type string count value
63
+ * @type bool exists
64
+ * }
65
+ */
66
+ public static $ALLOWED_COUNT_VALUES = array(
67
+ '' => true, // reset to default (horizontal)
68
+ 'none' => true, // hide Tweet count
69
+ 'vertical' => true, // display above Tweet button
70
+ );
71
+
72
+ /**
73
+ * Allowed values for the align variable
74
+ *
75
+ * @since 1.0.0
76
+ *
77
+ * @type array allowed values {
78
+ * @type string align value
79
+ * @type bool exists
80
+ * }
81
+ */
82
+ public static $ALLOWED_ALIGN_VALUES = array(
83
+ 'left' => true,
84
+ 'right' => true,
85
+ );
86
+
87
+ /**
88
+ * Tweet Web Intent
89
+ *
90
+ * @since 1.0.0
91
+ *
92
+ * @type \Twitter\Intents\Tweet
93
+ */
94
+ protected $intent;
95
+
96
+ /**
97
+ * Size of the Tweet button
98
+ *
99
+ * Large is currently the only supported size.
100
+ *
101
+ * @since 1.0.0
102
+ *
103
+ * @type string
104
+ */
105
+ protected $size;
106
+
107
+ /**
108
+ * Show the number of Tweets mentioning this URL
109
+ *
110
+ * @since 1.0.0
111
+ *
112
+ * @type string
113
+ */
114
+ protected $count = '';
115
+
116
+ /**
117
+ * URL to use for Tweet count purposes
118
+ *
119
+ * Count should also be true for the URL used for the count to affect the button
120
+ *
121
+ * @since 1.0.0
122
+ *
123
+ * @type string
124
+ */
125
+ protected $counturl;
126
+
127
+ /**
128
+ * Force align the button to the left or right of the generated iframe
129
+ *
130
+ * @since 1.0.0
131
+ *
132
+ * @type string
133
+ */
134
+ protected $align;
135
+
136
+ /**
137
+ * Create a new button. Initialize the web intent.
138
+ *
139
+ * @since 1.0.0
140
+ *
141
+ * @param bool $validate Validate inputs such as screen
142
+ */
143
+ public function __construct($validate = true)
144
+ {
145
+ $intent_class = static::INTENT_CLASS;
146
+ $this->intent = new $intent_class();
147
+ if (! $validate) {
148
+ $this->intent->disableValidation();
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Set a new intent
154
+ *
155
+ * @since 1.0.0
156
+ *
157
+ * @param \Twitter\Intents\Tweet $intent Tweet Intent
158
+ *
159
+ * @return __CLASS__ support chaining
160
+ */
161
+ public function setIntent($intent)
162
+ {
163
+ if (is_a($intent, self::INTENT_CLASS)) {
164
+ $this->intent = $intent;
165
+ }
166
+
167
+ return $this;
168
+ }
169
+
170
+ /**
171
+ * Set the size of the Tweet button
172
+ *
173
+ * @since 1.0.0
174
+ *
175
+ * @param string $size button size
176
+ *
177
+ * @return __CLASS__ support chaining
178
+ */
179
+ public function setSize($size)
180
+ {
181
+ // only one size override supported
182
+ if ('large' === $size) {
183
+ $this->size = $size;
184
+ }
185
+ return $this;
186
+ }
187
+
188
+ /**
189
+ * Show the Tweet count next to the Tweet button
190
+ *
191
+ * @since 1.0.0
192
+ *
193
+ * @param string $count a valid count value
194
+ *
195
+ * @return __CLASS__ support chaining
196
+ */
197
+ public function setCount($count)
198
+ {
199
+ if (is_string($count) && isset(static::$ALLOWED_COUNT_VALUES[$count])) {
200
+ $this->count = $count;
201
+ }
202
+
203
+ return $this;
204
+ }
205
+
206
+ /**
207
+ * Set the URL used for Tweet counts
208
+ *
209
+ * @since 1.0.0
210
+ *
211
+ * @param string $url absolute URL to be used for Tweet count
212
+ *
213
+ * @return __CLASS__ support chaining
214
+ */
215
+ public function setCountURL($url)
216
+ {
217
+ $url = trim($url);
218
+ if ($url) {
219
+ if ($this->intent->shouldValidate()) {
220
+ if ($this->intent->isHTTPURL($url)) {
221
+ $this->counturl = $url;
222
+ }
223
+ } else {
224
+ $this->counturl = $url;
225
+ }
226
+ }
227
+
228
+ return $this;
229
+ }
230
+
231
+ /**
232
+ * Force the alignment of the button inside the iframe
233
+ *
234
+ * @since 1.0.0
235
+ *
236
+ * @param string $align left|right
237
+ *
238
+ * @return __CLASS__ support chaining
239
+ */
240
+ public function setAlign($align)
241
+ {
242
+ if (isset(static::$ALLOWED_ALIGN_VALUES[$align])) {
243
+ $this->align = $align;
244
+ }
245
+
246
+ return $this;
247
+ }
248
+
249
+ /**
250
+ * Define a parent Tweet by ID
251
+ *
252
+ * @since 1.0.0
253
+ *
254
+ * @param string $tweet_id Parent Tweet ID
255
+ *
256
+ * @return __CLASS__ support chaining
257
+ */
258
+ public function setInReplyTo($tweet_id)
259
+ {
260
+ $this->intent->setInReplyTo($tweet_id);
261
+
262
+ return $this;
263
+ }
264
+
265
+ /**
266
+ * Pre-populate Tweet text
267
+ *
268
+ * @since 1.0.0
269
+ *
270
+ * @param string $text Tweet text
271
+ *
272
+ * @return __CLASS__ support chaining
273
+ */
274
+ public function setText($text)
275
+ {
276
+ $this->intent->setText($text);
277
+
278
+ return $this;
279
+ }
280
+
281
+ /**
282
+ * Share a URL
283
+ *
284
+ * @since 1.0.0
285
+ *
286
+ * @param string $url absolute URL
287
+ *
288
+ * @return __CLASS__ support chaining
289
+ */
290
+ public function setURL($url)
291
+ {
292
+ $this->intent->setURL($url);
293
+
294
+ return $this;
295
+ }
296
+
297
+ /**
298
+ * Add a hashtag
299
+ *
300
+ * @since 1.0.0
301
+ *
302
+ * @param string $hashtag hashtag
303
+ *
304
+ * @return __CLASS__ support chaining
305
+ */
306
+ public function addHashtag($hashtag)
307
+ {
308
+ $this->intent->addHashtag($hashtag);
309
+
310
+ return $this;
311
+ }
312
+
313
+ /**
314
+ * Associate Tweet with a source account
315
+ *
316
+ * @since 1.0.0
317
+ *
318
+ * @param string $username Twitter username
319
+ *
320
+ * @return __CLASS__ support chaining
321
+ */
322
+ public function setVia($username)
323
+ {
324
+ $this->intent->setVia($username);
325
+ }
326
+
327
+ /**
328
+ * Add a related Twitter account
329
+ *
330
+ * @since 1.0.0
331
+ *
332
+ * @param string $username Twitter username
333
+ * @param string $label brief description of how the account relates to the Tweet content
334
+ *
335
+ * @return __CLASS__ support chaining
336
+ */
337
+ public function addRelated($username, $label = '')
338
+ {
339
+ $this->intent->addRelated($username, $label);
340
+
341
+ return $this;
342
+ }
343
+
344
+ /**
345
+ * Create a Tweet button from an associative array
346
+ *
347
+ * @param array $options {
348
+ * @type string parameter name
349
+ * @type string|int|bool parameter value
350
+ * }
351
+ *
352
+ * @return __CLASS__ new button with configured parameters
353
+ */
354
+ public static function fromArray($options)
355
+ {
356
+ if (! is_array($options)) {
357
+ // initialize a Tweet button with default parameters
358
+ $options = array();
359
+ }
360
+
361
+ $class = __CLASS__;
362
+ $button = new $class();
363
+ unset( $class );
364
+
365
+ // remove values which evaluate to false
366
+ $options = array_filter($options);
367
+ $button->setBaseOptions($options);
368
+
369
+ // intent parameters
370
+ $intent_class = static::INTENT_CLASS;
371
+ $button->setIntent($intent_class::fromArray($options));
372
+ unset( $intent_class );
373
+
374
+ // button parameters
375
+ if (isset( $options['size'] )) {
376
+ $button->setSize($options['size']);
377
+ }
378
+ if (isset( $options['count'] )) {
379
+ $button->setCount($options['count']);
380
+ }
381
+ if (isset( $options['counturl'] )) {
382
+ $button->setCountURL($options['counturl']);
383
+ }
384
+ if (isset( $options['align'] )) {
385
+ $button->setAlign($options['align']);
386
+ }
387
+
388
+ return $button;
389
+ }
390
+
391
+ /**
392
+ * Return Tweet button parameters suitable
393
+ *
394
+ * @since 1.0.0
395
+ *
396
+ * @return array Tweet button parameter array {
397
+ * @type string dashed parameter name
398
+ * @type string parameter value
399
+ * }
400
+ */
401
+ public function toArray()
402
+ {
403
+ $data = parent::toArray();
404
+
405
+ if ($this->size) {
406
+ $data['size'] = $this->size;
407
+ }
408
+
409
+ // empty string is default value
410
+ if ($this->count) {
411
+ $data['count'] = $this->count;
412
+ }
413
+ // only include counturl if a count will be shown
414
+ if ('none' !== $this->count && $this->counturl) {
415
+ $data['counturl'] = $this->counturl;
416
+ }
417
+ if ($this->align) {
418
+ $data['align'] = $this->align;
419
+ }
420
+
421
+ return $data;
422
+ }
423
+
424
+ /**
425
+ * Tweet button HTML
426
+ *
427
+ * @since 1.0.0
428
+ *
429
+ * @param string $anchor_text inner text of the generated anchor element. Default: Tweet
430
+ * @param string $html_builder_class callable HTML builder with a static anchorElement class
431
+ *
432
+ * @return string HTML markup
433
+ */
434
+ public function toHTML($anchor_text = 'Tweet', $html_builder_class = '\Twitter\Helpers\HTMLBuilder')
435
+ {
436
+ if (! ( is_string($anchor_text) && $anchor_text )) {
437
+ return '';
438
+ }
439
+
440
+ // test for invalid passed class
441
+ if (! ( class_exists($html_builder_class) && method_exists($html_builder_class, 'anchorElement') )) {
442
+ return '';
443
+ }
444
+
445
+ $intent_url = $this->intent->getIntentURL();
446
+ // no screen name stored
447
+ if (! $intent_url) {
448
+ return '';
449
+ }
450
+
451
+ return $html_builder_class::anchorElement(
452
+ $intent_url,
453
+ $anchor_text,
454
+ array(
455
+ 'class' => static::HTML_CLASS,
456
+ ),
457
+ $this->toArray()
458
+ );
459
+ }
460
+ }
src/Twitter/WordPress/Admin/Post/MetaBox.php ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Post;
27
+
28
+ /**
29
+ * Post meta box for setting Twitter Card and Tweet text custom values
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class MetaBox
34
+ {
35
+ /**
36
+ * Check page origin before saving
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @var string
41
+ */
42
+ const NONCE_NAME = 'twitter_custom';
43
+
44
+ /**
45
+ * Attach hooks when the post edit screen loads
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @return void
50
+ */
51
+ public static function init()
52
+ {
53
+ // load meta box hooks on post creation screens
54
+ foreach ( array( 'post', 'post-new' ) as $hook ) {
55
+ add_action( 'load-' . $hook . '.php', array( __CLASS__, 'load' ), 1, 0 );
56
+ }
57
+ add_action( 'save_post', array( __CLASS__, 'save' ) );
58
+ }
59
+
60
+ /**
61
+ * Attach meta boxes and save actions to the post edit screen
62
+ *
63
+ * @since 1.0.0
64
+ *
65
+ * @return void
66
+ */
67
+ public static function load()
68
+ {
69
+ add_action( 'wp', array( '\Twitter\WordPress\Admin\Post\TweetIntent', 'registerPostMeta' ), 10, 0 );
70
+ add_action( 'add_meta_boxes', array( __CLASS__, 'addMetaBox' ), 1, 0 );
71
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueueScripts' ) );
72
+ }
73
+
74
+ /**
75
+ * Is the current post type meant for display on the public Web?
76
+ *
77
+ * @since 1.0.0
78
+ *
79
+ * @param string $post_type WordPress post type
80
+ *
81
+ * @return bool true if meant for public viewing and sharing
82
+ */
83
+ public static function isPostTypePublic( $post_type )
84
+ {
85
+ if ( ! $post_type ) {
86
+ return false;
87
+ }
88
+
89
+ $post_type_object = get_post_type_object( $post_type );
90
+ if ( isset( $post_type_object->public ) && $post_type_object->public ) {
91
+ return true;
92
+ }
93
+
94
+ return false;
95
+ }
96
+
97
+ /**
98
+ * Add a post meta box to the post editor page to set custom Twitter values for the post
99
+ *
100
+ * @since 1.0.0
101
+ *
102
+ * @uses add_meta_box
103
+ *
104
+ * @return void
105
+ */
106
+ public static function addMetaBox()
107
+ {
108
+ $post = get_post();
109
+
110
+ if ( ! ( $post && is_a( $post, 'WP_Post' ) ) ) {
111
+ return;
112
+ }
113
+
114
+ // is the post type meant to be public?
115
+ $post_type = get_post_type( $post );
116
+ if ( ! $post_type ) {
117
+ return;
118
+ }
119
+ $post_type_object = get_post_type_object( $post_type );
120
+ if ( ! ( isset( $post_type_object->public ) && $post_type_object->public ) ) {
121
+ return;
122
+ }
123
+
124
+ add_meta_box(
125
+ 'twitter-custom',
126
+ _x( 'Twitter Custom Text', 'Text displayed for a Twitter audience', 'twitter' ),
127
+ array( __CLASS__, 'content' ),
128
+ $post_type
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Queue JS and CSS resources for use on the page
134
+ *
135
+ * @since 1.0.0
136
+ *
137
+ * @return void
138
+ */
139
+ public static function enqueueScripts()
140
+ {
141
+ wp_enqueue_style(
142
+ 'twitter-admin-edit-post', // handle
143
+ plugins_url(
144
+ 'static/css/admin/post/edit.min.css',
145
+ \Twitter\WordPress\PluginLoader::getPluginMainFile()
146
+ ), // absolute URI
147
+ array(), // no dependencies
148
+ '1.0.0' // last chance. used for caching
149
+ );
150
+ }
151
+
152
+ /**
153
+ * Display meta box content
154
+ *
155
+ * @since 1.0.0
156
+ *
157
+ * @param WP_Post $post WordPress post object
158
+ *
159
+ * @return void
160
+ */
161
+ public static function content( $post )
162
+ {
163
+ // Use nonce for verification
164
+ wp_nonce_field( plugin_basename( __FILE__ ), self::NONCE_NAME );
165
+
166
+ \Twitter\WordPress\Admin\Post\TweetIntent::metaBoxContent();
167
+ \Twitter\WordPress\Admin\Post\TwitterCard::metaBoxContent();
168
+ }
169
+
170
+ /**
171
+ * Get post capability singular base to be used when gating access.
172
+ *
173
+ * @since 1.0.0
174
+ *
175
+ * @param string $post_type post type
176
+ *
177
+ * @return string post type object capability type or empty string
178
+ */
179
+ public static function postTypeCapabilityBase( $post_type )
180
+ {
181
+ $post_type_object = get_post_type_object( $post_type );
182
+
183
+ if ( ! isset( $post_type_object->capability_type ) ) {
184
+ return '';
185
+ }
186
+
187
+ $capability_singular_base = '';
188
+ if ( is_string( $post_type_object->capability_type ) ) {
189
+ $capability_singular_base = $post_type_object->capability_type;
190
+ } else if ( is_array( $post_type_object->capability_type ) ) {
191
+ if ( isset( $post_type_object->capability_type[0] ) ) {
192
+ $capability_singular_base = $post_type_object->capability_type[0];
193
+ }
194
+ }
195
+ return $capability_singular_base;
196
+ }
197
+
198
+ /**
199
+ * Save custom values entered in the post edit screen
200
+ *
201
+ * @since 1.0.0
202
+ *
203
+ * @param int $post_id WordPress post identifier
204
+ *
205
+ * @return void
206
+ */
207
+ public static function save( $post_id )
208
+ {
209
+ // verify if this is an auto save routine
210
+ // do not take action until the form is submitted
211
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
212
+ return;
213
+ }
214
+
215
+ // verify nonce
216
+ if ( empty( $_POST[ self::NONCE_NAME ] ) || ! wp_verify_nonce( $_POST[ self::NONCE_NAME ], plugin_basename( __FILE__ ) ) ) {
217
+ return;
218
+ }
219
+
220
+ if ( ! $post_id ) {
221
+ return;
222
+ }
223
+ // does post exist?
224
+ $post = get_post( $post_id );
225
+ if ( ! $post ) {
226
+ return;
227
+ }
228
+
229
+ // check permissions
230
+ $post_type = get_post_type( $post );
231
+ if ( ! $post_type ) {
232
+ return;
233
+ }
234
+ $capability_singular_base = static::postTypeCapabilityBase( $post_type );
235
+ if ( ! ( $capability_singular_base && current_user_can( 'edit_' . $capability_singular_base, $post_id ) ) ) {
236
+ return;
237
+ }
238
+
239
+ \Twitter\WordPress\Admin\Post\TweetIntent::save( $post );
240
+ \Twitter\WordPress\Admin\Post\TwitterCard::save( $post );
241
+ }
242
+ }
src/Twitter/WordPress/Admin/Post/TweetIntent.php ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Post;
27
+
28
+ /**
29
+ * Store custom Tweet Intent data for a WordPress post
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class TweetIntent
34
+ {
35
+ /**
36
+ * Meta field key used to store custom Tweet Intent data
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const META_KEY = 'twitter_share';
43
+
44
+ /**
45
+ * Associative array key representing the hashtags CSV value inside the META_KEY array
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ const HASHTAGS_KEY = 'hashtags';
52
+
53
+ /**
54
+ * Associative array key representing the Tweet text string inside META_KEY array
55
+ *
56
+ * @since 1.0.0
57
+ *
58
+ * @type string
59
+ */
60
+ const TEXT_KEY = 'text';
61
+
62
+ /**
63
+ * Register the post meta key and its sanizitzer
64
+ *
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
+ */
72
+ public static function registerPostMeta()
73
+ {
74
+ register_meta(
75
+ 'post',
76
+ static::META_KEY,
77
+ array( __CLASS__, 'sanitizeFields' )
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Get a Twitter configuration object for up-to-date character counts for a wrapped URL
83
+ *
84
+ * Hard coded in this plugin to avoid requiring Twitter application credentials
85
+ * Implementing sites may want to override this function with a cached value from Twitter's help/configuration API response
86
+ *
87
+ * @since 1.0.0
88
+ *
89
+ * @return object object with short_url_length and optional short_url_length_https properties
90
+ */
91
+ public static function getTwitterConfiguration()
92
+ {
93
+ $config = new \stdClass();
94
+ $config->short_url_length = 22;
95
+ $config->short_url_length_https = $config->short_url_length + 1;
96
+
97
+ return $config;
98
+ }
99
+
100
+ /**
101
+ * Get the length of a wrapped post URL shared on Twitter
102
+ *
103
+ * @since 1.0.0
104
+ *
105
+ * @return int wrapped URL length or 0 if no length info found
106
+ */
107
+ public static function getShortURLLength()
108
+ {
109
+ $config = static::getTwitterConfiguration();
110
+
111
+ if ( ! ( is_object( $config ) && isset( $config->short_url_length ) ) ) {
112
+ return 0;
113
+ }
114
+ $url_length = absint( $config->short_url_length );
115
+
116
+ // check if the post URL to be wrapped uses the HTTPS scheme
117
+ if ( isset( $config->short_url_length_https ) ) {
118
+ $post_url = get_permalink();
119
+ if ( $post_url ) {
120
+ $is_https = false;
121
+ try {
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
+ }
129
+ }
130
+ }
131
+
132
+ return $url_length;
133
+ }
134
+
135
+ /**
136
+ * Display Tweet Web Intent customizations
137
+ *
138
+ * @since 1.0.0
139
+ *
140
+ * @return void
141
+ */
142
+ public static function metaBoxContent()
143
+ {
144
+ $post = get_post();
145
+
146
+ $stored_values = array();
147
+ if ( $post && is_a( $post, 'WP_Post' ) && isset( $post->ID ) ) {
148
+ $stored_values = get_post_meta( $post->ID, static::META_KEY, true );
149
+ if ( ! is_array( $stored_values ) ) {
150
+ $stored_values = array();
151
+ }
152
+ }
153
+
154
+ echo '<h4>' . esc_html( _x( 'Tweet', 'Tweet verb. Sharing.', 'twitter' ) ) . '</h4>';
155
+ echo '<table id="tweet-intent">';
156
+ echo '<thead><tr><th scope="col">' . esc_html( _x( 'Parameter', 'Customization or variable', 'twitter' ) ) . '</th><th scope="col">' . esc_html( __( 'Value' ) ) . '</th></tr></thead><tbody>';
157
+
158
+ $available_characters = 140;
159
+ // t.co wrapped URL length
160
+ $short_url_length = static::getShortURLLength();
161
+ if ( $short_url_length ) {
162
+ // tweet length after accounting for the shared URL with a space separator
163
+ $available_characters = $available_characters - $short_url_length - 1;
164
+ }
165
+
166
+ echo '<tr>';
167
+ echo '<td 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 '<td 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>';
186
+ echo '<p class="description">' . esc_html( __( 'Pre-populate Tweet text', 'twitter' ) ) . '</p>';
187
+ }
188
+
189
+ /**
190
+ * Sanitize user inputs for Tweet Intent data before saving as a post meta value
191
+ *
192
+ * @since 1.0.0
193
+ *
194
+ * @param array $fields POST fields for META_KEY
195
+ *
196
+ * @return array|bool sanizited array or false if none set
197
+ */
198
+ public static function sanitizeFields( $fields )
199
+ {
200
+ if ( ! is_array( $fields ) ) {
201
+ // store nothing
202
+ return false;
203
+ }
204
+
205
+ // overwrite everything
206
+ if ( empty( $fields ) ) {
207
+ return array();
208
+ }
209
+
210
+ $cleaned_fields = array();
211
+
212
+ if ( isset( $fields[ static::HASHTAGS_KEY ] ) ) {
213
+ $hashtags = static::sanitizeCommaSeparatedHashtags( $fields[ static::HASHTAGS_KEY ] );
214
+ if ( ! empty( $hashtags ) ) {
215
+ $cleaned_fields[ static::HASHTAGS_KEY ] = $hashtags;
216
+ }
217
+ unset( $hashtags );
218
+ }
219
+
220
+ if ( isset( $fields[ static::TEXT_KEY ] ) ) {
221
+ // allow Tweet text length overruns
222
+ // the text will appear pre-populated in the Tweet composer but require editing before posting a Tweet
223
+ $text = trim( $fields[ static::TEXT_KEY ] );
224
+ if ( $text ) {
225
+ $cleaned_fields[ static::TEXT_KEY ] = $text;
226
+ }
227
+ unset( $text );
228
+ }
229
+
230
+ return $cleaned_fields;
231
+ }
232
+
233
+ /**
234
+ * Sanitize an expected comma-separated list of hashtags into an array
235
+ *
236
+ * @since 1.0.0
237
+ *
238
+ * @param string $hashtag_string comma-separated list of hashtags
239
+ *
240
+ * @return array list of hashtags
241
+ */
242
+ public static function sanitizeCommaSeparatedHashtags( $hashtag_string )
243
+ {
244
+ if ( ! is_string( $hashtag_string ) ) {
245
+ return array();
246
+ }
247
+
248
+ $hashtag_string = trim( $hashtag_string );
249
+ if ( ! $hashtag_string ) {
250
+ return array();
251
+ }
252
+
253
+ $intent = \Twitter\Intents\Tweet::fromArray( array( 'hashtags' => $hashtag_string ) );
254
+ if ( ! $intent ) {
255
+ return array();
256
+ }
257
+
258
+ return $intent->getHashtags();
259
+
260
+ }
261
+
262
+ /**
263
+ * Save post meta values
264
+ *
265
+ * Basic capability tests should already be applied by \Twitter\WordPress\Admin\Post\MetaBox::save before calling this method
266
+ *
267
+ * @since 1.0.0
268
+ *
269
+ * @param WP_Post $post WordPress post object
270
+ *
271
+ * @return void
272
+ */
273
+ public static function save( $post )
274
+ {
275
+ // test if post ID exists on object
276
+ if ( ! is_a( $post, 'WP_Post' ) ) {
277
+ return;
278
+ }
279
+
280
+ if ( ! isset( $_POST[ static::META_KEY ] ) ) {
281
+ return;
282
+ }
283
+
284
+ $fields = $_POST[ static::META_KEY ];
285
+ if ( ! is_array( $fields ) ) {
286
+ return;
287
+ }
288
+
289
+ $fields = static::sanitizeFields( $fields );
290
+ if ( empty( $fields ) ) {
291
+ delete_post_meta_by_key( static::META_KEY );
292
+ } else {
293
+ update_post_meta( $post->ID, static::META_KEY, $fields );
294
+ }
295
+ }
296
+ }
src/Twitter/WordPress/Admin/Post/TwitterCard.php ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Post;
27
+
28
+ /**
29
+ * Store Twitter Card data for a WordPress post
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class TwitterCard
34
+ {
35
+ /**
36
+ * Meta field key used to store custom Twitter Card data
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const META_KEY = 'twitter_card';
43
+
44
+ /**
45
+ * Associative array key representing the title value inside the META_KEY array
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @type string
50
+ */
51
+ const TITLE_KEY = 'title';
52
+
53
+ /**
54
+ * Associative array key representing the description value inside the META_KEY array
55
+ *
56
+ * @since 1.0.0
57
+ *
58
+ * @type string
59
+ */
60
+ const DESCRIPTION_KEY = 'description';
61
+
62
+ /**
63
+ * Register the post meta key and its sanizitzer
64
+ *
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
+ */
72
+ public static function registerPostMeta()
73
+ {
74
+ register_meta(
75
+ 'post',
76
+ static::META_KEY,
77
+ array( __CLASS__, 'sanitizeFields' )
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Limit displayed card fields to fields supported by the post type
83
+ *
84
+ * @since 1.0.0
85
+ *
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 )
94
+ {
95
+ if ( ! is_string( $post_type ) && $post_type ) {
96
+ return array();
97
+ }
98
+
99
+ $features = array();
100
+ if ( post_type_supports( $post_type, 'title' ) ) {
101
+ $features[ static::TITLE_KEY ] = true;
102
+ }
103
+ if ( post_type_supports( $post_type, 'excerpt' ) ) {
104
+ $features[ static::DESCRIPTION_KEY ] = true;
105
+ }
106
+ return $features;
107
+ }
108
+
109
+ /**
110
+ * Display Twitter Card customizations
111
+ *
112
+ * @since 1.0.0
113
+ *
114
+ * @return void
115
+ */
116
+ public static function metaBoxContent()
117
+ {
118
+ $post = get_post();
119
+ $cards_fields_supported_by_post_type = array();
120
+
121
+ $stored_values = array();
122
+ if ( $post && is_a( $post, 'WP_Post' ) && isset( $post->ID ) ) {
123
+ $stored_values = get_post_meta( $post->ID, static::META_KEY, true );
124
+ if ( ! is_array( $stored_values ) ) {
125
+ $stored_values = array();
126
+ }
127
+ $cards_fields_supported_by_post_type = static::supportedCardFieldsByPostType( get_post_type( $post ) );
128
+ }
129
+
130
+ // no supported Twitter Cards fields
131
+ if ( empty( $cards_fields_supported_by_post_type ) ) {
132
+ return;
133
+ }
134
+
135
+ // separate Twitter Cards content from Intent content above
136
+ echo '<hr' . \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
137
+
138
+ echo '<h4>' . esc_html( __( 'Twitter Card', 'twitter' ) ) . '</h4>';
139
+
140
+ if ( isset( $cards_fields_supported_by_post_type[ static::TITLE_KEY ] ) ) {
141
+ echo '<table id="twitter-card">';
142
+ echo '<thead><tr><th scope="col">' . esc_html( _x( 'Property', 'Object component, such as a title and description of an article', 'twitter' ) ) . '</th><th scope="col">' . esc_html( __( 'Value' ) ) . '</th></tr></thead><tbody>';
143
+
144
+ echo '<tr>';
145
+ echo '<td scope="row" class="left"><label for="twitter-card-title">' . esc_html( __( 'Title' ) ) . '</label></th>';
146
+ echo '<td><input type="text" id="twitter-card-title" name="' . esc_attr( static::META_KEY . '[' . static::TITLE_KEY . ']' ) . '" maxlength="70" autocomplete="off"';
147
+ if ( isset( $stored_values[ static::TITLE_KEY ] ) ) {
148
+ echo ' value="' . esc_attr( $stored_values[ static::TITLE_KEY ] ) . '"';
149
+ } else {
150
+ echo ' placeholder="' . esc_attr( get_the_title( $post ) ) . '"';
151
+ }
152
+
153
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '></td>';
154
+ echo '</tr>';
155
+ }
156
+
157
+ if ( isset( $cards_fields_supported_by_post_type[ static::DESCRIPTION_KEY ] ) ) {
158
+ echo '<tr>';
159
+ echo '<td scope="row" class="left"><label for="twitter-card-description">' . esc_html( __( 'Description' ) ) . '</label></th>';
160
+ echo '<td><input type="text" id="twitter-card-description" name="' . esc_attr( static::META_KEY . '[' . static::DESCRIPTION_KEY . ']' ) . '" maxlength="200" autocomplete="off"';
161
+ if ( isset( $stored_values[ static::DESCRIPTION_KEY ] ) ) {
162
+ echo ' value="' . esc_attr( $stored_values[ static::DESCRIPTION_KEY ] ) . '"';
163
+ }
164
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '></td>';
165
+ echo '</tr>';
166
+
167
+ echo '</tbody></table>';
168
+ echo '<p class="description">' . esc_html( __( 'Customize Twitter link previews', 'twitter' ) ) . '</p>';
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Sanitize user inputs for Twitter Card data before saving as a post meta value
174
+ *
175
+ * @since 1.0.0
176
+ *
177
+ * @param array $fields POST fields for META_KEY
178
+ *
179
+ * @return array|bool sanizited array or false if none set
180
+ */
181
+ public static function sanitizeFields( $fields )
182
+ {
183
+ if ( ! is_array( $fields ) ) {
184
+ // store nothing
185
+ return false;
186
+ }
187
+
188
+ // overwrite everything
189
+ if ( empty( $fields ) ) {
190
+ return array();
191
+ }
192
+
193
+ $cleaned_fields = array();
194
+
195
+ if ( isset( $fields[ static::TITLE_KEY ] ) ) {
196
+ $title = \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $fields[ static::TITLE_KEY ], /* remove breaks */ true );
197
+ if ( $title ) {
198
+ $cleaned_fields[ static::TITLE_KEY ] = $title;
199
+ }
200
+ unset($title);
201
+ }
202
+ if ( isset( $fields[ static::DESCRIPTION_KEY ] ) ) {
203
+ $description = \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $fields[ static::DESCRIPTION_KEY ], /* remove breaks */ true );
204
+ if ( $description ) {
205
+ $cleaned_fields[ static::DESCRIPTION_KEY ] = $description;
206
+ }
207
+ unset($description);
208
+ }
209
+
210
+ return $cleaned_fields;
211
+ }
212
+
213
+ /**
214
+ * Save post meta values
215
+ *
216
+ * Basic capability tests should already be applied by \Twitter\WordPress\Admin\Post\MetaBox::save before calling this method
217
+ *
218
+ * @since 1.0.0
219
+ *
220
+ * @param WP_Post $post WordPress post object
221
+ *
222
+ * @return void
223
+ */
224
+ public static function save( $post )
225
+ {
226
+ // test if post ID exists on object
227
+ if ( ! is_a( $post, 'WP_Post' ) ) {
228
+ return;
229
+ }
230
+
231
+ if ( ! isset( $_POST[ static::META_KEY ] ) ) {
232
+ return;
233
+ }
234
+
235
+ $fields = $_POST[ static::META_KEY ];
236
+ if ( ! is_array( $fields ) ) {
237
+ return;
238
+ }
239
+
240
+ $fields = static::sanitizeFields( $fields );
241
+ if ( empty( $fields ) ) {
242
+ delete_post_meta_by_key( static::META_KEY );
243
+ } else {
244
+ update_post_meta( $post->ID, static::META_KEY, $fields );
245
+ }
246
+ }
247
+ }
src/Twitter/WordPress/Admin/Profile/User.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Profile;
27
+
28
+ /**
29
+ * Associate a WordPress account with a Twitter account
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class User
34
+ {
35
+ /**
36
+ * Conditionally load features on the edit profile page.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @return void
41
+ */
42
+ public static function init()
43
+ {
44
+ // only show to authors
45
+ if ( ! current_user_can( 'edit_posts' ) ) {
46
+ return;
47
+ }
48
+
49
+ // add Twitter after website
50
+ add_filter( 'user_contactmethods', array( __CLASS__, 'addContactMethod' ), 1, 2 );
51
+ // provide additional hints in label text
52
+ add_filter( 'user_twitter_label', array( __CLASS__, 'contactMethodLabel' ), 10, 1 );
53
+ // clean up user input
54
+ add_filter( 'sanitize_user_meta_twitter', array( __CLASS__, 'sanitize' ), 10, 1 );
55
+ }
56
+
57
+ /**
58
+ * Add Twitter as a contact method in the Contact Info profile section
59
+ *
60
+ * @since 1.0.0
61
+ *
62
+ * @see wp_get_user_contact_methods()
63
+ *
64
+ * @param array $methods contact methods and their labels {
65
+ * @type string contact method
66
+ * @type string label
67
+ * }
68
+ * @param WP_User $user WP_User object.
69
+ *
70
+ * @return array contact methods and their labels {
71
+ * @type string contact method
72
+ * @type string label
73
+ * }
74
+ */
75
+ public static function addContactMethod( $methods, $user )
76
+ {
77
+ $methods['twitter'] = 'Twitter';
78
+ return $methods;
79
+ }
80
+
81
+ /**
82
+ * Customize HTML display label for contact method
83
+ *
84
+ * @since 1.0.0
85
+ *
86
+ * @param string $label HTML label
87
+ *
88
+ * @return string HTML label
89
+ */
90
+ public static function contactMethodLabel( $label = '' )
91
+ {
92
+ return _x( 'Twitter @username', 'Prompt requesting entry of a Twitter username', 'twitter' );
93
+ }
94
+
95
+ /**
96
+ * Clean up user inputted Twitter username value before saving the option
97
+ *
98
+ * @since 1.0.0
99
+ *
100
+ * @param string $screen_name inputted Twitter username value
101
+ *
102
+ * @return string sanitized Twitter username value
103
+ */
104
+ public static function sanitize( $screen_name )
105
+ {
106
+ if ( ! is_string( $screen_name ) ) {
107
+ return '';
108
+ }
109
+ $screen_name = trim( $screen_name );
110
+ if ( ! $screen_name ) {
111
+ return '';
112
+ }
113
+ $screen_name = sanitize_text_field( $screen_name );
114
+ if ( ! $screen_name ) {
115
+ return '';
116
+ }
117
+
118
+ return \Twitter\Helpers\Validators\ScreenName::sanitize( $screen_name );
119
+ }
120
+ }
src/Twitter/WordPress/Admin/Settings/Loader.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Settings;
27
+
28
+ /**
29
+ * Initialize hooks related to WordPress admin interface settings
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Loader
34
+ {
35
+ /**
36
+ * Add hooks
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @return void
41
+ */
42
+ public static function init()
43
+ {
44
+ add_action( 'admin_menu', array( __CLASS__, 'settingsMenuItems' ) );
45
+ add_filter( 'plugin_action_links', array( __CLASS__, 'pluginActionLinks' ), 10, 2 );
46
+ }
47
+
48
+ /**
49
+ * Add Twitter settings to the WordPress administration menu.
50
+ *
51
+ * @since 1.0.0
52
+ *
53
+ * @return void
54
+ */
55
+ public static function settingsMenuItems()
56
+ {
57
+ \Twitter\WordPress\Admin\Settings\SinglePage::menuItem();
58
+ }
59
+
60
+ /**
61
+ * Link to settings from the plugin listing page
62
+ *
63
+ * @since 1.0.0
64
+ *
65
+ * @param array $links links displayed under the plugin
66
+ * @param string $file plugin main file path relative to plugin dir
67
+ *
68
+ * @return array links array passed in, possibly with our settings link added
69
+ */
70
+ public static function pluginActionLinks( $links, $file )
71
+ {
72
+ if ( $file === plugin_basename( \Twitter\WordPress\PluginLoader::getPluginMainFile() ) ) {
73
+ 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>' );
74
+ }
75
+
76
+ return $links;
77
+ }
78
+ }
src/Twitter/WordPress/Admin/Settings/SinglePage.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Settings;
27
+
28
+ /**
29
+ * Combine Twitter settings into a single settings page
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class SinglePage
34
+ {
35
+ use Template;
36
+
37
+ /**
38
+ * Settings page identifier.
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const PAGE_SLUG = 'twitter';
45
+
46
+ /**
47
+ * The hook suffix assigned by add_utility_page()
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ protected $hook_suffix;
54
+
55
+ /**
56
+ * Class names containing single page settings components
57
+ *
58
+ * The addToSettingsPage method is called on each class to initialize its components
59
+ *
60
+ * @since 1.0.0
61
+ *
62
+ * @type array settings component fully qualified class names {
63
+ * @type string fully qualified class name
64
+ * }
65
+ */
66
+ protected static $SETTINGS_COMPONENTS = array( '\Twitter\WordPress\Admin\Settings\Theme', '\Twitter\WordPress\Admin\Settings\SiteAttribution', '\Twitter\WordPress\Admin\Settings\TweetButton' );
67
+
68
+ /**
69
+ * Reference the feature by name
70
+ *
71
+ * @since 1.0.0
72
+ *
73
+ * @return string translated feature name
74
+ */
75
+ public static function featureName()
76
+ {
77
+ return __( 'Twitter settings', 'twitter' );
78
+ }
79
+
80
+ /**
81
+ * Add a submenu item to WordPress admin.
82
+ *
83
+ * @since 1.0.0
84
+ *
85
+ * @return string|null page hook or null if page capability requirements not met
86
+ */
87
+ public static function menuItem()
88
+ {
89
+ $settings = new static();
90
+
91
+ $hook_suffix = add_utility_page(
92
+ static::featureName(), // page <title>
93
+ 'Twitter', // brand name. not translated
94
+ 'manage_options', // capability needed
95
+ static::PAGE_SLUG, // unique menu slug
96
+ array( &$settings, 'settingsPage' ), // pageload callback
97
+ 'dashicons-twitter' // to be replaced by dashicon
98
+ );
99
+
100
+ // hook_suffix may be false if current viewer does not have the manage_options capability
101
+ if ( ! $hook_suffix ) {
102
+ return;
103
+ }
104
+ $settings->hook_suffix = $hook_suffix;
105
+
106
+ // add each settings component to the single page settings page
107
+ foreach ( static::$SETTINGS_COMPONENTS as $settings_component ) {
108
+ $settings_component::addToSettingsPage( $hook_suffix );
109
+ }
110
+
111
+ add_action(
112
+ 'load-' . $hook_suffix,
113
+ array( &$settings, 'addContextualHelp' ),
114
+ 99,
115
+ 0
116
+ );
117
+
118
+ return $hook_suffix;
119
+ }
120
+
121
+ /**
122
+ * Add contextual help content to the settings screen
123
+ *
124
+ * @since 1.0.0
125
+ *
126
+ * @return void
127
+ */
128
+ public function addContextualHelp()
129
+ {
130
+ $screen = get_current_screen();
131
+ if ( ! $screen ) { // null if global not set
132
+ return;
133
+ }
134
+
135
+ do_action( 'add-' . $this->hook_suffix . '-help-tab', $screen );
136
+ }
137
+ }
src/Twitter/WordPress/Admin/Settings/SiteAttribution.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Settings;
27
+
28
+ /**
29
+ * Store a Twitter username for attribution of site content
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class SiteAttribution
34
+ {
35
+ /**
36
+ * Define our option name
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const OPTION_NAME = 'twitter_site_attribution';
43
+
44
+ /**
45
+ * Priority of the section affecting insertion order on the single settings page
46
+ *
47
+ * Display after theme section
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @type int
52
+ */
53
+ const SECTION_PRIORITY = 5;
54
+
55
+ /**
56
+ * The hook suffix of the parent settings page
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @type string
61
+ */
62
+ protected $hook_suffix;
63
+
64
+ /**
65
+ * Reference the feature by name
66
+ *
67
+ * @since 1.0.0
68
+ *
69
+ * @return string translated feature name
70
+ */
71
+ public static function featureName()
72
+ {
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
+ *
79
+ * @since 1.0.0
80
+ *
81
+ * @param string $hook_suffix hook suffix of an existing settings page
82
+ *
83
+ * @return bool site attribution settings loaded
84
+ */
85
+ public static function addToSettingsPage( $hook_suffix )
86
+ {
87
+ if ( ! ( is_string( $hook_suffix ) && $hook_suffix ) ) {
88
+ return false;
89
+ }
90
+
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' ),
98
+ 5, // after theme settings
99
+ 0
100
+ );
101
+
102
+ return true;
103
+ }
104
+
105
+ /**
106
+ * Set up settings section
107
+ *
108
+ * @since 1.0.0
109
+ *
110
+ * @return void
111
+ */
112
+ public function onload()
113
+ {
114
+ if ( ! isset( $this->hook_suffix ) ) {
115
+ return;
116
+ }
117
+
118
+ add_action(
119
+ 'add-' . $this->hook_suffix . '-section',
120
+ array( &$this, 'defineSection' ),
121
+ static::SECTION_PRIORITY,
122
+ 0 // no parameters
123
+ );
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
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Define site attribution section and the fields within
136
+ *
137
+ * @since 1.0.0
138
+ *
139
+ * @return void
140
+ */
141
+ public function defineSection()
142
+ {
143
+ if ( ! isset( $this->hook_suffix ) ) {
144
+ return;
145
+ }
146
+
147
+ $section = 'site-attribution';
148
+ add_settings_section(
149
+ $section,
150
+ static::featureName(),
151
+ array( __CLASS__, 'sectionHeader' ),
152
+ $this->hook_suffix
153
+ );
154
+
155
+ add_settings_field(
156
+ 'site-attribution-username',
157
+ \Twitter\WordPress\Admin\Profile\User::contactMethodLabel(),
158
+ array( &$this, 'displaySiteAttributionUsername' ),
159
+ $this->hook_suffix,
160
+ $section,
161
+ array( 'label_for' => self::OPTION_NAME )
162
+ );
163
+ }
164
+
165
+ /**
166
+ * Introduce the settings section
167
+ *
168
+ * @since 1.0.0
169
+ *
170
+ * @return void
171
+ */
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
+
179
+ /**
180
+ * Input a Twitter screen_name to attribute to site content
181
+ *
182
+ * @since 1.0.0
183
+ *
184
+ * @return void
185
+ */
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
+ /**
202
+ * Display inline help content
203
+ *
204
+ * @since 1.0.0
205
+ *
206
+ * @param WP_Screen $screen current screen
207
+ *
208
+ * @return void
209
+ */
210
+ public static function addHelpTab( $screen )
211
+ {
212
+ if ( ! $screen ) {
213
+ return;
214
+ }
215
+
216
+ $content = '<p>' . sprintf( esc_html( __( 'Site attribution is used to attribute site content to a Twitter account in %1$s and %2$s.', 'twitter' ) ), '<a href="' . esc_url( 'https://dev.twitter.com/cards/overview', array( 'https', 'http' ) ) . '">' . esc_html( __( 'Twitter Cards', 'twitter' ) ) . '</a>', '<a href="' . esc_url( 'https://support.twitter.com/articles/20170934-twitter-card-analytics-dashboard', array( 'https', 'http' ) ) . '">' . esc_html( __( 'Twitter Analytics', 'twitter' ) ) . '</a>' ) . '</p>';
217
+ $content .= '<p>' . esc_html( __( 'The account may also be used to note a Tweet originated from a Tweet Button on your site.', 'twitter' ) ) . '</p>';
218
+
219
+ $screen->add_help_tab( array(
220
+ 'id' => 'twitter-site-attribution-help',
221
+ 'title' => static::featureName(),
222
+ 'content' => $content,
223
+ ) );
224
+ }
225
+
226
+ /**
227
+ * Clean up user inputted Twitter username value before saving the option
228
+ *
229
+ * @since 1.0.0
230
+ *
231
+ * @param string $screen_name inputted Twitter username value
232
+ *
233
+ * @return string sanitized Twitter username value
234
+ */
235
+ public static function sanitize( $screen_name )
236
+ {
237
+ if ( ! is_string( $screen_name ) ) {
238
+ return '';
239
+ }
240
+ $screen_name = trim( $screen_name );
241
+ if ( ! $screen_name ) {
242
+ return '';
243
+ }
244
+ $screen_name = sanitize_text_field( $screen_name );
245
+ if ( ! $screen_name ) {
246
+ return '';
247
+ }
248
+
249
+ return \Twitter\Helpers\Validators\ScreenName::sanitize( $screen_name );
250
+ }
251
+ }
src/Twitter/WordPress/Admin/Settings/Template.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Settings;
27
+
28
+ /**
29
+ * Share common template functionality across Twitter setting pages
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ trait Template
34
+ {
35
+
36
+ /**
37
+ * Load the settings page
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @return void
42
+ */
43
+ public function settingsPage()
44
+ {
45
+ if ( ! isset( $this->hook_suffix ) ) {
46
+ return;
47
+ }
48
+
49
+ do_action( 'add-' . $this->hook_suffix . '-section' );
50
+
51
+ echo '<div class="wrap">';
52
+
53
+ echo '<header><h2>' . esc_html( static::featureName() ) . '</h2></header>';
54
+ // handle general messages such as settings updated up top
55
+ // place individual settings errors alongside their fields
56
+ settings_errors( 'general' );
57
+
58
+ echo '<form method="post" action="' . esc_url( admin_url( 'options.php' ), array( 'https', 'http' ) ) . '">';
59
+ settings_fields( $this->hook_suffix );
60
+ do_settings_sections( $this->hook_suffix );
61
+ submit_button();
62
+ echo '</form>';
63
+
64
+ echo '</div>';
65
+ }
66
+
67
+ }
src/Twitter/WordPress/Admin/Settings/Theme.php ADDED
@@ -0,0 +1,507 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Settings;
27
+
28
+ /**
29
+ * Site-level settings for Twitter widget themes
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Theme
34
+ {
35
+ /**
36
+ * Define our option array value.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const OPTION_NAME = 'twitter_widgets';
43
+
44
+ /**
45
+ * Priority of the section affecting insertion order on the single settings page
46
+ *
47
+ * First section
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @type int
52
+ */
53
+ const SECTION_PRIORITY = 1;
54
+
55
+ /**
56
+ * The hook suffix of the parent settings page
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @type string
61
+ */
62
+ protected $hook_suffix;
63
+
64
+ /**
65
+ * Reference the feature by name
66
+ *
67
+ * @since 1.0.0
68
+ *
69
+ * @return string translated feature name
70
+ */
71
+ public static function featureName()
72
+ {
73
+ return _x( 'Theme', 'Visual and color theme', 'twitter' );
74
+ }
75
+
76
+ /**
77
+ * Theme options and labels
78
+ *
79
+ * @since 1.0.0
80
+ *
81
+ * @return array associative array of accepted values and the value's translated label
82
+ */
83
+ public static function themeChoices()
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
+
91
+ /**
92
+ * Default options if no options exist
93
+ *
94
+ * @since 1.0.0
95
+ *
96
+ * @return array associative array of option values
97
+ */
98
+ public static function defaultOptions()
99
+ {
100
+ return array(
101
+ 'theme' => 'light',
102
+ );
103
+ }
104
+
105
+ /**
106
+ * Add site attribution option and settings section to an existing settings page
107
+ *
108
+ * @since 1.0.0
109
+ *
110
+ * @param string $hook_suffix hook suffix of an existing settings page
111
+ *
112
+ * @return bool site attribution settings loaded
113
+ */
114
+ public static function addToSettingsPage( $hook_suffix )
115
+ {
116
+ if ( ! ( is_string( $hook_suffix ) && $hook_suffix ) ) {
117
+ return false;
118
+ }
119
+
120
+ $settings = new static();
121
+ $settings->hook_suffix = $hook_suffix;
122
+
123
+ register_setting(
124
+ $hook_suffix,
125
+ self::OPTION_NAME,
126
+ array( __CLASS__, 'sanitizeOptions' )
127
+ );
128
+ add_action(
129
+ 'load-' . $hook_suffix,
130
+ array( &$settings, 'onload' ),
131
+ 1, // first section
132
+ 0
133
+ );
134
+
135
+ return true;
136
+ }
137
+
138
+ /**
139
+ * Store existing options. Set up page sections
140
+ *
141
+ * @since 1.0.0
142
+ *
143
+ * @return void
144
+ */
145
+ public function onload()
146
+ {
147
+ if ( ! isset( $this->hook_suffix ) ) {
148
+ return;
149
+ }
150
+
151
+ $options = get_option( self::OPTION_NAME );
152
+ if ( ! is_array( $options ) ) {
153
+ $options = static::defaultOptions();
154
+ }
155
+ $this->existing_options = $options;
156
+
157
+ // theme
158
+ add_action(
159
+ 'add-' . $this->hook_suffix . '-section',
160
+ array( &$this, 'defineThemeSection' ),
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
170
+ );
171
+
172
+ $late_priority = 10;
173
+
174
+ // warnings
175
+ add_action(
176
+ 'add-' . $this->hook_suffix . '-section',
177
+ array( &$this, 'defineWarningsSection' ),
178
+ $late_priority, // default priority
179
+ 0 // no parameters
180
+ );
181
+ }
182
+
183
+ /**
184
+ * Add theme settings section and fields
185
+ *
186
+ * @since 1.0.0
187
+ *
188
+ * @return void
189
+ */
190
+ public function defineThemeSection()
191
+ {
192
+ $section = 'twitter-colorscheme';
193
+ add_settings_section(
194
+ $section,
195
+ static::featureName(),
196
+ array( __CLASS__, 'sectionHeader' ),
197
+ $this->hook_suffix
198
+ );
199
+
200
+ add_settings_field(
201
+ 'twitter-theme',
202
+ _x( 'Theme', 'A choice of color options grouped as a theme', 'twitter' ),
203
+ array( &$this, 'displayTheme' ),
204
+ $this->hook_suffix,
205
+ $section,
206
+ array( 'label_for' => 'twitter-theme' )
207
+ );
208
+
209
+ add_settings_field(
210
+ 'twitter-link-color',
211
+ _x( 'Link color', 'CSS color styling of a link text', 'twitter' ),
212
+ array( &$this, 'displayLinkColor' ),
213
+ $this->hook_suffix,
214
+ $section,
215
+ array( 'label_for' => 'twitter-link-color' )
216
+ );
217
+
218
+ add_settings_field(
219
+ 'twitter-border-color',
220
+ _x( 'Border color', 'CSS color styling of a box border', 'twitter' ),
221
+ array( &$this, 'displayBorderColor' ),
222
+ $this->hook_suffix,
223
+ $section,
224
+ array( 'label_for' => 'twitter-border-color' )
225
+ );
226
+ }
227
+
228
+ /**
229
+ * Add warnings settings section and fields
230
+ *
231
+ * @since 1.0.0
232
+ *
233
+ * @return void
234
+ */
235
+ public function defineWarningsSection()
236
+ {
237
+ $section = 'warnings';
238
+ add_settings_section(
239
+ $section,
240
+ __( 'Warnings', 'twitter' ),
241
+ array( __CLASS__, 'warningsSectionHeader' ),
242
+ $this->hook_suffix
243
+ );
244
+
245
+ add_settings_field(
246
+ 'twitter-csp',
247
+ __( 'Content Security Policy', 'twitter' ),
248
+ array( &$this, 'displayContentSecurityPolicy' ),
249
+ $this->hook_suffix,
250
+ $section
251
+ );
252
+ }
253
+
254
+ /**
255
+ * Add warnings settings section and fields
256
+ *
257
+ * @since 1.0.0
258
+ *
259
+ * @return void
260
+ */
261
+ public function defineRestrictionsSection()
262
+ {
263
+ $section = 'twitter-restrictions';
264
+ add_settings_section(
265
+ $section,
266
+ __( 'Restrictions', 'twitter' ),
267
+ array( __CLASS__, 'restrictionsSectionHeader' ),
268
+ $this->hook_suffix
269
+ );
270
+
271
+ // do not track
272
+ add_settings_field(
273
+ 'twitter-dnt',
274
+ _x( 'Personalization', 'Personalize Twitter content based on website visits', 'twitter' ),
275
+ array( &$this, 'displayDoNotTrack' ),
276
+ $this->hook_suffix,
277
+ $section
278
+ );
279
+ }
280
+
281
+ /**
282
+ * Introduce the settings page
283
+ *
284
+ * @since 1.0.0
285
+ *
286
+ * @return void
287
+ */
288
+ public static function sectionHeader()
289
+ {
290
+ echo '<p>';
291
+ echo esc_html( __( 'Customize colors used in Twitter widgets to match your site\'s theme', 'twitter' ) );
292
+ echo '</p>';
293
+ }
294
+
295
+ /**
296
+ * Choose a theme
297
+ *
298
+ * @since 1.0.0
299
+ *
300
+ * @return void
301
+ */
302
+ public function displayTheme()
303
+ {
304
+ $key = 'theme';
305
+
306
+ $existing_value = 'light';
307
+ if ( isset( $this->existing_options[ $key ] ) ) {
308
+ $existing_value = $this->existing_options[ $key ];
309
+ }
310
+
311
+ $name_attribute = esc_attr( esc_attr( self::OPTION_NAME . '[' . $key . ']' ) );
312
+ $close_void_element = \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
313
+ $choices = static::themeChoices();
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>';
322
+ }
323
+
324
+ /**
325
+ * Choose a hex color value as the link color
326
+ *
327
+ * @since 1.0.0
328
+ *
329
+ * @return void
330
+ */
331
+ public function displayLinkColor()
332
+ {
333
+ $key = 'link-color';
334
+
335
+ echo '<input type="color" name="' . esc_attr( self::OPTION_NAME . '[' . $key . ']' ) . '" id="twitter-link-color"';
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' ) );
343
+ echo '</p>';
344
+ }
345
+
346
+ /**
347
+ * Choose a hex color value as the border color
348
+ *
349
+ * @since 1.0.0
350
+ *
351
+ * @return void
352
+ */
353
+ public function displayBorderColor()
354
+ {
355
+ $key = 'border-color';
356
+
357
+ echo '<input type="color" name="' . esc_attr( self::OPTION_NAME . '[' . $key . ']' ) . '" id="twitter-border-color"';
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' ) );
365
+ echo '</p>';
366
+ }
367
+
368
+ /**
369
+ * Display inline help content
370
+ *
371
+ * @since 1.0.0
372
+ *
373
+ * @param WP_Screen $screen current screen
374
+ *
375
+ * @return void
376
+ */
377
+ public static function addHelpTab( $screen )
378
+ {
379
+ if ( ! $screen ) {
380
+ return;
381
+ }
382
+
383
+ $content = '<p>' . esc_html( __( 'Twitter Embedded Tweet and Timeline widgets support customization of color theme, link color, and border color inside a rendered widget.', 'twitter' ) ) . '</p>';
384
+ $content .= '<p>' . esc_html( __( 'Choose a light or dark theme to best match the background color and text color of surrounding content.', 'twitter' ) ) . '</p>';
385
+ $content .= '<p>' . esc_html( __( 'Link color is applied to linked text including URLs, #hashtags, and @mentions.', 'twitter' ) ) . '</p>';
386
+ $content .= '<p>' . esc_html( __( 'Border color applies to borders separating Tweet sections or individual Tweets.', 'twitter' ) ) . '</p>';
387
+
388
+ $screen->add_help_tab( array(
389
+ 'id' => 'twitter-theme-help',
390
+ 'title' => static::featureName(),
391
+ 'content' => $content,
392
+ ) );
393
+ }
394
+
395
+ /**
396
+ * Introduction to the Twitter restrictions section
397
+ *
398
+ * @since 1.0.0
399
+ *
400
+ * @return void
401
+ */
402
+ public static function restrictionsSectionHeader()
403
+ {
404
+ echo '<p>';
405
+ echo esc_html( __( 'Limit Twitter functionality', 'twitter' ) );
406
+ echo '</p>';
407
+ }
408
+
409
+ /**
410
+ * Do not track visitors for use in Twitter advertisers and site suggestions
411
+ *
412
+ * @since 1.0.0
413
+ *
414
+ * @return void
415
+ */
416
+ public function displayDoNotTrack()
417
+ {
418
+ $key = 'dnt';
419
+
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>';
433
+ }
434
+
435
+ /**
436
+ * Introduce the warnings section
437
+ *
438
+ * @since 1.0.0
439
+ *
440
+ * @return void
441
+ */
442
+ public static function warningsSectionHeader()
443
+ {
444
+ echo '<p>';
445
+ echo esc_html( __( 'Warnings and error display', 'twitter' ) );
446
+ echo '</p>';
447
+ }
448
+
449
+ /**
450
+ * Suppress a Content Security Policy warning
451
+ *
452
+ * @since 1.0.0
453
+ *
454
+ * @return void
455
+ */
456
+ public function displayContentSecurityPolicy()
457
+ {
458
+ $key = 'csp';
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
+
466
+ echo '<p class="description">';
467
+ echo sprintf( esc_html( __( 'Please note: Not all widget functionality is supported when a %s is applied', 'twitter' ) ), '<a href="' . esc_url( 'https://developer.mozilla.org/docs/Web/Security/CSP/Introducing_Content_Security_Policy', array( 'https', 'http' ) ) . '">' . esc_html( __( 'Content Security Policy', 'twitter' ) ) . '</a>' );
468
+ echo '</p>';
469
+ }
470
+
471
+ /**
472
+ * Sanitize theme options
473
+ *
474
+ * @since 1.0.0
475
+ *
476
+ * @param array $options submitted options
477
+ *
478
+ * @return array associative array of clean options
479
+ */
480
+ public static function sanitizeOptions( $options )
481
+ {
482
+ if ( ! is_array( $options ) || empty( $options ) ) {
483
+ return array();
484
+ }
485
+ $clean_options = array();
486
+
487
+ if ( isset( $options['theme'] ) && 'dark' === $options['theme'] ) {
488
+ $clean_options['theme'] = 'dark';
489
+ }
490
+ foreach ( array( 'link-color', 'border-color' ) as $color_option ) {
491
+ if ( ! ( isset( $options[ $color_option ] ) && is_string( $options[ $color_option ] ) ) ) {
492
+ continue;
493
+ }
494
+ $color_hex = ltrim( trim( $options[ $color_option ] ), '#' );
495
+ if ( strlen( $color_hex ) <= 6 && ctype_xdigit( $color_hex ) ) {
496
+ $clean_options[ $color_option ] = strtolower( $color_hex );
497
+ }
498
+ }
499
+ foreach ( array( 'dnt', 'csp' ) as $bool_option ) {
500
+ if ( isset( $options[ $bool_option ] ) && $options[ $bool_option ] ) {
501
+ $clean_options[ $bool_option ] = true;
502
+ }
503
+ }
504
+
505
+ return $clean_options;
506
+ }
507
+ }
src/Twitter/WordPress/Admin/Settings/TweetButton.php ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Admin\Settings;
27
+
28
+ /**
29
+ * Store a Twitter username for attribution of site content
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class TweetButton
34
+ {
35
+ /**
36
+ * Define our option name
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @type string
41
+ */
42
+ const OPTION_NAME = 'twitter_share';
43
+
44
+ /**
45
+ * Priority of the section affecting insertion order on the single settings page
46
+ *
47
+ * Display after site attribution section
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @type int
52
+ */
53
+ const SECTION_PRIORITY = 6;
54
+
55
+ /**
56
+ * The hook suffix of the parent settings page
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @type string
61
+ */
62
+ protected $hook_suffix;
63
+
64
+ /**
65
+ * Reference the feature by name
66
+ *
67
+ * @since 1.0.0
68
+ *
69
+ * @return string translated feature name
70
+ */
71
+ public static function featureName()
72
+ {
73
+ return __( 'Tweet Button', 'twitter' );
74
+ }
75
+
76
+ /**
77
+ * Add Tweet button content wrapper option and settings section to an existing settings page
78
+ *
79
+ * @since 1.0.0
80
+ *
81
+ * @param string $hook_suffix hook suffix of an existing settings page
82
+ *
83
+ * @return bool site attribution settings loaded
84
+ */
85
+ public static function addToSettingsPage( $hook_suffix )
86
+ {
87
+ if ( ! ( is_string( $hook_suffix ) && $hook_suffix ) ) {
88
+ return false;
89
+ }
90
+
91
+ $settings = new static();
92
+ $settings->hook_suffix = $hook_suffix;
93
+
94
+ register_setting( $hook_suffix, self::OPTION_NAME, array( __CLASS__, 'sanitizeOption' ) );
95
+ add_action(
96
+ 'load-' . $hook_suffix,
97
+ array( &$settings, 'onload' ),
98
+ 5, // after theme settings
99
+ 0
100
+ );
101
+
102
+ return true;
103
+ }
104
+
105
+ /**
106
+ * Set up settings section
107
+ *
108
+ * @since 1.0.0
109
+ *
110
+ * @return void
111
+ */
112
+ public function onload()
113
+ {
114
+ if ( ! isset( $this->hook_suffix ) ) {
115
+ return;
116
+ }
117
+
118
+ $options = get_option( self::OPTION_NAME );
119
+ if ( ! is_array( $options ) ) {
120
+ $options = array();
121
+ }
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
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Define Tweet button section and the fields within
134
+ *
135
+ * @since 1.0.0
136
+ *
137
+ * @return void
138
+ */
139
+ public function defineSection()
140
+ {
141
+ if ( ! isset( $this->hook_suffix ) ) {
142
+ return;
143
+ }
144
+
145
+ $section = 'tweet-button';
146
+ add_settings_section(
147
+ $section,
148
+ static::featureName(),
149
+ array( __CLASS__, 'sectionHeader' ),
150
+ $this->hook_suffix
151
+ );
152
+
153
+ add_settings_field(
154
+ 'tweet-button-position',
155
+ _x( 'Position', 'Display of content relative to other content. above, below, left, right', 'twitter' ),
156
+ array( &$this, 'displayPosition' ),
157
+ $this->hook_suffix,
158
+ $section
159
+ );
160
+ add_settings_field(
161
+ 'tweet-button-count',
162
+ _x( 'Count', 'Numeric sum: number of Tweets referencing this URL', 'twitter' ),
163
+ array( &$this, 'displayCount' ),
164
+ $this->hook_suffix,
165
+ $section
166
+ );
167
+ add_settings_field(
168
+ 'tweet-button-size',
169
+ __( 'Size' ),
170
+ array( &$this, 'displaySize' ),
171
+ $this->hook_suffix,
172
+ $section
173
+ );
174
+ }
175
+
176
+ /**
177
+ * Introduce the settings section
178
+ *
179
+ * @since 1.0.0
180
+ *
181
+ * @return void
182
+ */
183
+ public static function sectionHeader()
184
+ {
185
+ echo '<p>';
186
+ echo esc_html( __( 'Add a Tweet Button to every public post.', 'twitter' ) );
187
+ echo '</p>';
188
+ }
189
+
190
+ /**
191
+ * Get option values and labels for dropdown display
192
+ *
193
+ * @since 1.0.0
194
+ *
195
+ * @return array option values and labels {
196
+ * @type string option value
197
+ * @type string translated option label
198
+ * }
199
+ */
200
+ public static function getPositionOptions()
201
+ {
202
+ return array(
203
+ '' => ' ',
204
+ 'before' => _x( 'before', 'before another piece of content', 'twitter' ),
205
+ 'after' => _x( 'after', 'after another piece of content', 'twitter' ),
206
+ 'both' => _x( 'before & after', 'before and after another piece of content', 'twitter' )
207
+ );
208
+ }
209
+
210
+ /**
211
+ * Choose to display a Tweet button before, after, or before & after every public post
212
+ *
213
+ * @since 1.0.0
214
+ *
215
+ * @return void
216
+ */
217
+ public function displayPosition()
218
+ {
219
+ $key = 'position';
220
+
221
+ $position_options = static::getPositionOptions();
222
+ $existing_value = '';
223
+ if ( isset( $this->existing_options ) && isset( $this->existing_options[ $key ] ) && $this->existing_options[ $key ] && isset( $position_options[ $this->existing_options[ $key ] ] ) ) {
224
+ $existing_value = $this->existing_options[ $key ];
225
+ }
226
+
227
+ $select = '<select name="' . esc_attr( static::OPTION_NAME . '[' . $key . ']' ) . '">';
228
+ foreach ( $position_options as $option => $label ) {
229
+ $select .= '<option';
230
+ if ( $option ) {
231
+ $select .= ' value="' . esc_attr( $option ) . '"';
232
+ }
233
+ if ( $option === $existing_value ) {
234
+ $select .= ' selected';
235
+ if ( ! current_theme_supports( 'html5' ) ) {
236
+ $select .= '="selected"';
237
+ }
238
+ }
239
+ $select .= '>' . esc_html( $label ) . '</option>';
240
+ }
241
+ $select .= '</select>';
242
+
243
+ echo sprintf( esc_html( _x( 'Display Tweet Button %s post content', 'display Tweet Button relative to the content of an article', 'twitter' ) ), $select );
244
+ }
245
+
246
+ /**
247
+ * Choose a large button size, overriding the default
248
+ *
249
+ * @since 1.0.0
250
+ *
251
+ * @return void
252
+ */
253
+ public function displaySize()
254
+ {
255
+ $key = 'size';
256
+
257
+ echo '<label><input type="checkbox" name="' . esc_attr( static::OPTION_NAME . '[' . $key . ']' ) . '" value="large"';
258
+
259
+ if ( isset( $this->existing_options ) && isset( $this->existing_options[ $key ] ) && 'large' === $this->existing_options[ $key ] ) {
260
+ echo ' checked';
261
+ if ( ! current_theme_supports( 'html5' ) ) {
262
+ echo '="checked"';
263
+ }
264
+ }
265
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '> ' . esc_html( __( 'Large button', 'twitter' ) ) . '</label>';
266
+ }
267
+
268
+ /**
269
+ * Control display of the total number of Tweets citing the Tweet URL
270
+ *
271
+ * @since 1.0.0
272
+ *
273
+ * @return void
274
+ */
275
+ public function displayCount()
276
+ {
277
+ $key = 'count';
278
+
279
+ echo '<label><input type="checkbox" name="' . esc_attr( static::OPTION_NAME . '[' . $key . ']' ) . '" value="1"';
280
+ if ( ! ( isset( $this->existing_options ) && isset( $this->existing_options[ $key ] ) && 'none' === $this->existing_options[ $key ] ) ) {
281
+ echo ' checked';
282
+ if ( ! current_theme_supports( 'html5' ) ) {
283
+ echo '="checked"';
284
+ }
285
+ }
286
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '> ' . esc_html( __( 'Show the number of Tweets citing the post URL', 'twitter' ) ) . '</label>';
287
+ }
288
+
289
+ /**
290
+ * Sanitize posted option before saving
291
+ *
292
+ * @since 1.0.0
293
+ *
294
+ * @param array $options submitted option {
295
+ * @type string option name
296
+ * @type mixed option value
297
+ * }
298
+ *
299
+ * @return array $options cleaned option {
300
+ * @type string option name
301
+ * @type string option value
302
+ * }
303
+ */
304
+ public static function sanitizeOption( $options )
305
+ {
306
+ if ( ! is_array( $options ) ) {
307
+ return array();
308
+ }
309
+
310
+ $clean_options = array();
311
+
312
+ $key = 'position';
313
+ if ( isset( $options[ $key ] ) && $options[ $key ] ) {
314
+ $position_options = static::getPositionOptions();
315
+ if ( isset( $position_options[ $options[ $key ] ] ) ) {
316
+ $clean_options[ $key ] = $options[ $key ];
317
+ }
318
+ unset( $position_options );
319
+ }
320
+ unset($key);
321
+
322
+ if ( isset( $options['size'] ) && 'large' === $options['size'] ) {
323
+ $clean_options['size'] = 'large';
324
+ }
325
+
326
+ $key = 'count';
327
+ if ( ! ( isset( $options[ $key ] ) && 1 == $options[ $key ] ) ) {
328
+ $clean_options[ $key ] = 'none';
329
+ }
330
+ unset( $key );
331
+
332
+ return $clean_options;
333
+ }
334
+ }
src/Twitter/WordPress/Cards/Compatibility.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Compatability wrappers for popular WordPress plugins with existing Twitter Cards functionality
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Compatibility
34
+ {
35
+ /**
36
+ * Disable Twitter Cards markup output in Jetpack
37
+ *
38
+ * Filter should be hooked before plugins_loaded priority 99
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @return void
43
+ */
44
+ public static function disableJetpack()
45
+ {
46
+ add_filter( 'jetpack_disable_twitter_cards', '__return_true' );
47
+ }
48
+
49
+ /**
50
+ * Add compatability wrappers for known, popular Twitter Cards plugins which may conflict with our plugin
51
+ *
52
+ * @since 1.0.0
53
+ *
54
+ * @return void
55
+ */
56
+ public static function init()
57
+ {
58
+ static::disableJetpack();
59
+ }
60
+ }
src/Twitter/WordPress/Cards/Generator.php ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Generate Twitter Card data based on the current WordPress context
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Generator
34
+ {
35
+ /**
36
+ * Card types supported by the Twitter plugin for WordPress
37
+ *
38
+ * Maps short names to Twitter Card builder class names
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type array Twitter Card options {
43
+ * @type string Twitter Card type
44
+ * @type string Twitter Card class name
45
+ * }
46
+ */
47
+ public static $SUPPORTED_CARDS = array(
48
+ 'summary' => '\Twitter\Cards\Summary',
49
+ 'summary_large_image' => '\Twitter\Cards\SummaryLargeImage',
50
+ 'photo' => '\Twitter\Cards\Photo',
51
+ 'gallery' => '\Twitter\Cards\Gallery',
52
+ 'product' => '\Twitter\Cards\Product',
53
+ );
54
+
55
+ /**
56
+ * Is the passed card type a Twitter Card type supported by the plugin
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @param string $card_type Twitter Card type
61
+ *
62
+ * @return bool true if card type supported by plugin
63
+ */
64
+ public static function isSupportedCardType( $card_type )
65
+ {
66
+ return ( is_string( $card_type ) && $card_type && isset( static::$SUPPORTED_CARDS[ $card_type ] ) );
67
+ }
68
+
69
+ /**
70
+ * Initialize a Twitter Card object for a given card type string
71
+ *
72
+ * @since 1.0.0
73
+ *
74
+ * @param string $card_type Twitter Card global type
75
+ *
76
+ * @return \Twitter\Cards\Card|null Twitter Card object
77
+ */
78
+ public static function getCardForType( $card_type )
79
+ {
80
+ if ( static::isSupportedCardType( $card_type ) ) {
81
+ return (new static::$SUPPORTED_CARDS[ $card_type ]);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Set up a card object for the query type
87
+ *
88
+ * Allows sites to override the Twitter Card type for multiple query types through a filter
89
+ * Set up card attributes common to all queries
90
+ *
91
+ * @param string|null $query_type short name for current query type, similar to WP_Query flags
92
+ * @param int|string|null $object_id current object identifier: post ID, author ID, etc. depending on query type
93
+ * @param string $card_type default Twitter Card type for the query
94
+ *
95
+ * @return \Twitter\Cards\Card|null Twitter Card object or null if minimum requirements not met
96
+ */
97
+ public static function getCardObject( $query_type = null, $object_id = null, $card_type = 'summary' )
98
+ {
99
+ if ( ! static::isSupportedCardType( $card_type ) ) {
100
+ return;
101
+ }
102
+ if ( ! ( is_string( $query_type ) && $query_type ) ) {
103
+ $query_type = null;
104
+ }
105
+
106
+ /**
107
+ * Filter the Twitter Card template to be applied for the given query type and object
108
+ *
109
+ * @since 1.0.0
110
+ *
111
+ * @link https://dev.twitter.com/cards/types Twitter Card types
112
+ *
113
+ * @param string $card_type Twitter Card type
114
+ * @param string|null $query_type current rendering context. similar to WP_Query flags (home, archive, author, post)
115
+ * @param int|string|null $object_id current object identifier: post ID, author ID, etc. depending on query type
116
+ */
117
+ $card_type = apply_filters( 'twitter_card_type', $card_type, $query_type, $object_id );
118
+
119
+ $card = static::getCardForType( $card_type );
120
+ if ( ! ( $card && is_a( $card, '\Twitter\Cards\Card' ) ) ) {
121
+ return;
122
+ }
123
+
124
+ return static::addSiteAttribution( $card, ( $query_type === 'post' ? $object_id : null ) );
125
+ }
126
+
127
+ /**
128
+ * Add site attibution to the passed card if a Twitter username is stored for the current site
129
+ *
130
+ * @since 1.0.0
131
+ *
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 Twitter Card object
136
+ */
137
+ public static function addSiteAttribution( $card, $post_id = null )
138
+ {
139
+ if ( ! is_a( $card, '\Twitter\Cards\Card' ) ) {
140
+ return;
141
+ }
142
+ if ( ! method_exists( $card, 'setSite' ) ) {
143
+ return $card;
144
+ }
145
+
146
+ $site_username = \Twitter\WordPress\Site\Username::getSiteAttribution( $post_id );
147
+ if ( $site_username ) {
148
+ $card->setSite( \Twitter\Cards\Components\Account::fromScreenName( $site_username ) );
149
+ }
150
+
151
+ return $card;
152
+ }
153
+
154
+ /**
155
+ * Output Twitter Card markup for the current display context
156
+ *
157
+ * @since 1.0.0
158
+ *
159
+ * @return \Twitter\Cards\Card|null Twitter Card object or null if no card available or condition not met for query type
160
+ */
161
+ public static function get()
162
+ {
163
+ if ( is_home() || is_front_page() ) {
164
+ return static::buildHomepageCard();
165
+ } else if ( is_singular() ) {
166
+ return static::buildPostCard();
167
+ } else if ( is_author() ) {
168
+ return static::buildAuthorCard();
169
+ } else if ( is_archive() ) {
170
+ return static::buildArchiveCard();
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Twitter Card markup for the site homepage
176
+ *
177
+ * @since 1.0.0
178
+ *
179
+ * @return \Twitter\Cards\Card|null Twitter Card or null if minimum requirements not met
180
+ */
181
+ public static function buildHomepageCard()
182
+ {
183
+ $query_type = 'home';
184
+ $card = static::getCardObject( $query_type );
185
+ if ( ! $card ) {
186
+ return;
187
+ }
188
+
189
+ /**
190
+ * Filter the title displayed in a Twitter Card template
191
+ *
192
+ * A title is only passed through this filter when not explicitly provided by the website for display in a Twitter Card template.
193
+ * This distinction is meant to simplify possible overrides by other plugins.
194
+ * Act on the `twitter_card` filter to always override this value late in the card builder process.
195
+ *
196
+ * @since 1.0.0
197
+ *
198
+ * @param string $title default title
199
+ * @param string $query_type current context, similar to a WP_Query
200
+ * @param string $object_id object identifier for the query type, if applicable (post ID, author ID)
201
+ */
202
+ $title = apply_filters( 'twitter_card_title', get_bloginfo( 'name' ) ?: '', $query_type, null );
203
+ if ( $title ) {
204
+ $card->setTitle( \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $title ) );
205
+ }
206
+ unset( $title );
207
+
208
+ if ( method_exists( $card, 'setDescription' ) ) {
209
+ /**
210
+ * Filter the description displayed in a Twitter Card template
211
+ *
212
+ * A title is only passed through this filter when not explicitly provided by the website for display in a Twitter Card template.
213
+ * This distinction is meant to simplify possible overrides by other plugins.
214
+ * Act on the `twitter_card` filter to always override this value late in the card builder process.
215
+ *
216
+ * @since 1.0.0
217
+ *
218
+ * @param string $description default description
219
+ * @param string $query_type current context, similar to a WP_Query
220
+ * @param string $object_id object identifier for the query type, if applicable (post ID, author ID)
221
+ */
222
+ $description = apply_filters( 'twitter_card_description', get_bloginfo( 'description' ) ?: '', $query_type, null );
223
+ if ( $description ) {
224
+ $card->setDescription( \Twitter\WordPress\Cards\Sanitize::sanitizeDescription( $description ) );
225
+ }
226
+ unset( $description );
227
+ }
228
+
229
+ return $card;
230
+ }
231
+
232
+ /**
233
+ * Build a card for an author view
234
+ *
235
+ * @since 1.0.0
236
+ *
237
+ * @return \Twitter\Cards\Card|null Twitter Card object or null if minimum requirements not met
238
+ */
239
+ public static function buildAuthorCard()
240
+ {
241
+ $author = get_queried_object();
242
+ if ( ! ( $author && isset( $author->ID ) ) ) {
243
+ return;
244
+ }
245
+
246
+ $query_type = 'author';
247
+ $card = static::getCardObject( $query_type, $author->ID );
248
+ if ( ! $card ) {
249
+ return;
250
+ }
251
+
252
+ /** This filter is documented in ::buildHomepageCard */
253
+ $title = apply_filters( 'twitter_card_title', get_the_author_meta( 'display_name', $author->ID ) ?: '', $query_type, $author->ID );
254
+ if ( $title ) {
255
+ $card->setTitle( \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $title ) );
256
+ }
257
+ unset( $title );
258
+
259
+ if ( method_exists( $card, 'setDescription' ) ) {
260
+ /** This filter is documented in ::buildHomepageCard */
261
+ $description = apply_filters( 'twitter_card_description', get_the_author_meta( 'description', $author->ID ) ?: '', $query_type, $author->ID );
262
+ if ( $description ) {
263
+ $card->setDescription( \Twitter\WordPress\Cards\Sanitize::sanitizeDescription( $description ) );
264
+ }
265
+ unset( $description );
266
+ }
267
+ if ( method_exists( $card, 'setCreator' ) ) {
268
+ $author_twitter_username = \Twitter\WordPress\User\Meta::getTwitterUsername( $author->ID );
269
+ if ( $author_twitter_username ) {
270
+ $card->setCreator( \Twitter\Cards\Components\Account::fromScreenName( $author_twitter_username ) );
271
+ }
272
+ unset( $author_twitter_username );
273
+ }
274
+
275
+ return $card;
276
+ }
277
+
278
+ /**
279
+ * Build a card for an archive view
280
+ *
281
+ * @since 1.0.0
282
+ *
283
+ * @return \Twitter\Cards\Card|null Twitter Card object or null if minimum requirements not met
284
+ */
285
+ public static function buildArchiveCard()
286
+ {
287
+ $query_type = 'archive';
288
+ $card = static::getCardObject( $query_type );
289
+ if ( ! $card ) {
290
+ return;
291
+ }
292
+
293
+ // WP 4.1+ functions
294
+ if ( function_exists( 'get_the_archive_title' ) ) {
295
+ /** This filter is documented in ::buildHomepageCard */
296
+ $title = apply_filters( 'twitter_card_title', get_the_archive_title(), $query_type, null );
297
+ if ( $title ) {
298
+ $card->setTitle( \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $title ) );
299
+ }
300
+ unset( $title );
301
+ }
302
+ if ( method_exists( $card, 'setDescription' ) && function_exists( 'get_the_archive_description' ) ) {
303
+ /** This filter is documented in ::buildHomepageCard */
304
+ $description = apply_filters( 'twitter_card_description', get_the_archive_description(), $query_type, null );
305
+ if ( $description ) {
306
+ $card->setDescription( \Twitter\WordPress\Cards\Sanitize::sanitizeDescription( $description ) );
307
+ }
308
+ unset( $description );
309
+ }
310
+
311
+ return $card;
312
+ }
313
+
314
+ /**
315
+ * Build a card for a single-post view
316
+ *
317
+ * @since 1.0.0
318
+ *
319
+ * @return \Twitter\Cards\Card|null Twitter Card object or null
320
+ */
321
+ public static function buildPostCard()
322
+ {
323
+ $post = get_post();
324
+
325
+ if ( ! $post || ! isset( $post->ID ) ) {
326
+ return;
327
+ }
328
+ setup_postdata( $post );
329
+
330
+ // do not publish card markup for password-protected posts
331
+ if ( ! empty( $post->post_password ) ) {
332
+ return;
333
+ }
334
+
335
+ // only publish card markup for public posts
336
+ $post_status_object = get_post_status_object( get_post_status( $post->ID ) );
337
+ if ( ! ( $post_status_object && isset( $post_status_object->public ) && $post_status_object->public ) ) {
338
+ return;
339
+ }
340
+
341
+ // only output Twitter Card markup for public post types
342
+ // don't waste page generation time if the page is not meant to be consumed by TwitterBot
343
+ $post_type = get_post_type( $post );
344
+ if ( ! $post_type ) {
345
+ return;
346
+ }
347
+ $post_type_object = get_post_type_object( $post_type );
348
+ if ( ! ( $post_type_object && isset( $post_type_object->public ) && $post_status_object->public ) ) {
349
+ return;
350
+ }
351
+
352
+ $card_type = 'summary';
353
+ if ( has_post_format( 'image', $post->ID ) ) {
354
+ $card_type = 'photo';
355
+ } else if ( has_post_format( 'gallery', $post->ID ) ) {
356
+ $card_type = 'gallery';
357
+ }
358
+
359
+ $query_type = 'post';
360
+ $card = static::getCardObject( $query_type, $post->ID, $card_type );
361
+ if ( ! $card ) {
362
+ return;
363
+ }
364
+
365
+ $card_class = get_class( $card );
366
+ if ( ! $card_class ) {
367
+ return;
368
+ }
369
+
370
+ // get post-specific overrides
371
+ $cards_post_meta = get_post_meta(
372
+ $post->ID,
373
+ \Twitter\WordPress\Admin\Post\TwitterCard::META_KEY,
374
+ true // single
375
+ );
376
+
377
+ // all cards support title
378
+ if ( post_type_supports( $post_type, 'title' ) ) {
379
+ $title = '';
380
+ if ( isset( $cards_post_meta['title'] ) && $cards_post_meta['title'] ) {
381
+ // do not pass an explicitly defined Twitter Card title through the title filter
382
+ $title = $cards_post_meta['title'];
383
+ } else {
384
+ /** This filter is documented in ::buildHomepageCard */
385
+ $title = apply_filters( 'twitter_card_title', get_the_title( $post->ID ), $query_type, $post->ID );
386
+ }
387
+ if ( $title ) {
388
+ $card->setTitle( \Twitter\WordPress\Cards\Sanitize::sanitizePlainTextString( $title ) );
389
+ }
390
+ unset( $title );
391
+ }
392
+
393
+ // add description if card supports
394
+ if ( method_exists( $card, 'setDescription' ) && post_type_supports( $post_type, 'excerpt' ) ) {
395
+ $description = '';
396
+ if ( isset( $cards_post_meta['description'] ) ) {
397
+ // do not pass an explicitly defined Twitter Card description through the description filter
398
+ $description = $cards_post_meta['description'];
399
+ } else if ( ! empty( $post->post_excerpt ) ) {
400
+ /** This filter is documented in wp-includes/post-template.php */
401
+ $description = apply_filters( 'get_the_excerpt', $post->post_excerpt );
402
+ /** This filter is documented in ::buildHomepageCard */
403
+ $description = apply_filters( 'twitter_card_description', $description, $query_type, $post->ID );
404
+ } else {
405
+ /** This filter is documented in ::buildHomepageCard */
406
+ $description = apply_filters( 'twitter_card_description', $post->post_content, $query_type, $post->ID );
407
+ }
408
+
409
+ $description = \Twitter\WordPress\Cards\Sanitize::sanitizeDescription( $description );
410
+ if ( $description ) {
411
+ $card->setDescription( $description );
412
+ }
413
+ unset( $description );
414
+ }
415
+
416
+ if ( defined( $card_class . '::MIN_IMAGE_WIDTH' ) && defined( $card_class . '::MIN_IMAGE_HEIGHT' ) ) {
417
+ if ( method_exists( $card, 'setImage' ) ) {
418
+ // single image card type
419
+
420
+ $cards_image_handler = new \Twitter\WordPress\Cards\ImageHandler();
421
+ $cards_image_handler->setLimit( 1 );
422
+ $cards_image_handler->setMinWidth( $card::MIN_IMAGE_WIDTH );
423
+ $cards_image_handler->setMinHeight( $card::MIN_IMAGE_HEIGHT );
424
+
425
+ // discover images associated with the post
426
+ $cards_image_handler->addPostImages( $post );
427
+
428
+ $images = $cards_image_handler->getTwitterCardImages();
429
+ if ( ! empty( $images ) ) {
430
+ $card->setImage( array_shift( $images ) );
431
+ }
432
+ unset( $images );
433
+
434
+ unset( $cards_image_handler );
435
+ } else if ( defined( $card_class . '::MAX_IMAGES' ) && method_exists( $card, 'addImage' ) ) {
436
+ // multiple image card type
437
+
438
+ $cards_image_handler = new \Twitter\WordPress\Cards\ImageHandler();
439
+ $cards_image_handler->setLimit( $card::MAX_IMAGES );
440
+ $cards_image_handler->setMinWidth( $card::MIN_IMAGE_WIDTH );
441
+ $cards_image_handler->setMinHeight( $card::MIN_IMAGE_HEIGHT );
442
+
443
+ // discover images associated with the post
444
+ $cards_image_handler->addPostImages( $post );
445
+ $images = $cards_image_handler->getTwitterCardImages();
446
+ if ( ! empty( $images ) ) {
447
+ array_walk( $images, array( $card, 'addImage' ) );
448
+ }
449
+ unset( $images );
450
+
451
+ unset( $cards_image_handler );
452
+ }
453
+ }
454
+
455
+ if ( post_type_supports( $post_type, 'author' ) && isset( $post->post_author ) && method_exists( $card, 'setCreator' ) ) {
456
+ $author_twitter_username = \Twitter\WordPress\User\Meta::getTwitterUsername( $post->post_author );
457
+ if ( $author_twitter_username ) {
458
+ $card->setCreator( \Twitter\Cards\Components\Account::fromScreenName( $author_twitter_username ) );
459
+ }
460
+ unset( $author_twitter_username );
461
+ }
462
+
463
+ return $card;
464
+ }
465
+ }
src/Twitter/WordPress/Cards/ImageHandler.php ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Discover images in a WordPress post eligible for inclusion as a Twitter Card image
30
+ *
31
+ * Rejects images under the minimum dimensions specified for the card type. Rejection allows the search for a suitable image to continue, up to the total number of images used in the card template.
32
+ *
33
+ * @since 1.0.0
34
+ */
35
+ class ImageHandler
36
+ {
37
+
38
+ /**
39
+ * 1 MB in decimal units
40
+ *
41
+ * @since 1.0.0
42
+ *
43
+ * @var int
44
+ */
45
+ const MAX_FILESIZE = 1000000;
46
+
47
+ /**
48
+ * Maximum number of images supported by the Twitter Cards template
49
+ *
50
+ * @since 1.0.0
51
+ *
52
+ * @var int
53
+ */
54
+ protected $limit = 1;
55
+
56
+ /**
57
+ * Minimum required width of a Twitter Card image
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @var int
62
+ */
63
+ protected $min_width = 120;
64
+
65
+ /**
66
+ * Minimum required height of a Twitter Card image
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * @var int
71
+ */
72
+ protected $min_height = 120;
73
+
74
+ /**
75
+ * Store discovered images for the post
76
+ *
77
+ * @since 1.0.0
78
+ *
79
+ * @var array images {
80
+ * @type string image URL
81
+ * @type \Twitter\Cards\Components\Image
82
+ * }
83
+ */
84
+ protected $images = array();
85
+
86
+ /**
87
+ * Number of images currently stored in $images
88
+ *
89
+ * @since 1.0.0
90
+ *
91
+ * @var int number of stored images
92
+ */
93
+ protected $images_count = 0;
94
+
95
+ /**
96
+ * Maximum number of images displayed in the Twitter Card template
97
+ *
98
+ * @since 1.0.0
99
+ *
100
+ * @param int $limit maximum number of images displayed in the Twitter Card template
101
+ *
102
+ * @return \Twitter\WordPress\Cards\ImageHandler support chaining
103
+ */
104
+ public function setLimit( $limit )
105
+ {
106
+ if ( is_int( $limit ) && $limit > 0 ) {
107
+ // set max range to extended entities limit
108
+ if ( $limit > 25 ) {
109
+ $limit = 25;
110
+ }
111
+
112
+ $this->limit = $limit;
113
+ }
114
+
115
+ return $this;
116
+ }
117
+
118
+ /**
119
+ * Have we stored the maximum number of images displayed in the Twitter Card template?
120
+ *
121
+ * @since 1.0.0
122
+ *
123
+ * @return bool True if no more images needed, else False
124
+ */
125
+ public function isFull()
126
+ {
127
+ return ( $this->images_count === $this->limit );
128
+ }
129
+
130
+ /**
131
+ * Set the minimum image width of the current card
132
+ *
133
+ * @since 1.0.0
134
+ *
135
+ * @param int $width minimum width of the Twitter Cards image in whole pixels
136
+ *
137
+ * @return \Twitter\WordPress\Cards\ImageHandler support chaining
138
+ */
139
+ public function setMinWidth( $width )
140
+ {
141
+ if ( is_int( $width ) && $width >= 120 ) {
142
+ $this->min_width = $width;
143
+ }
144
+ return $this;
145
+ }
146
+
147
+ /**
148
+ * Set the minimum image height of hte current card
149
+ *
150
+ * @since 1.0.0
151
+ *
152
+ * @param int $height minimum height of the Twitter Cards image in whole pixels
153
+ *
154
+ * @return \Twitter\WordPress\Cards\ImageHandler support chaining
155
+ */
156
+ public function setMinHeight( $height )
157
+ {
158
+ if ( is_int( $height ) && $height >= 120 ) {
159
+ $this->min_height = $height;
160
+ }
161
+ return $this;
162
+ }
163
+
164
+ /**
165
+ * Get a flattened array of Twitter Card images
166
+ *
167
+ * @since 1.0.0
168
+ *
169
+ * @return array Twitter Card images {
170
+ * @type \Twitter\Cards\Components\Image Twitter Card image
171
+ * }
172
+ */
173
+ public function getTwitterCardImages()
174
+ {
175
+ if ( empty( $this->images ) ) {
176
+ return array();
177
+ }
178
+
179
+ return array_values( $this->images );
180
+ }
181
+
182
+ /**
183
+ * Possibly add a new image for a given WordPress attachment ID
184
+ *
185
+ * @since 1.0.0
186
+ *
187
+ * @param int $attachment_id WordPress attachment ID
188
+ *
189
+ * @return bool was an image added to the array?
190
+ */
191
+ public function addImageByAttachmentID( $attachment_id )
192
+ {
193
+ if ( ! $attachment_id ) {
194
+ return false;
195
+ }
196
+
197
+ $image = $this->attachmentToTwitterImage( $attachment_id );
198
+ if ( ! $image ) {
199
+ return false;
200
+ }
201
+
202
+ $image_url = $image->getURL();
203
+ if ( ! $image_url || isset( $this->images[ $image_url ] ) ) {
204
+ return false;
205
+ }
206
+
207
+ $this->images[ $image_url ] = $image;
208
+ $this->images_count++;
209
+
210
+ return true;
211
+ }
212
+
213
+ /**
214
+ * Add images associated with a WP_Post up to limit.
215
+ *
216
+ * @since 1.0.0
217
+ *
218
+ * @param WP_Post $post post of interest
219
+ *
220
+ * @return void
221
+ */
222
+ public function addPostImages( $post )
223
+ {
224
+ // bad parameter
225
+ if ( ! $post || ! isset( $post->ID ) ) {
226
+ return;
227
+ }
228
+
229
+ // does current post type and the current theme support post thumbnails?
230
+ if ( post_type_supports( get_post_type( $post ), 'thumbnail' ) && function_exists( 'has_post_thumbnail' ) && has_post_thumbnail() ) {
231
+ if ( $this->addImageByAttachmentID( get_post_thumbnail_id( $post->ID ) ) ) {
232
+ if ( $this->isFull() ) {
233
+ return;
234
+ }
235
+ }
236
+ }
237
+
238
+ // image attachments
239
+ $attached_images = get_attached_media( 'image', $post );
240
+ if ( ! empty( $attached_images ) ) {
241
+ foreach ( $attached_images as $attached_image ) {
242
+ if ( ! isset( $attached_image->ID ) ) {
243
+ continue;
244
+ }
245
+
246
+ if ( ! $this->addImageByAttachmentID( $attached_image->ID ) ) {
247
+ continue;
248
+ }
249
+
250
+ // hit the limit. end the search
251
+ if ( $this->isFull() ) {
252
+ return;
253
+ }
254
+ }
255
+ }
256
+ unset( $attached_images );
257
+
258
+ // post galleries
259
+ $galleries = get_post_galleries( $post, /* html */ false );
260
+ foreach ( $galleries as $gallery ) {
261
+ if ( ! ( isset( $gallery['ids'] ) && $gallery['ids'] ) ) {
262
+ continue;
263
+ }
264
+
265
+ $gallery_ids = explode( ',', $gallery['ids'] );
266
+ foreach ( $gallery_ids as $attachment_id ) {
267
+ if ( ! $this->addImageByAttachmentID( $attachment_id ) ) {
268
+ continue;
269
+ }
270
+
271
+ // hit the limit. end the search
272
+ if ( $this->isFull() ) {
273
+ return;
274
+ }
275
+ }
276
+ unset( $gallery_ids );
277
+ }
278
+ unset( $galleries );
279
+ }
280
+
281
+ /**
282
+ * Convert a WordPress image attachment to a Twitter Card image object
283
+ *
284
+ * @since 1.0.0
285
+ *
286
+ * @param int $attachment_id WordPress attachment ID
287
+ * @param string $size desired size
288
+ *
289
+ * @return \Twitter\Cards\Components\Image|null Twitter Card image
290
+ */
291
+ public function attachmentToTwitterImage( $attachment_id, $size = 'full' )
292
+ {
293
+ if ( ! ( is_string( $size ) && $size ) ) {
294
+ $size = 'full';
295
+ }
296
+
297
+ // request large version of image if full version exceeds filesize limit
298
+ if ( 'full' === $size ) {
299
+ $attached_file = get_attached_file( $attachment_id );
300
+ if ( $attached_file && file_exists( $attached_file ) ) {
301
+ $bytes = filesize( $attached_file );
302
+ if ( $bytes && $bytes > self::MAX_FILESIZE ) {
303
+ /**
304
+ * Filter the intermediate image size to be provided for Twitter thumbnail when a full-size image exceeds Twitter's filesize limit
305
+ *
306
+ * Twitter will consume the largest available image under the filesize limit and generate thumbnails appropriate for Twitter Card display in various dimension and DPI contexts
307
+ *
308
+ * @since 1.0.0
309
+ *
310
+ * @param string $size The intermediate size. Default: large
311
+ * @param int $attachment_id Attachment identifier
312
+ */
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
+ }
322
+ unset( $attached_file );
323
+ }
324
+
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
+ }
341
+ // width and height are a resizing hint. must exist as a pair
342
+ if ( ! empty( $height ) ) {
343
+ $height = absint( $height );
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
+ }
351
+ }
352
+ }
353
+
354
+ return $image;
355
+ }
356
+ }
src/Twitter/WordPress/Cards/Sanitize.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Cards;
27
+
28
+ /**
29
+ * Clean up data from WordPress to prepare for a Twitter Card
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Sanitize
34
+ {
35
+
36
+ /**
37
+ * Remove HTML, leading and trailing whitespaces, and other unexpected qualities of a plain text string
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @param string $s string to sanitize
42
+ *
43
+ * @return string sanitized string
44
+ */
45
+ public static function sanitizePlainTextString( $s )
46
+ {
47
+ if ( ! ( is_string( $s ) && $s ) ) {
48
+ return '';
49
+ }
50
+
51
+ $s = trim( $s );
52
+ if ( $s ) {
53
+ // strip HTML
54
+ $s = wp_kses( $s, array() );
55
+ }
56
+
57
+ return $s;
58
+ }
59
+
60
+ /**
61
+ * Clean up a string before including as a Twitter Card description
62
+ *
63
+ * @since 1.0.0
64
+ *
65
+ * @param string $description short description of the page
66
+ *
67
+ * @return string sanitized short description of the page
68
+ */
69
+ public static function sanitizeDescription( $description )
70
+ {
71
+ // strip HTML
72
+ $description = static::sanitizePlainTextString( $description );
73
+ if ( ! $description ) {
74
+ return '';
75
+ }
76
+
77
+ // strip shortcodes
78
+ $description = strip_shortcodes( $description );
79
+ if ( ! $description ) {
80
+ return '';
81
+ }
82
+
83
+ // strip URLs on their own line (possible oEmbeds)
84
+ // @see \WP_Embed::autoembed
85
+ $description = preg_replace( '|^(\s*)(https?://[^\s"]+)(\s*)$|im', '', $description );
86
+ if ( ! $description ) {
87
+ return '';
88
+ }
89
+
90
+ $description = wp_trim_words(
91
+ $description,
92
+ /**
93
+ * Filter excerpt length, measured in number of words, used by a Twitter Card description
94
+ *
95
+ * Override the default excerpt length used by the site for a Twitter-specific context
96
+ * Twitter will truncate text at 200 characters
97
+ *
98
+ * @since 1.0.0
99
+ *
100
+ * @see wp_trim_excerpt()
101
+ *
102
+ * @param int $num_words The number of words in an excerpt
103
+ */
104
+ apply_filters(
105
+ 'twitter_excerpt_length',
106
+ /** This filter is documented in wp-includes/formatting.php */
107
+ apply_filters( 'excerpt_length', 55 )
108
+ ),
109
+ /**
110
+ * Filter the string used after a trimmed excerpt for Twitter Card description text if text is longer than the specified word count
111
+ *
112
+ * @since 1.0.0
113
+ *
114
+ * @see wp_trim_words()
115
+ *
116
+ * @param string $more_string the string shown at the end of generated excerpt text
117
+ */
118
+ apply_filters(
119
+ 'twitter_excerpt_more',
120
+ __( '&hellip;' )
121
+ )
122
+ );
123
+ if ( ! $description ) {
124
+ return '';
125
+ }
126
+ return $description;
127
+ }
128
+ }
src/Twitter/WordPress/Content/TweetButton.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Content;
27
+
28
+ /**
29
+ * Add a Tweet button to post content
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class TweetButton
34
+ {
35
+
36
+ /**
37
+ * Get the stored site option for Tweet button content
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @return array stored options {
42
+ * @type string option name
43
+ * @type string option value
44
+ * }
45
+ */
46
+ protected static function getOption()
47
+ {
48
+ return get_option( \Twitter\WordPress\Admin\Settings\TweetButton::OPTION_NAME, array() );
49
+ }
50
+
51
+ /**
52
+ * Filter the_content, possibly adding Tweet button(s)
53
+ *
54
+ * @since 1.0.0
55
+ *
56
+ * @param string $content content of the current post
57
+ *
58
+ * @return string $content content of the current post, possibly with Tweet button markup added
59
+ */
60
+ public static function contentFilter( $content )
61
+ {
62
+ $options = static::getOption();
63
+ if ( isset( $options['position'] ) ) {
64
+ $position = $options['position'];
65
+ unset( $options['position'] );
66
+
67
+ $tweet_button = \Twitter\WordPress\Shortcodes\Share::shortcodeHandler( $options );
68
+ if ( $tweet_button ) {
69
+ // wrap in newlines to preserve content scanners looking for adjacent content on its own line
70
+ $tweet_button = "\n" . $tweet_button . "\n";
71
+ if ( 'before' === $position ) {
72
+ return $tweet_button . $content;
73
+ } else if ( 'after' === $position ) {
74
+ return $content . $tweet_button;
75
+ } else if ( 'both' === $position ) {
76
+ return $tweet_button . $content . $tweet_button;
77
+ }
78
+ }
79
+ }
80
+
81
+ return $content;
82
+ }
83
+ }
src/Twitter/WordPress/Head/AuthorshipLink.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Head;
27
+
28
+ /**
29
+ * Identify the Twitter profile of a site using XFN relationship tokens
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class AuthorshipLink
34
+ {
35
+ /**
36
+ * Link to a site's Twitter profile using rel me
37
+ *
38
+ * Adds via attribution to Twitter widgets on the page
39
+ *
40
+ * @link http://microformats.org/wiki/rel-me XFN rel me
41
+ *
42
+ * @return void
43
+ */
44
+ public static function relMe()
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
+ }
src/Twitter/WordPress/Head/CardsMetaElements.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Head;
27
+
28
+ /**
29
+ * Generate and output Twitter Cards meta elements
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class CardsMetaElements
34
+ {
35
+
36
+ /**
37
+ * Convert a Twitter card into <meta name="" content=""> pairs
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @return string HTML <meta> elements
42
+ */
43
+ public static function buildMetaElements()
44
+ {
45
+ $card = \Twitter\WordPress\Cards\Generator::get();
46
+ if ( ! $card ) {
47
+ return '';
48
+ }
49
+
50
+ /**
51
+ * Filter associative array of Twitter Card values
52
+ *
53
+ * Resulting array will be output to the page as <meta> elements
54
+ * All keys receive the "twitter:" prefix inside a name attribute before output
55
+ * Structured values are passed as an array. example: 'image' => array( 'src' => 'http://example.com/i.jpg', 'width' => 640 )
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @param array $card_properties associative array of Twitter Card values {
60
+ * @type string card property name
61
+ * @type string|int|array property value
62
+ * }
63
+ */
64
+ $card_properties = apply_filters( 'twitter_card', $card->toArray() );
65
+ if ( ! is_array( $card_properties ) || empty( $card_properties ) ) {
66
+ return '';
67
+ }
68
+
69
+ $html = '';
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( $name . ':' . $structured_name, $structured_value );
74
+ }
75
+ } else {
76
+ $html .= \Twitter\WordPress\Head\MetaElement::fromNameContentPair( $name, $content );
77
+ }
78
+ }
79
+ return $html;
80
+ }
81
+
82
+ /**
83
+ * Output a HTML containing all card elements for the current context
84
+ *
85
+ * @since 1.0.0
86
+ *
87
+ * @return void
88
+ */
89
+ public static function outputMetaElements()
90
+ {
91
+ echo "\n" . static::buildMetaElements() . "\n";
92
+ }
93
+ }
src/Twitter/WordPress/Head/MetaElement.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Head;
27
+
28
+ /**
29
+ * Build a HTML meta element
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @link http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#the-meta-element meta element
34
+ */
35
+ class MetaElement
36
+ {
37
+ /**
38
+ * Meta element name prefix
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const PREFIX = 'twitter:';
45
+
46
+ /**
47
+ * Build a HTML meta element from name and content values
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @param string $name name attribute value
52
+ * @param mixed $content content attribute value. will be converted to string for output
53
+ *
54
+ * @return string HTML meta element or empty string if name and content attribute values not provided
55
+ */
56
+ public static function fromNameContentPair( $name, $content )
57
+ {
58
+ if ( ! ( $name && $content ) ) {
59
+ return '';
60
+ }
61
+
62
+ return '<meta name="' . esc_attr( self::PREFIX . $name ) . '" content="' . esc_attr( (string) $content ) . '"' . \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement() . '>';
63
+ }
64
+ }
src/Twitter/WordPress/Head/WidgetsMetaElements.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Head;
27
+
28
+ /**
29
+ * Output Twitter for Websites <meta> elements
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class WidgetsMetaElements
34
+ {
35
+ /**
36
+ * Get any stored widget theme options
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @return array|bool array of set options or false if no option exists
41
+ */
42
+ public static function getWidgetOptions()
43
+ {
44
+ return get_option( \Twitter\WordPress\Admin\Settings\Theme::OPTION_NAME );
45
+ }
46
+
47
+ /**
48
+ * Opt-out of using visits to this website to influence Twitter tailored content and other suggestions for Twitter users
49
+ *
50
+ * Applies when Twitter's widgets.js appears on the page to enhance a button or widget
51
+ *
52
+ * @since 1.0.0
53
+ *
54
+ * @link https://dev.twitter.com/web/overview/privacy Twitter for Websites privacy
55
+ *
56
+ * @return bool opt out of Twitter tailored audiences
57
+ */
58
+ public static function optOutOfTwitterTailoredAudiences()
59
+ {
60
+ /**
61
+ * Do not use website visits to tailor content and suggestions for the Twitter audience
62
+ *
63
+ * This setting does not apply to Twitter's advertising tracker, opt.js, which you may choose to include on the page to build a target audience or track conversions.
64
+ *
65
+ * @since 1.0.0
66
+ *
67
+ * @link https://dev.twitter.com/web/overview/privacy Twitter for Websites privacy
68
+ *
69
+ * @param bool $override opt out of Twitter tailoring
70
+ */
71
+ return apply_filters( 'twitter_dnt', false );
72
+ }
73
+
74
+ /**
75
+ * Convert stored option associative array into a new associative array of meta element name-content pairs for consumption by Twitter's widget JS
76
+ *
77
+ * @since 1.0.0
78
+ *
79
+ * @param array $options widget options stored by the site
80
+ *
81
+ * @return array associative array
82
+ */
83
+ public static function widgetOptionsToMetaElementPairs( array $options )
84
+ {
85
+ if ( empty( $options ) ) {
86
+ return array();
87
+ }
88
+
89
+ $widgets_prefix = 'widgets:';
90
+
91
+ $meta = array();
92
+
93
+ // Suppress Content Security Policy warning
94
+ if ( isset( $options['csp'] ) ) {
95
+ $meta[ $widgets_prefix . 'csp' ] = 'on';
96
+ }
97
+
98
+ // dark theme
99
+ if ( isset( $options['theme'] ) && 'dark' === $options['theme'] ) {
100
+ $meta[ $widgets_prefix . 'theme' ] = 'dark';
101
+ }
102
+
103
+ // colors
104
+ foreach ( array( 'link-color', 'border-color' ) as $color_option ) {
105
+ if ( isset( $options[ $color_option ] ) && $options[ $color_option ] ) {
106
+ $meta[ $widgets_prefix . $color_option ] = '#' . $options[ $color_option ];
107
+ }
108
+ }
109
+
110
+ return $meta;
111
+ }
112
+
113
+ /**
114
+ * Build HTML meta elements to be included as children of <head>
115
+ *
116
+ * @since 1.0.0
117
+ *
118
+ * @return string HTML meta elements
119
+ */
120
+ public static function buildMetaElements()
121
+ {
122
+ $meta = array();
123
+
124
+ $widget_options = static::getWidgetOptions();
125
+ if ( ! empty( $widget_options ) ) {
126
+ $meta = static::widgetOptionsToMetaElementPairs( $widget_options );
127
+ }
128
+ unset( $widget_options );
129
+
130
+ // opt out of tailored audiences
131
+ if ( static::optOutOfTwitterTailoredAudiences() ) {
132
+ $meta['dnt'] = 'on';
133
+ }
134
+
135
+ // powered by Twitter plugin for WordPress
136
+ // please leave for stats
137
+ $meta['partner'] = 'tfwp';
138
+
139
+ if ( empty( $meta ) ) {
140
+ return '';
141
+ }
142
+
143
+ $html = '';
144
+ foreach ( $meta as $name => $content ) {
145
+ $html .= \Twitter\WordPress\Head\MetaElement::fromNameContentPair( $name, $content );
146
+ }
147
+ return $html;
148
+ }
149
+
150
+ /**
151
+ * Output a HTML string if wiidget theme or other preferences exist
152
+ *
153
+ * @since 1.0.0
154
+ *
155
+ * @return void
156
+ */
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
+ }
src/Twitter/WordPress/Helpers/HTMLBuilder.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Helpers;
27
+
28
+ /**
29
+ * Help build HTML strings
30
+ *
31
+ * Extensible class meant to be extended for use in a framework's HTML builders
32
+ *
33
+ * @since 1.0.0
34
+ */
35
+ class HTMLBuilder extends \Twitter\Helpers\HTMLBuilder
36
+ {
37
+ /**
38
+ * Escape HTML class names in the WordPress way
39
+ *
40
+ * @since 1.0.0
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
+ {
48
+ return sanitize_html_class( $class );
49
+ }
50
+
51
+ /**
52
+ * Escape an element's inner text
53
+ *
54
+ * @since 1.0.0
55
+ *
56
+ * @param string $inner_text inner text of a DOM element
57
+ *
58
+ * @return string escaped string or empty string if passed string failed to parse
59
+ */
60
+ public static function escapeInnerText( $inner_text )
61
+ {
62
+ return esc_html( $inner_text );
63
+ }
64
+
65
+ /**
66
+ * Escape an element attribute value
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * @param string $value element attribute value
71
+ *
72
+ * @return string escaped string or empty string if passed string failed to parse
73
+ */
74
+ public static function escapeAttributeValue( $value )
75
+ {
76
+ return esc_attr( $value );
77
+ }
78
+
79
+ /**
80
+ * Escape an element attribute value
81
+ *
82
+ * @since 1.0.0
83
+ *
84
+ * @param string $url web URL
85
+ *
86
+ * @return string escaped string or empty string if passed string failed to parse
87
+ */
88
+ public static function escapeURL( $url )
89
+ {
90
+ return esc_url( $url, array( 'http', 'https' ) );
91
+ }
92
+
93
+ /**
94
+ * Close a void HTML element in a HTML or xHTML (default)
95
+ *
96
+ * @since 1.0.0
97
+ *
98
+ * @return string empty string if known HTML, else space and slash
99
+ */
100
+ public static function closeVoidHTMLElement()
101
+ {
102
+ return (current_theme_supports( 'html5' ) ? '' : ' /');
103
+ }
104
+ }
src/Twitter/WordPress/Helpers/TwitterAPI.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Helpers;
27
+
28
+ /**
29
+ * Request information from the Twitter API using OAuth
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class TwitterAPI
34
+ {
35
+
36
+ /**
37
+ * Twitter API FQDN
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @type string
42
+ */
43
+ const HOST = 'api.twitter.com';
44
+
45
+ /**
46
+ * Response formats supported by the Twitter API
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @type array supported formats {
51
+ * @type string filename extension
52
+ * @type string media type
53
+ * }
54
+ */
55
+ public static $RESPONSE_FORMATS = array( 'json' => 'application/json', 'xml' => 'application/xml' );
56
+
57
+ /**
58
+ * Build a Twitter REST API URL
59
+ *
60
+ * @since 1.0.0
61
+ *
62
+ * @param string $relative_path API path
63
+ * @param array $query_parameters query parameters to append to the URL
64
+ * @param string $response_format requested response format
65
+ *
66
+ * @return string absolute API URL or empty string if invalid relative_path passed
67
+ */
68
+ public static function getAPIURL( $relative_path, $query_parameters = null, $response_format = 'json' )
69
+ {
70
+ if ( ! is_string( $relative_path ) ) {
71
+ return '';
72
+ }
73
+ $relative_path = trim( trim( $relative_path, '/' ) );
74
+ if ( ! $relative_path ) {
75
+ return '';
76
+ }
77
+
78
+ if ( ! ( is_string( $response_format ) && $response_format && isset( static::$RESPONSE_FORMATS[ $response_format ] ) ) ) {
79
+ $response_format = '';
80
+ }
81
+
82
+ $api_version = '1.1';
83
+ if ( 'statuses/oembed' === $relative_path ) {
84
+ $api_version = '1';
85
+ }
86
+
87
+ $url = 'https://' . implode( '/', array( static::HOST, $api_version, $relative_path ) );
88
+ if ( $response_format ) {
89
+ $url .= '.' . $response_format;
90
+ }
91
+ if ( is_array( $query_parameters ) && ! empty( $query_parameters ) ) {
92
+ $url .= '?' . http_build_query( $query_parameters, '', '&' );
93
+ }
94
+
95
+ return $url;
96
+ }
97
+
98
+ /**
99
+ * Build a User-Agent string for use in a HTTP request to Twitter REST API servers
100
+ *
101
+ * @since 1.0.0
102
+ *
103
+ * @return string User-Agent
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
+ /**
113
+ * Request JSON data from the Twitter API. JSON decode the results
114
+ *
115
+ * @since 1.0.0
116
+ *
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(
134
+ $request_url,
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 );
150
+
151
+ // account for parse failures
152
+ if ( $json_response ) {
153
+ return $json_response;
154
+ }
155
+ }
156
+ }
src/Twitter/WordPress/JavaScriptLoaders/Tracking.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\JavaScriptLoaders;
27
+
28
+ /**
29
+ * Load the remotely hosted Twitter advertising tracking JavaScript
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Tracking
34
+ {
35
+ /**
36
+ * Twitter tracking JavaScript handle
37
+ *
38
+ * Used in WordPress JavaScript queue
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const QUEUE_HANDLE = 'twitter-tracking';
45
+
46
+ /**
47
+ * Proactively resolve Twitter advertising JS FQDN asynchronously before later use
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @link http://dev.chromium.org/developers/design-documents/dns-prefetching Chromium prefetch behavior
52
+ * @link https://developer.mozilla.org/en-US/docs/Controlling_DNS_prefetching Firefox prefetch behavior
53
+ *
54
+ * @return void
55
+ */
56
+ public static function dnsPrefetch()
57
+ {
58
+ echo '<link rel="dns-prefetch" href="//platform.twitter.com"';
59
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
60
+ echo '>' . "\n";
61
+ }
62
+
63
+ /**
64
+ * Register Twitter advertising JavaScript
65
+ *
66
+ * @since 1.0.0
67
+ *
68
+ * @return void
69
+ */
70
+ public static function register()
71
+ {
72
+ wp_register_script(
73
+ self::QUEUE_HANDLE,
74
+ static::getAbsoluteURI(),
75
+ array(), // no dependencies
76
+ null, // no not add extra query parameters for cache busting
77
+ true // in footer
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Enqueue the advertising JavaScript
83
+ *
84
+ * @since 1.0.0
85
+ *
86
+ * @uses wp_enqueue_script()
87
+ *
88
+ * @return void
89
+ */
90
+ public static function enqueue()
91
+ {
92
+ wp_enqueue_script( self::QUEUE_HANDLE );
93
+ }
94
+
95
+ /**
96
+ * The absolute URI of the Twitter advertising tracker JavaScript file
97
+ *
98
+ * Prefer absolute URI over scheme-relative URI
99
+ *
100
+ * @since 1.0.0
101
+ *
102
+ * @return string absolute URI for the Twitter advertising tracker JavaScript file
103
+ */
104
+ public static function getAbsoluteURI()
105
+ {
106
+ return 'http' . ( is_ssl() ? 's' : '' ) . '://platform.twitter.com/oct.js';
107
+ }
108
+ }
src/Twitter/WordPress/JavaScriptLoaders/Widgets.php ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\JavaScriptLoaders;
27
+
28
+ /**
29
+ * Load the remotely hosted Twitter widget JavaScript
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Widgets
34
+ {
35
+ /**
36
+ * Twitter widget JavaScript handle
37
+ * Used in WordPress JavaScript queue
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @type string
42
+ */
43
+ const QUEUE_HANDLE = 'twitter-wjs';
44
+
45
+ /**
46
+ * Proactively resolve Twitter widget JS FQDN asynchronously before later use
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @link http://dev.chromium.org/developers/design-documents/dns-prefetching Chromium prefetch behavior
51
+ * @link https://developer.mozilla.org/en-US/docs/Controlling_DNS_prefetching Firefox prefetch behavior
52
+ *
53
+ * @return void
54
+ */
55
+ public static function dnsPrefetch()
56
+ {
57
+ echo '<link rel="dns-prefetch" href="//platform.twitter.com"';
58
+ echo \Twitter\WordPress\Helpers\HTMLBuilder::closeVoidHTMLElement();
59
+ echo '>' . "\n";
60
+ }
61
+
62
+ /**
63
+ * Register Twitter widget JavaScript
64
+ *
65
+ * @since 1.0.0
66
+ *
67
+ * @return void
68
+ */
69
+ public static function register()
70
+ {
71
+ global $wp_scripts;
72
+
73
+ wp_register_script(
74
+ self::QUEUE_HANDLE,
75
+ static::getAbsoluteURI(), // should be overridden during queue output by asyncScriptLoaderSrc
76
+ array(), // no dependencies
77
+ null, // no not add extra query parameters for cache busting
78
+ true // in footer
79
+ );
80
+
81
+ // initialize the twttr variable to attach ready events before JS loaded
82
+ $script = 'window.twttr=(function(w){t=w.twttr||{};t._e=[];t.ready=function(f){t._e.push(f);};return t;}(window));';
83
+ $data = $wp_scripts->get_data( self::QUEUE_HANDLE, 'data' );
84
+ if ( $data ) {
85
+ $script = $data . "\n" . $script;
86
+ }
87
+ $wp_scripts->add_data( self::QUEUE_HANDLE, 'data', $script );
88
+
89
+ // replace standard script element with async script element
90
+ add_filter( 'script_loader_src', array( __CLASS__, 'asyncScriptLoaderSrc' ), 1, 2 );
91
+ }
92
+
93
+ /**
94
+ * Enqueue the widgets JavaScript
95
+ *
96
+ * @since 1.0.0
97
+ *
98
+ * @uses wp_enqueue_script()
99
+ *
100
+ * @return void
101
+ */
102
+ public static function enqueue()
103
+ {
104
+ if ( ! wp_script_is( self::QUEUE_HANDLE, 'registered' ) ) {
105
+ static::register();
106
+ }
107
+
108
+ wp_enqueue_script( self::QUEUE_HANDLE );
109
+ }
110
+
111
+ /**
112
+ * The absolute URI of the Twitter widgets JavaScript file
113
+ *
114
+ * Prefer absolute URI over scheme-relative URI
115
+ *
116
+ * @since 1.0.0
117
+ *
118
+ * @return string absolute URI for the Twitter widgets JavaScript file
119
+ */
120
+ public static function getAbsoluteURI()
121
+ {
122
+ return 'http' . ( is_ssl() ? 's' : '' ) . '://platform.twitter.com/widgets.js';
123
+ }
124
+
125
+ /**
126
+ * Get the script element HTML markup used to load widgets.js in a browser
127
+ *
128
+ * @since 1.0.0
129
+ *
130
+ * @return string HTML <script> element
131
+ */
132
+ public static function getScriptElement()
133
+ {
134
+ // type = text/javascript to match default WP_Scripts output
135
+ // async property to unlock page load, preload scanner discoverable in modern browsers
136
+ // defer property for IE 9 and older
137
+ return '<script type="text/javascript" id="' . esc_attr( self::QUEUE_HANDLE ) . '" async defer src="' . esc_url( static::getAbsoluteURI(), array( 'http', 'https' ) ) . '" charset="utf-8"></script>' . "\n";
138
+ }
139
+
140
+ /**
141
+ * Create our own script element markup, replacing WordPress default with async loading
142
+ *
143
+ * Can be used with `script_loader_tag` filter in WordPress 4.1+
144
+ *
145
+ * @since 1.0.0
146
+ *
147
+ * @param string $tag The `<script>` tag for the enqueued script.
148
+ * @param string $handle The script's registered handle.
149
+ * @param string $src The script's source URL.
150
+ *
151
+ * @return string The `<script>` tag for the enqueued script
152
+ */
153
+ public static function scriptLoaderTag( $tag, $handle, $src = '' )
154
+ {
155
+ if ( ! ( is_string( $handle ) && $handle === static::QUEUE_HANDLE ) ) {
156
+ return $tag;
157
+ }
158
+
159
+ return static::getScriptElement();
160
+ }
161
+
162
+ /**
163
+ * Load Twitter widget JS using async deferred JavaScript properties
164
+ *
165
+ * Called from `script_loader_src` filter.
166
+ *
167
+ * @since 1.0.0
168
+ *
169
+ * @param string $src script URL
170
+ * @param string $handle WordPress registered script handle
171
+ * @global WP_Scripts $wp_scripts match concatenation preferences
172
+ *
173
+ * @return string empty string if Twitter widget JS, else give back the src variable
174
+ */
175
+ public static function asyncScriptLoaderSrc( $src, $handle )
176
+ {
177
+ global $wp_scripts;
178
+
179
+ if ( ! ( is_string( $handle ) && $handle === self::QUEUE_HANDLE ) ) {
180
+ return $src;
181
+ }
182
+
183
+ $html = static::getScriptElement();
184
+
185
+ if ( isset( $wp_scripts ) && $wp_scripts->do_concat ) {
186
+ $wp_scripts->print_html .= $html;
187
+ } else {
188
+ echo $html;
189
+ }
190
+
191
+ // empty out the src response to avoid extra <script>
192
+ return '';
193
+ }
194
+ }
src/Twitter/WordPress/Language.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress;
27
+
28
+ /**
29
+ * Match WordPress locale to a Twitter language
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Language extends \Twitter\Widgets\Language
34
+ {
35
+
36
+ /**
37
+ * Convert a WordPress locale into a Twitter-supported language code
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @return string Twitter language code or empty string if no suitable match found
42
+ */
43
+ public static function localeToTwitterLang()
44
+ {
45
+ /**
46
+ * Filter the locale used to display translated text inside Twitter widgets
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @link https://dev.twitter.com/web/overview/languages supported languages
51
+ * @see \Twitter\Widgets\Language::$SUPPORTED_LANGUAGES
52
+ *
53
+ * @param string $locale locale returned by get_locale()
54
+ */
55
+ $locale = apply_filters( 'twitter_locale', get_locale() );
56
+
57
+ if ( ! ( is_string( $locale ) && $locale ) ) {
58
+ return '';
59
+ }
60
+ $locale = strtolower( $locale );
61
+
62
+ if ( 'tl' === $locale ) {
63
+ return 'fil';
64
+ } else if ( 'ms' === $locale ) {
65
+ return 'msa';
66
+ }
67
+
68
+ // handle regional
69
+ if ( 'zh_cn' === $locale ) {
70
+ return 'zh-cn';
71
+ } else if ( 'zh_tw' === $locale ) {
72
+ return 'zh-tw';
73
+ }
74
+
75
+ if ( isset( static::$SUPPORTED_LANGUAGES[ $locale ] ) ) {
76
+ return $locale;
77
+ }
78
+
79
+ if ( strlen( $locale ) > 2 ) {
80
+ $locale = substr( $locale, 0, 2 );
81
+ if ( isset( static::$SUPPORTED_LANGUAGES[ $locale ] ) ) {
82
+ return $locale;
83
+ }
84
+ if ( 'ms' === $locale ) {
85
+ return 'msa';
86
+ }
87
+ }
88
+
89
+ return '';
90
+ }
91
+ }
src/Twitter/WordPress/PluginLoader.php ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress;
27
+
28
+ /**
29
+ * Hook the WordPress plugin into the appropriate WordPress actions and filters
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class PluginLoader
34
+ {
35
+ /**
36
+ * Uniquely identify plugin version
37
+ *
38
+ * Bust caches based on this value
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @type string
43
+ */
44
+ const VERSION = '1.0.0';
45
+
46
+ /**
47
+ * Unique domain of the plugin's translated text
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @type string
52
+ */
53
+ const TEXT_DOMAIN = 'twitter';
54
+
55
+ /**
56
+ * Bind to hooks and filters
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @return void
61
+ */
62
+ public static function init()
63
+ {
64
+ // load translated text
65
+ add_action( 'init', array( __CLASS__, 'loadTranslatedText' ) );
66
+
67
+ // compatibility wrappers to coexist with other popular plugins
68
+ add_action( 'plugins_loaded', array( __CLASS__, 'compatibility' ) );
69
+
70
+ // make widgets available on front and back end
71
+ add_action( 'widgets_init', array( __CLASS__, 'widgetsInit' ) );
72
+
73
+ // register Twitter JavaScript to eligible for later enqueueing
74
+ add_action( 'wp_enqueue_scripts', array( __CLASS__, 'registerScripts' ), 1, 0 );
75
+
76
+ if ( is_admin() ) {
77
+ // admin-specific functionality
78
+ add_action( 'init', array( __CLASS__, 'adminInit' ) );
79
+ } else {
80
+ // hooks to be executed on general execution of WordPress such as public pageviews
81
+ static::registerShortcodeHandlers();
82
+ add_action( 'init', array( __CLASS__, 'publicInit' ) );
83
+ add_action( 'wp_head', array( __CLASS__, 'wpHead' ), 1, 0 );
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Full path to the directory containing the Twitter for WordPress plugin files
89
+ *
90
+ * @since 1.0.0
91
+ *
92
+ * @return string full directory path
93
+ */
94
+ public static function getPluginDirectory()
95
+ {
96
+ return dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) . '/';
97
+ }
98
+
99
+ /**
100
+ * Full path to the main file of the Twitter for WordPress plugin
101
+ *
102
+ * @since 1.0.0
103
+ *
104
+ * @return string full path to file
105
+ */
106
+ public static function getPluginMainFile()
107
+ {
108
+ return static::getPluginDirectory() . 'twitter.php';
109
+ }
110
+
111
+ /**
112
+ * Load translated strings for the current locale, if a translation exists
113
+ *
114
+ * @since 1.0.0
115
+ *
116
+ * @return void
117
+ */
118
+ public static function loadTranslatedText()
119
+ {
120
+ load_plugin_textdomain(
121
+ static::TEXT_DOMAIN,
122
+ false, // deprecated parameter as of WP 2.7
123
+ dirname( plugin_basename( static::getPluginMainFile() ) ) . '/languages/' // directory location of MO files
124
+ );
125
+ }
126
+
127
+ /**
128
+ * Register widgets
129
+ *
130
+ * @since 1.0.0
131
+ *
132
+ * @return void
133
+ */
134
+ public static function widgetsInit()
135
+ {
136
+ register_widget( '\Twitter\WordPress\Widgets\Follow' );
137
+ }
138
+
139
+ /**
140
+ * Hook into actions and filters specific to a WordPress administrative view
141
+ *
142
+ * @since 1.0.0
143
+ *
144
+ * @return void
145
+ */
146
+ public static function adminInit()
147
+ {
148
+ // Twitter settings menu
149
+ \Twitter\WordPress\Admin\Settings\Loader::init();
150
+
151
+ // Edit post meta box
152
+ add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Post\MetaBox', 'init' ) );
153
+
154
+ // User profile fields
155
+ add_action( 'admin_init', array( '\Twitter\WordPress\Admin\Profile\User', 'init' ) );
156
+ }
157
+
158
+ /**
159
+ * Register actions and filters shown in a non-admin context
160
+ *
161
+ * @since 1.0.0
162
+ *
163
+ * @return void
164
+ */
165
+ public static function publicInit()
166
+ {
167
+ // enhance web browser view only
168
+ if ( is_feed() ) {
169
+ return;
170
+ }
171
+
172
+ // load widgets JS if a Twitter widget is active
173
+ if ( is_active_widget( false, false, \Twitter\WordPress\Widgets\Follow::BASE_ID, true ) ) {
174
+ // enqueue after the script is registered in wp_enqueue_scripts action priority 1
175
+ add_action( 'wp_enqueue_scripts', array( '\Twitter\WordPress\JavaScriptLoaders\Widgets', 'enqueue' ) );
176
+ }
177
+
178
+ // do not add content filters to HTTP 404 response
179
+ if ( is_404() ) {
180
+ return;
181
+ }
182
+
183
+ $twitter_content_priority = apply_filters( 'twitter_content_filter_priority', 10 );
184
+ add_filter(
185
+ 'the_content',
186
+ array( '\Twitter\WordPress\Content\TweetButton', 'contentFilter' ),
187
+ $twitter_content_priority
188
+ );
189
+ }
190
+
191
+ /**
192
+ * Register shortcodes handlers and callbacks
193
+ *
194
+ * @since 1.0.0
195
+ *
196
+ * @return void
197
+ */
198
+ public static function registerShortcodeHandlers()
199
+ {
200
+ // features requiring HTTPS remote requests
201
+ if ( wp_http_supports( array( 'ssl' => true ) ) ) {
202
+ // Embedded Tweet
203
+ add_action(
204
+ 'plugins_loaded',
205
+ array( '\Twitter\WordPress\Shortcodes\EmbeddedTweet', 'init' ),
206
+ 5,
207
+ 0
208
+ );
209
+ // Twitter embedded videos
210
+ add_action(
211
+ 'plugins_loaded',
212
+ array( '\Twitter\WordPress\Shortcodes\EmbeddedTweetVideo', 'init' ),
213
+ 5,
214
+ 0
215
+ );
216
+ }
217
+
218
+ // Follow button
219
+ add_action(
220
+ 'plugins_loaded',
221
+ array( '\Twitter\WordPress\Shortcodes\Follow', 'init' ),
222
+ 5,
223
+ 0
224
+ );
225
+
226
+ // Tweet button
227
+ add_action(
228
+ 'plugins_loaded',
229
+ array( '\Twitter\WordPress\Shortcodes\Share', 'init' ),
230
+ 5,
231
+ 0
232
+ );
233
+
234
+ // Ad conversion and audience tracking
235
+ add_action(
236
+ 'plugins_loaded',
237
+ array( '\Twitter\WordPress\Shortcodes\Tracking', 'init' ),
238
+ 5,
239
+ 0
240
+ );
241
+ }
242
+
243
+ /**
244
+ * Attach actions to the wp_head action
245
+ *
246
+ * @since 1.0.0
247
+ *
248
+ * @return void
249
+ */
250
+ public static function wpHead()
251
+ {
252
+ // Twitter Cards markup
253
+ add_action(
254
+ 'wp_head',
255
+ array( '\Twitter\WordPress\Head\CardsMetaElements', 'outputMetaElements' ),
256
+ 99, // late priority to override if multiple values provided
257
+ 0 // expects no arguments
258
+ );
259
+
260
+ // page-level customizations referenced by Twitter JavaScript
261
+ add_action(
262
+ 'wp_head',
263
+ array( '\Twitter\WordPress\Head\WidgetsMetaElements', 'outputMetaElements' ),
264
+ 11, // priority
265
+ 0 // expects no arguments
266
+ );
267
+
268
+ if ( ! is_singular() && ! is_author() ) {
269
+ // attribute authorship to site or site section when a post author does not exist
270
+ add_action(
271
+ 'wp_head',
272
+ array( '\Twitter\WordPress\Head\AuthorshipLink', 'relMe' ),
273
+ 10, // default priority
274
+ 0 // no parameters
275
+ );
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Register JavaScript during the enqueue scripts action
281
+ *
282
+ * @since 1.0.0
283
+ *
284
+ * @return void
285
+ */
286
+ public static function registerScripts()
287
+ {
288
+ // widgets.js
289
+ \Twitter\WordPress\JavaScriptLoaders\Widgets::register();
290
+
291
+ // ad tracker
292
+ \Twitter\WordPress\JavaScriptLoaders\Tracking::register();
293
+ }
294
+
295
+ /**
296
+ * Compatibility wrappers for popular plugins
297
+ *
298
+ * @since 1.0.0
299
+ *
300
+ * @return void
301
+ */
302
+ public static function compatibility()
303
+ {
304
+ \Twitter\WordPress\Cards\Compatibility::init();
305
+ }
306
+ }
src/Twitter/WordPress/Shortcodes/EmbeddedTweet.php ADDED
@@ -0,0 +1,451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
34
+ {
35
+
36
+ /**
37
+ * Shortcode tag to be matched
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @type string
42
+ */
43
+ const SHORTCODE_TAG = 'tweet';
44
+
45
+ /**
46
+ * Relative path for the oEmbed API relative to Twitter API base path
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @type string
51
+ */
52
+ const OEMBED_API_ENDPOINT = 'statuses/oembed';
53
+
54
+ /**
55
+ * oEmbed regex registered by WordPress Core
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @type string
60
+ */
61
+ const OEMBED_CORE_REGEX = '#https?://(www\.)?twitter\.com/.+?/status(es)?/.*#i';
62
+
63
+ /**
64
+ * Regex used to match a Tweet in text
65
+ *
66
+ * More specific than WordPress Core regex
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * @type string
71
+ */
72
+ const TWEET_URL_REGEX = '#^https?://(www\.)?twitter\.com/.+?/status(es)?/([0-9]+)#i';
73
+
74
+ /**
75
+ * Accepted values for the align parameter
76
+ *
77
+ * @since 1.0.0
78
+ *
79
+ * @type array
80
+ */
81
+ public static $ALIGN_OPTIONS = array( 'left' => true, 'center' => true, 'right' => true );
82
+
83
+ /**
84
+ * Accepted shortcode attributes and their default values
85
+ *
86
+ * @since 1.0.0
87
+ *
88
+ * @type array
89
+ */
90
+ public static $SHORTCODE_DEFAULTS = array( 'id' => '', 'conversation' => true, 'cards' => true, 'align' => '' );
91
+
92
+ /**
93
+ * Attach handlers for embedded Tweets
94
+ *
95
+ * @since 1.0.0
96
+ *
97
+ * @return void
98
+ */
99
+ public static function init()
100
+ {
101
+ // register our shortcode and its handler
102
+ add_shortcode( self::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
103
+
104
+ // unhook the WordPress Core oEmbed handler
105
+ wp_oembed_remove_provider( static::OEMBED_CORE_REGEX );
106
+ // pass a Tweet detail URL through the Tweet shortcode handler
107
+ wp_embed_register_handler(
108
+ self::SHORTCODE_TAG,
109
+ static::TWEET_URL_REGEX,
110
+ array( __CLASS__, 'linkHandler' ),
111
+ 1
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Handle a URL matched by an embed handler
117
+ *
118
+ * @since 1.0.0
119
+ *
120
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
121
+ * @param array $attr Embed attributes. Not used.
122
+ * @param string $url The original URL that was matched by the regex. Not used.
123
+ * @param array $rawattr The original unmodified attributes. Not used.
124
+ *
125
+ * @return string HTML markup for the Tweet or an empty string if requirements not met
126
+ */
127
+ public static function linkHandler( $matches, $attr, $url, $rawattr )
128
+ {
129
+ if ( ! ( is_array( $matches ) && isset( $matches[3] ) && $matches[3] ) ) {
130
+ return '';
131
+ }
132
+
133
+ return static::shortcodeHandler( array( 'id' => $matches[3] ) );
134
+ }
135
+
136
+ /**
137
+ * Convert a Tweet ID in ID or URL form into a trimmed ID
138
+ *
139
+ * @since 1.0.0
140
+ *
141
+ * @param string $tweet_id Tweet identifier
142
+ *
143
+ * @return string $tweet_id Tweet identifier or empty string if minimum requirements not met
144
+ */
145
+ public static function sanitizeTweetID( $tweet_id )
146
+ {
147
+ if ( ! is_string( $tweet_id ) ) {
148
+ return '';
149
+ }
150
+
151
+ $tweet_id = trim( $tweet_id );
152
+ if ( ! $tweet_id ) {
153
+ return '';
154
+ }
155
+ $tweet_id = trim( rtrim( trim( $tweet_id ), '/' ) );
156
+ if ( ! $tweet_id ) {
157
+ return '';
158
+ }
159
+
160
+ $last_slash = strrpos( $tweet_id, '/' );
161
+ if ( false !== $last_slash ) {
162
+ $tweet_id = substr( $tweet_id, $last_slash + 1 );
163
+ }
164
+
165
+ return $tweet_id;
166
+ }
167
+
168
+ /**
169
+ * Convert shortcode parameters, attributes, and defaults into a clean set of Tweet parameters
170
+ *
171
+ * @since 1.0.0
172
+ *
173
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
174
+ * @type string|int attribute name or positional int
175
+ * @type mixed shortcode value
176
+ * }
177
+ *
178
+ * @return array cleaned up options ready for comparison {
179
+ * @type string option name
180
+ * @type string|bool option value
181
+ * }
182
+ */
183
+ public static function sanitizeShortcodeParameters( $attributes = array() )
184
+ {
185
+ if ( ! is_array( $attributes ) ) {
186
+ return array();
187
+ }
188
+
189
+ $options = array();
190
+
191
+ if ( isset( $attributes['id'] ) ) {
192
+ $tweet_id = static::sanitizeTweetID( (string) $attributes['id'] );
193
+ if ( $tweet_id ) {
194
+ $options['id'] = $tweet_id;
195
+ }
196
+ unset( $tweet_id );
197
+ } else if ( isset( $attributes[0] ) ) {
198
+ // compatibility with WordPress.com positional shortcode
199
+ $tweet_id = static::sanitizeTweetID( (string) $attributes[0] );
200
+ if ( $tweet_id ) {
201
+ $options['id'] = $tweet_id;
202
+ }
203
+ unset( $tweet_id );
204
+ }
205
+
206
+ foreach ( array( 'cards' => 'hide_media', 'conversation' => 'hide_thread' ) as $falsey_option => $alternate_naming ) {
207
+ // check for falsey values passed to shortcode
208
+ if ( isset( $attributes[ $falsey_option ] ) ) {
209
+ if ( false === $attributes[ $falsey_option ] || '0' == $attributes[ $falsey_option ] || ( is_string( $attributes[ $falsey_option ] ) && in_array( strtolower( $attributes[ $falsey_option ] ), array( 'false', 'no', 'off' ) ) ) ) {
210
+ $options[ $falsey_option ] = false;
211
+ }
212
+ } else if ( isset( $attributes[ $alternate_naming ] ) ) {
213
+ // test for an oEmbed-style parameter provided in the shortcode if the equivalent parameter was not defined
214
+ if ( true === $attributes[ $alternate_naming ] || '1' == $attributes[ $alternate_naming ] || ( is_string( $attributes[ $alternate_naming ] ) && in_array( strtolower( $attributes[ $alternate_naming ] ), array( 'true', 'yes', 'on' ) ) ) ) {
215
+ // test alternate attribute used by other shortcodes
216
+ $options[ $falsey_option ] = false;
217
+ }
218
+ }
219
+ }
220
+
221
+ if ( isset( $attributes['align'] ) && $attributes['align'] ) {
222
+ $attributes['align'] = trim( strtolower( $attributes['align'] ) );
223
+ if ( array_key_exists( $attributes['align'], static::$ALIGN_OPTIONS ) ) {
224
+ $options['align'] = $attributes['align'];
225
+ }
226
+ }
227
+
228
+ return $options;
229
+ }
230
+
231
+ /**
232
+ * Handle shortcode macro
233
+ *
234
+ * @since 1.0.0
235
+ *
236
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
237
+ * @type string|int attribute name or positional int
238
+ * @type mixed shortcode value
239
+ * }
240
+ * @param string $content content inside a shortcode macro. no effect on this shortcode
241
+ *
242
+ * @return string HTML markup. empty string if parameter requirement not met or no Tweet info found
243
+ */
244
+ public static function shortcodeHandler( $attributes, $content = '' )
245
+ {
246
+ // clean up attribute to shortcode option mappings before passing to filter
247
+ // apply the same filter as shortcode_atts
248
+ /** This filter is documented in wp-includes/shortcodes.php */
249
+ $options = apply_filters(
250
+ 'shortcode_atts_' . self::SHORTCODE_TAG,
251
+ array_merge(
252
+ static::$SHORTCODE_DEFAULTS,
253
+ static::sanitizeShortcodeParameters( (array) $attributes )
254
+ ),
255
+ static::$SHORTCODE_DEFAULTS,
256
+ $attributes
257
+ );
258
+
259
+ if ( ! $options['id'] ) {
260
+ return '';
261
+ }
262
+ $tweet_id = $options['id'];
263
+ unset( $options['id'] );
264
+
265
+ $oembed_params = static::shortcodeParamsToOEmbedParams( $tweet_id, $options );
266
+ if ( empty( $oembed_params ) ) {
267
+ return '';
268
+ }
269
+
270
+ // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
271
+ $html = trim( static::getOEmbedMarkup( $oembed_params ) );
272
+ if ( ! $html ) {
273
+ return '';
274
+ }
275
+
276
+ \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
277
+ return '<div class="twitter-tweet">' . $html . '</div>';
278
+ }
279
+
280
+ /**
281
+ * Get the base set of oEmbed params before applying shortcode customizations
282
+ *
283
+ * @since 1.0.0
284
+ *
285
+ * @param string $tweet_id Tweet identifier
286
+ *
287
+ * @return array associative array of query parameters ready for http_build_query {
288
+ * @type string query parameter name
289
+ * @type string|bool query parameter value
290
+ * }
291
+ */
292
+ public static function getBaseOEmbedParams( $tweet_id )
293
+ {
294
+ $tweet_id = trim( $tweet_id );
295
+ if ( ! $tweet_id ) {
296
+ return array();
297
+ }
298
+
299
+ // omit JavaScript. enqueue separately
300
+ $query_parameters = array(
301
+ 'id' => $tweet_id,
302
+ 'omit_script' => true,
303
+ );
304
+
305
+ // attempt to customize text for site language
306
+ $lang = \Twitter\WordPress\Language::localeToTwitterLang();
307
+ if ( $lang ) {
308
+ $query_parameters['lang'] = $lang;
309
+ }
310
+
311
+ return $query_parameters;
312
+ }
313
+
314
+ /**
315
+ * Convert shortcode parameters into query parameters supported by the Twitter oEmbed endpoint
316
+ *
317
+ * @since 1.0.0
318
+ *
319
+ * @param string $tweet_id Tweet identifier
320
+ * @param array $shortcode_options customizations specified in the shortcode
321
+ *
322
+ * @return array associative array of query parameters ready for http_build_query {
323
+ * @type string query parameter name
324
+ * @type string|bool query parameter value
325
+ * }
326
+ */
327
+ public static function shortcodeParamsToOEmbedParams( $tweet_id, $shortcode_options = array() )
328
+ {
329
+ $query_parameters = static::getBaseOEmbedParams( $tweet_id );
330
+ if ( empty( $query_parameters ) ) {
331
+ return array();
332
+ }
333
+
334
+ // test for valid align value
335
+ if ( isset( $shortcode_options['align'] ) && $shortcode_options['align'] && array_key_exists( $shortcode_options['align'], static::$ALIGN_OPTIONS ) ) {
336
+ $query_parameters['align'] = $shortcode_options['align'];
337
+ }
338
+
339
+ // oembed parameters are the opposite of widget parameters
340
+ // hide_* in oEmbed API
341
+ foreach ( array( 'cards' => 'hide_media', 'conversation' => 'hide_thread' ) as $bool_option => $oembed_parameter ) {
342
+ if ( isset( $shortcode_options[ $bool_option ] ) && false === $shortcode_options[ $bool_option ] ) {
343
+ $query_parameters[ $oembed_parameter ] = true;
344
+ }
345
+ }
346
+
347
+ return $query_parameters;
348
+ }
349
+
350
+ /**
351
+ * Generate a unique string representing oEmbed result customizations set by shortcode parameters
352
+ *
353
+ * @since 1.0.0
354
+ *
355
+ * @param array $query_parameters associative array of query parameters sent to the oEmbed endpoint {
356
+ * @type string query parameter name
357
+ * @type string|bool query parameter value
358
+ * }
359
+ *
360
+ * @return string cache key component
361
+ */
362
+ public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
363
+ {
364
+ $customizations = '';
365
+
366
+ if ( isset( $query_parameters['hide_media'] ) && $query_parameters['hide_media'] ) {
367
+ $customizations .= 'm';
368
+ }
369
+ if ( isset( $query_parameters['hide_thread'] ) && $query_parameters['hide_thread'] ) {
370
+ $customizations .= 't';
371
+ }
372
+ // left, right, center
373
+ if ( isset( $query_parameters['align'] ) && $query_parameters['align'] && array_key_exists( $query_parameters['align'], static::$ALIGN_OPTIONS ) ) {
374
+ $customizations .= substr( $query_parameters['align'], 0, 1 );
375
+ }
376
+
377
+ return $customizations;
378
+ }
379
+
380
+ /**
381
+ * Construct a cache key for the oEmbed response. Account for query parameters needing to bust cache
382
+ *
383
+ * @since 1.0.0
384
+ *
385
+ * @link https://dev.twitter.com/rest/reference/get/statuses/oembed oEmbed doc
386
+ * @param array $query_parameters oEmbed API query parameters
387
+ *
388
+ * @return string cache key
389
+ */
390
+ public static function oEmbedCacheKey( array $query_parameters )
391
+ {
392
+ if ( ! ( isset( $query_parameters['id'] ) && $query_parameters['id'] ) ) {
393
+ return '';
394
+ }
395
+
396
+ $key_pieces = array( self::SHORTCODE_TAG, $query_parameters['id'] );
397
+
398
+ // separate cache for each explicitly-defined display language
399
+ if ( isset( $query_parameters['lang'] ) && $query_parameters['lang'] ) {
400
+ $key_pieces[] = $query_parameters['lang'];
401
+ }
402
+
403
+ $customizations = static::getOEmbedCacheKeyCustomParameters( $query_parameters );
404
+ if ( $customizations ) {
405
+ $key_pieces[] = $customizations;
406
+ }
407
+
408
+ return implode( '_', $key_pieces );
409
+ }
410
+
411
+ /**
412
+ * Request and parse oEmbed markup from Twitter's API servers
413
+ *
414
+ * @since 1.0.0
415
+ *
416
+ * @param array $query_parameters request parameters
417
+ *
418
+ * @return string HTML markup returned by the oEmbed endpoint
419
+ */
420
+ public static function getOEmbedMarkup( array $query_parameters )
421
+ {
422
+ $cache_key = static::oEmbedCacheKey( $query_parameters );
423
+ if ( ! $cache_key ) {
424
+ return '';
425
+ }
426
+
427
+ // check for cached result
428
+ $html = get_transient( $cache_key );
429
+ if ( $html ) {
430
+ return $html;
431
+ }
432
+
433
+ $ttl = DAY_IN_SECONDS;
434
+
435
+ $oembed_response = \Twitter\WordPress\Helpers\TwitterAPI::getJSON( self::OEMBED_API_ENDPOINT, $query_parameters );
436
+ if ( ! $oembed_response || ! isset( $oembed_response->type ) || 'rich' !== $oembed_response->type || ! ( isset( $oembed_response->html ) && $oembed_response->html ) ) {
437
+ // do not rerequest errors with every page request
438
+ set_transient( $cache_key, ' ', $ttl );
439
+ return '';
440
+ }
441
+
442
+ $html = $oembed_response->html;
443
+
444
+ if ( isset( $oembed_response->cache_age ) ) {
445
+ $ttl = absint( $oembed_response->cache_age );
446
+ }
447
+ set_transient( $cache_key, $html, $ttl );
448
+
449
+ return $html;
450
+ }
451
+ }
src/Twitter/WordPress/Shortcodes/EmbeddedTweetVideo.php ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 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
+ */
43
+ const SHORTCODE_TAG = 'twitter_video';
44
+
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
+ */
61
+ public static function init()
62
+ {
63
+ // register our shortcode and its handler
64
+ add_shortcode( self::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
65
+ }
66
+
67
+ /**
68
+ * Convert shortcode parameters into a clean set of Twitter embedded video options parameters
69
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @param array $attributes set of shortcode attribute-value pairs matching the WordPress shortcode regex {
73
+ * @type string attribute name
74
+ * @type mixed shortcode value
75
+ * }
76
+ *
77
+ * @return array cleaned up options ready for comparison {
78
+ * @type string option name
79
+ * @type string|bool option value
80
+ * }
81
+ */
82
+ public static function sanitizeShortcodeParameters( $attributes = array() )
83
+ {
84
+ if ( ! is_array( $attributes ) ) {
85
+ return array();
86
+ }
87
+
88
+ $options = array();
89
+
90
+ // clean up Tweet ID
91
+ if ( isset( $attributes['id'] ) ) {
92
+ $tweet_id = static::sanitizeTweetID( (string) $attributes['id'] );
93
+ if ( $tweet_id ) {
94
+ $options['id'] = $tweet_id;
95
+ }
96
+ unset( $tweet_id );
97
+ }
98
+
99
+ // allow option style or oEmbed style parameter
100
+ if ( isset( $attributes['status'] ) ) {
101
+ if ( false === $attributes['status'] || '0' == $attributes['status'] || ( is_string( $attributes['status'] ) && in_array( strtolower( $attributes['status'] ), array( 'false', 'no', 'off' ) ) ) ) {
102
+ $options['status'] = false;
103
+ }
104
+ } else if ( isset( $attributes['hide_tweet'] ) ) {
105
+ if ( true === $attributes['hide_tweet'] || '1' == $attributes['hide_tweet'] || ( is_string( $attributes['hide_tweet'] ) && in_array( strtolower( $attributes['hide_tweet'] ), array( 'true', 'yes', 'on' ) ) ) ) {
106
+ $options['status'] = false;
107
+ }
108
+ }
109
+
110
+ return $options;
111
+ }
112
+
113
+ /**
114
+ * Handle shortcode macro
115
+ *
116
+ * @since 1.0.0
117
+ *
118
+ * @param array $attributes shortcode attributes
119
+ * @param string $content shortcode content. no effect
120
+ *
121
+ * @return string HTML markup
122
+ */
123
+ public static function shortcodeHandler( $attributes, $content = '' )
124
+ {
125
+ // clean up attribute to shortcode option mappings before passing to filter
126
+ // apply the same filter as shortcode_atts
127
+ /** This filter is documented in wp-includes/shortcodes.php */
128
+ $options = apply_filters(
129
+ 'shortcode_atts_' . self::SHORTCODE_TAG,
130
+ array_merge(
131
+ static::$SHORTCODE_DEFAULTS,
132
+ static::sanitizeShortcodeParameters( (array) $attributes )
133
+ ),
134
+ static::$SHORTCODE_DEFAULTS,
135
+ $attributes
136
+ );
137
+
138
+ if ( ! $options['id'] ) {
139
+ return '';
140
+ }
141
+ $tweet_id = $options['id'];
142
+ unset( $options['id'] );
143
+
144
+ $oembed_params = static::shortcodeParamsToOEmbedParams( $tweet_id, $options );
145
+ if ( empty( $oembed_params ) ) {
146
+ return '';
147
+ }
148
+
149
+ // fetch HTML markup from Twitter oEmbed endpoint for the given parameters
150
+ $html = trim( static::getOEmbedMarkup( $oembed_params ) );
151
+ if ( ! $html ) {
152
+ return '';
153
+ }
154
+
155
+ \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
156
+ return '<div class="twitter-video">' . $html . '</div>';
157
+ }
158
+
159
+ /**
160
+ * Convert shortcode parameters into query parameters supported by the Twitter oEmbed endpoint
161
+ *
162
+ * @since 1.0.0
163
+ *
164
+ * @param string $tweet_id Tweet identifier
165
+ * @param array $shortcode_options customizations specified in the shortcode
166
+ *
167
+ * @return array associative array of query parameters ready for http_build_query
168
+ */
169
+ public static function shortcodeParamsToOEmbedParams( $tweet_id, $shortcode_options = array() )
170
+ {
171
+ $query_parameters = static::getBaseOEmbedParams( $tweet_id );
172
+ if ( empty( $query_parameters ) ) {
173
+ return array();
174
+ }
175
+ $query_parameters['widget_type'] = 'video';
176
+
177
+ if ( isset( $shortcode_options['status'] ) && false === $shortcode_options['status'] ) {
178
+ $query_parameters['hide_tweet'] = true;
179
+ }
180
+
181
+ return $query_parameters;
182
+ }
183
+
184
+ /**
185
+ * Generate a unique string representing oEmbed result customizations set by shortcode parameters
186
+ *
187
+ * @since 1.0.0
188
+ *
189
+ * @param array $query_parameters associative array of query parameters sent to the oEmbed endpoint {
190
+ * @type string query parameter name
191
+ * @type string|bool query parameter value
192
+ * }
193
+ *
194
+ * @return string cache key component
195
+ */
196
+ public static function getOEmbedCacheKeyCustomParameters( array $query_parameters )
197
+ {
198
+ $customizations = '';
199
+
200
+ if ( isset( $query_parameters['hide_tweet'] ) && $query_parameters['hide_tweet'] ) {
201
+ $customizations .= 'h';
202
+ }
203
+
204
+ return $customizations;
205
+ }
206
+ }
src/Twitter/WordPress/Shortcodes/Follow.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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
+ * Accepted shortcode attributes and their default values
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @type array
51
+ */
52
+ public static $SHORTCODE_DEFAULTS = array( 'screen_name' => '', 'show_count' => true, 'show_screen_name' => true, 'size' => 'medium' );
53
+
54
+ /**
55
+ * Register shortcode macro and handler
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @return void
60
+ */
61
+ public static function init()
62
+ {
63
+ add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
64
+ }
65
+
66
+ /**
67
+ * Clean up provided shortcode values
68
+ *
69
+ * Be liberal in what we accept in shortcode syntax before constructing a Follow button
70
+ *
71
+ * @since 1.0.0
72
+ *
73
+ * @param array $attributes provided shortcode attributes {
74
+ * @type string shortcode attribute name
75
+ * @type mixed shortcode attribute value
76
+ * }
77
+ *
78
+ * @return array simplified shortcode values with defaults removed {
79
+ * @type string shortcode attribute name
80
+ * @type bool|string shortcode attribute value
81
+ * }
82
+ */
83
+ public static function sanitizeShortcodeParameters( $attributes = array() )
84
+ {
85
+ if ( ! is_array( $attributes ) ) {
86
+ return array();
87
+ }
88
+
89
+ $options = array();
90
+
91
+ if ( isset( $attributes['screen_name'] ) ) {
92
+ $screen_name = \Twitter\Helpers\Validators\ScreenName::trim( $attributes['screen_name'] );
93
+ if ( $screen_name ) {
94
+ $options['screen_name'] = $screen_name;
95
+ }
96
+ unset( $screen_name );
97
+ }
98
+
99
+ foreach ( array( 'show_count', 'show_screen_name' ) as $falsey_option ) {
100
+ // check for falsey values passed to shortcode
101
+ if ( isset( $attributes[ $falsey_option ] ) ) {
102
+ if ( false === $attributes[ $falsey_option ] || '0' == $attributes[ $falsey_option ] || ( is_string( $attributes[ $falsey_option ] ) && in_array( strtolower( $attributes[ $falsey_option ] ), array( 'false', 'no', 'off' ) ) ) ) {
103
+ $options[ $falsey_option ] = false;
104
+ }
105
+ }
106
+ }
107
+
108
+ // large is the only option
109
+ if ( isset( $attributes['size'] ) ) {
110
+ if ( is_string( $attributes['size'] ) && in_array( strtolower( $attributes['size'] ), array( 'large', 'l' ) ) ) {
111
+ $options['size'] = 'large';
112
+ }
113
+ }
114
+
115
+ return $options;
116
+ }
117
+
118
+ /**
119
+ * Get the Twitter screen name of the author of the current post
120
+ *
121
+ * @since 1.0.0
122
+ *
123
+ * @return string Twitter screen name or empty if no screen name stored
124
+ */
125
+ public static function getScreenName()
126
+ {
127
+ if ( ! in_the_loop() ) {
128
+ return '';
129
+ }
130
+
131
+ $screen_name = \Twitter\WordPress\User\Meta::getTwitterUsername( get_the_author_meta( 'ID' ) );
132
+ if ( ! $screen_name ) {
133
+ return '';
134
+ }
135
+
136
+ return $screen_name;
137
+ }
138
+
139
+ /**
140
+ * Handle shortcode macro
141
+ *
142
+ * @since 1.0.0
143
+ *
144
+ * @param array $attributes shortcode attributes
145
+ * @param string $content shortcode content. no effect
146
+ *
147
+ * @return string Follow button HTML or empty string
148
+ */
149
+ public static function shortcodeHandler( $attributes, $content = '' )
150
+ {
151
+ // clean up attribute to shortcode option mappings before passing to filter
152
+ // apply the same filter as shortcode_atts
153
+ /** This filter is documented in wp-includes/shortcodes.php */
154
+ $options = apply_filters(
155
+ 'shortcode_atts_' . self::SHORTCODE_TAG,
156
+ array_merge(
157
+ static::$SHORTCODE_DEFAULTS,
158
+ static::sanitizeShortcodeParameters( (array) $attributes )
159
+ ),
160
+ static::$SHORTCODE_DEFAULTS,
161
+ $attributes
162
+ );
163
+
164
+ $screen_name = '';
165
+ if ( isset( $options['screen_name'] ) ) {
166
+ $screen_name = $options['screen_name'];
167
+ unset( $options['screen_name'] );
168
+ }
169
+ if ( ! $screen_name ) {
170
+ $screen_name = static::getScreenName();
171
+ // follow target required
172
+ if ( ! $screen_name ) {
173
+ return '';
174
+ }
175
+ }
176
+
177
+ // update the options array with the Follow screen name
178
+ $options['screen_name'] = $screen_name;
179
+
180
+ $follow = \Twitter\Widgets\FollowButton::fromArray( $options );
181
+ if ( ! $follow ) {
182
+ return '';
183
+ }
184
+
185
+ $html = $follow->toHTML( _x( 'Follow %s', 'Follow a Twitter user', 'twitter' ), '\Twitter\WordPress\Helpers\HTMLBuilder' );
186
+ if ( ! $html ) {
187
+ return '';
188
+ }
189
+
190
+ \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
191
+ return '<div class="twitter-follow">' . $html . '</div>';
192
+ }
193
+ }
src/Twitter/WordPress/Shortcodes/Share.php ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 Web Intent and queue JavaScript for conversion to a Tweet button
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Share
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_share';
44
+
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( 'in_reply_to' => '', 'text' => '', 'url' => '', 'hashtags' => array(), 'via' => '', 'related' => array(), 'size' => '', 'align' => '', 'count' => '', 'counturl' => '' );
53
+
54
+ /**
55
+ * Attach handlers for Tweet button
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @return void
60
+ */
61
+ public static function init()
62
+ {
63
+ add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
64
+ }
65
+
66
+ /**
67
+ * Get any Tweet values stored for an individual post
68
+ *
69
+ * @since 1.0.0
70
+ *
71
+ * @return array post meta Tweet values or empty array if no values stored
72
+ */
73
+ public static function getPostMeta()
74
+ {
75
+ $post = get_post();
76
+
77
+ if ( ! ( $post && isset( $post->ID ) ) ) {
78
+ return array();
79
+ }
80
+
81
+ $post_values = get_post_meta(
82
+ $post->ID,
83
+ \Twitter\WordPress\Admin\Post\TweetIntent::META_KEY,
84
+ true // single value
85
+ );
86
+ if ( ! is_array( $post_values ) ) {
87
+ return array();
88
+ }
89
+ return $post_values;
90
+ }
91
+
92
+ /**
93
+ * Add post meta values to a Tweet button options array
94
+ *
95
+ * @since 1.0.0
96
+ *
97
+ * @param array $options Tweet button options array {
98
+ * @type string option name
99
+ * @type string|array option value
100
+ * }
101
+ *
102
+ * @return array Tweet button options array {
103
+ * @type string option name
104
+ * @type string|array option value
105
+ * }
106
+ */
107
+ protected static function addPostMetaOptions( $options )
108
+ {
109
+ if ( ! is_array( $options ) ) {
110
+ $options = array();
111
+ }
112
+
113
+ $post_meta = static::getPostMeta();
114
+ if ( empty( $post_meta ) ) {
115
+ return $options;
116
+ }
117
+
118
+ // allow shortcode text to override post text
119
+ // example: multiple Tweet buttons in a post
120
+ if ( ! ( isset( $options['text'] ) && trim( $options['text'] ) ) ) {
121
+ if ( isset( $post_meta['text'] ) ) {
122
+ $text = trim( $post_meta['text'] );
123
+ if ( $text ) {
124
+ $options['text'] = $text;
125
+ }
126
+ unset( $text );
127
+ }
128
+ }
129
+
130
+ // allow shortcode hashtags to override post hashtags
131
+ // example: multiple Tweet buttons in a post
132
+ if ( ! isset( $options['hashtags'] ) || empty( $options['hashtags'] ) ) {
133
+ if ( isset( $post_meta['hashtags'] ) && is_array( $post_meta['hashtags'] ) && ! empty( $post_meta['hashtags'] ) ) {
134
+ $options['hashtags'] = $post_meta['hashtags'];
135
+ }
136
+ }
137
+
138
+ return $options;
139
+ }
140
+
141
+ /**
142
+ * Convert shortcode parameters, attributes, and defaults into a clean set of Tweet parameters
143
+ *
144
+ * @since 1.0.0
145
+ *
146
+ * @param array $attributes set of shortcode attribute-value pairs or positional content matching the WordPress shortcode regex {
147
+ * @type string|int attribute name or positional int
148
+ * @type mixed shortcode value
149
+ * }
150
+ *
151
+ * @return array cleaned up options ready for comparison {
152
+ * @type string option name
153
+ * @type string|bool option value
154
+ * }
155
+ */
156
+ public static function sanitizeShortcodeParameters( $attributes = array() )
157
+ {
158
+ if ( ! is_array( $attributes ) ) {
159
+ return array();
160
+ }
161
+
162
+ $options = array();
163
+
164
+ if ( isset( $attributes['in_reply_to'] ) ) {
165
+ $tweet_id = \Twitter\WordPress\Shortcodes\EmbeddedTweet::sanitizeTweetID( (string) $attributes['in_reply_to'] );
166
+ if ( $tweet_id ) {
167
+ $options['in_reply_to'] = $tweet_id;
168
+ }
169
+ unset( $tweet_id );
170
+ }
171
+
172
+ if ( isset( $attributes['text'] ) && is_string( $attributes['text'] ) ) {
173
+ $options['text'] = $attributes['text'];
174
+ }
175
+
176
+ foreach ( array( 'url', 'counturl' ) as $url_param ) {
177
+ if ( ! ( isset( $attributes[ $url_param ] ) && $attributes[ $url_param ] ) ) {
178
+ continue;
179
+ }
180
+
181
+ // filter the URL
182
+ $url = esc_url_raw( trim( $attributes[ $url_param ] ), array( 'http', 'https' ) );
183
+ if ( $url ) {
184
+ $options[ $url_param ] = $url;
185
+ }
186
+ unset( $url );
187
+ }
188
+
189
+ if ( isset( $attributes['related'] ) ) {
190
+ $intent = \Twitter\Intents\Tweet::fromArray( array( 'related' => $attributes['related'] ) );
191
+ if ( $intent ) {
192
+ $related = $intent->getRelated();
193
+ if ( ! empty( $related ) ) {
194
+ $options['related'] = $related;
195
+ }
196
+ unset( $related );
197
+ }
198
+ unset( $intent );
199
+ }
200
+
201
+ if ( isset( $attributes['hashtags'] ) ) {
202
+ $intent = \Twitter\Intents\Tweet::fromArray( array( 'hashtags' => $attributes['hashtags'] ) );
203
+ if ( $intent ) {
204
+ $hashtags = $intent->getHashtags();
205
+ if ( ! empty( $hashtags ) ) {
206
+ $options['hashtags'] = $hashtags;
207
+ }
208
+ unset( $hashtags );
209
+ }
210
+ unset( $intent );
211
+ }
212
+
213
+ if ( isset( $attributes['align'] ) && is_string( $attributes['align'] ) && $attributes['align'] ) {
214
+ $align = strtolower( trim( $attributes['align'] ) );
215
+ if ( array_key_exists( $align, \Twitter\Widgets\TweetButton::$ALLOWED_ALIGN_VALUES ) ) {
216
+ $options['align'] = $align;
217
+ }
218
+ unset( $align );
219
+ }
220
+
221
+ if ( isset( $attributes['count'] ) && is_string( $attributes['count'] ) ) {
222
+ $count = strtolower( trim( $attributes['count'] ) );
223
+ if ( array_key_exists( $count, \Twitter\Widgets\TweetButton::$ALLOWED_COUNT_VALUES ) ) {
224
+ $options['count'] = $count;
225
+ }
226
+ unset( $count );
227
+ }
228
+
229
+ // large is the only option
230
+ if ( isset( $attributes['size'] ) ) {
231
+ if ( is_string( $attributes['size'] ) && in_array( strtolower( $attributes['size'] ), array( 'large', 'l' ) ) ) {
232
+ $options['size'] = 'large';
233
+ }
234
+ }
235
+
236
+ return $options;
237
+ }
238
+
239
+ /**
240
+ * Add explicit Tweet button data related to the post and its author
241
+ *
242
+ * @since 1.0.0
243
+ *
244
+ * @param array $options Tweet button options {
245
+ * @type string option name
246
+ * @type string|bool option value
247
+ * }
248
+ * @param WP_Post $post post of interest
249
+ *
250
+ * @return array Tweet button options with possible additions based on post data {
251
+ * @type string option name
252
+ * @type string|bool option value
253
+ * }
254
+ */
255
+ protected static function addPostData( $options, $post )
256
+ {
257
+ if ( ! is_array( $options ) ) {
258
+ $options = array();
259
+ }
260
+
261
+ // explicitly define post URL
262
+ // maintains Tweet button context on a page listing multiple posts
263
+ if ( ! ( isset( $options['url'] ) && $options['url'] ) ) {
264
+ /**
265
+ * Filter the URL shared in Tweet text
266
+ *
267
+ * All URLs are wrapped in Twitter's t.co link wrapper
268
+ *
269
+ * @since 1.0.0
270
+ *
271
+ * @param string $url The URL returned by get_permalink() when in the loop
272
+ */
273
+ $url = apply_filters( 'twitter_url', get_permalink( $post ) );
274
+ if ( $url ) {
275
+ $options['url'] = $url;
276
+ }
277
+ unset( $url );
278
+ }
279
+
280
+ $author_id = get_the_author_meta( 'ID' );
281
+ if ( $author_id ) {
282
+ $author_twitter_username = \Twitter\WordPress\User\Meta::getTwitterUsername( $author_id );
283
+ if ( $author_twitter_username ) {
284
+ $author_display_name = trim( get_the_author_meta( 'display_name', $author_id ) );
285
+ if ( ! isset( $options['related'] ) || ! is_array( $options['related'] ) ) {
286
+ $options['related'] = array();
287
+ }
288
+ if ( ! isset( $options['related'][ $author_twitter_username ] ) ) {
289
+ $options['related'][ $author_twitter_username ] = $author_display_name;
290
+ }
291
+ unset( $author_display_name );
292
+ }
293
+ unset( $author_twitter_username );
294
+ }
295
+ unset( $author_id );
296
+
297
+ return $options;
298
+ }
299
+
300
+ /**
301
+ * Handle shortcode macro
302
+ *
303
+ * @since 1.0.0
304
+ *
305
+ * @param array $attributes shortcode attributes
306
+ * @param string $content shortcode content. no effect
307
+ *
308
+ * @return string Tweet button HTML or empty string
309
+ */
310
+ public static function shortcodeHandler( $attributes, $content = null )
311
+ {
312
+ // clean up attribute to shortcode option mappings before passing to filter
313
+ // apply the same filter as shortcode_atts
314
+ /** This filter is documented in wp-includes/shortcodes.php */
315
+ $options = apply_filters(
316
+ 'shortcode_atts_' . self::SHORTCODE_TAG,
317
+ array_merge(
318
+ static::$SHORTCODE_DEFAULTS,
319
+ static::sanitizeShortcodeParameters( (array) $attributes )
320
+ ),
321
+ static::$SHORTCODE_DEFAULTS,
322
+ $attributes
323
+ );
324
+
325
+ // add options shared to post meta
326
+ $options = static::addPostMetaOptions( $options );
327
+
328
+ // add parameters based on per-post render context
329
+ if ( in_the_loop() ) {
330
+ $post = get_post();
331
+
332
+ // do not share posts requiring a password to access
333
+ if ( $post && ! empty( $post->post_password ) ) {
334
+ return '';
335
+ }
336
+
337
+ // protect sites from themselves
338
+ // do not display Tweet button for non-public content to avoid leaking content
339
+ $post_status_object = get_post_status_object( get_post_status( $post ) );
340
+ if ( ! ( $post_status_object && isset( $post_status_object->public ) && $post_status_object->public ) ) {
341
+ return '';
342
+ }
343
+ unset( $post_status_object );
344
+
345
+ // add parameters based on post data
346
+ $options = static::addPostData( $options, $post );
347
+
348
+ unset( $post );
349
+ }
350
+ if ( ! ( isset( $options['via'] ) && $options['via'] ) ) {
351
+ // attribute the Tweet to the site Twitter username
352
+ $via_username = \Twitter\WordPress\Site\Username::getViaAttribution( ( in_the_loop() ? get_the_ID() : null ) );
353
+ if ( $via_username ) {
354
+ $options['via'] = $via_username;
355
+ }
356
+ unset( $via_username );
357
+ }
358
+
359
+ $button = \Twitter\Widgets\TweetButton::fromArray( $options );
360
+ if ( ! $button ) {
361
+ return '';
362
+ }
363
+
364
+ $html = $button->toHTML( _x( 'Tweet', 'Tweet verb. Sharing.', 'twitter' ), '\Twitter\WordPress\Helpers\HTMLBuilder' );
365
+ if ( ! $html ) {
366
+ return '';
367
+ }
368
+
369
+ \Twitter\WordPress\JavaScriptLoaders\Widgets::enqueue();
370
+ return '<div class="twitter-share">' . $html . '</div>';
371
+ }
372
+ }
src/Twitter/WordPress/Shortcodes/Tracking.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Track a Twitter conversion and/or remarketing audience
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Tracking
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_tracking';
44
+
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' => '' );
53
+
54
+ /**
55
+ * Stored tracking ids
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @type array
60
+ */
61
+ protected static $tracking_ids = array();
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
+ add_shortcode( static::SHORTCODE_TAG, array( __CLASS__, 'shortcodeHandler' ) );
73
+ }
74
+
75
+ /**
76
+ * Handle shortcode macro
77
+ *
78
+ * @since 1.0.0
79
+ *
80
+ * @param array $attributes shortcode attributes
81
+ * @param string $content shortcode content. no effect
82
+ *
83
+ * @return string empty string. markup is queued for inclusion in wp_footer output
84
+ */
85
+ public static function shortcodeHandler( $attributes, $content = null )
86
+ {
87
+ $options = shortcode_atts(
88
+ static::$SHORTCODE_DEFAULTS,
89
+ $attributes,
90
+ static::SHORTCODE_TAG
91
+ );
92
+
93
+ $tracking_id = trim( $options['id'] );
94
+ if ( ! $tracking_id ) {
95
+ return '';
96
+ }
97
+
98
+ \Twitter\WordPress\JavaScriptLoaders\Tracking::enqueue();
99
+ static::$tracking_ids[ $tracking_id ] = true;
100
+
101
+ if ( false === has_action( 'wp_footer', array( __CLASS__, 'trackerJavaScript' ) ) ) {
102
+ // execute script after wp_print_footer_scripts action completes at priority 20
103
+ add_action( 'wp_footer', array( __CLASS__, 'trackerJavaScript' ), 25 );
104
+ }
105
+
106
+ // execute all trackers just before </body>
107
+ return '';
108
+ }
109
+
110
+ /**
111
+ * Track a Twitter advertising event using the twttr.conversion.trackPid JavaScript function
112
+ *
113
+ * @since 1.0.0
114
+ *
115
+ * @param string $tracking_id Twitter ads tracking ID
116
+ *
117
+ * @return void
118
+ */
119
+ protected static function trackEventUsingJavaScript( $tracking_id )
120
+ {
121
+ echo 'twttr.conversion.trackPid(' . ( function_exists( 'wp_json_encode' ) ? wp_json_encode( $tracking_id ) : json_encode( $tracking_id ) ) . ');';
122
+ }
123
+
124
+ /**
125
+ * Track a Twitter advertising event using 1x1 images
126
+ *
127
+ * @since 1.0.0
128
+ *
129
+ * @param string $tracking_id Twitter ads tracking ID
130
+ *
131
+ * @return void
132
+ */
133
+ protected static function trackEventUsingFallbackImages( $tracking_id )
134
+ {
135
+ $query_parameters = http_build_query( array( 'txn_id' => $tracking_id, 'p_id' => 'Twitter' ), '', '&' );
136
+
137
+ 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() . '>';
138
+ 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() . '>';
139
+ }
140
+
141
+ /**
142
+ * Record tracking ID actions
143
+ *
144
+ * @since 1.0.0
145
+ *
146
+ * @return void
147
+ */
148
+ public static function trackerJavaScript()
149
+ {
150
+ if ( empty( static::$tracking_ids ) ) {
151
+ return;
152
+ }
153
+
154
+ $tracking_ids = array_keys( static::$tracking_ids );
155
+
156
+ // JavaScript available
157
+ echo '<script type="text/javascript">';
158
+ array_walk( $tracking_ids, array( __CLASS__, 'trackEventUsingJavaScript' ) );
159
+ echo '</script>';
160
+
161
+ // JavaScript unavailable
162
+ echo '<noscript>';
163
+ array_walk( $tracking_ids, array( __CLASS__, 'trackEventUsingFallbackImages' ) );
164
+ echo '</noscript>';
165
+ }
166
+ }
src/Twitter/WordPress/Site/Username.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\Site;
27
+
28
+ /**
29
+ * Associate site Twitter username(s) with hooks used across the site
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Username
34
+ {
35
+
36
+ /**
37
+ * Attribute a webpage to a Twitter @username for the site or site section
38
+ *
39
+ * Similar to a site byline or masthead with Twitter attribution
40
+ * Affects Twitter Analytics
41
+ *
42
+ * @since 1.0.0
43
+ *
44
+ * @link https://dev.twitter.com/cards/markup#twitter-site Twitter Card site attribution
45
+ * @link https://dev.twitter.com/cards/analytics Twitter Card analytics
46
+ *
47
+ * @param int|string|bool|null $post_id WP_Post->ID, false response of get_the_ID, proprietary post ID, or null if outside of a post context or no post ID found
48
+ *
49
+ * @return string Twitter username or empty string
50
+ */
51
+ public static function getSiteAttribution( $post_id = null )
52
+ {
53
+ // simplify passed types for filter
54
+ if ( false === $post_id ) {
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 = '';
62
+ }
63
+
64
+ /**
65
+ * Allow sites to provide a WordPress site or site section Twitter username through a filter
66
+ *
67
+ * A username should be provided without its @ prefix.
68
+ * A site username might be overridden if a better match is available based on the post or archive context, such as AcmeSports overriding a general site username of Acme when displaying content inside the sports category
69
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @param string $username Twitter username stored for the site
73
+ * @param int|string|null $post_id WP_Post->ID, proprietary post ID, or null if outside of a post context or no post ID found
74
+ */
75
+ return apply_filters( 'twitter_site_username', $username, $post_id );
76
+ }
77
+
78
+ /**
79
+ * Attribute a Tweet created through a link on your site to a Twitter username
80
+ *
81
+ * @since 1.0.0
82
+ *
83
+ * @link https://dev.twitter.com/web/tweet-button/web-intent#tweet-web-intent-via Tweet Web Intent via parameter
84
+ *
85
+ * @param int|string|null $post_id WP_Post->ID, proprietary post ID, or null if outside of a post context or no post ID found
86
+ *
87
+ * @return string Twitter username or empty string
88
+ */
89
+ public static function getViaAttribution( $post_id = null )
90
+ {
91
+ /**
92
+ * Allow sites to provide a WordPress site or site section Twitter username through a filter
93
+ *
94
+ * A username should be provided without its @ prefix
95
+ *
96
+ * @since 1.0.0
97
+ *
98
+ * @param @param string $username Twitter username stored for the site
99
+ * @param int|string|null $post_id WP_Post->ID, proprietary post ID, or null if outside of a post context or no post ID found
100
+ */
101
+ return apply_filters( 'twitter_via_username', static::getSiteAttribution( $post_id ), $post_id );
102
+ }
103
+ }
src/Twitter/WordPress/User/Meta.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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\User;
27
+
28
+ /**
29
+ * Get WordPress user meta values
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ class Meta
34
+ {
35
+
36
+ /**
37
+ * Get a Twitter @username stored for a given WordPress user identifier
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
42
+ *
43
+ * @return string Twitter username value stored for the given WordPress user identifier
44
+ */
45
+ public static function getTwitterUsername( $user_id )
46
+ {
47
+ // basic test for invalid passed parameter
48
+ if ( ! $user_id ) {
49
+ return '';
50
+ }
51
+
52
+ $meta_key = 'twitter';
53
+ if ( function_exists( 'get_user_attribute' ) ) {
54
+ $username = get_user_attribute( $user_id, $meta_key );
55
+ } else {
56
+ $username = get_user_meta( $user_id, $meta_key, /* single */ true );
57
+ }
58
+
59
+ if ( ! is_string( $username ) ) {
60
+ $username = '';
61
+ }
62
+
63
+ // pass a username through a filter if not explicitly defined through user meta
64
+ if ( ! $username ) {
65
+ /**
66
+ * Allow sites to provide a WordPress user's Twitter username through a filter
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * @param string $username Twitter username associated with a WordPress user ID
71
+ * @param int|string $user_id WordPress user identifier. may be WP_User->ID or a separate identifier used by an extending system
72
+ */
73
+ $username = apply_filters( 'twitter_username', $username, $user_id );
74
+ }
75
+
76
+ return $username;
77
+ }
78
+ }
src/Twitter/WordPress/Widgets/Follow.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Twitter Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ namespace Twitter\WordPress\Widgets;
27
+
28
+ /**
29
+ * Add 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
+ /**
39
+ * Widget base ID
40
+ *
41
+ * Widget identifiers will derive from the base based on their positioning. e.g. twitter-follow-1
42
+ *
43
+ * @since 1.0.0
44
+ *
45
+ * @type string
46
+ */
47
+ const BASE_ID = 'twitter-follow';
48
+
49
+ /**
50
+ * Register widget with WordPress
51
+ *
52
+ * @since 1.0.0
53
+ *
54
+ * @return void
55
+ */
56
+ public function __construct()
57
+ {
58
+ parent::__construct(
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
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
73
+ * @param array $instance The settings for the particular instance of the widget
74
+ *
75
+ * @return void
76
+ */
77
+ public function widget( $args, $instance )
78
+ {
79
+ // no 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
+
105
+ /**
106
+ * Settings update form
107
+ *
108
+ * @since 1.0.0
109
+ *
110
+ * @param array $instance Current settings
111
+ *
112
+ * @return void
113
+ */
114
+ public function form( $instance )
115
+ {
116
+ $instance = wp_parse_args(
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
+ }
145
+
146
+ /**
147
+ * Update a widget instance
148
+ *
149
+ * @since 1.0.0
150
+ *
151
+ * @param array $new_instance New settings for this instance as input by the user via form()
152
+ * @param array $old_instance Old settings for this instance
153
+ *
154
+ * @return bool|array settings to save or false to cancel saving
155
+ */
156
+ public function update( $new_instance, $old_instance )
157
+ {
158
+ $instance = array();
159
+ $new_instance = (array) $new_instance;
160
+ $title = trim( strip_tags( $new_instance['title'] ) );
161
+ if ( $title ) {
162
+ $instance['title'] = $title;
163
+ }
164
+ unset( $new_instance['title'] );
165
+
166
+ foreach ( array( 'show_screen_name', 'show_count' ) as $bool_option ) {
167
+ if ( isset( $new_instance[ $bool_option ] ) && $new_instance[ $bool_option ] ) {
168
+ $new_instance[ $bool_option ] = true;
169
+ } else {
170
+ $new_instance[ $bool_option ] = false;
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
+ if ( empty( $filtered_options ) ) {
181
+ return false;
182
+ }
183
+ $screen_name = $follow_button->getScreenName();
184
+ if ( $screen_name ) {
185
+ $filtered_options['screen_name'] = $screen_name;
186
+ }
187
+ unset( $screen_name );
188
+
189
+ // convert string to bool equivalent
190
+ if ( isset( $filtered_options['show-screen-name'] ) ) {
191
+ if ( 'false' == $filtered_options['show-screen-name'] ) {
192
+ $filtered_options['show_screen_name'] = false;
193
+ }
194
+ unset( $filtered_options['show-screen-name'] );
195
+ }
196
+ if ( isset( $filtered_options['show-count'] ) ) {
197
+ if ( 'false' == $filtered_options['show-count'] ) {
198
+ $filtered_options['show_count'] = false;
199
+ }
200
+ unset( $filtered_options['show-count'] );
201
+ }
202
+
203
+ return array_merge( $instance, $filtered_options );
204
+ }
205
+ }
static/css/admin/post/edit.min.css ADDED
@@ -0,0 +1 @@
 
1
+ #twitter-custom table{margin:0;width:100%;border:1px solid #dfdfdf;border-spacing:0;background-color:#f9f9f9}#twitter-custom thead th{padding:5px 8px 8px;background-color:#f1f1f1}#twitter-custom input{width:96%;margin:8px}#tweet-intent{width:100%}#twitter-card{width:100%}
twitter.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package twitter
4
+ * @version 1.0.0
5
+ */
6
+ /*
7
+ Plugin Name: Twitter
8
+ Plugin URI: http://wordpress.org/plugins/twitter/
9
+ Description: Official Twitter plugin for WordPress. Embed Twitter content and grow your audience on Twitter. Requires PHP 5.4 or newer.
10
+ Version: 1.0.0
11
+ Author: Twitter
12
+ Author URI: https://dev.twitter.com/
13
+ License: MIT
14
+ Text Domain: twitter
15
+ Domain Path: /languages/
16
+ */
17
+
18
+ /*
19
+ The MIT License (MIT)
20
+
21
+ Copyright (c) 2015 Twitter Inc.
22
+
23
+ Permission is hereby granted, free of charge, to any person obtaining a copy
24
+ of this software and associated documentation files (the "Software"), to deal
25
+ in the Software without restriction, including without limitation the rights
26
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27
+ copies of the Software, and to permit persons to whom the Software is
28
+ furnished to do so, subject to the following conditions:
29
+
30
+ The above copyright notice and this permission notice shall be included in
31
+ all copies or substantial portions of the Software.
32
+
33
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39
+ THE SOFTWARE.
40
+ */
41
+
42
+
43
+ // make sure the plugin does not expose any info if called directly
44
+ if ( ! function_exists( 'add_action' ) ) {
45
+ if ( ! headers_sent() ) {
46
+ if ( function_exists( 'http_response_code' ) ) {
47
+ http_response_code( 403 );
48
+ } else {
49
+ header( 'HTTP/1.1 403 Forbidden', true, 403 );
50
+ }
51
+ }
52
+ exit( 'Hi there! I am a WordPress plugin requiring functions included with WordPress. I am not meant to be addressed directly.' );
53
+ }
54
+
55
+ // plugin requires PHP 5.4 or newer
56
+ if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
57
+ trigger_error( 'The Twitter plugin for WordPress requires PHP version 5.4 or higher.' );
58
+ return;
59
+ }
60
+
61
+ // PHP namespace autoloader
62
+ require_once( dirname( __FILE__ ) . '/autoload.php' );
63
+
64
+ // initialize on plugins loaded
65
+ add_action(
66
+ 'plugins_loaded',
67
+ array( '\\Twitter\\WordPress\\PluginLoader', 'init' ),
68
+ 0, // priority
69
+ 0 // expected arguments
70
+ );
uninstall.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ if (
27
+ ! defined( 'WP_UNINSTALL_PLUGIN' )
28
+ ||
29
+ ! WP_UNINSTALL_PLUGIN
30
+ ||
31
+ dirname( WP_UNINSTALL_PLUGIN ) != dirname( plugin_basename( __FILE__ ) )
32
+ ) {
33
+ if ( ! headers_sent() ) {
34
+ if ( function_exists( 'status_header' ) ) {
35
+ status_header( 404 );
36
+ } else if ( function_exists( 'http_response_code' ) ) {
37
+ http_response_code( 404 );
38
+ } else {
39
+ header( 'HTTP/1.1 404 Not Found', true, 404 );
40
+ }
41
+ }
42
+ exit;
43
+ }
44
+
45
+ // site options
46
+ if ( function_exists( 'delete_option' ) ) {
47
+ // Delete all admin options
48
+ $__options = array(
49
+ 'twitter_widgets',
50
+ 'twitter_site_attribution',
51
+ 'twitter_share',
52
+ );
53
+ array_walk( $__options, 'delete_option' );
54
+ }
55
+
56
+ // post meta
57
+ if ( function_exists( 'delete_post_meta_by_key' ) ) {
58
+ // delete Twitter customizations stored at the post level
59
+ $__post_meta_keys = array(
60
+ 'twitter_share',
61
+ 'twitter_card',
62
+ );
63
+ array_walk( $__post_meta_keys, 'delete_post_meta_by_key' );
64
+ }
65
+
66
+ // user meta
67
+ if ( function_exists( 'delete_metadata' ) ) {
68
+ // delete Twitter customizations stored for a WordPress user account
69
+ delete_metadata(
70
+ 'user', // meta type
71
+ 0, // user ID (ignored)
72
+ 'twitter', // meta key
73
+ '', // delete all values
74
+ true // delete all. ignore passed user id
75
+ );
76
+ }