Custom Twitter Feeds - Version 1.8.2

Version Description

  • Tested with WordPress 5.8 update.
  • Fix: "Display your feed" tab was showing the wrong shortcode for the "layout" setting available in the pro version.
  • Fix: Fixed an issue with GDPR Cookie Consent by Web Toffee integration.
Download this release

Release Info

Developer Craig at Smash Balloon
Plugin Icon 128x128 Custom Twitter Feeds
Version 1.8.2
Comparing to
See all releases

Code changes from version 1.8.1 to 1.8.2

README.txt CHANGED
@@ -1,297 +1,302 @@
1
- === Custom Twitter Feeds (Tweets Widget) ===
2
- Author: Smash Balloon
3
- Contributors: smashballoon, craig-at-smash-balloon
4
- Support Website: http://smashballoon/custom-twitter-feeds/
5
- Tags: Twitter, Twitter feed, Twitter Tweets, Twitter widget, Custom Twitter Feed
6
- Requires at least: 3.4
7
- Tested up to: 5.7
8
- Stable tag: 1.8.1
9
- License: GPLv2 or later
10
- License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
-
12
- Custom Twitter Feeds allows you to display completely customizable Twitter Tweets from any user timeline, Twitter hashtag, or home timeline on your website.
13
-
14
- == Description ==
15
- Display **completely customizable**, **responsive** and **search engine crawlable** Twitter feeds on your website. The Custom Twitter Feeds plugin will display Tweets that completely match the look and feel of your site and includes tons of customization options!
16
-
17
- = Features =
18
- * **Completely Customizable** - by default the Twitter feeds will inherit your theme's styles
19
- * Twitter feed content is **crawlable by search engines** adding SEO value to your site
20
- * **Completely responsive and mobile optimized** - your Twitter feeds work on any screen size
21
- * Display Tweets from any user, your own Twitter account and those you follow, or from a specific hashtag
22
- * Display **multiple Twitter feeds** from different Twitter users on multiple pages or widgets
23
- * Post caching means that your Twitter feed loads **lightning fast** and minimizes Twitter API requests
24
- * **Infinitely load more** of your Tweets with the 'Load More' button
25
- * Built-in easy to use "Custom Twitter Feeds" Widget makes it easy to display Tweets from any Twitter account
26
- * Fully internationalized and translatable into any language
27
- * Display a beautiful header at the top of your Twitter feed
28
- * Enter your own custom CSS or JavaScript for even deeper customization of your Tweets
29
-
30
- For simple step-by-step directions on how to set up the Custom Twitter Feeds plugin please refer to our [setup guide](http://smashballoon.com/custom-twitter-feeds/free/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds setup guide').
31
-
32
- = Benefits =
33
- * **Increase social engagement** between you and your users, customers, or fans
34
- * **Save time** by using the Custom Twitter Feeds plugin to generate dynamic, search engine crawlable content on your website
35
- * **Get more follows** by displaying your Twitter content directly on your site
36
- * **Improve your SEO** as all of that quality keyword-rich Twitter content is directly embedded into your website
37
- * **Keep users on your site** by letting them reply and like your Tweets directly on your site
38
- * Display your Twitter content **your way** to perfectly match your website's style
39
- * The Custom Twitter Feeds plugin is **updated regularly** with new features, bug-fixes and Twitter API changes
40
- * Support is quick and effective
41
- * We're dedicated to providing the **most customizable**, **robust** and **well supported** Twitter feed plugin in the world!
42
-
43
- [View the reviews](https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/) to see what other users are saying about the Custom Twitter Feeds plugin.
44
-
45
- = Feedback or Support =
46
- We're dedicated to providing the most robust and well supported Twitter feed plugin in the world, so if you have an issue or any feedback on how to improve the plugin then please [let us know](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf 'Twitter Feed Support').
47
-
48
- If you like the plugin then please consider leaving a review, as it really helps to support the plugin. If you have an issue then please allow us to help you fix it before leaving a review. Just [let us know](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf 'Twitter Feed Support') what the problem is and we'll get back to you right away.
49
-
50
- = Featured Reviews =
51
- "***Great for Customization** - Great plugin! Superb customization options! Am changing all twitter feeds for all my websites to this! I have tried several Twitter plugins so far. This is the best I have come across :)*" - [@nehavivekpatil](https://wordpress.org/support/topic/great-for-customization-2/)
52
-
53
- "***Excellent plug-in. Excellent support.** - Excellent instructions and guidance. Patient and prompt support for the newbies. Regular updates. You can't lose. Thank you Smash Balloon.* - [@nuspa](https://wordpress.org/support/topic/excellent-plug-in-excellent-support-2/)
54
-
55
- "***Amazing Support and a Great Twitter Plugin** - I am using the free version of this Twitter plugin and identified a real problem in the display on my website. I emailed support and Craig was all over the fix for me. Seven or so emails later (even on a Sunday) the problem was fixed and Craig was still reaching out to make sure all was well.*
56
-
57
- *In a world of crappy support for premium plugins, and even worse support for free plugins, this kind of support (and listening to customers) that Craig and Smash Balloon provided me make their premium products worth every penny. In fact, I am going to buy them on my next project. They deserve it.*
58
-
59
- *Thanks again, Craig!*" - [@kcwebguy](https://wordpress.org/support/topic/amazing-support-and-a-great-twitter-plugin/)
60
-
61
- "***At last a configurable Twitter Feed that WORKS!** - I have messed with Twitter feeds on sites so many times only to be frustrated. This Plugin is the best I have tried. Stable, configurable and not too hefty a drag on performance. Well done!* - [@mikemac67](https://wordpress.org/support/topic/at-last-a-configurable-feed-that-works/)
62
-
63
- "***Everything you'll need in a Twitter feed plugin** - We've tried a few Twitter feed plugins across a bunch of our sites, and this one is now our go-to favorite. Most of the others we've tried have either styling or performance issues, but we've been using this plugin for a few months now, and it's been perfect. There are tons of customization options, the feed looks great, and... it works!*
64
-
65
- *Kudos to the SmashBalloon team on a well supported, tightly developed, feature-packed WordPress Plugin.* - [@laymanlab](https://wordpress.org/support/topic/everything-youll-need-in-a-twitter-feed-plugin/)
66
-
67
- = Pro Version =
68
- We recently released a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features:
69
-
70
- * Display Tweets from **multiple users or hashtags in the same Twitter feed**
71
- * Display **photos**, **videos**, and **gifs** and view them in a **popup lightbox** directly on your site
72
- * Multi-column **Masonry layout** [demo](http://smashballoon.com/custom-twitter-feeds/demo/masonry?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Masonry Demo')
73
- * Allow **filtering** of Twitter timelines include/exclude any/all keywords/hashtags
74
- * Customizable **Carousels** and **Slideshows** of your Tweets
75
- * Fully functional Twitter **search endpoint**
76
- * Display Tweets you're mentioned in
77
- * Tweet-specific **moderation system**
78
- * Visual **Twitter cards** displayed with links which support them
79
- * Include **Tweet replies** (in reply to tweets)
80
- * **Combine multiple Twitter feeds** into one
81
- * **Autoload more Tweets** when scrolling
82
-
83
- Try the Pro version [demo here](http://smashballoon.com/custom-twitter-feeds/demo?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Demo')
84
-
85
- == Installation ==
86
- 1. Install the Custom Twitter Feeds plugin either via the WordPress plugin directory, or by uploading the files to your web server (in the /wp-content/plugins/ directory).
87
- 2. Activate the Custom Twitter Feeds plugin through the 'Plugins' menu in WordPress.
88
- 3. Navigate to the 'Twitter Feed' settings page to configure your Twitter feed.
89
- 4. Use the shortcode [custom-twitter-feeds] in your page, post or widget to display your Twitter feed.
90
- 5. You can display multiple Twitter feeds with different configurations by specifying the necessary parameters directly in the shortcode: [custom-twitter-feeds hashtag=#smashballoon].
91
-
92
- For simple step-by-step directions on how to set up the Custom Twitter Feeds plugin please refer to our [setup guide](http://smashballoon.com/custom-twitter-feeds/free/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds setup guide').
93
-
94
- = Setting up the Free Custom Twitter Feeds WordPress Plugin =
95
-
96
- If you have an issue or question while setting up or using the plugin then please submit a support ticket and we'll get back to you as soon as we can.
97
-
98
- 1) Once you've installed the Custom Twitter Feeds plugin click on the "Twitter Feeds" item in your WordPress menu
99
-
100
- 2) Click on the large blue Twitter login button to get your Twitter Access Token and Twitter Secret. Note; if you have your own Twitter Developer App set up then you can enter your Twitter information manually by enabling the checkbox below the Twitter login button.
101
-
102
- 3) Authorize the Custom Twitter Feeds plugin to read your Tweets.
103
- Note; the plugin does not obtain permission to edit or write to your Twitter account, only to read your Twitter content.
104
-
105
- 4) Twitter sends back your Twitter Access Token and Twitter Secret which are then automatically saved by the Custom Twitter Feeds plugin. This information is required in order to connect to the Twitter API.
106
-
107
- 5) Enter a Twitter screenname to display Tweets from. Alternatively, choose to display Tweets from your Twitter home timeline or a Twitter hashtag.
108
-
109
- 6) Navigate to the Customize and Style pages to customize your Twitter feed.
110
-
111
- 7) Once you've customized your Twitter feed, click on the "Display Your Feed" tab for directions on how to display your Twitter feed (or multiple Twitter feeds).
112
-
113
- 8) Copy the [custom-twitter-feeds] shortcode and paste it into any page, post or widget where you want the Twitter feed to appear.
114
-
115
- 9) You can paste the [custom-twitter-feeds] shortcode directly into your page editor.
116
-
117
- 10) You can use the default WordPress 'Text' widget to display your Twitter Feed in a sidebar or other widget area.
118
-
119
- 11) View your website to see your Twitter feed(s) in all their glory!
120
-
121
- == Frequently Asked Questions ==
122
-
123
- = Can I display multiple Twitter feeds on my site or on the same page? =
124
-
125
- Yep. You can display multiple Twitter feeds by using our built-in shortcode options, for example: `[custom-twitter-feeds screenname="smashballoon" num=3]`. You can add it to any post, page, or widget, to easily display your Twitter feed in a beautiful Twitter widget.
126
-
127
- = How do I embed the Twitter Feed directly into a WordPress page template? =
128
-
129
- You can embed your Twitter feed directly into a template file by using the WordPress [do_shortcode](http://codex.wordpress.org/Function_Reference/do_shortcode) function: `<?php echo do_shortcode('[custom-twitter-feeds]'); ?>`.
130
-
131
- = Is the content of my Twitter feed crawlable by search engines and how does it help improve my SEO? =
132
-
133
- Unlike other Twitter plugins which use iframes to embed your Twitter feed into your page in a widget, the Custom Twitter Feeds uses server-side code to embed your Twitter feed widget content directly into your page. This adds dynamic, search engine crawlable content to your site.
134
-
135
- = Will Custom Twitter Feeds work with W3 Total Cache or other caching plugins? =
136
-
137
- The Custom Twitter Feeds plugin should work in compatibility with most, if not all, caching plugins, but you may need to tweak the settings in order to allow the Twitter feed to update successfully and display your latest Twitter posts. If you are experiencing problems with your Twitter feed widget not updating then try disabling either 'Page Caching' or 'Object Caching' in W3 Total Cache (or any other similar caching plugin) to see whether that fixes the problem and the Twitter feed displays and updates successfully.
138
-
139
- = The font in my Twitter feed isn't inherited from my website =
140
-
141
- Be default your Twitter feed should inherit the font from your website. If this isn't the case then it's possible that something in the theme is preventing this from happening. An issue that we've run into before is that &lt;code&gt; tags are added into the Twitter feed widget HTML by either the theme, another plugin, or perhaps even the custom-twitter-feeds shortcode being wrapped in code tags in your page editor. To fix this;
142
-
143
- 1) First – go to your page editor, click on the 'Text' tab in the top right of the editor box, and check whether the [custom-twitter-feeds] shortcode is being wrapped in &lt;code&gt; tags, like so: &lt;code&gt;[custom-twitter-feeds]&lt;/code&gt;. If it is, then remove them.
144
-
145
- 2) If this doesn't solve the problem then try adding the following to the plugin's "Custom CSS" section (Twitter Feeds > Customize > Custom CSS):
146
-
147
- `#ctf code { font-family: sans-serif; }`
148
-
149
- You can replace the font-family with whatever font your theme is using.
150
-
151
- = Differences between the free version and Pro version of the Custom Twitter Feeds plugin =
152
-
153
- The main differences between the free version and Pro version of the Custom Twitter Feeds plugin are listed below. The extra features available in the Pro version are as follows:
154
-
155
- - Display photos, videos, and gifs in your Tweets, with support for 3rd party services such as YouTube, Vimeo, Vine, and SoundCloud
156
- - View Twitter media in a pop-up lightbox directly on your site
157
- - Display Tweets in a multi-column Masonry layout
158
- - Display your Twitter feed in rotating Carousels / Slideshows
159
- - Twitter Cards (rich, standout links) are displayed in Tweets for links that support them
160
- - Create advanced Twitter "Search" feeds or use the "Mentions" timeline
161
- - Combine Twitter feeds of multiple types (i.e. User feeds and Hashtag Twitter feeds combined)
162
- - Filter feeds by hashtag, word(s), or remove specific tweets
163
- - Autoload more tweets when scrolling to the bottom of the Twitter feed
164
- - Include Tweet replies ("in reply to")
165
-
166
- You can see the demo of the Custom Twitter Feeds Pro version [here](https://smashballoon.com/custom-twitter-feeds/demo/?utm_source=wordpress&utm_campaign=ctf), and a video screencast [here](https://smashballoon.com/custom-twitter-feeds/#watch-video?utm_source=wordpress&utm_campaign=ctf).
167
-
168
- = I'm receiving an error message when trying to display my Twitter feed =
169
-
170
- If you receive an error message when trying to display your Twitter Feed widget then you can use the error reference below to diagnose the issue and find the relevant solution.
171
-
172
- **Twitter Error:**
173
- Could not authenticate you
174
-
175
- **Causes:**
176
- - You may be using Twitter access tokens that are not valid - See #1 below
177
- - You checked the box to enter your own Twitter app information, but one or more of the fields are incorrect - See #2 below
178
-
179
- **Twitter Error:**
180
- Invalid or expired token
181
-
182
- **Causes:**
183
- - You may not have entered your Twitter access tokens or they are not valid - See #1 below
184
-
185
- **Twitter Error:**
186
- Unable to load tweets (with no other explanation)
187
-
188
- **Causes:**
189
- - You may need to raise the number of tweets to retrieve and display in your Twitter feed widget - See #3 below
190
-
191
- **Twitter Error:**
192
- Sorry, that Twitter page does not exist
193
-
194
- **Causes:**
195
- - There may be a typo in the Twitter screen name or hashtag you are attempting to use
196
-
197
- **Twitter Error:**
198
- Over capacity
199
-
200
- **Causes:**
201
- - Twitter's servers are over capacity so the API is unusable. This will correct itself after some time.
202
-
203
- **Twitter Error:**
204
- Connection timed out after 10000 milliseconds
205
-
206
- **Causes:**
207
- - Most likely this is due to your server blocking access to the Twitter API – See #4 below
208
-
209
- **Solutions**
210
-
211
- **#1 - Your Twitter access tokens might not be valid**
212
-
213
- The easiest way to verify this is by going back to the Settings page for the Custom Twitter Feeds plugin and clicking the big blue Twitter login button on the "Configure" tab to get new Twitter access tokens. If you haven't set up your own Twitter App, the only way to use the Custom Twitter Feeds plugin is to click the big blue button to get a Twitter access token and Twitter access token secret that is compatible with the default Twitter client used by Smash Balloon.
214
-
215
- **#2 - You have checked the box to use your own Twitter app information but one or more of the fields are incorrect**
216
-
217
- All four fields, Twitter consumer token, Twitter consumer secret, Twitter access token, and Twitter access token secret, need to come from the Twitter app that you set up on Twitter.com. Try returning to your personal Twitter app management page https://apps.twitter.com/ and confirming that all four fields, Twitter consumer token, Twitter consumer secret, Twitter access token, and Twitter access token secret are entered correctly in the corresponding fields on the Custom Twitter Feeds plugin's "Configure" tab.
218
-
219
- **#3 - You may need to raise the number of tweets to retrieve**
220
-
221
- Navigate to the type of Twitter feed you are trying to display on twitter.com. For example, if you are displaying a Twitter feed from the screenname "smashballoon", go to https://twitter.com/smashballoon. Make sure there are tweets visible. Then click on the link "Tweets & replies". If there are a more recent replies than original tweets, there may be too many tweets being filtered out to display any. Twitter replies are removed by default. You can raise the number of tweets to retrieve initially by going to the Custom Twitter Feed Settings page, "Customize" tab, and navigating to the "Advanced" area. Then raise the "Tweet Multiplier" and test to see if your Twitter feed now displays tweets.
222
-
223
- If you're still having trouble displaying your Tweets after trying the common issues above then please [contact support](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf) for assistance.
224
-
225
- == Screenshots ==
226
-
227
- 1. By default the Custom Twitter Feeds plugin inherits styles from your theme
228
- 2. Display multiple Twitter feeds and customize each one
229
- 3. Custom Twitter Feeds Settings pages
230
- 4. Built in customization settings make customizing your Twitter feed widget easy
231
- 5. Additional styling options are built into the Custom Twitter Feeds plugin
232
- 6. Use handy shortcode options to customize individual Twitter feeds
233
- 7. To display a Twitter feed just copy and paste the shortcode into a Twitter widget or page
234
-
235
- == Changelog ==
236
- = 1.8.1 =
237
- * Fix: Fixed several issues with GDPR Cookie Consent by Web Toffee integration.
238
- * Tweak: Added an easy link to the [Pro demo](https://smashballoon.com/custom-twitter-feeds/demo/)
239
-
240
- = 1.8 =
241
- * New: The locations of the Twitter feeds on your site will now be logged and listed on a single page for easier management. After this feature has been active for awhile, a "Feed Finder" link will appear next to the Feed Type setting on the plugin Settings page which allows you to see a list of all feeds on your site along with their locations.
242
- * New: Added support for WordPress based translations using language files.
243
- * Tweak: Changed the default method of retrieving posts to use the WP_HTTP class.
244
- * Fix: Updated jQuery methods for compatibility with WordPress 5.7.
245
-
246
- = 1.7 =
247
- * New: Integrations with popular GDPR cookie consent solutions added: Cookie Notice by dFactory, GDPR Cookie Consent by WebToffee, Cookiebot by Cybot A/S, Complianz by Really Simple Plugins, and Borlabs Cookie by Borlabs. Visit the Twitter Feed settings page, Customize tab, Advanced section for more information.
248
- * Tweak: Icon font support was discontinued. Only SVGs will be used for icons in feeds.
249
- * Tweak: Twitter Intents JS updated to the latest version.
250
-
251
- = 1.6.1 =
252
- * Tweak: Added support for improved dashboard notices on the plugin settings page.
253
- * Tweak: HTML attribute rel="noopener noreferrer" added to all outbound links for extra security.
254
- * Fix: Fixed PHP warning "non-numeric value encountered" when setting the tweet multiplier option to something that wasn't a number.
255
-
256
- = 1.6 =
257
- * New: Updated icons in the feed to match Twitter.
258
- * Tweak: Several CSS changes to make the Twitter feed text responsive with more themes.
259
- * Fix: Removed debugging code causing issue when saving settings on the "Customize" tab.
260
-
261
- = 1.5.1 =
262
- * New: To help us improve the plugin we have added usage tracking so that we can understand what features and settings are being used, and which features matter to you the most. The plugin will send a report in the background once per week with your plugin settings and basic information about your website environment. No personal or sensitive data is collected (such as email addresses, Twitter account information, license keys, etc). You can opt-out by simply disabling the setting at: Twitter Feed > Customize > Misc > Enable Usage Tracking. See [here](https://smashballoon.com/custom-twitter-feeds/docs/usage-tracking/) for more information.
263
- * Tweak: When a feed is from a Twitter account that has a lot of reply tweets, the plugin will collect more tweets initially for better performance.
264
- * Tweak: Hashtag feeds have retweets filtered out by default for improved feed performance.
265
- * Fix: Several strings updated to be translatable.
266
-
267
- = 1.5 =
268
- * New: Added a "Twitter Feeds" Gutenberg block to use in the block editor, allowing you to easily add a Twitter feed to posts and pages.
269
- * Tweak: Added function ctf_init() to easily rerun JavaScript for the plugin.
270
- * Tweak: Non Feeds for YouTube admin notices are removed when viewing settings pages for Feeds for YouTube.
271
-
272
- = 1.4.1 =
273
- * Tweak: Added aria-label attributes to SVG icons for improved accessibility.
274
- * Fix: Fixed PHP warning that occurred when a URL in the tweet text did not have a full length URL available from the Twitter API.
275
- * Fix: Using the setting to link the tweet text to Twitter would cause nested links and invalid HTML.
276
- * Fix: Fixed Twitter logo not displaying if avatar, author, and date were hidden.
277
-
278
- = 1.4 =
279
- * New: Twitter logo added to the top right of each tweet to fit with Twitter's display guidelines. Customize the logo on the "Style" tab, "Author" area or hide it using the settings at Twitter Feeds > Customize > Show/Hide.
280
- * New: Minified versions of JavaScript and CSS files now used.
281
- * New: JavaScript file added to the source of the page only when the Custom Twitter Feeds shortcode is used on the page.
282
- * New: SVGs used for icons instead of Font Awesome font file. You can switch back to using the Font Awesome font file using the setting at Twitter Feeds > Customize > Advanced.
283
- * New: Tweets that are replies to the same Twitter account or mention the same account can be included in the Twitter feed widget by enabling the setting "Always include replies to self in the feed".
284
- * New: Added a setting to completely disable Twitter intents widget.js file.
285
- * Tweak: The HTML element used for icons has been switched from "i" to "span" for better accessibility.
286
- * Tweak: Changed styling of Twitter media placeholder icons.
287
- * Tweak: Combined widgets.js file with native JavaScript file for the plugin.
288
- * Tweak: Twitter widgets.js will only be enqueued when "actions" are included in the Twitter feed.
289
- * Tweak: Tweet text line height style set to 1.4.
290
- * Fix: Bio text not displaying in header even when enabled.
291
-
292
- = 1.3 =
293
- * New: You can now choose to only display a certain amount of text characters in the Tweets in your Twitter widget, with a clickable link to display the rest. This is set to be 280 characters by default, but can be changed by using the following setting: Twitter Feeds > Customize > Style > Tweet Text > Text Length, or by using the "textlength" shortcode setting.
294
- * Tweak: Twitter intents JavaScript not loaded on the page if tweet actions are removed
295
- * Fix: Removed target="_blank" from div element
296
-
 
 
 
 
 
297
  [See changelog for all versions](https://plugins.svn.wordpress.org/custom-twitter-feeds/trunk/changelog.txt).
1
+ === Custom Twitter Feeds (Tweets Widget) ===
2
+ Author: Smash Balloon
3
+ Contributors: smashballoon, craig-at-smash-balloon
4
+ Support Website: http://smashballoon/custom-twitter-feeds/
5
+ Tags: Twitter, Twitter feed, Twitter Tweets, Twitter widget, Custom Twitter Feed
6
+ Requires at least: 3.4
7
+ Tested up to: 5.8
8
+ Stable tag: 1.8.2
9
+ License: GPLv2 or later
10
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
+
12
+ Custom Twitter Feeds allows you to display completely customizable Twitter Tweets from any user timeline, Twitter hashtag, or home timeline on your website.
13
+
14
+ == Description ==
15
+ Display **completely customizable**, **responsive** and **search engine crawlable** Twitter feeds on your website. The Custom Twitter Feeds plugin will display Tweets that completely match the look and feel of your site and includes tons of customization options!
16
+
17
+ = Features =
18
+ * **Completely Customizable** - by default the Twitter feeds will inherit your theme's styles
19
+ * Twitter feed content is **crawlable by search engines** adding SEO value to your site
20
+ * **Completely responsive and mobile optimized** - your Twitter feeds work on any screen size
21
+ * Display Tweets from any user, your own Twitter account and those you follow, or from a specific hashtag
22
+ * Display **multiple Twitter feeds** from different Twitter users on multiple pages or widgets
23
+ * Post caching means that your Twitter feed loads **lightning fast** and minimizes Twitter API requests
24
+ * **Infinitely load more** of your Tweets with the 'Load More' button
25
+ * Built-in easy to use "Custom Twitter Feeds" Widget makes it easy to display Tweets from any Twitter account
26
+ * Fully internationalized and translatable into any language
27
+ * Display a beautiful header at the top of your Twitter feed
28
+ * Enter your own custom CSS or JavaScript for even deeper customization of your Tweets
29
+
30
+ For simple step-by-step directions on how to set up the Custom Twitter Feeds plugin please refer to our [setup guide](http://smashballoon.com/custom-twitter-feeds/free/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds setup guide').
31
+
32
+ = Benefits =
33
+ * **Increase social engagement** between you and your users, customers, or fans
34
+ * **Save time** by using the Custom Twitter Feeds plugin to generate dynamic, search engine crawlable content on your website
35
+ * **Get more follows** by displaying your Twitter content directly on your site
36
+ * **Improve your SEO** as all of that quality keyword-rich Twitter content is directly embedded into your website
37
+ * **Keep users on your site** by letting them reply and like your Tweets directly on your site
38
+ * Display your Twitter content **your way** to perfectly match your website's style
39
+ * The Custom Twitter Feeds plugin is **updated regularly** with new features, bug-fixes and Twitter API changes
40
+ * Support is quick and effective
41
+ * We're dedicated to providing the **most customizable**, **robust** and **well supported** Twitter feed plugin in the world!
42
+
43
+ [View the reviews](https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/) to see what other users are saying about the Custom Twitter Feeds plugin.
44
+
45
+ = Feedback or Support =
46
+ We're dedicated to providing the most robust and well supported Twitter feed plugin in the world, so if you have an issue or any feedback on how to improve the plugin then please [let us know](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf 'Twitter Feed Support').
47
+
48
+ If you like the plugin then please consider leaving a review, as it really helps to support the plugin. If you have an issue then please allow us to help you fix it before leaving a review. Just [let us know](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf 'Twitter Feed Support') what the problem is and we'll get back to you right away.
49
+
50
+ = Featured Reviews =
51
+ "***Great for Customization** - Great plugin! Superb customization options! Am changing all twitter feeds for all my websites to this! I have tried several Twitter plugins so far. This is the best I have come across :)*" - [@nehavivekpatil](https://wordpress.org/support/topic/great-for-customization-2/)
52
+
53
+ "***Excellent plug-in. Excellent support.** - Excellent instructions and guidance. Patient and prompt support for the newbies. Regular updates. You can't lose. Thank you Smash Balloon.* - [@nuspa](https://wordpress.org/support/topic/excellent-plug-in-excellent-support-2/)
54
+
55
+ "***Amazing Support and a Great Twitter Plugin** - I am using the free version of this Twitter plugin and identified a real problem in the display on my website. I emailed support and Craig was all over the fix for me. Seven or so emails later (even on a Sunday) the problem was fixed and Craig was still reaching out to make sure all was well.*
56
+
57
+ *In a world of crappy support for premium plugins, and even worse support for free plugins, this kind of support (and listening to customers) that Craig and Smash Balloon provided me make their premium products worth every penny. In fact, I am going to buy them on my next project. They deserve it.*
58
+
59
+ *Thanks again, Craig!*" - [@kcwebguy](https://wordpress.org/support/topic/amazing-support-and-a-great-twitter-plugin/)
60
+
61
+ "***At last a configurable Twitter Feed that WORKS!** - I have messed with Twitter feeds on sites so many times only to be frustrated. This Plugin is the best I have tried. Stable, configurable and not too hefty a drag on performance. Well done!* - [@mikemac67](https://wordpress.org/support/topic/at-last-a-configurable-feed-that-works/)
62
+
63
+ "***Everything you'll need in a Twitter feed plugin** - We've tried a few Twitter feed plugins across a bunch of our sites, and this one is now our go-to favorite. Most of the others we've tried have either styling or performance issues, but we've been using this plugin for a few months now, and it's been perfect. There are tons of customization options, the feed looks great, and... it works!*
64
+
65
+ *Kudos to the SmashBalloon team on a well supported, tightly developed, feature-packed WordPress Plugin.* - [@laymanlab](https://wordpress.org/support/topic/everything-youll-need-in-a-twitter-feed-plugin/)
66
+
67
+ = Pro Version =
68
+ We recently released a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features:
69
+
70
+ * Display Tweets from **multiple users or hashtags in the same Twitter feed**
71
+ * Display **photos**, **videos**, and **gifs** and view them in a **popup lightbox** directly on your site
72
+ * Multi-column **Masonry layout** [demo](http://smashballoon.com/custom-twitter-feeds/demo/masonry?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Masonry Demo')
73
+ * Allow **filtering** of Twitter timelines include/exclude any/all keywords/hashtags
74
+ * Customizable **Carousels** and **Slideshows** of your Tweets
75
+ * Fully functional Twitter **search endpoint**
76
+ * Display Tweets you're mentioned in
77
+ * Tweet-specific **moderation system**
78
+ * Visual **Twitter cards** displayed with links which support them
79
+ * Include **Tweet replies** (in reply to tweets)
80
+ * **Combine multiple Twitter feeds** into one
81
+ * **Autoload more Tweets** when scrolling
82
+
83
+ Try the Pro version [demo here](http://smashballoon.com/custom-twitter-feeds/demo?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Demo')
84
+
85
+ == Installation ==
86
+ 1. Install the Custom Twitter Feeds plugin either via the WordPress plugin directory, or by uploading the files to your web server (in the /wp-content/plugins/ directory).
87
+ 2. Activate the Custom Twitter Feeds plugin through the 'Plugins' menu in WordPress.
88
+ 3. Navigate to the 'Twitter Feed' settings page to configure your Twitter feed.
89
+ 4. Use the shortcode [custom-twitter-feeds] in your page, post or widget to display your Twitter feed.
90
+ 5. You can display multiple Twitter feeds with different configurations by specifying the necessary parameters directly in the shortcode: [custom-twitter-feeds hashtag=#smashballoon].
91
+
92
+ For simple step-by-step directions on how to set up the Custom Twitter Feeds plugin please refer to our [setup guide](http://smashballoon.com/custom-twitter-feeds/free/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds setup guide').
93
+
94
+ = Setting up the Free Custom Twitter Feeds WordPress Plugin =
95
+
96
+ If you have an issue or question while setting up or using the plugin then please submit a support ticket and we'll get back to you as soon as we can.
97
+
98
+ 1) Once you've installed the Custom Twitter Feeds plugin click on the "Twitter Feeds" item in your WordPress menu
99
+
100
+ 2) Click on the large blue Twitter login button to get your Twitter Access Token and Twitter Secret. Note; if you have your own Twitter Developer App set up then you can enter your Twitter information manually by enabling the checkbox below the Twitter login button.
101
+
102
+ 3) Authorize the Custom Twitter Feeds plugin to read your Tweets.
103
+ Note; the plugin does not obtain permission to edit or write to your Twitter account, only to read your Twitter content.
104
+
105
+ 4) Twitter sends back your Twitter Access Token and Twitter Secret which are then automatically saved by the Custom Twitter Feeds plugin. This information is required in order to connect to the Twitter API.
106
+
107
+ 5) Enter a Twitter screenname to display Tweets from. Alternatively, choose to display Tweets from your Twitter home timeline or a Twitter hashtag.
108
+
109
+ 6) Navigate to the Customize and Style pages to customize your Twitter feed.
110
+
111
+ 7) Once you've customized your Twitter feed, click on the "Display Your Feed" tab for directions on how to display your Twitter feed (or multiple Twitter feeds).
112
+
113
+ 8) Copy the [custom-twitter-feeds] shortcode and paste it into any page, post or widget where you want the Twitter feed to appear.
114
+
115
+ 9) You can paste the [custom-twitter-feeds] shortcode directly into your page editor.
116
+
117
+ 10) You can use the default WordPress 'Text' widget to display your Twitter Feed in a sidebar or other widget area.
118
+
119
+ 11) View your website to see your Twitter feed(s) in all their glory!
120
+
121
+ == Frequently Asked Questions ==
122
+
123
+ = Can I display multiple Twitter feeds on my site or on the same page? =
124
+
125
+ Yep. You can display multiple Twitter feeds by using our built-in shortcode options, for example: `[custom-twitter-feeds screenname="smashballoon" num=3]`. You can add it to any post, page, or widget, to easily display your Twitter feed in a beautiful Twitter widget.
126
+
127
+ = How do I embed the Twitter Feed directly into a WordPress page template? =
128
+
129
+ You can embed your Twitter feed directly into a template file by using the WordPress [do_shortcode](http://codex.wordpress.org/Function_Reference/do_shortcode) function: `<?php echo do_shortcode('[custom-twitter-feeds]'); ?>`.
130
+
131
+ = Is the content of my Twitter feed crawlable by search engines and how does it help improve my SEO? =
132
+
133
+ Unlike other Twitter plugins which use iframes to embed your Twitter feed into your page in a widget, the Custom Twitter Feeds uses server-side code to embed your Twitter feed widget content directly into your page. This adds dynamic, search engine crawlable content to your site.
134
+
135
+ = Will Custom Twitter Feeds work with W3 Total Cache or other caching plugins? =
136
+
137
+ The Custom Twitter Feeds plugin should work in compatibility with most, if not all, caching plugins, but you may need to tweak the settings in order to allow the Twitter feed to update successfully and display your latest Twitter posts. If you are experiencing problems with your Twitter feed widget not updating then try disabling either 'Page Caching' or 'Object Caching' in W3 Total Cache (or any other similar caching plugin) to see whether that fixes the problem and the Twitter feed displays and updates successfully.
138
+
139
+ = The font in my Twitter feed isn't inherited from my website =
140
+
141
+ Be default your Twitter feed should inherit the font from your website. If this isn't the case then it's possible that something in the theme is preventing this from happening. An issue that we've run into before is that &lt;code&gt; tags are added into the Twitter feed widget HTML by either the theme, another plugin, or perhaps even the custom-twitter-feeds shortcode being wrapped in code tags in your page editor. To fix this;
142
+
143
+ 1) First – go to your page editor, click on the 'Text' tab in the top right of the editor box, and check whether the [custom-twitter-feeds] shortcode is being wrapped in &lt;code&gt; tags, like so: &lt;code&gt;[custom-twitter-feeds]&lt;/code&gt;. If it is, then remove them.
144
+
145
+ 2) If this doesn't solve the problem then try adding the following to the plugin's "Custom CSS" section (Twitter Feeds > Customize > Custom CSS):
146
+
147
+ `#ctf code { font-family: sans-serif; }`
148
+
149
+ You can replace the font-family with whatever font your theme is using.
150
+
151
+ = Differences between the free version and Pro version of the Custom Twitter Feeds plugin =
152
+
153
+ The main differences between the free version and Pro version of the Custom Twitter Feeds plugin are listed below. The extra features available in the Pro version are as follows:
154
+
155
+ - Display photos, videos, and gifs in your Tweets, with support for 3rd party services such as YouTube, Vimeo, Vine, and SoundCloud
156
+ - View Twitter media in a pop-up lightbox directly on your site
157
+ - Display Tweets in a multi-column Masonry layout
158
+ - Display your Twitter feed in rotating Carousels / Slideshows
159
+ - Twitter Cards (rich, standout links) are displayed in Tweets for links that support them
160
+ - Create advanced Twitter "Search" feeds or use the "Mentions" timeline
161
+ - Combine Twitter feeds of multiple types (i.e. User feeds and Hashtag Twitter feeds combined)
162
+ - Filter feeds by hashtag, word(s), or remove specific tweets
163
+ - Autoload more tweets when scrolling to the bottom of the Twitter feed
164
+ - Include Tweet replies ("in reply to")
165
+
166
+ You can see the demo of the Custom Twitter Feeds Pro version [here](https://smashballoon.com/custom-twitter-feeds/demo/?utm_source=wordpress&utm_campaign=ctf), and a video screencast [here](https://smashballoon.com/custom-twitter-feeds/#watch-video?utm_source=wordpress&utm_campaign=ctf).
167
+
168
+ = I'm receiving an error message when trying to display my Twitter feed =
169
+
170
+ If you receive an error message when trying to display your Twitter Feed widget then you can use the error reference below to diagnose the issue and find the relevant solution.
171
+
172
+ **Twitter Error:**
173
+ Could not authenticate you
174
+
175
+ **Causes:**
176
+ - You may be using Twitter access tokens that are not valid - See #1 below
177
+ - You checked the box to enter your own Twitter app information, but one or more of the fields are incorrect - See #2 below
178
+
179
+ **Twitter Error:**
180
+ Invalid or expired token
181
+
182
+ **Causes:**
183
+ - You may not have entered your Twitter access tokens or they are not valid - See #1 below
184
+
185
+ **Twitter Error:**
186
+ Unable to load tweets (with no other explanation)
187
+
188
+ **Causes:**
189
+ - You may need to raise the number of tweets to retrieve and display in your Twitter feed widget - See #3 below
190
+
191
+ **Twitter Error:**
192
+ Sorry, that Twitter page does not exist
193
+
194
+ **Causes:**
195
+ - There may be a typo in the Twitter screen name or hashtag you are attempting to use
196
+
197
+ **Twitter Error:**
198
+ Over capacity
199
+
200
+ **Causes:**
201
+ - Twitter's servers are over capacity so the API is unusable. This will correct itself after some time.
202
+
203
+ **Twitter Error:**
204
+ Connection timed out after 10000 milliseconds
205
+
206
+ **Causes:**
207
+ - Most likely this is due to your server blocking access to the Twitter API – See #4 below
208
+
209
+ **Solutions**
210
+
211
+ **#1 - Your Twitter access tokens might not be valid**
212
+
213
+ The easiest way to verify this is by going back to the Settings page for the Custom Twitter Feeds plugin and clicking the big blue Twitter login button on the "Configure" tab to get new Twitter access tokens. If you haven't set up your own Twitter App, the only way to use the Custom Twitter Feeds plugin is to click the big blue button to get a Twitter access token and Twitter access token secret that is compatible with the default Twitter client used by Smash Balloon.
214
+
215
+ **#2 - You have checked the box to use your own Twitter app information but one or more of the fields are incorrect**
216
+
217
+ All four fields, Twitter consumer token, Twitter consumer secret, Twitter access token, and Twitter access token secret, need to come from the Twitter app that you set up on Twitter.com. Try returning to your personal Twitter app management page https://apps.twitter.com/ and confirming that all four fields, Twitter consumer token, Twitter consumer secret, Twitter access token, and Twitter access token secret are entered correctly in the corresponding fields on the Custom Twitter Feeds plugin's "Configure" tab.
218
+
219
+ **#3 - You may need to raise the number of tweets to retrieve**
220
+
221
+ Navigate to the type of Twitter feed you are trying to display on twitter.com. For example, if you are displaying a Twitter feed from the screenname "smashballoon", go to https://twitter.com/smashballoon. Make sure there are tweets visible. Then click on the link "Tweets & replies". If there are a more recent replies than original tweets, there may be too many tweets being filtered out to display any. Twitter replies are removed by default. You can raise the number of tweets to retrieve initially by going to the Custom Twitter Feed Settings page, "Customize" tab, and navigating to the "Advanced" area. Then raise the "Tweet Multiplier" and test to see if your Twitter feed now displays tweets.
222
+
223
+ If you're still having trouble displaying your Tweets after trying the common issues above then please [contact support](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf) for assistance.
224
+
225
+ == Screenshots ==
226
+
227
+ 1. By default the Custom Twitter Feeds plugin inherits styles from your theme
228
+ 2. Display multiple Twitter feeds and customize each one
229
+ 3. Custom Twitter Feeds Settings pages
230
+ 4. Built in customization settings make customizing your Twitter feed widget easy
231
+ 5. Additional styling options are built into the Custom Twitter Feeds plugin
232
+ 6. Use handy shortcode options to customize individual Twitter feeds
233
+ 7. To display a Twitter feed just copy and paste the shortcode into a Twitter widget or page
234
+
235
+ == Changelog ==
236
+ = 1.8.2 =
237
+ * Tested with WordPress 5.8 update.
238
+ * Fix: "Display your feed" tab was showing the wrong shortcode for the "layout" setting available in the pro version.
239
+ * Fix: Fixed an issue with GDPR Cookie Consent by Web Toffee integration.
240
+
241
+ = 1.8.1 =
242
+ * Fix: Fixed several issues with GDPR Cookie Consent by Web Toffee integration.
243
+ * Tweak: Added an easy link to the [Pro demo](https://smashballoon.com/custom-twitter-feeds/demo/)
244
+
245
+ = 1.8 =
246
+ * New: The locations of the Twitter feeds on your site will now be logged and listed on a single page for easier management. After this feature has been active for awhile, a "Feed Finder" link will appear next to the Feed Type setting on the plugin Settings page which allows you to see a list of all feeds on your site along with their locations.
247
+ * New: Added support for WordPress based translations using language files.
248
+ * Tweak: Changed the default method of retrieving posts to use the WP_HTTP class.
249
+ * Fix: Updated jQuery methods for compatibility with WordPress 5.7.
250
+
251
+ = 1.7 =
252
+ * New: Integrations with popular GDPR cookie consent solutions added: Cookie Notice by dFactory, GDPR Cookie Consent by WebToffee, Cookiebot by Cybot A/S, Complianz by Really Simple Plugins, and Borlabs Cookie by Borlabs. Visit the Twitter Feed settings page, Customize tab, Advanced section for more information.
253
+ * Tweak: Icon font support was discontinued. Only SVGs will be used for icons in feeds.
254
+ * Tweak: Twitter Intents JS updated to the latest version.
255
+
256
+ = 1.6.1 =
257
+ * Tweak: Added support for improved dashboard notices on the plugin settings page.
258
+ * Tweak: HTML attribute rel="noopener noreferrer" added to all outbound links for extra security.
259
+ * Fix: Fixed PHP warning "non-numeric value encountered" when setting the tweet multiplier option to something that wasn't a number.
260
+
261
+ = 1.6 =
262
+ * New: Updated icons in the feed to match Twitter.
263
+ * Tweak: Several CSS changes to make the Twitter feed text responsive with more themes.
264
+ * Fix: Removed debugging code causing issue when saving settings on the "Customize" tab.
265
+
266
+ = 1.5.1 =
267
+ * New: To help us improve the plugin we have added usage tracking so that we can understand what features and settings are being used, and which features matter to you the most. The plugin will send a report in the background once per week with your plugin settings and basic information about your website environment. No personal or sensitive data is collected (such as email addresses, Twitter account information, license keys, etc). You can opt-out by simply disabling the setting at: Twitter Feed > Customize > Misc > Enable Usage Tracking. See [here](https://smashballoon.com/custom-twitter-feeds/docs/usage-tracking/) for more information.
268
+ * Tweak: When a feed is from a Twitter account that has a lot of reply tweets, the plugin will collect more tweets initially for better performance.
269
+ * Tweak: Hashtag feeds have retweets filtered out by default for improved feed performance.
270
+ * Fix: Several strings updated to be translatable.
271
+
272
+ = 1.5 =
273
+ * New: Added a "Twitter Feeds" Gutenberg block to use in the block editor, allowing you to easily add a Twitter feed to posts and pages.
274
+ * Tweak: Added function ctf_init() to easily rerun JavaScript for the plugin.
275
+ * Tweak: Non Feeds for YouTube admin notices are removed when viewing settings pages for Feeds for YouTube.
276
+
277
+ = 1.4.1 =
278
+ * Tweak: Added aria-label attributes to SVG icons for improved accessibility.
279
+ * Fix: Fixed PHP warning that occurred when a URL in the tweet text did not have a full length URL available from the Twitter API.
280
+ * Fix: Using the setting to link the tweet text to Twitter would cause nested links and invalid HTML.
281
+ * Fix: Fixed Twitter logo not displaying if avatar, author, and date were hidden.
282
+
283
+ = 1.4 =
284
+ * New: Twitter logo added to the top right of each tweet to fit with Twitter's display guidelines. Customize the logo on the "Style" tab, "Author" area or hide it using the settings at Twitter Feeds > Customize > Show/Hide.
285
+ * New: Minified versions of JavaScript and CSS files now used.
286
+ * New: JavaScript file added to the source of the page only when the Custom Twitter Feeds shortcode is used on the page.
287
+ * New: SVGs used for icons instead of Font Awesome font file. You can switch back to using the Font Awesome font file using the setting at Twitter Feeds > Customize > Advanced.
288
+ * New: Tweets that are replies to the same Twitter account or mention the same account can be included in the Twitter feed widget by enabling the setting "Always include replies to self in the feed".
289
+ * New: Added a setting to completely disable Twitter intents widget.js file.
290
+ * Tweak: The HTML element used for icons has been switched from "i" to "span" for better accessibility.
291
+ * Tweak: Changed styling of Twitter media placeholder icons.
292
+ * Tweak: Combined widgets.js file with native JavaScript file for the plugin.
293
+ * Tweak: Twitter widgets.js will only be enqueued when "actions" are included in the Twitter feed.
294
+ * Tweak: Tweet text line height style set to 1.4.
295
+ * Fix: Bio text not displaying in header even when enabled.
296
+
297
+ = 1.3 =
298
+ * New: You can now choose to only display a certain amount of text characters in the Tweets in your Twitter widget, with a clickable link to display the rest. This is set to be 280 characters by default, but can be changed by using the following setting: Twitter Feeds > Customize > Style > Tweet Text > Text Length, or by using the "textlength" shortcode setting.
299
+ * Tweak: Twitter intents JavaScript not loaded on the page if tweet actions are removed
300
+ * Fix: Removed target="_blank" from div element
301
+
302
  [See changelog for all versions](https://plugins.svn.wordpress.org/custom-twitter-feeds/trunk/changelog.txt).
changelog.txt CHANGED
@@ -1,156 +1,156 @@
1
- == Changelog ==
2
- = 1.8.1 =
3
- * Fix: Fixed several issues with GDPR Cookie Consent by Web Toffee integration.
4
-
5
- = 1.8 =
6
- * New: The locations of the Twitter feeds on your site will now be logged and listed on a single page for easier management. After this feature has been active for awhile, a "Feed Finder" link will appear next to the Feed Type setting on the plugin Settings page which allows you to see a list of all feeds on your site along with their locations.
7
- * New: Added support for WordPress based translations using language files.
8
- * Tweak: Changed the default method of retrieving posts to use the WP_HTTP class.
9
- * Fix: Updated jQuery methods for compatibility with WordPress 5.7.
10
-
11
- = 1.7 =
12
- * New: Integrations with popular GDPR cookie consent solutions added: Cookie Notice by dFactory, GDPR Cookie Consent by WebToffee, Cookiebot by Cybot A/S, Complianz by Really Simple Plugins, and Borlabs Cookie by Borlabs. Visit the Twitter Feed settings page, Customize tab, Advanced section for more information.
13
- * Tweak: Icon font support was discontinued. Only SVGs will be used for icons in feeds.
14
- * Tweak: Twitter Intents JS updated to the latest version.
15
-
16
- = 1.6.1 =
17
- * Tweak: Added support for improved dashboard notices on the plugin settings page.
18
- * Tweak: HTML attribute rel="noopener noreferrer" added to all outbound links for extra security.
19
- * Fix: Fixed PHP warning "non-numeric value encountered" when setting the tweet multiplier option to something that wasn't a number.
20
-
21
- = 1.6 =
22
- * New: Updated icons in the feed to match Twitter.
23
- * Tweak: Several CSS changes to make feed text responsive with more themes.
24
- * Fix: Removed debugging code causing issue when saving settings on the "Customize" tab.
25
-
26
- = 1.5.1 =
27
- * New: To help us improve the plugin we have added usage tracking so that we can understand what features and settings are being used, and which features matter to you the most. The plugin will send a report in the background once per week with your plugin settings and basic information about your website environment. No personal or sensitive data is collected (such as email addresses, Twitter account information, license keys, etc). You can opt-out by simply disabling the setting at: Twitter Feed > Customize > Misc > Enable Usage Tracking. See [here](https://smashballoon.com/custom-twitter-feeds/docs/usage-tracking/) for more information.
28
- * Tweak: When a feed is from a Twitter account that has a lot of reply tweets, the plugin will collect more tweets initially for better performance.
29
- * Tweak: Hashtag feeds have retweets filtered out by default for improved feed performance.
30
- * Fix: Several strings updated to be translatable.
31
-
32
- = 1.5 =
33
- * New: Added a "Twitter Feeds" Gutenberg block to use in the block editor, allowing you to easily add a feed to posts and pages.
34
- * Tweak: Added function ctf_init() to easily rerun JavaScript for the plugin.
35
-
36
- = 1.4.1 =
37
- * Tweak: Added aria-label attributes to SVG icons for improved accessibility.
38
- * Fix: Fixed PHP warning that occurred when a URL in the tweet text did not have a full length URL available from the Twitter API.
39
- * Fix: Using the setting to link the tweet text to Twitter would cause nested links and invalid HTML.
40
- * Fix: Fixed Twitter logo not displaying if avatar, author, and date were hidden.
41
-
42
- = 1.4 =
43
- * New: Twitter logo added to the top right of each tweet to fit with Twitter's display guidelines. Customize the logo on the "Style" tab, "Author" area or hide it using the settings on the "Customize" tab "Show/Hide" settings.
44
- * New: Minified versions of JavaScript and CSS files now used.
45
- * New: JavaScript file added to the source of the page only when the shortcode is used on the page.
46
- * New: SVGs used for icons instead of Font Awesome font file. You can switch back to using the Font Awesome font file on the "Customize" tab, "Advanced" area.
47
- * New: Tweets that are replies to the same account or mention the same account can be included in the feed by enabling the setting "Always include replies to self in the feed".
48
- * New: Added a setting to completely disable Twitter intents widget.js file.
49
- * Tweak: The HTML element used for icons has been switched from "i" to "span" for better accessibility.
50
- * Tweak: Changed styling of media placeholder icons.
51
- * Tweak: Combined widgets.js file with native JavaScript file for the plugin.
52
- * Tweak: Twitter widgets.js will only be enqueued when "actions" are included in the feed.
53
- * Tweak: Tweet text line height style set to 1.4.
54
- * Fix: Bio text not displaying in header even when enabled.
55
-
56
- = 1.3 =
57
- * New: You can now choose to only display a certain amount of text characters in your Tweets, with a clickable link to display the rest. This is set to be 280 characters by default, but can be changed by using the following setting: Customize > Style > Tweet Text > Text Length, or by using the "textlength" shortcode setting.
58
- * Tweak: Twitter intents JavaScript not loaded on the page if tweet actions are removed
59
- * Fix: Removed target="_blank" from div element
60
-
61
- = 1.2.11 =
62
- * Fix: Quoted tweets would not display if saving settings on the "Customize tab". After updating, Enable "Quoted tweet box" in the "Show/Hide" section to display quoted tweets
63
- * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
64
-
65
- = 1.2.10 =
66
- * Tweak: Updated the Twitter login button URL
67
- * Fix: Avatar would appear to the right of Tweets or not at all in some versions of Firefox
68
- * Fix: Minor bug fixes
69
-
70
- = 1.2.9 =
71
- * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
72
- * Fix: AJAX load more not working when AJAX themes being used
73
-
74
- = 1.2.8 =
75
- * Fix: "Preserve settings on uninstall" setting not working correctly
76
- * Fix: Clear cache button would not clear all caches in some situations
77
-
78
- = 1.2.7 =
79
- * New: Added screen reader labels for improved accessibility
80
- * Fix: Bug with "persistent" cache duplicating tweets
81
-
82
- = 1.2.6 =
83
- * Fix: Twitter hashtag feeds now work for hashtags beginning with a number e.g. #1august
84
- * Fix: Added a workaround for a minor formatting issue caused by some themes
85
-
86
- = 1.2.5 =
87
- * New: Added an option to remove the placeholder icon that's used when a Tweet contains media. This can be found under `Customize > Show/Hide > Media placeholder` or using the `exclude` shortcode option, eg: `exclude="placeholder"`.
88
- * Fix: Individual tweet links in mobile would not properly link to mobile.twitter.com
89
- * Fix: Error message displayed instead of empty tweets for empty persistent caches
90
-
91
- = 1.2.4 =
92
- * Fix: Custom date format not working in settings area
93
- * Fix: Twitter avatars would disappear from the feed in Firefox for certain accounts
94
- * Fix: Changed http: links to https:
95
- * Fix: Fixed retweets always being included in persistent caches during the initial tweet retrieval
96
-
97
- = 1.2.3 =
98
- * Fix: Fix php warning when no media in tweets under certain circumstances
99
- * Fix: Missing "alt" attributes for Twitter avatars
100
-
101
- = 1.2.2 =
102
- * Fix: Fixed an issue with include/exclude string to array conversion warning
103
-
104
- = 1.2.1 =
105
- * Fix: Fixed an issue where line breaks in tweet text were being ignored
106
- * Fix: Fixed an issue where a small line was added to quoted tweets in retweets that have no media
107
-
108
- = 1.2 =
109
- * New: The plugin now uses persistent tweet caching for Twitter hashtag feeds. By default, when displaying hashtag feed Twitter only returns Tweets from the last 7 days, but the persistent cache now allows you to display these Tweets indefinitely.
110
- * New: Tweets with media will have an icon and label that links to the tweet on twitter.com
111
- * Fix: Fixed an issue with checkbox settings being changed unintentionally after obtaining a new Twitter access token
112
-
113
- = 1.1.8 =
114
- * Tweak: Updated Custom Twitter Feed plugin links for new WordPress.org repo
115
- * Fix: Minor bug fixes
116
-
117
- = 1.1.7 =
118
- * Fix: Fixed an issue with the Twitter Access Token and Twitter Secrets not automatically being saved when initially obtaining them
119
- * Fix: Fixed an issue with some customization settings not saving occasionally
120
-
121
- = 1.1.6 =
122
- * Fix: Fixed an issue when creating a Search feed using the built-in Custom Twitter Feeds widget box
123
- * Fix: Fixed an issue with the checkbox that allows you to toggle links on/off in the Tweet text
124
-
125
- = 1.1.5 =
126
- * Fix: Fixed a rare issue when loading more Tweets
127
- * Fix: When there is no bio text in the header then the Twitter screenname text is now automatically centered vertically
128
-
129
- = 1.1.4 =
130
- * Fix: Fixed an issue with some setting checkboxes
131
- * Fix: Fixed a rare encoding issue which occurred on some server configurations
132
- * Tested with the upcoming WordPress 4.6 update
133
-
134
- = 1.1.3 =
135
- * Fix: Prevented any duplicate Twitter Tweets from being shown
136
- * Fix: Fixed a rare issue where a couple of settings weren't being saved successfully
137
-
138
- = 1.1.2 =
139
- * New: Launched a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features!
140
- * Fix: Minor bug fixes
141
-
142
- = 1.1.1 =
143
- * Fix: Added SSL support for Twitter avatar images so https version is used
144
- * Fix: Fixed an issue with the "hours" custom text string displaying the "minutes" text instead
145
-
146
- = 1.1 =
147
- * New: Added a setting to translate the "Retweeted" text
148
- * Tweak: If there aren't enough Tweets to populate the feed them Ajax in more automatically
149
- * Fix: Custom JavaScript is now rerun every time the Load More button is used
150
- * Fix: CSS display tweaks
151
-
152
- = 1.0.1 =
153
- * Bug fixes
154
-
155
- = 1.0 =
156
  * Launched the Custom Twitter Feeds plugin!
1
+ == Changelog ==
2
+ = 1.8.1 =
3
+ * Fix: Fixed several issues with GDPR Cookie Consent by Web Toffee integration.
4
+
5
+ = 1.8 =
6
+ * New: The locations of the Twitter feeds on your site will now be logged and listed on a single page for easier management. After this feature has been active for awhile, a "Feed Finder" link will appear next to the Feed Type setting on the plugin Settings page which allows you to see a list of all feeds on your site along with their locations.
7
+ * New: Added support for WordPress based translations using language files.
8
+ * Tweak: Changed the default method of retrieving posts to use the WP_HTTP class.
9
+ * Fix: Updated jQuery methods for compatibility with WordPress 5.7.
10
+
11
+ = 1.7 =
12
+ * New: Integrations with popular GDPR cookie consent solutions added: Cookie Notice by dFactory, GDPR Cookie Consent by WebToffee, Cookiebot by Cybot A/S, Complianz by Really Simple Plugins, and Borlabs Cookie by Borlabs. Visit the Twitter Feed settings page, Customize tab, Advanced section for more information.
13
+ * Tweak: Icon font support was discontinued. Only SVGs will be used for icons in feeds.
14
+ * Tweak: Twitter Intents JS updated to the latest version.
15
+
16
+ = 1.6.1 =
17
+ * Tweak: Added support for improved dashboard notices on the plugin settings page.
18
+ * Tweak: HTML attribute rel="noopener noreferrer" added to all outbound links for extra security.
19
+ * Fix: Fixed PHP warning "non-numeric value encountered" when setting the tweet multiplier option to something that wasn't a number.
20
+
21
+ = 1.6 =
22
+ * New: Updated icons in the feed to match Twitter.
23
+ * Tweak: Several CSS changes to make feed text responsive with more themes.
24
+ * Fix: Removed debugging code causing issue when saving settings on the "Customize" tab.
25
+
26
+ = 1.5.1 =
27
+ * New: To help us improve the plugin we have added usage tracking so that we can understand what features and settings are being used, and which features matter to you the most. The plugin will send a report in the background once per week with your plugin settings and basic information about your website environment. No personal or sensitive data is collected (such as email addresses, Twitter account information, license keys, etc). You can opt-out by simply disabling the setting at: Twitter Feed > Customize > Misc > Enable Usage Tracking. See [here](https://smashballoon.com/custom-twitter-feeds/docs/usage-tracking/) for more information.
28
+ * Tweak: When a feed is from a Twitter account that has a lot of reply tweets, the plugin will collect more tweets initially for better performance.
29
+ * Tweak: Hashtag feeds have retweets filtered out by default for improved feed performance.
30
+ * Fix: Several strings updated to be translatable.
31
+
32
+ = 1.5 =
33
+ * New: Added a "Twitter Feeds" Gutenberg block to use in the block editor, allowing you to easily add a feed to posts and pages.
34
+ * Tweak: Added function ctf_init() to easily rerun JavaScript for the plugin.
35
+
36
+ = 1.4.1 =
37
+ * Tweak: Added aria-label attributes to SVG icons for improved accessibility.
38
+ * Fix: Fixed PHP warning that occurred when a URL in the tweet text did not have a full length URL available from the Twitter API.
39
+ * Fix: Using the setting to link the tweet text to Twitter would cause nested links and invalid HTML.
40
+ * Fix: Fixed Twitter logo not displaying if avatar, author, and date were hidden.
41
+
42
+ = 1.4 =
43
+ * New: Twitter logo added to the top right of each tweet to fit with Twitter's display guidelines. Customize the logo on the "Style" tab, "Author" area or hide it using the settings on the "Customize" tab "Show/Hide" settings.
44
+ * New: Minified versions of JavaScript and CSS files now used.
45
+ * New: JavaScript file added to the source of the page only when the shortcode is used on the page.
46
+ * New: SVGs used for icons instead of Font Awesome font file. You can switch back to using the Font Awesome font file on the "Customize" tab, "Advanced" area.
47
+ * New: Tweets that are replies to the same account or mention the same account can be included in the feed by enabling the setting "Always include replies to self in the feed".
48
+ * New: Added a setting to completely disable Twitter intents widget.js file.
49
+ * Tweak: The HTML element used for icons has been switched from "i" to "span" for better accessibility.
50
+ * Tweak: Changed styling of media placeholder icons.
51
+ * Tweak: Combined widgets.js file with native JavaScript file for the plugin.
52
+ * Tweak: Twitter widgets.js will only be enqueued when "actions" are included in the feed.
53
+ * Tweak: Tweet text line height style set to 1.4.
54
+ * Fix: Bio text not displaying in header even when enabled.
55
+
56
+ = 1.3 =
57
+ * New: You can now choose to only display a certain amount of text characters in your Tweets, with a clickable link to display the rest. This is set to be 280 characters by default, but can be changed by using the following setting: Customize > Style > Tweet Text > Text Length, or by using the "textlength" shortcode setting.
58
+ * Tweak: Twitter intents JavaScript not loaded on the page if tweet actions are removed
59
+ * Fix: Removed target="_blank" from div element
60
+
61
+ = 1.2.11 =
62
+ * Fix: Quoted tweets would not display if saving settings on the "Customize tab". After updating, Enable "Quoted tweet box" in the "Show/Hide" section to display quoted tweets
63
+ * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
64
+
65
+ = 1.2.10 =
66
+ * Tweak: Updated the Twitter login button URL
67
+ * Fix: Avatar would appear to the right of Tweets or not at all in some versions of Firefox
68
+ * Fix: Minor bug fixes
69
+
70
+ = 1.2.9 =
71
+ * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
72
+ * Fix: AJAX load more not working when AJAX themes being used
73
+
74
+ = 1.2.8 =
75
+ * Fix: "Preserve settings on uninstall" setting not working correctly
76
+ * Fix: Clear cache button would not clear all caches in some situations
77
+
78
+ = 1.2.7 =
79
+ * New: Added screen reader labels for improved accessibility
80
+ * Fix: Bug with "persistent" cache duplicating tweets
81
+
82
+ = 1.2.6 =
83
+ * Fix: Twitter hashtag feeds now work for hashtags beginning with a number e.g. #1august
84
+ * Fix: Added a workaround for a minor formatting issue caused by some themes
85
+
86
+ = 1.2.5 =
87
+ * New: Added an option to remove the placeholder icon that's used when a Tweet contains media. This can be found under `Customize > Show/Hide > Media placeholder` or using the `exclude` shortcode option, eg: `exclude="placeholder"`.
88
+ * Fix: Individual tweet links in mobile would not properly link to mobile.twitter.com
89
+ * Fix: Error message displayed instead of empty tweets for empty persistent caches
90
+
91
+ = 1.2.4 =
92
+ * Fix: Custom date format not working in settings area
93
+ * Fix: Twitter avatars would disappear from the feed in Firefox for certain accounts
94
+ * Fix: Changed http: links to https:
95
+ * Fix: Fixed retweets always being included in persistent caches during the initial tweet retrieval
96
+
97
+ = 1.2.3 =
98
+ * Fix: Fix php warning when no media in tweets under certain circumstances
99
+ * Fix: Missing "alt" attributes for Twitter avatars
100
+
101
+ = 1.2.2 =
102
+ * Fix: Fixed an issue with include/exclude string to array conversion warning
103
+
104
+ = 1.2.1 =
105
+ * Fix: Fixed an issue where line breaks in tweet text were being ignored
106
+ * Fix: Fixed an issue where a small line was added to quoted tweets in retweets that have no media
107
+
108
+ = 1.2 =
109
+ * New: The plugin now uses persistent tweet caching for Twitter hashtag feeds. By default, when displaying hashtag feed Twitter only returns Tweets from the last 7 days, but the persistent cache now allows you to display these Tweets indefinitely.
110
+ * New: Tweets with media will have an icon and label that links to the tweet on twitter.com
111
+ * Fix: Fixed an issue with checkbox settings being changed unintentionally after obtaining a new Twitter access token
112
+
113
+ = 1.1.8 =
114
+ * Tweak: Updated Custom Twitter Feed plugin links for new WordPress.org repo
115
+ * Fix: Minor bug fixes
116
+
117
+ = 1.1.7 =
118
+ * Fix: Fixed an issue with the Twitter Access Token and Twitter Secrets not automatically being saved when initially obtaining them
119
+ * Fix: Fixed an issue with some customization settings not saving occasionally
120
+
121
+ = 1.1.6 =
122
+ * Fix: Fixed an issue when creating a Search feed using the built-in Custom Twitter Feeds widget box
123
+ * Fix: Fixed an issue with the checkbox that allows you to toggle links on/off in the Tweet text
124
+
125
+ = 1.1.5 =
126
+ * Fix: Fixed a rare issue when loading more Tweets
127
+ * Fix: When there is no bio text in the header then the Twitter screenname text is now automatically centered vertically
128
+
129
+ = 1.1.4 =
130
+ * Fix: Fixed an issue with some setting checkboxes
131
+ * Fix: Fixed a rare encoding issue which occurred on some server configurations
132
+ * Tested with the upcoming WordPress 4.6 update
133
+
134
+ = 1.1.3 =
135
+ * Fix: Prevented any duplicate Twitter Tweets from being shown
136
+ * Fix: Fixed a rare issue where a couple of settings weren't being saved successfully
137
+
138
+ = 1.1.2 =
139
+ * New: Launched a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features!
140
+ * Fix: Minor bug fixes
141
+
142
+ = 1.1.1 =
143
+ * Fix: Added SSL support for Twitter avatar images so https version is used
144
+ * Fix: Fixed an issue with the "hours" custom text string displaying the "minutes" text instead
145
+
146
+ = 1.1 =
147
+ * New: Added a setting to translate the "Retweeted" text
148
+ * Tweak: If there aren't enough Tweets to populate the feed them Ajax in more automatically
149
+ * Fix: Custom JavaScript is now rerun every time the Load More button is used
150
+ * Fix: CSS display tweaks
151
+
152
+ = 1.0.1 =
153
+ * Bug fixes
154
+
155
+ = 1.0 =
156
  * Launched the Custom Twitter Feeds plugin!
css/admin-notifications.css CHANGED
@@ -1,171 +1,171 @@
1
- #ctf-notifications {
2
- position: relative;
3
- background: #FFFFFF 0 0 no-repeat padding-box;
4
- box-shadow: 0px 5px 15px #0000000D;
5
- border-radius: 6px;
6
- opacity: 1;
7
- min-height: 48px;
8
- padding: 15px 102px 15px 72px;
9
- margin: 0 0 14px 0;
10
- }
11
-
12
- #ctf-notifications * {
13
- box-sizing: border-box;
14
- }
15
-
16
- #ctf-notifications .bell,
17
- #ctf-notifications .thumb{
18
- position: absolute;
19
- top: 15px;
20
- left: 15px;
21
- width: 42px;
22
- height: 48px;
23
- }
24
- #ctf-notifications .thumb img {
25
- max-width: 100%;
26
- }
27
- #ctf-notifications .thumb .img-overlay {
28
- top: 42px;
29
- left: -6px;
30
- width: 54px;
31
- position: absolute;
32
- background: #ca4a1f;
33
- color: #fff;
34
- padding: 2px 4px;
35
- border-radius: 3px;
36
- line-height: 1;
37
- font-size: 11px;
38
- font-weight: bold;
39
- text-align: center;
40
- }
41
-
42
- #ctf-notifications .messages .message {
43
- display: none;
44
- }
45
-
46
- #ctf-notifications .messages .message.current {
47
- display: block;
48
- }
49
-
50
- #ctf-notifications .messages .message .title {
51
- font-weight: bold;
52
- font-size: 17px;
53
- line-height: 20px;
54
- margin: 0;
55
- color: #444;
56
- }
57
-
58
- #ctf-notifications .messages .message .content {
59
- font-weight: normal;
60
- font-size: 13px;
61
- line-height: 20px;
62
- margin: 6px 0 40px 0;
63
- }
64
-
65
- #ctf-notifications .messages .message .buttons {
66
- margin: -30px 80px 0 0;
67
- }
68
-
69
- #ctf-notifications .messages .message .buttons a {
70
- margin: 0 6px 0 0;
71
- padding: 8px 10px;
72
- line-height: 13px;
73
- font-size: 13px;
74
- min-height: unset;
75
- }
76
-
77
- #ctf-notifications .messages .message .buttons .button-secondary {
78
- border: 1px solid #0071A1;
79
- }
80
-
81
- #ctf-notifications .dismiss {
82
- position: absolute;
83
- top: 15px;
84
- right: 15px;
85
- width: 16px;
86
- height: 16px;
87
- color: #72777C;
88
- font-size: 16px;
89
- cursor: pointer;
90
- text-align: center;
91
- vertical-align: middle;
92
- line-height: 16px;
93
- }
94
-
95
- #ctf-notifications .dismiss:hover {
96
- color: #dc3232;
97
- }
98
-
99
- #ctf-notifications .navigation {
100
- position: absolute;
101
- bottom: 15px;
102
- right: 15px;
103
- width: 63px;
104
- height: 30px;
105
- }
106
-
107
- #ctf-notifications .navigation a {
108
- display: block;
109
- width: 30px;
110
- height: 30px;
111
- border: 1px solid #7E8993;
112
- border-radius: 3px;
113
- font-size: 8px;
114
- text-align: center;
115
- vertical-align: middle;
116
- line-height: 30px;
117
- cursor: pointer;
118
- background-color: #ffffff;
119
- color: #41454A;
120
- }
121
- #ctf-notifications .navigation svg {
122
- width: 8px;
123
- height: 8px;
124
- }
125
-
126
- #ctf-notifications .navigation a:hover {
127
- background-color: #f1f1f1;
128
- }
129
-
130
- #ctf-notifications .navigation .prev {
131
- float: left;
132
- }
133
-
134
- #ctf-notifications .navigation .next {
135
- float: right;
136
- }
137
-
138
- #ctf-notifications .navigation .disabled {
139
- border-color: #dddddd;
140
- color: #A0A5AA;
141
- cursor: default;
142
- }
143
-
144
- #ctf-notifications .navigation .disabled:hover {
145
- background-color: #ffffff;
146
- }
147
-
148
- @media screen and (max-width: 768px) {
149
- #ctf-notifications {
150
- padding: 15px 15px 15px 72px;
151
- }
152
- #ctf-notifications .messages .message .title {
153
- margin: 0 30px 0 0;
154
- }
155
- #ctf-notifications .messages .message .content {
156
- font-size: 16px;
157
- line-height: 24px;
158
- }
159
- #ctf-notifications .messages .message .buttons {
160
- margin: -30px 80px 0 0;
161
- }
162
- #ctf-notifications .messages .message .buttons a {
163
- margin: 0;
164
- display: table;
165
- }
166
- #ctf-notifications .messages .message .buttons .button-secondary {
167
- margin-top: 6px;
168
- }
169
- }
170
-
171
- /*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRzL2Nzcy9hZG1pbi1ub3RpZmljYXRpb25zLmNzcyIsInNvdXJjZXMiOlsiYXNzZXRzL3Njc3MvYWRtaW4tbm90aWZpY2F0aW9ucy5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIEFkbWluIG5vdGlmaWNhdGlvbiBzdHlsZXMuXG5cbiN3cGZvcm1zLW5vdGlmaWNhdGlvbnMge1xuXG5cdHBvc2l0aW9uOiByZWxhdGl2ZTtcblx0YmFja2dyb3VuZDogI0ZGRkZGRiAwIDAgbm8tcmVwZWF0IHBhZGRpbmctYm94O1xuXHRib3gtc2hhZG93OiAwcHggNXB4IDE1cHggIzAwMDAwMDBEO1xuXHRib3JkZXItcmFkaXVzOiA2cHg7XG5cdG9wYWNpdHk6IDE7XG5cdG1pbi1oZWlnaHQ6IDQ4cHg7XG5cdHBhZGRpbmc6IDE1cHggMTAycHggMTVweCA3MnB4O1xuXHRtYXJnaW46IDAgMCAxNHB4IDA7XG5cblx0KiB7XG5cdFx0Ym94LXNpemluZzogYm9yZGVyLWJveDtcblx0fVxuXG5cdC5iZWxsIHtcblx0XHRwb3NpdGlvbjogYWJzb2x1dGU7XG5cdFx0dG9wOiAxNXB4O1xuXHRcdGxlZnQ6IDE1cHg7XG5cdFx0d2lkdGg6IDQycHg7XG5cdFx0aGVpZ2h0OiA0OHB4O1xuXHR9XG5cblx0Lm1lc3NhZ2VzIHtcblx0XHQubWVzc2FnZSB7XG5cdFx0XHRkaXNwbGF5OiBub25lO1xuXG5cdFx0XHQmLmN1cnJlbnQge1xuXHRcdFx0XHRkaXNwbGF5OiBibG9jaztcblx0XHRcdH1cblxuXHRcdFx0LnRpdGxlIHtcblx0XHRcdFx0Zm9udC13ZWlnaHQ6IGJvbGQ7XG5cdFx0XHRcdGZvbnQtc2l6ZTogMTdweDtcblx0XHRcdFx0bGluZS1oZWlnaHQ6IDIwcHg7XG5cdFx0XHRcdG1hcmdpbjogMDtcblx0XHRcdFx0Y29sb3I6ICM0NDQ7XG5cdFx0XHR9XG5cblx0XHRcdC5jb250ZW50IHtcblx0XHRcdFx0Zm9udC13ZWlnaHQ6IG5vcm1hbDtcblx0XHRcdFx0Zm9udC1zaXplOiAxM3B4O1xuXHRcdFx0XHRsaW5lLWhlaWdodDogMjBweDtcblx0XHRcdFx0bWFyZ2luOiA2cHggMCA0MHB4IDA7XG5cdFx0XHR9XG5cblx0XHRcdC5idXR0b25zIHtcblx0XHRcdFx0bWFyZ2luOiAtMzBweCA4MHB4IDAgMDtcblxuXHRcdFx0XHRhIHtcblx0XHRcdFx0XHRtYXJnaW46IDAgNnB4IDAgMDtcblx0XHRcdFx0XHRwYWRkaW5nOiA4cHggMTBweDtcblx0XHRcdFx0XHRsaW5lLWhlaWdodDogMTNweDtcblx0XHRcdFx0XHRmb250LXNpemU6IDEzcHg7XG5cdFx0XHRcdFx0bWluLWhlaWdodDogdW5zZXQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuYnV0dG9uLXNlY29uZGFyeSB7XG5cdFx0XHRcdFx0Ym9yZGVyOiAxcHggc29saWQgIzAwNzFBMTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC5kaXNtaXNzIHtcblx0XHRwb3NpdGlvbjogYWJzb2x1dGU7XG5cdFx0dG9wOiAxNXB4O1xuXHRcdHJpZ2h0OiAxNXB4O1xuXHRcdHdpZHRoOiAxNnB4O1xuXHRcdGhlaWdodDogMTZweDtcblx0XHRjb2xvcjogIzcyNzc3Qztcblx0XHRmb250LXNpemU6IDE2cHg7XG5cdFx0Y3Vyc29yOiBwb2ludGVyO1xuXHRcdHRleHQtYWxpZ246IGNlbnRlcjtcblx0XHR2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xuXHRcdGxpbmUtaGVpZ2h0OiAxNnB4O1xuXG5cdFx0Jjpob3ZlciB7XG5cdFx0XHRjb2xvcjogI2RjMzIzMjtcblx0XHR9XG5cdH1cblxuXHQubmF2aWdhdGlvbiB7XG5cdFx0cG9zaXRpb246IGFic29sdXRlO1xuXHRcdGJvdHRvbTogMTVweDtcblx0XHRyaWdodDogMTVweDtcblx0XHR3aWR0aDogNjNweDtcblx0XHRoZWlnaHQ6IDMwcHg7XG5cblx0XHRhIHtcblx0XHRcdGRpc3BsYXk6IGJsb2NrO1xuXHRcdFx0d2lkdGg6IDMwcHg7XG5cdFx0XHRoZWlnaHQ6IDMwcHg7XG5cdFx0XHRib3JkZXI6IDFweCBzb2xpZCAjN0U4OTkzO1xuXHRcdFx0Ym9yZGVyLXJhZGl1czogM3B4O1xuXHRcdFx0Zm9udC1zaXplOiA4cHg7XG5cdFx0XHR0ZXh0LWFsaWduOiBjZW50ZXI7XG5cdFx0XHR2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xuXHRcdFx0bGluZS1oZWlnaHQ6IDMwcHg7XG5cdFx0XHRjdXJzb3I6IHBvaW50ZXI7XG5cdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmO1xuXHRcdFx0Y29sb3I6ICM0MTQ1NEE7XG5cblx0XHRcdCY6aG92ZXIge1xuXHRcdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZjFmMWYxO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC5wcmV2IHtcblx0XHRcdGZsb2F0OiBsZWZ0O1xuXHRcdH1cblxuXHRcdC5uZXh0IHtcblx0XHRcdGZsb2F0OiByaWdodDtcblx0XHR9XG5cblx0XHQuZGlzYWJsZWQge1xuXHRcdFx0Ym9yZGVyLWNvbG9yOiAjZGRkZGRkO1xuXHRcdFx0Y29sb3I6ICNBMEE1QUE7XG5cdFx0XHRjdXJzb3I6IGRlZmF1bHQ7XG5cblx0XHRcdCY6aG92ZXIge1xuXHRcdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufVxuXG5AbWVkaWEgc2NyZWVuIGFuZCAobWF4LXdpZHRoOiA3NjhweCkge1xuXG5cdCN3cGZvcm1zLW5vdGlmaWNhdGlvbnMge1xuXHRcdHBhZGRpbmc6IDE1cHggMTVweCAxNXB4IDcycHg7XG5cblx0XHQubWVzc2FnZXMge1xuXG5cdFx0XHQubWVzc2FnZSB7XG5cblx0XHRcdFx0LnRpdGxlIHtcblx0XHRcdFx0XHRtYXJnaW46IDAgMzBweCAwIDA7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuY29udGVudCB7XG5cdFx0XHRcdFx0Zm9udC1zaXplOiAxNnB4O1xuXHRcdFx0XHRcdGxpbmUtaGVpZ2h0OiAyNHB4XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuYnV0dG9ucyB7XG5cdFx0XHRcdFx0bWFyZ2luOiAtMzBweCA4MHB4IDAgMDtcblxuXHRcdFx0XHRcdGEge1xuXHRcdFx0XHRcdFx0bWFyZ2luOiAwO1xuXHRcdFx0XHRcdFx0ZGlzcGxheTogdGFibGU7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0LmJ1dHRvbi1zZWNvbmRhcnkge1xuXHRcdFx0XHRcdFx0bWFyZ2luLXRvcDogNnB4O1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxufSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxBQUFBLHNCQUFzQixDQUFDO0VBRXRCLFFBQVEsRUFBRSxRQUFRO0VBQ2xCLFVBQVUsRUFBRSxpQ0FBaUM7RUFDN0MsVUFBVSxFQUFFLHNCQUFzQjtFQUNsQyxhQUFhLEVBQUUsR0FBRztFQUNsQixPQUFPLEVBQUUsQ0FBQztFQUNWLFVBQVUsRUFBRSxJQUFJO0VBQ2hCLE9BQU8sRUFBRSxvQkFBb0I7RUFDN0IsTUFBTSxFQUFFLFVBQVU7Q0FxSGxCOztBQTlIRCxBQVdDLHNCQVhxQixDQVdyQixDQUFDLENBQUM7RUFDRCxVQUFVLEVBQUUsVUFBVTtDQUN0Qjs7QUFiRixBQWVDLHNCQWZxQixDQWVyQixLQUFLLENBQUM7RUFDTCxRQUFRLEVBQUUsUUFBUTtFQUNsQixHQUFHLEVBQUUsSUFBSTtFQUNULElBQUksRUFBRSxJQUFJO0VBQ1YsS0FBSyxFQUFFLElBQUk7RUFDWCxNQUFNLEVBQUUsSUFBSTtDQUNaOztBQXJCRixBQXdCRSxzQkF4Qm9CLENBdUJyQixTQUFTLENBQ1IsUUFBUSxDQUFDO0VBQ1IsT0FBTyxFQUFFLElBQUk7Q0FvQ2I7O0FBN0RILEFBMkJHLHNCQTNCbUIsQ0F1QnJCLFNBQVMsQ0FDUixRQUFRLEFBR04sUUFBUSxDQUFDO0VBQ1QsT0FBTyxFQUFFLEtBQUs7Q0FDZDs7QUE3QkosQUErQkcsc0JBL0JtQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FPUCxNQUFNLENBQUM7RUFDTixXQUFXLEVBQUUsSUFBSTtFQUNqQixTQUFTLEVBQUUsSUFBSTtFQUNmLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLE1BQU0sRUFBRSxDQUFDO0VBQ1QsS0FBSyxFQUFFLElBQUk7Q0FDWDs7QUFyQ0osQUF1Q0csc0JBdkNtQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FlUCxRQUFRLENBQUM7RUFDUixXQUFXLEVBQUUsTUFBTTtFQUNuQixTQUFTLEVBQUUsSUFBSTtFQUNmLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLE1BQU0sRUFBRSxZQUFZO0NBQ3BCOztBQTVDSixBQThDRyxzQkE5Q21CLENBdUJyQixTQUFTLENBQ1IsUUFBUSxDQXNCUCxRQUFRLENBQUM7RUFDUixNQUFNLEVBQUUsY0FBYztDQWF0Qjs7QUE1REosQUFpREksc0JBakRrQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FzQlAsUUFBUSxDQUdQLENBQUMsQ0FBQztFQUNELE1BQU0sRUFBRSxTQUFTO0VBQ2pCLE9BQU8sRUFBRSxRQUFRO0VBQ2pCLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLFNBQVMsRUFBRSxJQUFJO0VBQ2YsVUFBVSxFQUFFLEtBQUs7Q0FDakI7O0FBdkRMLEFBeURJLHNCQXpEa0IsQ0F1QnJCLFNBQVMsQ0FDUixRQUFRLENBc0JQLFFBQVEsQ0FXUCxpQkFBaUIsQ0FBQztFQUNqQixNQUFNLEVBQUUsaUJBQWlCO0NBQ3pCOztBQTNETCxBQWdFQyxzQkFoRXFCLENBZ0VyQixRQUFRLENBQUM7RUFDUixRQUFRLEVBQUUsUUFBUTtFQUNsQixHQUFHLEVBQUUsSUFBSTtFQUNULEtBQUssRUFBRSxJQUFJO0VBQ1gsS0FBSyxFQUFFLElBQUk7RUFDWCxNQUFNLEVBQUUsSUFBSTtFQUNaLEtBQUssRUFBRSxPQUFPO0VBQ2QsU0FBUyxFQUFFLElBQUk7RUFDZixNQUFNLEVBQUUsT0FBTztFQUNmLFVBQVUsRUFBRSxNQUFNO0VBQ2xCLGNBQWMsRUFBRSxNQUFNO0VBQ3RCLFdBQVcsRUFBRSxJQUFJO0NBS2pCOztBQWhGRixBQTZFRSxzQkE3RW9CLENBZ0VyQixRQUFRLEFBYU4sTUFBTSxDQUFDO0VBQ1AsS0FBSyxFQUFFLE9BQU87Q0FDZDs7QUEvRUgsQUFrRkMsc0JBbEZxQixDQWtGckIsV0FBVyxDQUFDO0VBQ1gsUUFBUSxFQUFFLFFBQVE7RUFDbEIsTUFBTSxFQUFFLElBQUk7RUFDWixLQUFLLEVBQUUsSUFBSTtFQUNYLEtBQUssRUFBRSxJQUFJO0VBQ1gsTUFBTSxFQUFFLElBQUk7Q0FzQ1o7O0FBN0hGLEFBeUZFLHNCQXpGb0IsQ0FrRnJCLFdBQVcsQ0FPVixDQUFDLENBQUM7RUFDRCxPQUFPLEVBQUUsS0FBSztFQUNkLEtBQUssRUFBRSxJQUFJO0VBQ1gsTUFBTSxFQUFFLElBQUk7RUFDWixNQUFNLEVBQUUsaUJBQWlCO0VBQ3pCLGFBQWEsRUFBRSxHQUFHO0VBQ2xCLFNBQVMsRUFBRSxHQUFHO0VBQ2QsVUFBVSxFQUFFLE1BQU07RUFDbEIsY0FBYyxFQUFFLE1BQU07RUFDdEIsV0FBVyxFQUFFLElBQUk7RUFDakIsTUFBTSxFQUFFLE9BQU87RUFDZixnQkFBZ0IsRUFBRSxPQUFPO0VBQ3pCLEtBQUssRUFBRSxPQUFPO0NBS2Q7O0FBMUdILEFBdUdHLHNCQXZHbUIsQ0FrRnJCLFdBQVcsQ0FPVixDQUFDLEFBY0MsTUFBTSxDQUFDO0VBQ1AsZ0JBQWdCLEVBQUUsT0FBTztDQUN6Qjs7QUF6R0osQUE0R0Usc0JBNUdvQixDQWtGckIsV0FBVyxDQTBCVixLQUFLLENBQUM7RUFDTCxLQUFLLEVBQUUsSUFBSTtDQUNYOztBQTlHSCxBQWdIRSxzQkFoSG9CLENBa0ZyQixXQUFXLENBOEJWLEtBQUssQ0FBQztFQUNMLEtBQUssRUFBRSxLQUFLO0NBQ1o7O0FBbEhILEFBb0hFLHNCQXBIb0IsQ0FrRnJCLFdBQVcsQ0FrQ1YsU0FBUyxDQUFDO0VBQ1QsWUFBWSxFQUFFLE9BQU87RUFDckIsS0FBSyxFQUFFLE9BQU87RUFDZCxNQUFNLEVBQUUsT0FBTztDQUtmOztBQTVISCxBQXlIRyxzQkF6SG1CLENBa0ZyQixXQUFXLENBa0NWLFNBQVMsQUFLUCxNQUFNLENBQUM7RUFDUCxnQkFBZ0IsRUFBRSxPQUFPO0NBQ3pCOztBQUtKLE1BQU0sQ0FBQyxNQUFNLE1BQU0sU0FBUyxFQUFFLEtBQUs7RUFFbEMsQUFBQSxzQkFBc0IsQ0FBQztJQUN0QixPQUFPLEVBQUUsbUJBQW1CO0dBNkI1QjtFQTlCRCxBQU9HLHNCQVBtQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQUVQLE1BQU0sQ0FBQztJQUNOLE1BQU0sRUFBRSxVQUFVO0dBQ2xCO0VBVEosQUFXRyxzQkFYbUIsQ0FHckIsU0FBUyxDQUVSLFFBQVEsQ0FNUCxRQUFRLENBQUM7SUFDUixTQUFTLEVBQUUsSUFBSTtJQUNmLFdBQVcsRUFBRSxJQUNkO0dBQUM7RUFkSixBQWdCRyxzQkFoQm1CLENBR3JCLFNBQVMsQ0FFUixRQUFRLENBV1AsUUFBUSxDQUFDO0lBQ1IsTUFBTSxFQUFFLGNBQWM7R0FVdEI7RUEzQkosQUFtQkksc0JBbkJrQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQVdQLFFBQVEsQ0FHUCxDQUFDLENBQUM7SUFDRCxNQUFNLEVBQUUsQ0FBQztJQUNULE9BQU8sRUFBRSxLQUFLO0dBQ2Q7RUF0QkwsQUF3Qkksc0JBeEJrQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQVdQLFFBQVEsQ0FRUCxpQkFBaUIsQ0FBQztJQUNqQixVQUFVLEVBQUUsR0FBRztHQUNmIn0= */
1
+ #ctf-notifications {
2
+ position: relative;
3
+ background: #FFFFFF 0 0 no-repeat padding-box;
4
+ box-shadow: 0px 5px 15px #0000000D;
5
+ border-radius: 6px;
6
+ opacity: 1;
7
+ min-height: 48px;
8
+ padding: 15px 102px 15px 72px;
9
+ margin: 0 0 14px 0;
10
+ }
11
+
12
+ #ctf-notifications * {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ #ctf-notifications .bell,
17
+ #ctf-notifications .thumb{
18
+ position: absolute;
19
+ top: 15px;
20
+ left: 15px;
21
+ width: 42px;
22
+ height: 48px;
23
+ }
24
+ #ctf-notifications .thumb img {
25
+ max-width: 100%;
26
+ }
27
+ #ctf-notifications .thumb .img-overlay {
28
+ top: 42px;
29
+ left: -6px;
30
+ width: 54px;
31
+ position: absolute;
32
+ background: #ca4a1f;
33
+ color: #fff;
34
+ padding: 2px 4px;
35
+ border-radius: 3px;
36
+ line-height: 1;
37
+ font-size: 11px;
38
+ font-weight: bold;
39
+ text-align: center;
40
+ }
41
+
42
+ #ctf-notifications .messages .message {
43
+ display: none;
44
+ }
45
+
46
+ #ctf-notifications .messages .message.current {
47
+ display: block;
48
+ }
49
+
50
+ #ctf-notifications .messages .message .title {
51
+ font-weight: bold;
52
+ font-size: 17px;
53
+ line-height: 20px;
54
+ margin: 0;
55
+ color: #444;
56
+ }
57
+
58
+ #ctf-notifications .messages .message .content {
59
+ font-weight: normal;
60
+ font-size: 13px;
61
+ line-height: 20px;
62
+ margin: 6px 0 40px 0;
63
+ }
64
+
65
+ #ctf-notifications .messages .message .buttons {
66
+ margin: -30px 80px 0 0;
67
+ }
68
+
69
+ #ctf-notifications .messages .message .buttons a {
70
+ margin: 0 6px 0 0;
71
+ padding: 8px 10px;
72
+ line-height: 13px;
73
+ font-size: 13px;
74
+ min-height: unset;
75
+ }
76
+
77
+ #ctf-notifications .messages .message .buttons .button-secondary {
78
+ border: 1px solid #0071A1;
79
+ }
80
+
81
+ #ctf-notifications .dismiss {
82
+ position: absolute;
83
+ top: 15px;
84
+ right: 15px;
85
+ width: 16px;
86
+ height: 16px;
87
+ color: #72777C;
88
+ font-size: 16px;
89
+ cursor: pointer;
90
+ text-align: center;
91
+ vertical-align: middle;
92
+ line-height: 16px;
93
+ }
94
+
95
+ #ctf-notifications .dismiss:hover {
96
+ color: #dc3232;
97
+ }
98
+
99
+ #ctf-notifications .navigation {
100
+ position: absolute;
101
+ bottom: 15px;
102
+ right: 15px;
103
+ width: 63px;
104
+ height: 30px;
105
+ }
106
+
107
+ #ctf-notifications .navigation a {
108
+ display: block;
109
+ width: 30px;
110
+ height: 30px;
111
+ border: 1px solid #7E8993;
112
+ border-radius: 3px;
113
+ font-size: 8px;
114
+ text-align: center;
115
+ vertical-align: middle;
116
+ line-height: 30px;
117
+ cursor: pointer;
118
+ background-color: #ffffff;
119
+ color: #41454A;
120
+ }
121
+ #ctf-notifications .navigation svg {
122
+ width: 8px;
123
+ height: 8px;
124
+ }
125
+
126
+ #ctf-notifications .navigation a:hover {
127
+ background-color: #f1f1f1;
128
+ }
129
+
130
+ #ctf-notifications .navigation .prev {
131
+ float: left;
132
+ }
133
+
134
+ #ctf-notifications .navigation .next {
135
+ float: right;
136
+ }
137
+
138
+ #ctf-notifications .navigation .disabled {
139
+ border-color: #dddddd;
140
+ color: #A0A5AA;
141
+ cursor: default;
142
+ }
143
+
144
+ #ctf-notifications .navigation .disabled:hover {
145
+ background-color: #ffffff;
146
+ }
147
+
148
+ @media screen and (max-width: 768px) {
149
+ #ctf-notifications {
150
+ padding: 15px 15px 15px 72px;
151
+ }
152
+ #ctf-notifications .messages .message .title {
153
+ margin: 0 30px 0 0;
154
+ }
155
+ #ctf-notifications .messages .message .content {
156
+ font-size: 16px;
157
+ line-height: 24px;
158
+ }
159
+ #ctf-notifications .messages .message .buttons {
160
+ margin: -30px 80px 0 0;
161
+ }
162
+ #ctf-notifications .messages .message .buttons a {
163
+ margin: 0;
164
+ display: table;
165
+ }
166
+ #ctf-notifications .messages .message .buttons .button-secondary {
167
+ margin-top: 6px;
168
+ }
169
+ }
170
+
171
+ /*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRzL2Nzcy9hZG1pbi1ub3RpZmljYXRpb25zLmNzcyIsInNvdXJjZXMiOlsiYXNzZXRzL3Njc3MvYWRtaW4tbm90aWZpY2F0aW9ucy5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIEFkbWluIG5vdGlmaWNhdGlvbiBzdHlsZXMuXG5cbiN3cGZvcm1zLW5vdGlmaWNhdGlvbnMge1xuXG5cdHBvc2l0aW9uOiByZWxhdGl2ZTtcblx0YmFja2dyb3VuZDogI0ZGRkZGRiAwIDAgbm8tcmVwZWF0IHBhZGRpbmctYm94O1xuXHRib3gtc2hhZG93OiAwcHggNXB4IDE1cHggIzAwMDAwMDBEO1xuXHRib3JkZXItcmFkaXVzOiA2cHg7XG5cdG9wYWNpdHk6IDE7XG5cdG1pbi1oZWlnaHQ6IDQ4cHg7XG5cdHBhZGRpbmc6IDE1cHggMTAycHggMTVweCA3MnB4O1xuXHRtYXJnaW46IDAgMCAxNHB4IDA7XG5cblx0KiB7XG5cdFx0Ym94LXNpemluZzogYm9yZGVyLWJveDtcblx0fVxuXG5cdC5iZWxsIHtcblx0XHRwb3NpdGlvbjogYWJzb2x1dGU7XG5cdFx0dG9wOiAxNXB4O1xuXHRcdGxlZnQ6IDE1cHg7XG5cdFx0d2lkdGg6IDQycHg7XG5cdFx0aGVpZ2h0OiA0OHB4O1xuXHR9XG5cblx0Lm1lc3NhZ2VzIHtcblx0XHQubWVzc2FnZSB7XG5cdFx0XHRkaXNwbGF5OiBub25lO1xuXG5cdFx0XHQmLmN1cnJlbnQge1xuXHRcdFx0XHRkaXNwbGF5OiBibG9jaztcblx0XHRcdH1cblxuXHRcdFx0LnRpdGxlIHtcblx0XHRcdFx0Zm9udC13ZWlnaHQ6IGJvbGQ7XG5cdFx0XHRcdGZvbnQtc2l6ZTogMTdweDtcblx0XHRcdFx0bGluZS1oZWlnaHQ6IDIwcHg7XG5cdFx0XHRcdG1hcmdpbjogMDtcblx0XHRcdFx0Y29sb3I6ICM0NDQ7XG5cdFx0XHR9XG5cblx0XHRcdC5jb250ZW50IHtcblx0XHRcdFx0Zm9udC13ZWlnaHQ6IG5vcm1hbDtcblx0XHRcdFx0Zm9udC1zaXplOiAxM3B4O1xuXHRcdFx0XHRsaW5lLWhlaWdodDogMjBweDtcblx0XHRcdFx0bWFyZ2luOiA2cHggMCA0MHB4IDA7XG5cdFx0XHR9XG5cblx0XHRcdC5idXR0b25zIHtcblx0XHRcdFx0bWFyZ2luOiAtMzBweCA4MHB4IDAgMDtcblxuXHRcdFx0XHRhIHtcblx0XHRcdFx0XHRtYXJnaW46IDAgNnB4IDAgMDtcblx0XHRcdFx0XHRwYWRkaW5nOiA4cHggMTBweDtcblx0XHRcdFx0XHRsaW5lLWhlaWdodDogMTNweDtcblx0XHRcdFx0XHRmb250LXNpemU6IDEzcHg7XG5cdFx0XHRcdFx0bWluLWhlaWdodDogdW5zZXQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuYnV0dG9uLXNlY29uZGFyeSB7XG5cdFx0XHRcdFx0Ym9yZGVyOiAxcHggc29saWQgIzAwNzFBMTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC5kaXNtaXNzIHtcblx0XHRwb3NpdGlvbjogYWJzb2x1dGU7XG5cdFx0dG9wOiAxNXB4O1xuXHRcdHJpZ2h0OiAxNXB4O1xuXHRcdHdpZHRoOiAxNnB4O1xuXHRcdGhlaWdodDogMTZweDtcblx0XHRjb2xvcjogIzcyNzc3Qztcblx0XHRmb250LXNpemU6IDE2cHg7XG5cdFx0Y3Vyc29yOiBwb2ludGVyO1xuXHRcdHRleHQtYWxpZ246IGNlbnRlcjtcblx0XHR2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xuXHRcdGxpbmUtaGVpZ2h0OiAxNnB4O1xuXG5cdFx0Jjpob3ZlciB7XG5cdFx0XHRjb2xvcjogI2RjMzIzMjtcblx0XHR9XG5cdH1cblxuXHQubmF2aWdhdGlvbiB7XG5cdFx0cG9zaXRpb246IGFic29sdXRlO1xuXHRcdGJvdHRvbTogMTVweDtcblx0XHRyaWdodDogMTVweDtcblx0XHR3aWR0aDogNjNweDtcblx0XHRoZWlnaHQ6IDMwcHg7XG5cblx0XHRhIHtcblx0XHRcdGRpc3BsYXk6IGJsb2NrO1xuXHRcdFx0d2lkdGg6IDMwcHg7XG5cdFx0XHRoZWlnaHQ6IDMwcHg7XG5cdFx0XHRib3JkZXI6IDFweCBzb2xpZCAjN0U4OTkzO1xuXHRcdFx0Ym9yZGVyLXJhZGl1czogM3B4O1xuXHRcdFx0Zm9udC1zaXplOiA4cHg7XG5cdFx0XHR0ZXh0LWFsaWduOiBjZW50ZXI7XG5cdFx0XHR2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xuXHRcdFx0bGluZS1oZWlnaHQ6IDMwcHg7XG5cdFx0XHRjdXJzb3I6IHBvaW50ZXI7XG5cdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmO1xuXHRcdFx0Y29sb3I6ICM0MTQ1NEE7XG5cblx0XHRcdCY6aG92ZXIge1xuXHRcdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZjFmMWYxO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC5wcmV2IHtcblx0XHRcdGZsb2F0OiBsZWZ0O1xuXHRcdH1cblxuXHRcdC5uZXh0IHtcblx0XHRcdGZsb2F0OiByaWdodDtcblx0XHR9XG5cblx0XHQuZGlzYWJsZWQge1xuXHRcdFx0Ym9yZGVyLWNvbG9yOiAjZGRkZGRkO1xuXHRcdFx0Y29sb3I6ICNBMEE1QUE7XG5cdFx0XHRjdXJzb3I6IGRlZmF1bHQ7XG5cblx0XHRcdCY6aG92ZXIge1xuXHRcdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufVxuXG5AbWVkaWEgc2NyZWVuIGFuZCAobWF4LXdpZHRoOiA3NjhweCkge1xuXG5cdCN3cGZvcm1zLW5vdGlmaWNhdGlvbnMge1xuXHRcdHBhZGRpbmc6IDE1cHggMTVweCAxNXB4IDcycHg7XG5cblx0XHQubWVzc2FnZXMge1xuXG5cdFx0XHQubWVzc2FnZSB7XG5cblx0XHRcdFx0LnRpdGxlIHtcblx0XHRcdFx0XHRtYXJnaW46IDAgMzBweCAwIDA7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuY29udGVudCB7XG5cdFx0XHRcdFx0Zm9udC1zaXplOiAxNnB4O1xuXHRcdFx0XHRcdGxpbmUtaGVpZ2h0OiAyNHB4XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuYnV0dG9ucyB7XG5cdFx0XHRcdFx0bWFyZ2luOiAtMzBweCA4MHB4IDAgMDtcblxuXHRcdFx0XHRcdGEge1xuXHRcdFx0XHRcdFx0bWFyZ2luOiAwO1xuXHRcdFx0XHRcdFx0ZGlzcGxheTogdGFibGU7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0LmJ1dHRvbi1zZWNvbmRhcnkge1xuXHRcdFx0XHRcdFx0bWFyZ2luLXRvcDogNnB4O1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxufSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxBQUFBLHNCQUFzQixDQUFDO0VBRXRCLFFBQVEsRUFBRSxRQUFRO0VBQ2xCLFVBQVUsRUFBRSxpQ0FBaUM7RUFDN0MsVUFBVSxFQUFFLHNCQUFzQjtFQUNsQyxhQUFhLEVBQUUsR0FBRztFQUNsQixPQUFPLEVBQUUsQ0FBQztFQUNWLFVBQVUsRUFBRSxJQUFJO0VBQ2hCLE9BQU8sRUFBRSxvQkFBb0I7RUFDN0IsTUFBTSxFQUFFLFVBQVU7Q0FxSGxCOztBQTlIRCxBQVdDLHNCQVhxQixDQVdyQixDQUFDLENBQUM7RUFDRCxVQUFVLEVBQUUsVUFBVTtDQUN0Qjs7QUFiRixBQWVDLHNCQWZxQixDQWVyQixLQUFLLENBQUM7RUFDTCxRQUFRLEVBQUUsUUFBUTtFQUNsQixHQUFHLEVBQUUsSUFBSTtFQUNULElBQUksRUFBRSxJQUFJO0VBQ1YsS0FBSyxFQUFFLElBQUk7RUFDWCxNQUFNLEVBQUUsSUFBSTtDQUNaOztBQXJCRixBQXdCRSxzQkF4Qm9CLENBdUJyQixTQUFTLENBQ1IsUUFBUSxDQUFDO0VBQ1IsT0FBTyxFQUFFLElBQUk7Q0FvQ2I7O0FBN0RILEFBMkJHLHNCQTNCbUIsQ0F1QnJCLFNBQVMsQ0FDUixRQUFRLEFBR04sUUFBUSxDQUFDO0VBQ1QsT0FBTyxFQUFFLEtBQUs7Q0FDZDs7QUE3QkosQUErQkcsc0JBL0JtQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FPUCxNQUFNLENBQUM7RUFDTixXQUFXLEVBQUUsSUFBSTtFQUNqQixTQUFTLEVBQUUsSUFBSTtFQUNmLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLE1BQU0sRUFBRSxDQUFDO0VBQ1QsS0FBSyxFQUFFLElBQUk7Q0FDWDs7QUFyQ0osQUF1Q0csc0JBdkNtQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FlUCxRQUFRLENBQUM7RUFDUixXQUFXLEVBQUUsTUFBTTtFQUNuQixTQUFTLEVBQUUsSUFBSTtFQUNmLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLE1BQU0sRUFBRSxZQUFZO0NBQ3BCOztBQTVDSixBQThDRyxzQkE5Q21CLENBdUJyQixTQUFTLENBQ1IsUUFBUSxDQXNCUCxRQUFRLENBQUM7RUFDUixNQUFNLEVBQUUsY0FBYztDQWF0Qjs7QUE1REosQUFpREksc0JBakRrQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FzQlAsUUFBUSxDQUdQLENBQUMsQ0FBQztFQUNELE1BQU0sRUFBRSxTQUFTO0VBQ2pCLE9BQU8sRUFBRSxRQUFRO0VBQ2pCLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLFNBQVMsRUFBRSxJQUFJO0VBQ2YsVUFBVSxFQUFFLEtBQUs7Q0FDakI7O0FBdkRMLEFBeURJLHNCQXpEa0IsQ0F1QnJCLFNBQVMsQ0FDUixRQUFRLENBc0JQLFFBQVEsQ0FXUCxpQkFBaUIsQ0FBQztFQUNqQixNQUFNLEVBQUUsaUJBQWlCO0NBQ3pCOztBQTNETCxBQWdFQyxzQkFoRXFCLENBZ0VyQixRQUFRLENBQUM7RUFDUixRQUFRLEVBQUUsUUFBUTtFQUNsQixHQUFHLEVBQUUsSUFBSTtFQUNULEtBQUssRUFBRSxJQUFJO0VBQ1gsS0FBSyxFQUFFLElBQUk7RUFDWCxNQUFNLEVBQUUsSUFBSTtFQUNaLEtBQUssRUFBRSxPQUFPO0VBQ2QsU0FBUyxFQUFFLElBQUk7RUFDZixNQUFNLEVBQUUsT0FBTztFQUNmLFVBQVUsRUFBRSxNQUFNO0VBQ2xCLGNBQWMsRUFBRSxNQUFNO0VBQ3RCLFdBQVcsRUFBRSxJQUFJO0NBS2pCOztBQWhGRixBQTZFRSxzQkE3RW9CLENBZ0VyQixRQUFRLEFBYU4sTUFBTSxDQUFDO0VBQ1AsS0FBSyxFQUFFLE9BQU87Q0FDZDs7QUEvRUgsQUFrRkMsc0JBbEZxQixDQWtGckIsV0FBVyxDQUFDO0VBQ1gsUUFBUSxFQUFFLFFBQVE7RUFDbEIsTUFBTSxFQUFFLElBQUk7RUFDWixLQUFLLEVBQUUsSUFBSTtFQUNYLEtBQUssRUFBRSxJQUFJO0VBQ1gsTUFBTSxFQUFFLElBQUk7Q0FzQ1o7O0FBN0hGLEFBeUZFLHNCQXpGb0IsQ0FrRnJCLFdBQVcsQ0FPVixDQUFDLENBQUM7RUFDRCxPQUFPLEVBQUUsS0FBSztFQUNkLEtBQUssRUFBRSxJQUFJO0VBQ1gsTUFBTSxFQUFFLElBQUk7RUFDWixNQUFNLEVBQUUsaUJBQWlCO0VBQ3pCLGFBQWEsRUFBRSxHQUFHO0VBQ2xCLFNBQVMsRUFBRSxHQUFHO0VBQ2QsVUFBVSxFQUFFLE1BQU07RUFDbEIsY0FBYyxFQUFFLE1BQU07RUFDdEIsV0FBVyxFQUFFLElBQUk7RUFDakIsTUFBTSxFQUFFLE9BQU87RUFDZixnQkFBZ0IsRUFBRSxPQUFPO0VBQ3pCLEtBQUssRUFBRSxPQUFPO0NBS2Q7O0FBMUdILEFBdUdHLHNCQXZHbUIsQ0FrRnJCLFdBQVcsQ0FPVixDQUFDLEFBY0MsTUFBTSxDQUFDO0VBQ1AsZ0JBQWdCLEVBQUUsT0FBTztDQUN6Qjs7QUF6R0osQUE0R0Usc0JBNUdvQixDQWtGckIsV0FBVyxDQTBCVixLQUFLLENBQUM7RUFDTCxLQUFLLEVBQUUsSUFBSTtDQUNYOztBQTlHSCxBQWdIRSxzQkFoSG9CLENBa0ZyQixXQUFXLENBOEJWLEtBQUssQ0FBQztFQUNMLEtBQUssRUFBRSxLQUFLO0NBQ1o7O0FBbEhILEFBb0hFLHNCQXBIb0IsQ0FrRnJCLFdBQVcsQ0FrQ1YsU0FBUyxDQUFDO0VBQ1QsWUFBWSxFQUFFLE9BQU87RUFDckIsS0FBSyxFQUFFLE9BQU87RUFDZCxNQUFNLEVBQUUsT0FBTztDQUtmOztBQTVISCxBQXlIRyxzQkF6SG1CLENBa0ZyQixXQUFXLENBa0NWLFNBQVMsQUFLUCxNQUFNLENBQUM7RUFDUCxnQkFBZ0IsRUFBRSxPQUFPO0NBQ3pCOztBQUtKLE1BQU0sQ0FBQyxNQUFNLE1BQU0sU0FBUyxFQUFFLEtBQUs7RUFFbEMsQUFBQSxzQkFBc0IsQ0FBQztJQUN0QixPQUFPLEVBQUUsbUJBQW1CO0dBNkI1QjtFQTlCRCxBQU9HLHNCQVBtQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQUVQLE1BQU0sQ0FBQztJQUNOLE1BQU0sRUFBRSxVQUFVO0dBQ2xCO0VBVEosQUFXRyxzQkFYbUIsQ0FHckIsU0FBUyxDQUVSLFFBQVEsQ0FNUCxRQUFRLENBQUM7SUFDUixTQUFTLEVBQUUsSUFBSTtJQUNmLFdBQVcsRUFBRSxJQUNkO0dBQUM7RUFkSixBQWdCRyxzQkFoQm1CLENBR3JCLFNBQVMsQ0FFUixRQUFRLENBV1AsUUFBUSxDQUFDO0lBQ1IsTUFBTSxFQUFFLGNBQWM7R0FVdEI7RUEzQkosQUFtQkksc0JBbkJrQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQVdQLFFBQVEsQ0FHUCxDQUFDLENBQUM7SUFDRCxNQUFNLEVBQUUsQ0FBQztJQUNULE9BQU8sRUFBRSxLQUFLO0dBQ2Q7RUF0QkwsQUF3Qkksc0JBeEJrQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQVdQLFFBQVEsQ0FRUCxpQkFBaUIsQ0FBQztJQUNqQixVQUFVLEVBQUUsR0FBRztHQUNmIn0= */
css/ctf-admin-styles.css CHANGED
@@ -1,1331 +1,1331 @@
1
- #ctf-admin .ctf-tooltip{
2
- display: none;
3
- padding: 10px 0;
4
- font-weight: normal;
5
- }
6
- #ctf-admin .ctf-tooltip ul{
7
- margin-top: 0;
8
- margin-bottom: 0;
9
- }
10
- #ctf-admin .ctf-tooltip li{
11
- padding: 4px 0;
12
- }
13
- #ctf-admin .ctf-tooltip-link,
14
- #ctf-admin .ctf-external-link{
15
- font-size: 13px;
16
- margin-left: 10px;
17
- }
18
- #ctf-admin .ctf-more-info{
19
- padding: 10px 15px;
20
- margin: 10px 0;
21
- font-size: 13px;
22
- background: #f9f9f9;
23
- background: rgba(255,255,255,0.8);
24
- -moz-border-radius: 8px;
25
- -webkit-border-radius: 8px;
26
- border-radius: 8px;
27
- }
28
- #ctf-admin .postbox .ctf-more-info{
29
- background: #eee;
30
- background: rgba(0,0,0,0.05);
31
- }
32
- #ctf-admin .ctf-more-info p{
33
- font-size: 13px;
34
- }
35
- #ctf-admin .ctf-contents-links{
36
- float: left;
37
- clear: both;
38
- width: 100%;
39
- padding-bottom: 12px;
40
- border-bottom: 1px solid #ccc;
41
- margin-bottom: 15px;
42
- }
43
- #ctf-admin .ctf-contents-links a,
44
- #ctf-admin .ctf-contents-links span{
45
- display: block;
46
- float: left;
47
- padding: 2px 5px;
48
- }
49
- #ctf-admin .ctf-contents-links span{
50
- padding-left: 0;
51
- }
52
- #ctf-admin .ctf-contents-links td,
53
- #ctf-admin .ctf-contents-links th {
54
- padding: 0;
55
- vertical-align: middle;
56
- }
57
- #ctf-admin .ctf_include_replies_toggle {
58
- display: block;
59
- margin: 8px 0 0 142px;
60
- }
61
- #ctf-admin table.ctf_shortcode_table{
62
- border-collapse: collapse;
63
- }
64
- #ctf-admin table.ctf_shortcode_table th,
65
- #ctf-admin table.ctf_shortcode_table td{
66
- border: 1px solid #999;
67
- padding: 0.5rem;
68
- text-align: left;
69
- }
70
- #ctf-admin table.ctf_shortcode_table th{
71
- background: rgba(0,0,0,0.1);
72
- }
73
- #ctf-admin table.ctf_shortcode_table td{
74
- background: rgba(255,255,255,0.5);
75
- }
76
- #ctf-admin .ctf_table_header{
77
- background: #ddd;
78
- font-weight: bold;
79
- }
80
- #ctf-admin .short-text {
81
- width: 50px;
82
- }
83
-
84
- /* Configure tab */
85
-
86
- #ctf-admin .ctf-radio-label {
87
- display: inline-block;
88
- width: 114px;
89
- }
90
- #ctf-admin .ctf-radio th,
91
- #ctf-admin .ctf-radio td {
92
- padding: 10px 10px 5px 0;
93
- }
94
- #ctf-admin #ctf-get-token{
95
- display: block;
96
- float: left;
97
- clear: both;
98
- padding: 0 21px 0 21px;
99
- height: 47px;
100
- line-height: 47px;
101
- background: #1da1f2;
102
- color: #e9eef3;
103
- text-decoration: none;
104
-
105
- -moz-border-radius: 5px;
106
- -webkit-border-radius: 5px;
107
- border-radius: 5px;
108
-
109
- -moz-transition: all 0.5s ease-in-out;
110
- -webkit-transition: all 0.5s ease-in-out;
111
- -o-transition: all 0.5s ease-in-out;
112
- transition: all 0.5s ease-in-out;
113
- }
114
- #ctf-admin #ctf-get-token .fa{
115
- margin-right: 10px;
116
- font-size: 22px;
117
- position: relative;
118
- top: 3px;
119
- }
120
- #ctf-admin #ctf-get-token:hover{
121
- background-color: #0c7abf;
122
- color: #fff;
123
-
124
- -moz-transition: all 0.1s ease-in-out;
125
- -webkit-transition: all 0.1s ease-in-out;
126
- -o-transition: all 0.1s ease-in-out;
127
- transition: all 0.1s ease-in-out;
128
- }
129
- .ctf-note-after-input {
130
- font-style: italic;
131
- font-size: 12px;
132
- }
133
- /* Config info */
134
- #ctf_config{
135
- float: left;
136
- width: 100%;
137
- clear: both;
138
- margin: 10px 0 10px 0;
139
- }
140
- #ctf_config .ctf-tooltip-link{
141
- position: relative;
142
- top: 13px;
143
- left: 6px;
144
- }
145
- #ctf_config_info{
146
- float: left;
147
- clear: both;
148
- padding: 5px 15px;
149
- margin: 10px 0 0 0;
150
-
151
- background: #fff;
152
- border: 1px solid #ddd;
153
-
154
- -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
155
- box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
156
-
157
- -moz-border-radius: 2px;
158
- -webkit-border-radius: 2px;
159
- border-radius: 2px;
160
- }
161
- #ctf-admin .ctf-success{
162
- color: #7ad03a;
163
- font-size: 16px;
164
- padding: 6px;
165
- }
166
- #ctf-admin .ctf-footnote{
167
- width: 100%;
168
- float: left;
169
- clear: both;
170
- padding: 20px 0 0 0;
171
- border-top: 1px solid #ccc;
172
- margin: 20px 0 0 0;
173
- }
174
-
175
- /* shortcode examples */
176
- #ctf-admin label{
177
- position: relative;
178
- }
179
- #ctf-admin .ctf_shortcode{
180
- display: none;
181
- position: absolute;
182
- z-index: 10;
183
- font-size: 11px;
184
- float: left;
185
- width: auto;
186
- white-space: pre-line;
187
- line-height: 1.4;
188
- max-width: 195px;
189
- }
190
- #ctf-admin .ctf_shortcode_symbol{
191
- position: absolute;
192
- right: -24px;
193
- bottom: 0px;
194
- padding: 2px 4px 3px 3px;
195
- width: auto;
196
- font-size: 11px;
197
- margin-left: 5px;
198
- }
199
-
200
- /* Error notice */
201
- #ctf-admin .ctf_error_notice, .ctf_error_notice{
202
- margin-top: 5px;
203
- background: #f9ecda;
204
- padding: 5px 10px;
205
- border: 1px solid #e89a2e;
206
- color: #cf6100;
207
-
208
- -moz-border-radius: 3px;
209
- -webkit-border-radius: 3px;
210
- border-radius: 3px;
211
- }
212
- #ctf-admin .ctf_error_notice a, .ctf_error_notice a{
213
- color: #d85600;
214
- }
215
- #ctf-admin .ctf_error_notice a:hover, .ctf_error_notice a:hover,
216
- #ctf-admin .ctf_error_notice a:focus, .ctf_error_notice a:focus{
217
- color: #a34100;
218
- }
219
- #ctf-admin .ctf_error_notice p, .ctf_error_notice p{
220
- margin: 0;
221
- padding: 5px 0;
222
- font-size: 13px;
223
- }
224
- .ctf_user_id_error{
225
- display: none;
226
-
227
- margin: 10px 0;
228
- background: #F7E6E6;
229
- padding: 5px 10px;
230
- border: 1px solid #BA7B7B;
231
- color: #592626;
232
-
233
- -moz-border-radius: 3px;
234
- -webkit-border-radius: 3px;
235
- border-radius: 3px;
236
- }
237
-
238
- /* Review notice */
239
- .ctf_notice {
240
- position: relative;
241
- overflow: hidden;
242
- max-width: 870px;
243
- margin-top: 10px;
244
- padding: 10px 10px 7px 10px;
245
- clear: both;
246
- background: #E6F0E8;
247
- border: 1px solid #6AB074;
248
- color: #214F28;
249
- }
250
- .ctf_notice .ctf_thumb{
251
- position: relative;
252
- display: inline-block;
253
- width: 74px;
254
- margin: 0 0 0 -100% !important;
255
-
256
- -moz-border-radius: 4px;
257
- -webkit-border-radius: 4px;
258
- border-radius: 4px;
259
- }
260
- .ctf_notice .ctf_thumb .img-overlay {
261
- position: absolute;
262
- top: 3px;
263
- padding: 6px 5px;
264
- font-size: 12px;
265
- font-weight: bold;
266
- background: #fff;
267
- line-height: 1;
268
- color: #000;
269
- opacity: .8;
270
- }
271
- .ctf_notice img{
272
- width: 74px;
273
-
274
- -moz-border-radius: 4px;
275
- -webkit-border-radius: 4px;
276
- border-radius: 4px;
277
- }
278
- .ctf_notice .ctf-notice-text{
279
- float: left;
280
- clear: none;
281
- width: 100%;
282
- padding: 0;
283
- }
284
- .ctf_notice p{
285
- float: left;
286
- clear: both;
287
- width: auto;
288
- margin: 0 0 0 90px !important;
289
- padding: 2px 40px 2px 0;
290
- line-height: 1.4;
291
- }
292
- .ctf_notice .ctf-links{
293
- margin-top: 4px !important;
294
- }
295
- .ctf_notice a{
296
- display: inline-block;
297
- padding: 0 8px;
298
- color: #178529;
299
- }
300
- .ctf_notice a:hover,
301
- .ctf_notice a:focus{
302
- color: #0c7abf;
303
- }
304
- .ctf_notice .links{
305
- margin: 0 0 0 82px !important;
306
- padding: 4px 0 0 0;
307
- margin-top: 6px !important;
308
- }
309
- .ctf_notice .ctf_notice_close,
310
- .ctf_notice .ctf_bfcm_sale_notice_close,
311
- .ctf_notice .ctf_new_user_sale_notice_close {
312
- position: absolute;
313
- top: 0;
314
- right: 0;
315
- padding: 10px;
316
- line-height: 1;
317
- }
318
- .ctf_notice .ctf_notice_close:hover,
319
- .ctf_notice .ctf_notice_close:focus{
320
- color: #a34100;
321
- }
322
- .ctf_notice .ctf_offer_btn {
323
- padding: 4px 12px 6px 12px;
324
- background: green;
325
- color: #fff;
326
- border-radius: 4px;
327
- display: inline-block;
328
- text-decoration: none;
329
- margin-left: 0;
330
- }
331
- .ctf_notice .ctf_offer_btn:hover, .ctf_notice .ctf_offer_btn:focus {
332
- background: #049404;
333
- color: #fff;
334
- }
335
- .ctf_notice .ctf_other_notice{
336
- padding-top: 10px;
337
- font-style: italic;
338
- font-size: 12px;
339
- }
340
- .ctf_notice .ctf_other_notice a{
341
- padding: 0;
342
- }
343
-
344
- /* Customize tab */
345
- #ctf-admin #ctf_width_options{
346
- margin-top: 10px;
347
- display: none;
348
- }
349
-
350
- /* Pro only notices */
351
- #ctf-admin .ctf_row{
352
- display: block;
353
- width: 100%;
354
- }
355
- #ctf-admin .ctf_pro,
356
- #ctf-admin .ctf_pro label,
357
- #ctf_admin .ctf_pro input{
358
- color: #999 !important;
359
- }
360
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child label,
361
- #ctf-admin .ctf_pro .ctf_layout_type_list {
362
- color: initial !important;
363
- }
364
- #ctf-admin .ctf_shortcode_table tr.ctf_pro{
365
- background: #eacccc;
366
- }
367
- #ctf-admin .ctf_table_key{
368
- display: block;
369
- float: left;
370
- width: 11px;
371
- height: 11px;
372
- border: 1px solid #999;
373
- background: #eacccc;
374
- margin: 3px 6px 0 0;
375
- }
376
- #ctf-admin .ctf_note {
377
- font-size: 12px;
378
- font-style: italic;
379
- margin-left: 5px;
380
- }
381
- #ctf-admin .ctf_checkbox,
382
- #ctf-admin .ctf_checkbox label{
383
- font-size: 13px;
384
- line-height: 1.4em;
385
- padding-top: 10px;
386
- vertical-align: top;
387
- }
388
- #ctf-admin .ctf_pro .ctf_layout_cell img{
389
- opacity: 0.4;
390
- }
391
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child img {
392
- opacity: 1 !important;
393
- }
394
- #ctf-admin .ctf_pro input,
395
- #ctf-admin .ctf_pro select {
396
- color: #999 !important;
397
- }
398
- #ctf-admin .ctf_lock {
399
- display: none;
400
- padding: 8px 10px 8px 13px;
401
- position: absolute;
402
- top: 50%;
403
- left: 50%;
404
- margin-top: -4px;
405
- margin-left: -34px;
406
- background: rgba(255,255,255,0.8);
407
- border-radius: 5px;
408
- box-shadow: 0 0 5px 0 rgba(0,0,0,0.1);
409
- color: #333;
410
- z-index: 99;
411
- font-size: 14px;
412
- color: #666;
413
- font-weight: 700;
414
- text-decoration: none;
415
- border: 1px solid #ccc;
416
- }
417
- #ctf-admin .ctf_lock i {
418
- margin-right: 6px;
419
- }
420
- #ctf-admin .ctf_pro .ctf_layout_cell:hover .ctf_lock{
421
- display: block;
422
- }
423
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child .ctf_lock,
424
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child:hover .ctf_lock {
425
- display: none;
426
- }
427
-
428
- /* Layout */
429
- .form-table .ctf_layout_type {
430
- display: inline-block;
431
- margin-left: 20px;
432
- }
433
- .form-table .ctf_layout_type:first-child {
434
- margin-left: 0;
435
- }
436
- .ctf_layout_setting,
437
- .ctf_box_setting{
438
- margin-bottom: 20px;
439
- }
440
- .ctf_layout_setting:last-child,
441
- .ctf_box_setting:last-child {
442
- margin-bottom: 0;
443
- }
444
- .ctf_layout_setting label,
445
- .ctf_box_setting label {
446
- display: inline-block;
447
- font-weight: bold;
448
- font-size: 14px;
449
- padding-bottom: 5px;
450
- padding-right: 5px;
451
- }
452
- #ctf-admin .ctf_layout_options_wrap .ctf_shortcode_symbol{
453
- bottom: 4px;
454
- right: -20px;
455
- }
456
- #ctf-admin .ctf_layout_options_wrap .ctf_shortcode,
457
- #ctf-admin .ctf_box_setting .ctf_shortcode{
458
- position: relative;
459
- float: none;
460
- top: -4px
461
- }
462
-
463
- .ctf_layout_cell {
464
- position: relative;
465
- padding: 0;
466
- display: inline-block;
467
- float: left;
468
- width: 155px;
469
- padding: 15px 15px 10px 15px;
470
- margin-right: 10px;
471
- background: #f6f6f6;
472
- border: 1px solid #ddd;
473
- -moz-border-radius: 5px;
474
- -webkit-border-radius: 5px;
475
- border-radius: 5px;
476
- vertical-align: top;
477
- }
478
- .ctf_layout_cell:nth-child(4) {
479
- margin-right: 0;
480
- }
481
- @media all and (max-width: 1200px){
482
- .ctf_layout_cell{
483
- width: 21%;
484
- padding: 1%;
485
- margin-right: 1%;
486
- }
487
- }
488
- @media all and (max-width: 480px){
489
- .ctf_layout_cell{
490
- width: 98%;
491
- padding: 1%;
492
- margin: 5px 0;
493
- }
494
- }
495
- .ctf_label {
496
- font-size: 16px;
497
- font-weight: bold;
498
- padding-left: 2px;
499
- }
500
- .ctf_layout_cell:hover,
501
- .ctf_layout_cell:focus{
502
- background: #fcfcfc;
503
- cursor: pointer;
504
- }
505
- .ctf_layout_cell.ctf_layout_selected{
506
- background: #f7faf1;
507
- border: 1px solid #7ad03a;
508
- }
509
- .ctf_layout_cell h3{
510
- font-size: 15px;
511
- margin-top: 0;
512
- }
513
- .ctf_layout_cell img{
514
- width: 100%;
515
- border: 1px solid #ddd;
516
- margin-top: 5px;
517
- }
518
-
519
- #ctf-admin #ctf_width_options{
520
- margin-top: 5px;
521
- display: none;
522
- }
523
- #ctf-admin #ctf_width_options label {
524
- font-size: 13px;
525
- position: relative;
526
- top: -2px;
527
- }
528
- #ctf-admin .ctf_layout_settings{
529
- float: left;
530
- clear: both;
531
- width: 96%;
532
-
533
- padding: 20px 2%;
534
- margin: 10px 0;
535
- font-size: 13px;
536
- background: #f9f9f9;
537
- background: rgba(255,255,255,0.8);
538
-
539
- -moz-border-radius: 8px;
540
- -webkit-border-radius: 8px;
541
- border-radius: 8px;
542
- }
543
- .ctf_layout_setting {
544
- clear:left;
545
- margin-bottom: 10px;
546
- }
547
- #ctf-admin .ctf_layout_options_wrap .ctf-tooltip{
548
- width: 98%;
549
- padding: 10px 1%;
550
- background: #eee;
551
- background: rgba(0,0,0,0.05);
552
- }
553
- #ctf-admin .ctf_layout_mobile_layout_setting {
554
- width: auto;
555
- }
556
-
557
- #ctf-admin .ctf-quick-start{
558
- display: block;
559
- float: left;
560
- clear: both;
561
- min-width: 808px;
562
- margin: 15px 0 0 0;
563
- padding: 15px 20px;
564
-
565
- border: 1px solid #ccc;
566
- background: #eee;
567
- background: rgba(255,255,255,0.5);
568
-
569
- -moz-border-radius: 5px;
570
- -webkit-border-radius: 5px;
571
- border-radius: 5px;
572
- }
573
- #ctf-admin .ctf-quick-start h3,
574
- #ctf-admin .ctf-quick-start p{
575
- margin: 0;
576
- padding: 5px 0;
577
- }
578
- #ctf-admin .ctf-pro-notice,
579
- #ctf-admin .ctf-pro-notice img{
580
- display: block;
581
- float: left;
582
- clear: both;
583
- margin: 20px 0 0 0;
584
- overflow: hidden;
585
-
586
- -moz-border-radius: 4px;
587
- -webkit-border-radius: 4px;
588
- border-radius: 4px;
589
-
590
- -moz-transition: all 0.5s ease-in-out;
591
- -webkit-transition: all 0.5s ease-in-out;
592
- -o-transition: all 0.5s ease-in-out;
593
- transition: all 0.5s ease-in-out;
594
- }
595
- #ctf-admin .ctf-pro-notice img{
596
- margin: 0;
597
- }
598
- #ctf-admin .ctf-pro-notice:hover{
599
- opacity: 0.95;
600
-
601
- -moz-transition: all 0.1s ease-in-out;
602
- -webkit-transition: all 0.1s ease-in-out;
603
- -o-transition: all 0.1s ease-in-out;
604
- transition: all 0.1s ease-in-out;
605
- }
606
- #ctf-admin .ctf-pro-options,
607
- #ctf-admin .ctf-pro-options label{
608
- color: #999 !important;
609
- }
610
- #ctf-admin .ctf-shortcode_table tr.ctf-pro-options{
611
- background: #eacccc;
612
- }
613
- #ctf-admin .ctf-pro-options label,
614
- #ctf-admin .ctf-pro-options .ctf_shortcode {
615
- color: #999 !important;
616
- }
617
- #ctf-admin .ctf_pro .ctf-tooltip-link,
618
- #ctf-admin .ctf-pro-options .ctf-tooltip-link {
619
- color: #aaa !important;
620
- }
621
- #ctf-admin .ctf-success{
622
- font-size: 14px;
623
- }
624
- #toplevel_page_custom-twitter-feeds .dashicons-admin-generic:before {
625
- content: "\f301";
626
- }
627
-
628
- /* GDPR Settings */
629
- #ctf-admin .ctf-list{
630
- list-style: inside disc;
631
- }
632
- #ctf-admin .ctf-box{
633
- float: left;
634
- clear: both;
635
- width: 96%;
636
- max-width: 900px;
637
-
638
- padding: 10px 15px;
639
- margin: 10px 0;
640
- font-size: 13px;
641
- background: #f9f9f9;
642
- background: rgba(255,255,255,0.8);
643
-
644
- -moz-border-radius: 8px;
645
- -webkit-border-radius: 8px;
646
- border-radius: 8px;
647
- }
648
- .gdpr_tooltip p{
649
- padding: 0 0 10px 0;
650
- }
651
- .gdpr_tooltip span{
652
- display: inline-block;
653
- margin: 0;
654
- font-weight: bold;
655
- font-size: 14px;
656
- }
657
- .gdpr_tooltip .ctf-list{
658
- padding: 0 0 10px 0;
659
- }
660
- .gdpr_tooltip li{
661
- padding: 0;
662
- }
663
- .ctf_gdpr_plugin_active .ctf_active{
664
- display: inline-block;
665
- padding: 10px 20px 15px 20px;
666
- border-radius: 5px;
667
- background: #edf4f0;
668
- border: 1px solid #2c8649;
669
- color: #2c8649;
670
- margin: 8px 0 0 0;
671
- }
672
- .ctf_gdpr_plugin_active svg{
673
- width: 16px;
674
- height: 16px;
675
- position: relative;
676
- top: 3px;
677
- margin-right: 2px;
678
- }
679
- .ctf_gdpr_plugin_active path{
680
- fill: green;
681
- }
682
- .ctf_gdpr_list,
683
- .ctf_gdpr_yes,
684
- .ctf_gdpr_no{
685
- display: none;
686
- }
687
- #ctf-admin .ctf_gdpr_error{
688
- padding: 10px 20px 15px 20px;
689
- border-radius: 5px;
690
- background: #F7E6E6;
691
- border: 1px solid #BA7B7B;
692
- color: #592626;
693
- }
694
-
695
- /* Lite Notice */
696
- @media screen and (max-width: 600px) {
697
- #ctf-notice-bar {
698
- display:none !important
699
- }
700
- }
701
-
702
- #ctf-notice-bar {
703
- background-color: #DDDDDD;
704
- color: #777777;
705
- text-align: center;
706
- position: relative;
707
- padding: 7px;
708
- margin-bottom: 0;
709
- opacity: 1;
710
- transition: all .3s ease-in-out;
711
- max-height: 100px;
712
- overflow: hidden
713
- }
714
-
715
- #ctf-notice-bar.out {
716
- opacity: .5;
717
- max-height: 0
718
- }
719
-
720
- #ctf-notice-bar a {
721
- color: #e34f0e;
722
- }
723
-
724
- #ctf-notice-bar a:hover {
725
- color: #b85a1b
726
- }
727
-
728
- #ctf-notice-bar .dismiss {
729
- position: absolute;
730
- top: 0;
731
- right: 0;
732
- border: none;
733
- padding: 5px;
734
- margin-top: 1px;
735
- background: 0 0;
736
- color: #72777c;
737
- cursor: pointer
738
- }
739
-
740
- #ctf-notice-bar .dismiss:before {
741
- background: 0 0;
742
- color: #72777c;
743
- content: "\f335";
744
- display: block;
745
- font: normal 20px/20px dashicons;
746
- speak: none;
747
- height: 20px;
748
- text-align: center;
749
- width: 20px;
750
- -webkit-font-smoothing: antialiased
751
- }
752
-
753
- /* Locator Summary */
754
- .ctf-feed-locator-summary-wrap {
755
- max-width: 1100px;
756
- margin-bottom: 40px;
757
- }
758
- .ctf-full-wrap {
759
- display: none;
760
- }
761
- .ctf-locator-more {
762
- display: inline-block;
763
- margin: 0 0 0 1px;
764
- padding: 0 5px;
765
- border: 1px solid rgba(0,0,0,0.1);
766
- border-radius: 3px;
767
- line-height: 1.6;
768
- }
769
-
770
- .ctf_shortcode_visible .ctf_locations_link {
771
- display: none;
772
- }
773
- .ctf_locations_link a {
774
- font-weight: normal;
775
- text-decoration: none;
776
- }
777
- .ctf_locations_link svg {
778
- width: 11px;
779
- position: relative;
780
- top: 1px;
781
- }
782
-
783
-
784
- /* More social feeds page */
785
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary .fa-spinner {
786
- color: #333;
787
- }
788
- #ctf-admin .ctf_more_plugins h2{
789
- margin-top: 15px;
790
- }
791
- #ctf-admin .ctf_more_plugins #ctf-admin-addons .addons-container{
792
- overflow: hidden;
793
- margin: 0;
794
- }
795
- #ctf-admin .ctf_more_plugins#ctf-admin-about #ctf-admin-addons{
796
- padding: 20px 0;
797
- }
798
- #ctf-admin .ctf-more-plugins-intro span{
799
- color: #e34f0e;
800
- }
801
- #ctf-admin .ctf_more_plugins h3{
802
- padding: 15px 0 0 0;
803
- }
804
- .sb_cross_install_modal .ctf-loader{
805
- position: absolute;
806
- left: 50%;
807
- top: 50%;
808
- margin: -10px 0 0 -10px;
809
- }
810
-
811
- /* Social Wall landing page */
812
- #ctf-admin.sw-landing-page{
813
- padding: 20px 0;
814
- }
815
- #ctf-admin .ctf-sw-icons{
816
- width: 100%;
817
- text-align: center;
818
- }
819
- #ctf-admin .ctf-sw-icons span{
820
- display: inline-block;
821
- width: 32px;
822
- margin: 0 7px;
823
- position: relative;
824
- top: -2px;
825
- }
826
- #ctf-admin .ctf-sw-icons .ctf-sb-plus{
827
- width: 12px;
828
- height: 12px;
829
- position: relative;
830
- top: -12px;
831
- opacity: 0.9;
832
- }
833
- #ctf-admin.sw-landing-page h1,
834
- #ctf-admin.sw-landing-page h2{
835
- width: 100%;
836
- text-align: center;
837
- }
838
- #ctf-admin.sw-landing-page h1{
839
- font-size: 44px;
840
- margin: 20px 0 0 0;
841
- line-height: 1.3;
842
- }
843
- #ctf-admin.sw-landing-page h2{
844
- font-size: 30px;
845
- font-weight: 100;
846
- line-height: 1.2;
847
- margin: 5px 0 20px 0;
848
- }
849
- #ctf-admin .ctf-sw-info{
850
- width: 95%;
851
- max-width: 1400px;
852
- margin: 0 auto;
853
- padding: 50px 10px;
854
- clear: both;
855
- overflow: hidden;
856
- box-sizing: border-box;
857
- }
858
- #ctf-admin .ctf-sw-features{
859
- float: left;
860
- width: 40%;
861
- margin-right: 5%;
862
- padding: 20px 0 0 0;
863
- }
864
- #ctf-admin .ctf-sw-screenshot{
865
- position: relative;
866
- width: 55%;
867
- float: left;
868
- box-sizing: border-box;
869
- border: 5px solid #fff;
870
- box-shadow: 0 0 20px 0 rgba(0,0,0,0.05);
871
- }
872
- #ctf-admin .ctf-sw-features p{
873
- margin: 35px 0;
874
- padding: 0;
875
- font-size: 15px;
876
- }
877
- #ctf-admin .ctf-sw-features p:first-child{
878
- margin-top: 0;
879
- }
880
- #ctf-admin .ctf-sw-features span{
881
- display: block;
882
- font-size: 20px;
883
- font-weight: bold;
884
- }
885
- #ctf-admin.sw-landing-page .cta {
886
- margin: 20px 0 0 5px;
887
- padding: 8px 40px 10px 40px;
888
- font-size: 20px;
889
- }
890
- #ctf-admin .ctf-sw-screenshot img{
891
- width: 100%;
892
- display: block;
893
- }
894
- #ctf-admin .ctf-sw-screenshot .cta{
895
- position: absolute;
896
- top: 50%;
897
- left: 50%;
898
- margin-top: -32px;
899
- margin-left: -80px;
900
- padding: 15px 30px 17px 30px;
901
- color: #333;
902
- text-decoration: none;
903
- background: rgba(255,255,255,0.95);
904
- border-radius: 3px;
905
- /*border: 2px solid rgba(0,0,0,0.1);*/
906
- box-shadow: 0 0 20px 0 rgba(0,0,0,0.1);
907
- }
908
- #ctf-admin .ctf-sw-screenshot:hover .cta,
909
- #ctf-admin .ctf-sw-screenshot:focus .cta{
910
- background: #e34717;
911
- color: #fff;
912
- box-shadow: 0;
913
- }
914
- #ctf-admin .ctf-sw-footer-cta{
915
- width: 100%;
916
- text-align: center;
917
- }
918
- #ctf-admin .ctf-sw-footer-cta a{
919
- font-size: 22px;
920
- display: inline-block;
921
- text-decoration: none;
922
- line-height: 25px;
923
- font-weight: normal;
924
- padding: 10px 0 30px 0;
925
- }
926
- #ctf-admin .ctf-sw-footer-cta span{
927
- width: 30px;
928
- height: 30px;
929
- display: inline-block;
930
- margin: 0 10px 0 0;
931
- position: relative;
932
- top: 4px;
933
- }
934
- #ctf-admin .ctf-sw-footer-cta span .emoji{
935
- width: 30px !important;
936
- height: 30px !important;
937
- }
938
-
939
- @media all and (max-width: 1500px){
940
- #ctf-admin .ctf-sw-screenshot{
941
- width: 55%;
942
- float: left;
943
- height: 500px;
944
- background: url('../img/sw-screenshot.png') no-repeat center center;
945
- background-size: cover;
946
- }
947
- #ctf-admin .ctf-sw-screenshot img{
948
- display: none;
949
- }
950
- }
951
- @media all and (max-width: 900px){
952
- #ctf-admin .ctf-sw-info{
953
- padding: 20px 0;
954
- }
955
- #ctf-admin .ctf-sw-features{
956
- width: 100%;
957
- padding: 10px 0;
958
- margin: 0;
959
- }
960
- #ctf-admin .ctf-sw-screenshot{
961
- width: 100%;
962
- padding: 0;
963
- height: auto;
964
- background: none;
965
- }
966
- #ctf-admin .ctf-sw-screenshot img{
967
- display: block;
968
- }
969
- #ctf-admin .ctf-sw-features .cta {
970
- width: 100%;
971
- box-sizing: border-box;
972
- text-align: center;
973
- margin: 20px 0;
974
- }
975
- }
976
-
977
- /* Menu Pro link */
978
- #adminmenu .wp-submenu .ctf_get_pro_highlight{
979
- background-color: #1da867;
980
- color: #fff;
981
- }
982
- #adminmenu .wp-submenu .ctf_get_pro_highlight:hover,
983
- #adminmenu .wp-submenu .ctf_get_pro_highlight:active{
984
- background-color: #2b9865;
985
- color: #fff;
986
- }
987
-
988
- /* Install another plugin modal */
989
- .sb_cross_install_modal {
990
- position: fixed;
991
- z-index: 999;
992
- width: 100%;
993
- top: 0;
994
- right: 0;
995
- bottom: 0;
996
- left: 0;
997
- background: rgba(0, 0, 0, 0.3);
998
- }
999
- .sb_cross_install_inner {
1000
- position: absolute;
1001
- top: 140px;
1002
- left: 50%;
1003
- width: 480px;
1004
- margin: 0 0 0 -245px;
1005
- padding: 35px;
1006
- background: #fff;
1007
- text-align: left;
1008
-
1009
- -webkit-box-shadow: 0 1px 20px rgba(0, 0, 0, 0.2);
1010
- box-shadow: 0 1px 20px rgba(0, 0, 0, 0.2);
1011
-
1012
- -moz-border-radius: 3px;
1013
- -webkit-border-radius: 3px;
1014
- border-radius: 3px;
1015
- }
1016
- .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons .addon-container,
1017
- .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons .addons-container{
1018
- width: 100%;
1019
- margin: 0;
1020
- padding: 0;
1021
- min-height: 198px;
1022
- }
1023
- #ctf-admin-addons .addon-item .action-button button{
1024
- background: #007cba;
1025
- border: 0;
1026
- }
1027
- #ctf-admin-addons .addon-item .action-button button:hover{
1028
- background: #0071a1;
1029
- }
1030
- .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons{
1031
- padding: 0;
1032
- }
1033
- /* More social feeds page */
1034
- .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary .fa-spinner {
1035
- color: #333;
1036
- }
1037
- #ctf-admin .ctf_more_plugins h2{
1038
- margin-top: 15px;
1039
- }
1040
- #ctf-admin .ctf_more_plugins #ctf-admin-addons .addons-container{
1041
- overflow: hidden;
1042
- margin: 0;
1043
- }
1044
- #ctf-admin .ctf_more_plugins#ctf-admin-about #ctf-admin-addons{
1045
- padding: 20px 0;
1046
- }
1047
- #ctf-admin .ctf-more-plugins-intro span{
1048
- color: #e34f0e;
1049
- }
1050
- #ctf-admin .ctf_more_plugins h3{
1051
- padding: 15px 0 0 0;
1052
- }
1053
- .sb_cross_install_modal .ctf-loader{
1054
- position: absolute;
1055
- left: 50%;
1056
- top: 50%;
1057
- margin: -10px 0 0 -10px;
1058
- }
1059
- /* AW Addons */
1060
- #ctf-admin-addons h3 {
1061
- padding: 10px 10px 0 10px;
1062
- clear: left;
1063
- }
1064
- #ctf-admin-addons *,#ctf-admin-addons *::before,#ctf-admin-addons *::after {
1065
- -webkit-box-sizing: border-box;
1066
- -moz-box-sizing: border-box;
1067
- box-sizing: border-box
1068
- }
1069
-
1070
- #ctf-admin-addons .addons-container {
1071
- margin-left: -20px;
1072
- margin-right: -20px
1073
- }
1074
-
1075
- #ctf-admin-addons .unlock-msg {
1076
- padding: 0 20px;
1077
- margin-top: -20px;
1078
- clear: both
1079
- }
1080
-
1081
- #ctf-admin-addons #ctf-admin-addons-search {
1082
- background-color: #fff;
1083
- border: 1px solid #ddd;
1084
- border-radius: 3px;
1085
- box-shadow: none;
1086
- color: #333;
1087
- vertical-align: middle;
1088
- padding: 7px 12px;
1089
- margin: -3px 10px 0 0;
1090
- width: 200px;
1091
- min-height: 35px;
1092
- float: right
1093
- }
1094
-
1095
- #ctf-admin-addons #ctf-admin-addons-search:focus {
1096
- border-color: #bbb
1097
- }
1098
-
1099
- #ctf-admin-addons .addon-container {
1100
- padding: 0 20px;
1101
- float: left;
1102
- width: 33.333333%;
1103
- margin-bottom: 20px
1104
- }
1105
-
1106
- @media (max-width: 1249px) {
1107
- #ctf-admin-addons .addon-container {
1108
- width:50%
1109
- }
1110
- }
1111
-
1112
- @media (max-width: 767px) {
1113
- #ctf-admin-addons .addon-container {
1114
- width:100%;
1115
- margin-bottom: 20px
1116
- }
1117
- }
1118
-
1119
- #ctf-admin-addons h4 {
1120
- font-size: 17px;
1121
- font-weight: 700
1122
- }
1123
-
1124
- #ctf-admin-addons .addon-item {
1125
- background-color: #fff;
1126
- border: 1px solid #ddd;
1127
- border-radius: 3px;
1128
- margin: 0
1129
- }
1130
-
1131
- @media (max-width: 767px) {
1132
- #ctf-admin-addons .addon-item {
1133
- margin:0 0
1134
- }
1135
- }
1136
-
1137
- #ctf-admin-addons .addon-item img {
1138
- border: 1px solid #eee;
1139
- float: left;
1140
- max-width: 75px
1141
- }
1142
-
1143
- #ctf-admin-addons .addon-item h5 {
1144
- margin: 0 0 10px 100px;
1145
- font-size: 16px
1146
- }
1147
-
1148
- #ctf-admin-addons .addon-item p {
1149
- margin: 0 0 0 100px
1150
- }
1151
-
1152
- #ctf-admin-addons .addon-item .details {
1153
- padding: 30px 20px
1154
- }
1155
-
1156
- #ctf-admin-addons .addon-item .actions {
1157
- background-color: #f7f7f7;
1158
- border-top: 1px solid #ddd;
1159
- padding: 20px;
1160
- position: relative
1161
- }
1162
-
1163
- #ctf-admin-addons .addon-item .actions .msg {
1164
- background-color: #f7f7f7;
1165
- position: absolute;
1166
- text-align: center;
1167
- font-weight: 600;
1168
- top: 0;
1169
- left: 0;
1170
- height: 100%;
1171
- width: 100%;
1172
- z-index: 99;
1173
- padding: 20px;
1174
- display: -webkit-box;
1175
- display: -moz-box;
1176
- display: -ms-flexbox;
1177
- display: -webkit-flex;
1178
- display: flex;
1179
- -webkit-align-items: center;
1180
- -ms-align-items: center;
1181
- align-items: center;
1182
- justify-content: center
1183
- }
1184
-
1185
- #ctf-admin-addons .addon-item .actions .msg.success {
1186
- color: #2a9b39
1187
- }
1188
-
1189
- #ctf-admin-addons .addon-item .actions .msg.error {
1190
- color: red
1191
- }
1192
-
1193
- #ctf-admin-addons .addon-item .status {
1194
- float: left;
1195
- padding-top: 8px
1196
- }
1197
-
1198
- #ctf-admin-addons .addon-item .status .status-inactive {
1199
- color: red
1200
- }
1201
-
1202
- #ctf-admin-addons .addon-item .status .status-download {
1203
- color: #666
1204
- }
1205
-
1206
- #ctf-admin-addons .addon-item .status .status-active {
1207
- color: #2a9b39
1208
- }
1209
-
1210
- #ctf-admin-addons .addon-item .upgrade-button {
1211
- text-align: center
1212
- }
1213
-
1214
- #ctf-admin-addons .addon-item .upgrade-button a {
1215
- font-weight: 600;
1216
- width: 140px;
1217
- text-align: center;
1218
- padding: 8px 5px
1219
- }
1220
-
1221
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button {
1222
- display: inline-block;
1223
- text-decoration: none;
1224
- font-size: 13px;
1225
- line-height: 26px;
1226
- height: 28px;
1227
- margin: 0;
1228
- padding: 0 10px 1px;
1229
- cursor: pointer;
1230
- -webkit-appearance: none;
1231
- border-radius: 3px;
1232
- border: 1px solid #cccccc;
1233
- background: #f7f7f7;
1234
- box-shadow: 0 1px 0 #cccccc;
1235
- font-weight: normal
1236
- }
1237
-
1238
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button:hover,#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button:focus {
1239
- background: #fafafa;
1240
- border-color: #999;
1241
- color: #23282d
1242
- }
1243
-
1244
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary {
1245
- background: #0085ba;
1246
- border-color: #0073aa #006799 #006799;
1247
- box-shadow: 0 1px 0 #006799;
1248
- color: #fff;
1249
- text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799
1250
- }
1251
-
1252
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary:hover,#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary:focus {
1253
- background: #008ec2;
1254
- border-color: #006799;
1255
- color: #fff
1256
- }
1257
-
1258
- #ctf-admin #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary .fa-spinner {
1259
- color: #fff
1260
- }
1261
-
1262
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-secondary {
1263
- color: #555;
1264
- border-color: #cccccc;
1265
- background: #f7f7f7;
1266
- box-shadow: 0 1px 0 #cccccc
1267
- }
1268
-
1269
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-secondary:hover,#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-secondary:focus {
1270
- background: #fafafa;
1271
- border-color: #999;
1272
- color: #23282d
1273
- }
1274
-
1275
- #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.disabled {
1276
- cursor: default
1277
- }
1278
-
1279
-
1280
- #ctf-admin-addons .addon-item .action-button {
1281
- float: right
1282
- }
1283
-
1284
- #ctf-admin-addons .addon-item .action-button button {
1285
- background: none;
1286
- border: 1px solid #ddd;
1287
- border-radius: 3px;
1288
- box-shadow: none;
1289
- cursor: pointer;
1290
- font-weight: 600;
1291
- width: 140px;
1292
- text-align: center;
1293
- padding: 8px 5px
1294
- }
1295
-
1296
- #ctf-admin-addons .addon-item .action-button button:hover,#ctf-admin-addons .addon-item .action-button button.loading {
1297
- background-color: #e9e9e9
1298
- }
1299
-
1300
- #ctf-admin-addons .addon-item .action-button button .fa {
1301
- margin-right: 8px
1302
- }
1303
-
1304
- #ctf-admin-addons .addon-item .action-button button .fa.fa-spinner {
1305
- margin-right: 0
1306
- }
1307
-
1308
- #ctf-admin-addons .addon-item .action-button button.status-active .fa {
1309
- color: #2a9b39
1310
- }
1311
-
1312
- #ctf-admin-addons .addon-item .action-button button.status-inactive .fa {
1313
- color: red
1314
- }
1315
-
1316
- #ctf-admin-addons .addon-item .action-button button.status-download .fa {
1317
- color: #999
1318
- }
1319
-
1320
- #ctf-admin-addons .addon-item .action-button button.disabled {
1321
- cursor: default
1322
- }
1323
-
1324
- #ctf-admin-addons .addon-item .action-button button.loading .fa {
1325
- color: #666
1326
- }
1327
- .ctf-clear:after{
1328
- display: table;
1329
- content: "";
1330
- clear: both;
1331
  }
1
+ #ctf-admin .ctf-tooltip{
2
+ display: none;
3
+ padding: 10px 0;
4
+ font-weight: normal;
5
+ }
6
+ #ctf-admin .ctf-tooltip ul{
7
+ margin-top: 0;
8
+ margin-bottom: 0;
9
+ }
10
+ #ctf-admin .ctf-tooltip li{
11
+ padding: 4px 0;
12
+ }
13
+ #ctf-admin .ctf-tooltip-link,
14
+ #ctf-admin .ctf-external-link{
15
+ font-size: 13px;
16
+ margin-left: 10px;
17
+ }
18
+ #ctf-admin .ctf-more-info{
19
+ padding: 10px 15px;
20
+ margin: 10px 0;
21
+ font-size: 13px;
22
+ background: #f9f9f9;
23
+ background: rgba(255,255,255,0.8);
24
+ -moz-border-radius: 8px;
25
+ -webkit-border-radius: 8px;
26
+ border-radius: 8px;
27
+ }
28
+ #ctf-admin .postbox .ctf-more-info{
29
+ background: #eee;
30
+ background: rgba(0,0,0,0.05);
31
+ }
32
+ #ctf-admin .ctf-more-info p{
33
+ font-size: 13px;
34
+ }
35
+ #ctf-admin .ctf-contents-links{
36
+ float: left;
37
+ clear: both;
38
+ width: 100%;
39
+ padding-bottom: 12px;
40
+ border-bottom: 1px solid #ccc;
41
+ margin-bottom: 15px;
42
+ }
43
+ #ctf-admin .ctf-contents-links a,
44
+ #ctf-admin .ctf-contents-links span{
45
+ display: block;
46
+ float: left;
47
+ padding: 2px 5px;
48
+ }
49
+ #ctf-admin .ctf-contents-links span{
50
+ padding-left: 0;
51
+ }
52
+ #ctf-admin .ctf-contents-links td,
53
+ #ctf-admin .ctf-contents-links th {
54
+ padding: 0;
55
+ vertical-align: middle;
56
+ }
57
+ #ctf-admin .ctf_include_replies_toggle {
58
+ display: block;
59
+ margin: 8px 0 0 142px;
60
+ }
61
+ #ctf-admin table.ctf_shortcode_table{
62
+ border-collapse: collapse;
63
+ }
64
+ #ctf-admin table.ctf_shortcode_table th,
65
+ #ctf-admin table.ctf_shortcode_table td{
66
+ border: 1px solid #999;
67
+ padding: 0.5rem;
68
+ text-align: left;
69
+ }
70
+ #ctf-admin table.ctf_shortcode_table th{
71
+ background: rgba(0,0,0,0.1);
72
+ }
73
+ #ctf-admin table.ctf_shortcode_table td{
74
+ background: rgba(255,255,255,0.5);
75
+ }
76
+ #ctf-admin .ctf_table_header{
77
+ background: #ddd;
78
+ font-weight: bold;
79
+ }
80
+ #ctf-admin .short-text {
81
+ width: 50px;
82
+ }
83
+
84
+ /* Configure tab */
85
+
86
+ #ctf-admin .ctf-radio-label {
87
+ display: inline-block;
88
+ width: 114px;
89
+ }
90
+ #ctf-admin .ctf-radio th,
91
+ #ctf-admin .ctf-radio td {
92
+ padding: 10px 10px 5px 0;
93
+ }
94
+ #ctf-admin #ctf-get-token{
95
+ display: block;
96
+ float: left;
97
+ clear: both;
98
+ padding: 0 21px 0 21px;
99
+ height: 47px;
100
+ line-height: 47px;
101
+ background: #1da1f2;
102
+ color: #e9eef3;
103
+ text-decoration: none;
104
+
105
+ -moz-border-radius: 5px;
106
+ -webkit-border-radius: 5px;
107
+ border-radius: 5px;
108
+
109
+ -moz-transition: all 0.5s ease-in-out;
110
+ -webkit-transition: all 0.5s ease-in-out;
111
+ -o-transition: all 0.5s ease-in-out;
112
+ transition: all 0.5s ease-in-out;
113
+ }
114
+ #ctf-admin #ctf-get-token .fa{
115
+ margin-right: 10px;
116
+ font-size: 22px;
117
+ position: relative;
118
+ top: 3px;
119
+ }
120
+ #ctf-admin #ctf-get-token:hover{
121
+ background-color: #0c7abf;
122
+ color: #fff;
123
+
124
+ -moz-transition: all 0.1s ease-in-out;
125
+ -webkit-transition: all 0.1s ease-in-out;
126
+ -o-transition: all 0.1s ease-in-out;
127
+ transition: all 0.1s ease-in-out;
128
+ }
129
+ .ctf-note-after-input {
130
+ font-style: italic;
131
+ font-size: 12px;
132
+ }
133
+ /* Config info */
134
+ #ctf_config{
135
+ float: left;
136
+ width: 100%;
137
+ clear: both;
138
+ margin: 10px 0 10px 0;
139
+ }
140
+ #ctf_config .ctf-tooltip-link{
141
+ position: relative;
142
+ top: 13px;
143
+ left: 6px;
144
+ }
145
+ #ctf_config_info{
146
+ float: left;
147
+ clear: both;
148
+ padding: 5px 15px;
149
+ margin: 10px 0 0 0;
150
+
151
+ background: #fff;
152
+ border: 1px solid #ddd;
153
+
154
+ -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
155
+ box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
156
+
157
+ -moz-border-radius: 2px;
158
+ -webkit-border-radius: 2px;
159
+ border-radius: 2px;
160
+ }
161
+ #ctf-admin .ctf-success{
162
+ color: #7ad03a;
163
+ font-size: 16px;
164
+ padding: 6px;
165
+ }
166
+ #ctf-admin .ctf-footnote{
167
+ width: 100%;
168
+ float: left;
169
+ clear: both;
170
+ padding: 20px 0 0 0;
171
+ border-top: 1px solid #ccc;
172
+ margin: 20px 0 0 0;
173
+ }
174
+
175
+ /* shortcode examples */
176
+ #ctf-admin label{
177
+ position: relative;
178
+ }
179
+ #ctf-admin .ctf_shortcode{
180
+ display: none;
181
+ position: absolute;
182
+ z-index: 10;
183
+ font-size: 11px;
184
+ float: left;
185
+ width: auto;
186
+ white-space: pre-line;
187
+ line-height: 1.4;
188
+ max-width: 195px;
189
+ }
190
+ #ctf-admin .ctf_shortcode_symbol{
191
+ position: absolute;
192
+ right: -24px;
193
+ bottom: 0px;
194
+ padding: 2px 4px 3px 3px;
195
+ width: auto;
196
+ font-size: 11px;
197
+ margin-left: 5px;
198
+ }
199
+
200
+ /* Error notice */
201
+ #ctf-admin .ctf_error_notice, .ctf_error_notice{
202
+ margin-top: 5px;
203
+ background: #f9ecda;
204
+ padding: 5px 10px;
205
+ border: 1px solid #e89a2e;
206
+ color: #cf6100;
207
+
208
+ -moz-border-radius: 3px;
209
+ -webkit-border-radius: 3px;
210
+ border-radius: 3px;
211
+ }
212
+ #ctf-admin .ctf_error_notice a, .ctf_error_notice a{
213
+ color: #d85600;
214
+ }
215
+ #ctf-admin .ctf_error_notice a:hover, .ctf_error_notice a:hover,
216
+ #ctf-admin .ctf_error_notice a:focus, .ctf_error_notice a:focus{
217
+ color: #a34100;
218
+ }
219
+ #ctf-admin .ctf_error_notice p, .ctf_error_notice p{
220
+ margin: 0;
221
+ padding: 5px 0;
222
+ font-size: 13px;
223
+ }
224
+ .ctf_user_id_error{
225
+ display: none;
226
+
227
+ margin: 10px 0;
228
+ background: #F7E6E6;
229
+ padding: 5px 10px;
230
+ border: 1px solid #BA7B7B;
231
+ color: #592626;
232
+
233
+ -moz-border-radius: 3px;
234
+ -webkit-border-radius: 3px;
235
+ border-radius: 3px;
236
+ }
237
+
238
+ /* Review notice */
239
+ .ctf_notice {
240
+ position: relative;
241
+ overflow: hidden;
242
+ max-width: 870px;
243
+ margin-top: 10px;
244
+ padding: 10px 10px 7px 10px;
245
+ clear: both;
246
+ background: #E6F0E8;
247
+ border: 1px solid #6AB074;
248
+ color: #214F28;
249
+ }
250
+ .ctf_notice .ctf_thumb{
251
+ position: relative;
252
+ display: inline-block;
253
+ width: 74px;
254
+ margin: 0 0 0 -100% !important;
255
+
256
+ -moz-border-radius: 4px;
257
+ -webkit-border-radius: 4px;
258
+ border-radius: 4px;
259
+ }
260
+ .ctf_notice .ctf_thumb .img-overlay {
261
+ position: absolute;
262
+ top: 3px;
263
+ padding: 6px 5px;
264
+ font-size: 12px;
265
+ font-weight: bold;
266
+ background: #fff;
267
+ line-height: 1;
268
+ color: #000;
269
+ opacity: .8;
270
+ }
271
+ .ctf_notice img{
272
+ width: 74px;
273
+
274
+ -moz-border-radius: 4px;
275
+ -webkit-border-radius: 4px;
276
+ border-radius: 4px;
277
+ }
278
+ .ctf_notice .ctf-notice-text{
279
+ float: left;
280
+ clear: none;
281
+ width: 100%;
282
+ padding: 0;
283
+ }
284
+ .ctf_notice p{
285
+ float: left;
286
+ clear: both;
287
+ width: auto;
288
+ margin: 0 0 0 90px !important;
289
+ padding: 2px 40px 2px 0;
290
+ line-height: 1.4;
291
+ }
292
+ .ctf_notice .ctf-links{
293
+ margin-top: 4px !important;
294
+ }
295
+ .ctf_notice a{
296
+ display: inline-block;
297
+ padding: 0 8px;
298
+ color: #178529;
299
+ }
300
+ .ctf_notice a:hover,
301
+ .ctf_notice a:focus{
302
+ color: #0c7abf;
303
+ }
304
+ .ctf_notice .links{
305
+ margin: 0 0 0 82px !important;
306
+ padding: 4px 0 0 0;
307
+ margin-top: 6px !important;
308
+ }
309
+ .ctf_notice .ctf_notice_close,
310
+ .ctf_notice .ctf_bfcm_sale_notice_close,
311
+ .ctf_notice .ctf_new_user_sale_notice_close {
312
+ position: absolute;
313
+ top: 0;
314
+ right: 0;
315
+ padding: 10px;
316
+ line-height: 1;
317
+ }
318
+ .ctf_notice .ctf_notice_close:hover,
319
+ .ctf_notice .ctf_notice_close:focus{
320
+ color: #a34100;
321
+ }
322
+ .ctf_notice .ctf_offer_btn {
323
+ padding: 4px 12px 6px 12px;
324
+ background: green;
325
+ color: #fff;
326
+ border-radius: 4px;
327
+ display: inline-block;
328
+ text-decoration: none;
329
+ margin-left: 0;
330
+ }
331
+ .ctf_notice .ctf_offer_btn:hover, .ctf_notice .ctf_offer_btn:focus {
332
+ background: #049404;
333
+ color: #fff;
334
+ }
335
+ .ctf_notice .ctf_other_notice{
336
+ padding-top: 10px;
337
+ font-style: italic;
338
+ font-size: 12px;
339
+ }
340
+ .ctf_notice .ctf_other_notice a{
341
+ padding: 0;
342
+ }
343
+
344
+ /* Customize tab */
345
+ #ctf-admin #ctf_width_options{
346
+ margin-top: 10px;
347
+ display: none;
348
+ }
349
+
350
+ /* Pro only notices */
351
+ #ctf-admin .ctf_row{
352
+ display: block;
353
+ width: 100%;
354
+ }
355
+ #ctf-admin .ctf_pro,
356
+ #ctf-admin .ctf_pro label,
357
+ #ctf_admin .ctf_pro input{
358
+ color: #999 !important;
359
+ }
360
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child label,
361
+ #ctf-admin .ctf_pro .ctf_layout_type_list {
362
+ color: initial !important;
363
+ }
364
+ #ctf-admin .ctf_shortcode_table tr.ctf_pro{
365
+ background: #eacccc;
366
+ }
367
+ #ctf-admin .ctf_table_key{
368
+ display: block;
369
+ float: left;
370
+ width: 11px;
371
+ height: 11px;
372
+ border: 1px solid #999;
373
+ background: #eacccc;
374
+ margin: 3px 6px 0 0;
375
+ }
376
+ #ctf-admin .ctf_note {
377
+ font-size: 12px;
378
+ font-style: italic;
379
+ margin-left: 5px;
380
+ }
381
+ #ctf-admin .ctf_checkbox,
382
+ #ctf-admin .ctf_checkbox label{
383
+ font-size: 13px;
384
+ line-height: 1.4em;
385
+ padding-top: 10px;
386
+ vertical-align: top;
387
+ }
388
+ #ctf-admin .ctf_pro .ctf_layout_cell img{
389
+ opacity: 0.4;
390
+ }
391
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child img {
392
+ opacity: 1 !important;
393
+ }
394
+ #ctf-admin .ctf_pro input,
395
+ #ctf-admin .ctf_pro select {
396
+ color: #999 !important;
397
+ }
398
+ #ctf-admin .ctf_lock {
399
+ display: none;
400
+ padding: 8px 10px 8px 13px;
401
+ position: absolute;
402
+ top: 50%;
403
+ left: 50%;
404
+ margin-top: -4px;
405
+ margin-left: -34px;
406
+ background: rgba(255,255,255,0.8);
407
+ border-radius: 5px;
408
+ box-shadow: 0 0 5px 0 rgba(0,0,0,0.1);
409
+ color: #333;
410
+ z-index: 99;
411
+ font-size: 14px;
412
+ color: #666;
413
+ font-weight: 700;
414
+ text-decoration: none;
415
+ border: 1px solid #ccc;
416
+ }
417
+ #ctf-admin .ctf_lock i {
418
+ margin-right: 6px;
419
+ }
420
+ #ctf-admin .ctf_pro .ctf_layout_cell:hover .ctf_lock{
421
+ display: block;
422
+ }
423
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child .ctf_lock,
424
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child:hover .ctf_lock {
425
+ display: none;
426
+ }
427
+
428
+ /* Layout */
429
+ .form-table .ctf_layout_type {
430
+ display: inline-block;
431
+ margin-left: 20px;
432
+ }
433
+ .form-table .ctf_layout_type:first-child {
434
+ margin-left: 0;
435
+ }
436
+ .ctf_layout_setting,
437
+ .ctf_box_setting{
438
+ margin-bottom: 20px;
439
+ }
440
+ .ctf_layout_setting:last-child,
441
+ .ctf_box_setting:last-child {
442
+ margin-bottom: 0;
443
+ }
444
+ .ctf_layout_setting label,
445
+ .ctf_box_setting label {
446
+ display: inline-block;
447
+ font-weight: bold;
448
+ font-size: 14px;
449
+ padding-bottom: 5px;
450
+ padding-right: 5px;
451
+ }
452
+ #ctf-admin .ctf_layout_options_wrap .ctf_shortcode_symbol{
453
+ bottom: 4px;
454
+ right: -20px;
455
+ }
456
+ #ctf-admin .ctf_layout_options_wrap .ctf_shortcode,
457
+ #ctf-admin .ctf_box_setting .ctf_shortcode{
458
+ position: relative;
459
+ float: none;
460
+ top: -4px
461
+ }
462
+
463
+ .ctf_layout_cell {
464
+ position: relative;
465
+ padding: 0;
466
+ display: inline-block;
467
+ float: left;
468
+ width: 155px;
469
+ padding: 15px 15px 10px 15px;
470
+ margin-right: 10px;
471
+ background: #f6f6f6;
472
+ border: 1px solid #ddd;
473
+ -moz-border-radius: 5px;
474
+ -webkit-border-radius: 5px;
475
+ border-radius: 5px;
476
+ vertical-align: top;
477
+ }
478
+ .ctf_layout_cell:nth-child(4) {
479
+ margin-right: 0;
480
+ }
481
+ @media all and (max-width: 1200px){
482
+ .ctf_layout_cell{
483
+ width: 21%;
484
+ padding: 1%;
485
+ margin-right: 1%;
486
+ }
487
+ }
488
+ @media all and (max-width: 480px){
489
+ .ctf_layout_cell{
490
+ width: 98%;
491
+ padding: 1%;
492
+ margin: 5px 0;
493
+ }
494
+ }
495
+ .ctf_label {
496
+ font-size: 16px;
497
+ font-weight: bold;
498
+ padding-left: 2px;
499
+ }
500
+ .ctf_layout_cell:hover,
501
+ .ctf_layout_cell:focus{
502
+ background: #fcfcfc;
503
+ cursor: pointer;
504
+ }
505
+ .ctf_layout_cell.ctf_layout_selected{
506
+ background: #f7faf1;
507
+ border: 1px solid #7ad03a;
508
+ }
509
+ .ctf_layout_cell h3{
510
+ font-size: 15px;
511
+ margin-top: 0;
512
+ }
513
+ .ctf_layout_cell img{
514
+ width: 100%;
515
+ border: 1px solid #ddd;
516
+ margin-top: 5px;
517
+ }
518
+
519
+ #ctf-admin #ctf_width_options{
520
+ margin-top: 5px;
521
+ display: none;
522
+ }
523
+ #ctf-admin #ctf_width_options label {
524
+ font-size: 13px;
525
+ position: relative;
526
+ top: -2px;
527
+ }
528
+ #ctf-admin .ctf_layout_settings{
529
+ float: left;
530
+ clear: both;
531
+ width: 96%;
532
+
533
+ padding: 20px 2%;
534
+ margin: 10px 0;
535
+ font-size: 13px;
536
+ background: #f9f9f9;
537
+ background: rgba(255,255,255,0.8);
538
+
539
+ -moz-border-radius: 8px;
540
+ -webkit-border-radius: 8px;
541
+ border-radius: 8px;
542
+ }
543
+ .ctf_layout_setting {
544
+ clear:left;
545
+ margin-bottom: 10px;
546
+ }
547
+ #ctf-admin .ctf_layout_options_wrap .ctf-tooltip{
548
+ width: 98%;
549
+ padding: 10px 1%;
550
+ background: #eee;
551
+ background: rgba(0,0,0,0.05);
552
+ }
553
+ #ctf-admin .ctf_layout_mobile_layout_setting {
554
+ width: auto;
555
+ }
556
+
557
+ #ctf-admin .ctf-quick-start{
558
+ display: block;
559
+ float: left;
560
+ clear: both;
561
+ min-width: 808px;
562
+ margin: 15px 0 0 0;
563
+ padding: 15px 20px;
564
+
565
+ border: 1px solid #ccc;
566
+ background: #eee;
567
+ background: rgba(255,255,255,0.5);
568
+
569
+ -moz-border-radius: 5px;
570
+ -webkit-border-radius: 5px;
571
+ border-radius: 5px;
572
+ }
573
+ #ctf-admin .ctf-quick-start h3,
574
+ #ctf-admin .ctf-quick-start p{
575
+ margin: 0;
576
+ padding: 5px 0;
577
+ }
578
+ #ctf-admin .ctf-pro-notice,
579
+ #ctf-admin .ctf-pro-notice img{
580
+ display: block;
581
+ float: left;
582
+ clear: both;
583
+ margin: 20px 0 0 0;
584
+ overflow: hidden;
585
+
586
+ -moz-border-radius: 4px;
587
+ -webkit-border-radius: 4px;
588
+ border-radius: 4px;
589
+
590
+ -moz-transition: all 0.5s ease-in-out;
591
+ -webkit-transition: all 0.5s ease-in-out;
592
+ -o-transition: all 0.5s ease-in-out;
593
+ transition: all 0.5s ease-in-out;
594
+ }
595
+ #ctf-admin .ctf-pro-notice img{
596
+ margin: 0;
597
+ }
598
+ #ctf-admin .ctf-pro-notice:hover{
599
+ opacity: 0.95;
600
+
601
+ -moz-transition: all 0.1s ease-in-out;
602
+ -webkit-transition: all 0.1s ease-in-out;
603
+ -o-transition: all 0.1s ease-in-out;
604
+ transition: all 0.1s ease-in-out;
605
+ }
606
+ #ctf-admin .ctf-pro-options,
607
+ #ctf-admin .ctf-pro-options label{
608
+ color: #999 !important;
609
+ }
610
+ #ctf-admin .ctf-shortcode_table tr.ctf-pro-options{
611
+ background: #eacccc;
612
+ }
613
+ #ctf-admin .ctf-pro-options label,
614
+ #ctf-admin .ctf-pro-options .ctf_shortcode {
615
+ color: #999 !important;
616
+ }
617
+ #ctf-admin .ctf_pro .ctf-tooltip-link,
618
+ #ctf-admin .ctf-pro-options .ctf-tooltip-link {
619
+ color: #aaa !important;
620
+ }
621
+ #ctf-admin .ctf-success{
622
+ font-size: 14px;
623
+ }
624
+ #toplevel_page_custom-twitter-feeds .dashicons-admin-generic:before {
625
+ content: "\f301";
626
+ }
627
+
628
+ /* GDPR Settings */
629
+ #ctf-admin .ctf-list{
630
+ list-style: inside disc;
631
+ }
632
+ #ctf-admin .ctf-box{
633
+ float: left;
634
+ clear: both;
635
+ width: 96%;
636
+ max-width: 900px;
637
+
638
+ padding: 10px 15px;
639
+ margin: 10px 0;
640
+ font-size: 13px;
641
+ background: #f9f9f9;
642
+ background: rgba(255,255,255,0.8);
643
+
644
+ -moz-border-radius: 8px;
645
+ -webkit-border-radius: 8px;
646
+ border-radius: 8px;
647
+ }
648
+ .gdpr_tooltip p{
649
+ padding: 0 0 10px 0;
650
+ }
651
+ .gdpr_tooltip span{
652
+ display: inline-block;
653
+ margin: 0;
654
+ font-weight: bold;
655
+ font-size: 14px;
656
+ }
657
+ .gdpr_tooltip .ctf-list{
658
+ padding: 0 0 10px 0;
659
+ }
660
+ .gdpr_tooltip li{
661
+ padding: 0;
662
+ }
663
+ .ctf_gdpr_plugin_active .ctf_active{
664
+ display: inline-block;
665
+ padding: 10px 20px 15px 20px;
666
+ border-radius: 5px;
667
+ background: #edf4f0;
668
+ border: 1px solid #2c8649;
669
+ color: #2c8649;
670
+ margin: 8px 0 0 0;
671
+ }
672
+ .ctf_gdpr_plugin_active svg{
673
+ width: 16px;
674
+ height: 16px;
675
+ position: relative;
676
+ top: 3px;
677
+ margin-right: 2px;
678
+ }
679
+ .ctf_gdpr_plugin_active path{
680
+ fill: green;
681
+ }
682
+ .ctf_gdpr_list,
683
+ .ctf_gdpr_yes,
684
+ .ctf_gdpr_no{
685
+ display: none;
686
+ }
687
+ #ctf-admin .ctf_gdpr_error{
688
+ padding: 10px 20px 15px 20px;
689
+ border-radius: 5px;
690
+ background: #F7E6E6;
691
+ border: 1px solid #BA7B7B;
692
+ color: #592626;
693
+ }
694
+
695
+ /* Lite Notice */
696
+ @media screen and (max-width: 600px) {
697
+ #ctf-notice-bar {
698
+ display:none !important
699
+ }
700
+ }
701
+
702
+ #ctf-notice-bar {
703
+ background-color: #DDDDDD;
704
+ color: #777777;
705
+ text-align: center;
706
+ position: relative;
707
+ padding: 7px;
708
+ margin-bottom: 0;
709
+ opacity: 1;
710
+ transition: all .3s ease-in-out;
711
+ max-height: 100px;
712
+ overflow: hidden
713
+ }
714
+
715
+ #ctf-notice-bar.out {
716
+ opacity: .5;
717
+ max-height: 0
718
+ }
719
+
720
+ #ctf-notice-bar a {
721
+ color: #e34f0e;
722
+ }
723
+
724
+ #ctf-notice-bar a:hover {
725
+ color: #b85a1b
726
+ }
727
+
728
+ #ctf-notice-bar .dismiss {
729
+ position: absolute;
730
+ top: 0;
731
+ right: 0;
732
+ border: none;
733
+ padding: 5px;
734
+ margin-top: 1px;
735
+ background: 0 0;
736
+ color: #72777c;
737
+ cursor: pointer
738
+ }
739
+
740
+ #ctf-notice-bar .dismiss:before {
741
+ background: 0 0;
742
+ color: #72777c;
743
+ content: "\f335";
744
+ display: block;
745
+ font: normal 20px/20px dashicons;
746
+ speak: none;
747
+ height: 20px;
748
+ text-align: center;
749
+ width: 20px;
750
+ -webkit-font-smoothing: antialiased
751
+ }
752
+
753
+ /* Locator Summary */
754
+ .ctf-feed-locator-summary-wrap {
755
+ max-width: 1100px;
756
+ margin-bottom: 40px;
757
+ }
758
+ .ctf-full-wrap {
759
+ display: none;
760
+ }
761
+ .ctf-locator-more {
762
+ display: inline-block;
763
+ margin: 0 0 0 1px;
764
+ padding: 0 5px;
765
+ border: 1px solid rgba(0,0,0,0.1);
766
+ border-radius: 3px;
767
+ line-height: 1.6;
768
+ }
769
+
770
+ .ctf_shortcode_visible .ctf_locations_link {
771
+ display: none;
772
+ }
773
+ .ctf_locations_link a {
774
+ font-weight: normal;
775
+ text-decoration: none;
776
+ }
777
+ .ctf_locations_link svg {
778
+ width: 11px;
779
+ position: relative;
780
+ top: 1px;
781
+ }
782
+
783
+
784
+ /* More social feeds page */
785
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary .fa-spinner {
786
+ color: #333;
787
+ }
788
+ #ctf-admin .ctf_more_plugins h2{
789
+ margin-top: 15px;
790
+ }
791
+ #ctf-admin .ctf_more_plugins #ctf-admin-addons .addons-container{
792
+ overflow: hidden;
793
+ margin: 0;
794
+ }
795
+ #ctf-admin .ctf_more_plugins#ctf-admin-about #ctf-admin-addons{
796
+ padding: 20px 0;
797
+ }
798
+ #ctf-admin .ctf-more-plugins-intro span{
799
+ color: #e34f0e;
800
+ }
801
+ #ctf-admin .ctf_more_plugins h3{
802
+ padding: 15px 0 0 0;
803
+ }
804
+ .sb_cross_install_modal .ctf-loader{
805
+ position: absolute;
806
+ left: 50%;
807
+ top: 50%;
808
+ margin: -10px 0 0 -10px;
809
+ }
810
+
811
+ /* Social Wall landing page */
812
+ #ctf-admin.sw-landing-page{
813
+ padding: 20px 0;
814
+ }
815
+ #ctf-admin .ctf-sw-icons{
816
+ width: 100%;
817
+ text-align: center;
818
+ }
819
+ #ctf-admin .ctf-sw-icons span{
820
+ display: inline-block;
821
+ width: 32px;
822
+ margin: 0 7px;
823
+ position: relative;
824
+ top: -2px;
825
+ }
826
+ #ctf-admin .ctf-sw-icons .ctf-sb-plus{
827
+ width: 12px;
828
+ height: 12px;
829
+ position: relative;
830
+ top: -12px;
831
+ opacity: 0.9;
832
+ }
833
+ #ctf-admin.sw-landing-page h1,
834
+ #ctf-admin.sw-landing-page h2{
835
+ width: 100%;
836
+ text-align: center;
837
+ }
838
+ #ctf-admin.sw-landing-page h1{
839
+ font-size: 44px;
840
+ margin: 20px 0 0 0;
841
+ line-height: 1.3;
842
+ }
843
+ #ctf-admin.sw-landing-page h2{
844
+ font-size: 30px;
845
+ font-weight: 100;
846
+ line-height: 1.2;
847
+ margin: 5px 0 20px 0;
848
+ }
849
+ #ctf-admin .ctf-sw-info{
850
+ width: 95%;
851
+ max-width: 1400px;
852
+ margin: 0 auto;
853
+ padding: 50px 10px;
854
+ clear: both;
855
+ overflow: hidden;
856
+ box-sizing: border-box;
857
+ }
858
+ #ctf-admin .ctf-sw-features{
859
+ float: left;
860
+ width: 40%;
861
+ margin-right: 5%;
862
+ padding: 20px 0 0 0;
863
+ }
864
+ #ctf-admin .ctf-sw-screenshot{
865
+ position: relative;
866
+ width: 55%;
867
+ float: left;
868
+ box-sizing: border-box;
869
+ border: 5px solid #fff;
870
+ box-shadow: 0 0 20px 0 rgba(0,0,0,0.05);
871
+ }
872
+ #ctf-admin .ctf-sw-features p{
873
+ margin: 35px 0;
874
+ padding: 0;
875
+ font-size: 15px;
876
+ }
877
+ #ctf-admin .ctf-sw-features p:first-child{
878
+ margin-top: 0;
879
+ }
880
+ #ctf-admin .ctf-sw-features span{
881
+ display: block;
882
+ font-size: 20px;
883
+ font-weight: bold;
884
+ }
885
+ #ctf-admin.sw-landing-page .cta {
886
+ margin: 20px 0 0 5px;
887
+ padding: 8px 40px 10px 40px;
888
+ font-size: 20px;
889
+ }
890
+ #ctf-admin .ctf-sw-screenshot img{
891
+ width: 100%;
892
+ display: block;
893
+ }
894
+ #ctf-admin .ctf-sw-screenshot .cta{
895
+ position: absolute;
896
+ top: 50%;
897
+ left: 50%;
898
+ margin-top: -32px;
899
+ margin-left: -80px;
900
+ padding: 15px 30px 17px 30px;
901
+ color: #333;
902
+ text-decoration: none;
903
+ background: rgba(255,255,255,0.95);
904
+ border-radius: 3px;
905
+ /*border: 2px solid rgba(0,0,0,0.1);*/
906
+ box-shadow: 0 0 20px 0 rgba(0,0,0,0.1);
907
+ }
908
+ #ctf-admin .ctf-sw-screenshot:hover .cta,
909
+ #ctf-admin .ctf-sw-screenshot:focus .cta{
910
+ background: #e34717;
911
+ color: #fff;
912
+ box-shadow: 0;
913
+ }
914
+ #ctf-admin .ctf-sw-footer-cta{
915
+ width: 100%;
916
+ text-align: center;
917
+ }
918
+ #ctf-admin .ctf-sw-footer-cta a{
919
+ font-size: 22px;
920
+ display: inline-block;
921
+ text-decoration: none;
922
+ line-height: 25px;
923
+ font-weight: normal;
924
+ padding: 10px 0 30px 0;
925
+ }
926
+ #ctf-admin .ctf-sw-footer-cta span{
927
+ width: 30px;
928
+ height: 30px;
929
+ display: inline-block;
930
+ margin: 0 10px 0 0;
931
+ position: relative;
932
+ top: 4px;
933
+ }
934
+ #ctf-admin .ctf-sw-footer-cta span .emoji{
935
+ width: 30px !important;
936
+ height: 30px !important;
937
+ }
938
+
939
+ @media all and (max-width: 1500px){
940
+ #ctf-admin .ctf-sw-screenshot{
941
+ width: 55%;
942
+ float: left;
943
+ height: 500px;
944
+ background: url('../img/sw-screenshot.png') no-repeat center center;
945
+ background-size: cover;
946
+ }
947
+ #ctf-admin .ctf-sw-screenshot img{
948
+ display: none;
949
+ }
950
+ }
951
+ @media all and (max-width: 900px){
952
+ #ctf-admin .ctf-sw-info{
953
+ padding: 20px 0;
954
+ }
955
+ #ctf-admin .ctf-sw-features{
956
+ width: 100%;
957
+ padding: 10px 0;
958
+ margin: 0;
959
+ }
960
+ #ctf-admin .ctf-sw-screenshot{
961
+ width: 100%;
962
+ padding: 0;
963
+ height: auto;
964
+ background: none;
965
+ }
966
+ #ctf-admin .ctf-sw-screenshot img{
967
+ display: block;
968
+ }
969
+ #ctf-admin .ctf-sw-features .cta {
970
+ width: 100%;
971
+ box-sizing: border-box;
972
+ text-align: center;
973
+ margin: 20px 0;
974
+ }
975
+ }
976
+
977
+ /* Menu Pro link */
978
+ #adminmenu .wp-submenu .ctf_get_pro_highlight{
979
+ background-color: #1da867;
980
+ color: #fff;
981
+ }
982
+ #adminmenu .wp-submenu .ctf_get_pro_highlight:hover,
983
+ #adminmenu .wp-submenu .ctf_get_pro_highlight:active{
984
+ background-color: #2b9865;
985
+ color: #fff;
986
+ }
987
+
988
+ /* Install another plugin modal */
989
+ .sb_cross_install_modal {
990
+ position: fixed;
991
+ z-index: 999;
992
+ width: 100%;
993
+ top: 0;
994
+ right: 0;
995
+ bottom: 0;
996
+ left: 0;
997
+ background: rgba(0, 0, 0, 0.3);
998
+ }
999
+ .sb_cross_install_inner {
1000
+ position: absolute;
1001
+ top: 140px;
1002
+ left: 50%;
1003
+ width: 480px;
1004
+ margin: 0 0 0 -245px;
1005
+ padding: 35px;
1006
+ background: #fff;
1007
+ text-align: left;
1008
+
1009
+ -webkit-box-shadow: 0 1px 20px rgba(0, 0, 0, 0.2);
1010
+ box-shadow: 0 1px 20px rgba(0, 0, 0, 0.2);
1011
+
1012
+ -moz-border-radius: 3px;
1013
+ -webkit-border-radius: 3px;
1014
+ border-radius: 3px;
1015
+ }
1016
+ .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons .addon-container,
1017
+ .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons .addons-container{
1018
+ width: 100%;
1019
+ margin: 0;
1020
+ padding: 0;
1021
+ min-height: 198px;
1022
+ }
1023
+ #ctf-admin-addons .addon-item .action-button button{
1024
+ background: #007cba;
1025
+ border: 0;
1026
+ }
1027
+ #ctf-admin-addons .addon-item .action-button button:hover{
1028
+ background: #0071a1;
1029
+ }
1030
+ .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons{
1031
+ padding: 0;
1032
+ }
1033
+ /* More social feeds page */
1034
+ .sb_cross_install_inner#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary .fa-spinner {
1035
+ color: #333;
1036
+ }
1037
+ #ctf-admin .ctf_more_plugins h2{
1038
+ margin-top: 15px;
1039
+ }
1040
+ #ctf-admin .ctf_more_plugins #ctf-admin-addons .addons-container{
1041
+ overflow: hidden;
1042
+ margin: 0;
1043
+ }
1044
+ #ctf-admin .ctf_more_plugins#ctf-admin-about #ctf-admin-addons{
1045
+ padding: 20px 0;
1046
+ }
1047
+ #ctf-admin .ctf-more-plugins-intro span{
1048
+ color: #e34f0e;
1049
+ }
1050
+ #ctf-admin .ctf_more_plugins h3{
1051
+ padding: 15px 0 0 0;
1052
+ }
1053
+ .sb_cross_install_modal .ctf-loader{
1054
+ position: absolute;
1055
+ left: 50%;
1056
+ top: 50%;
1057
+ margin: -10px 0 0 -10px;
1058
+ }
1059
+ /* AW Addons */
1060
+ #ctf-admin-addons h3 {
1061
+ padding: 10px 10px 0 10px;
1062
+ clear: left;
1063
+ }
1064
+ #ctf-admin-addons *,#ctf-admin-addons *::before,#ctf-admin-addons *::after {
1065
+ -webkit-box-sizing: border-box;
1066
+ -moz-box-sizing: border-box;
1067
+ box-sizing: border-box
1068
+ }
1069
+
1070
+ #ctf-admin-addons .addons-container {
1071
+ margin-left: -20px;
1072
+ margin-right: -20px
1073
+ }
1074
+
1075
+ #ctf-admin-addons .unlock-msg {
1076
+ padding: 0 20px;
1077
+ margin-top: -20px;
1078
+ clear: both
1079
+ }
1080
+
1081
+ #ctf-admin-addons #ctf-admin-addons-search {
1082
+ background-color: #fff;
1083
+ border: 1px solid #ddd;
1084
+ border-radius: 3px;
1085
+ box-shadow: none;
1086
+ color: #333;
1087
+ vertical-align: middle;
1088
+ padding: 7px 12px;
1089
+ margin: -3px 10px 0 0;
1090
+ width: 200px;
1091
+ min-height: 35px;
1092
+ float: right
1093
+ }
1094
+
1095
+ #ctf-admin-addons #ctf-admin-addons-search:focus {
1096
+ border-color: #bbb
1097
+ }
1098
+
1099
+ #ctf-admin-addons .addon-container {
1100
+ padding: 0 20px;
1101
+ float: left;
1102
+ width: 33.333333%;
1103
+ margin-bottom: 20px
1104
+ }
1105
+
1106
+ @media (max-width: 1249px) {
1107
+ #ctf-admin-addons .addon-container {
1108
+ width:50%
1109
+ }
1110
+ }
1111
+
1112
+ @media (max-width: 767px) {
1113
+ #ctf-admin-addons .addon-container {
1114
+ width:100%;
1115
+ margin-bottom: 20px
1116
+ }
1117
+ }
1118
+
1119
+ #ctf-admin-addons h4 {
1120
+ font-size: 17px;
1121
+ font-weight: 700
1122
+ }
1123
+
1124
+ #ctf-admin-addons .addon-item {
1125
+ background-color: #fff;
1126
+ border: 1px solid #ddd;
1127
+ border-radius: 3px;
1128
+ margin: 0
1129
+ }
1130
+
1131
+ @media (max-width: 767px) {
1132
+ #ctf-admin-addons .addon-item {
1133
+ margin:0 0
1134
+ }
1135
+ }
1136
+
1137
+ #ctf-admin-addons .addon-item img {
1138
+ border: 1px solid #eee;
1139
+ float: left;
1140
+ max-width: 75px
1141
+ }
1142
+
1143
+ #ctf-admin-addons .addon-item h5 {
1144
+ margin: 0 0 10px 100px;
1145
+ font-size: 16px
1146
+ }
1147
+
1148
+ #ctf-admin-addons .addon-item p {
1149
+ margin: 0 0 0 100px
1150
+ }
1151
+
1152
+ #ctf-admin-addons .addon-item .details {
1153
+ padding: 30px 20px
1154
+ }
1155
+
1156
+ #ctf-admin-addons .addon-item .actions {
1157
+ background-color: #f7f7f7;
1158
+ border-top: 1px solid #ddd;
1159
+ padding: 20px;
1160
+ position: relative
1161
+ }
1162
+
1163
+ #ctf-admin-addons .addon-item .actions .msg {
1164
+ background-color: #f7f7f7;
1165
+ position: absolute;
1166
+ text-align: center;
1167
+ font-weight: 600;
1168
+ top: 0;
1169
+ left: 0;
1170
+ height: 100%;
1171
+ width: 100%;
1172
+ z-index: 99;
1173
+ padding: 20px;
1174
+ display: -webkit-box;
1175
+ display: -moz-box;
1176
+ display: -ms-flexbox;
1177
+ display: -webkit-flex;
1178
+ display: flex;
1179
+ -webkit-align-items: center;
1180
+ -ms-align-items: center;
1181
+ align-items: center;
1182
+ justify-content: center
1183
+ }
1184
+
1185
+ #ctf-admin-addons .addon-item .actions .msg.success {
1186
+ color: #2a9b39
1187
+ }
1188
+
1189
+ #ctf-admin-addons .addon-item .actions .msg.error {
1190
+ color: red
1191
+ }
1192
+
1193
+ #ctf-admin-addons .addon-item .status {
1194
+ float: left;
1195
+ padding-top: 8px
1196
+ }
1197
+
1198
+ #ctf-admin-addons .addon-item .status .status-inactive {
1199
+ color: red
1200
+ }
1201
+
1202
+ #ctf-admin-addons .addon-item .status .status-download {
1203
+ color: #666
1204
+ }
1205
+
1206
+ #ctf-admin-addons .addon-item .status .status-active {
1207
+ color: #2a9b39
1208
+ }
1209
+
1210
+ #ctf-admin-addons .addon-item .upgrade-button {
1211
+ text-align: center
1212
+ }
1213
+
1214
+ #ctf-admin-addons .addon-item .upgrade-button a {
1215
+ font-weight: 600;
1216
+ width: 140px;
1217
+ text-align: center;
1218
+ padding: 8px 5px
1219
+ }
1220
+
1221
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button {
1222
+ display: inline-block;
1223
+ text-decoration: none;
1224
+ font-size: 13px;
1225
+ line-height: 26px;
1226
+ height: 28px;
1227
+ margin: 0;
1228
+ padding: 0 10px 1px;
1229
+ cursor: pointer;
1230
+ -webkit-appearance: none;
1231
+ border-radius: 3px;
1232
+ border: 1px solid #cccccc;
1233
+ background: #f7f7f7;
1234
+ box-shadow: 0 1px 0 #cccccc;
1235
+ font-weight: normal
1236
+ }
1237
+
1238
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button:hover,#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button:focus {
1239
+ background: #fafafa;
1240
+ border-color: #999;
1241
+ color: #23282d
1242
+ }
1243
+
1244
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary {
1245
+ background: #0085ba;
1246
+ border-color: #0073aa #006799 #006799;
1247
+ box-shadow: 0 1px 0 #006799;
1248
+ color: #fff;
1249
+ text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799
1250
+ }
1251
+
1252
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary:hover,#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary:focus {
1253
+ background: #008ec2;
1254
+ border-color: #006799;
1255
+ color: #fff
1256
+ }
1257
+
1258
+ #ctf-admin #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-primary .fa-spinner {
1259
+ color: #fff
1260
+ }
1261
+
1262
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-secondary {
1263
+ color: #555;
1264
+ border-color: #cccccc;
1265
+ background: #f7f7f7;
1266
+ box-shadow: 0 1px 0 #cccccc
1267
+ }
1268
+
1269
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-secondary:hover,#ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.button-secondary:focus {
1270
+ background: #fafafa;
1271
+ border-color: #999;
1272
+ color: #23282d
1273
+ }
1274
+
1275
+ #ctf-admin-about #ctf-admin-addons .addon-item .action-button .button.disabled {
1276
+ cursor: default
1277
+ }
1278
+
1279
+
1280
+ #ctf-admin-addons .addon-item .action-button {
1281
+ float: right
1282
+ }
1283
+
1284
+ #ctf-admin-addons .addon-item .action-button button {
1285
+ background: none;
1286
+ border: 1px solid #ddd;
1287
+ border-radius: 3px;
1288
+ box-shadow: none;
1289
+ cursor: pointer;
1290
+ font-weight: 600;
1291
+ width: 140px;
1292
+ text-align: center;
1293
+ padding: 8px 5px
1294
+ }
1295
+
1296
+ #ctf-admin-addons .addon-item .action-button button:hover,#ctf-admin-addons .addon-item .action-button button.loading {
1297
+ background-color: #e9e9e9
1298
+ }
1299
+
1300
+ #ctf-admin-addons .addon-item .action-button button .fa {
1301
+ margin-right: 8px
1302
+ }
1303
+
1304
+ #ctf-admin-addons .addon-item .action-button button .fa.fa-spinner {
1305
+ margin-right: 0
1306
+ }
1307
+
1308
+ #ctf-admin-addons .addon-item .action-button button.status-active .fa {
1309
+ color: #2a9b39
1310
+ }
1311
+
1312
+ #ctf-admin-addons .addon-item .action-button button.status-inactive .fa {
1313
+ color: red
1314
+ }
1315
+
1316
+ #ctf-admin-addons .addon-item .action-button button.status-download .fa {
1317
+ color: #999
1318
+ }
1319
+
1320
+ #ctf-admin-addons .addon-item .action-button button.disabled {
1321
+ cursor: default
1322
+ }
1323
+
1324
+ #ctf-admin-addons .addon-item .action-button button.loading .fa {
1325
+ color: #666
1326
+ }
1327
+ .ctf-clear:after{
1328
+ display: table;
1329
+ content: "";
1330
+ clear: both;
1331
  }
css/ctf-blocks.css CHANGED
@@ -1,3 +1,3 @@
1
- .ctf {
2
- pointer-events: none !important;
3
  }
1
+ .ctf {
2
+ pointer-events: none !important;
3
  }
css/ctf-styles.css CHANGED
@@ -1,829 +1,829 @@
1
-
2
- /* General */
3
- #ctf{
4
- overflow-y: auto;
5
- -ms-overflow-y: auto;
6
- }
7
- #ctf .ctf-item{
8
- padding: 15px 5px;
9
- border-top: 1px solid #ddd;
10
- border-top: 1px solid rgba(0,0,0,0.1);
11
- overflow: hidden;
12
- }
13
- #ctf .ctf-item:first-child{
14
- border-top: none;
15
- }
16
- #ctf .ctf_remaining {
17
- display: none;
18
- }
19
- #ctf .ctf_more{
20
- padding: 1px;
21
- border: 1px solid transparent;
22
- border-radius: 3px;
23
- display: inline-block;
24
- line-height: 1;
25
- }
26
- #ctf .ctf_more:hover {
27
- cursor: pointer;
28
- background: rgba(0,0,0,0.05);
29
- border: 1px solid rgba(0,0,0,0.1);
30
- text-decoration: none;
31
- }
32
-
33
- /* Header */
34
- #ctf .ctf-header{
35
- float: left;
36
- clear: both;
37
- margin: 0;
38
- padding: 5px 5px 15px 5px;
39
- line-height: 1.2;
40
- width: 100%;
41
- border-bottom: 1px solid #ddd;
42
- border-bottom: 1px solid rgba(0,0,0,0.1);
43
-
44
- -webkit-box-sizing: border-box;
45
- -moz-box-sizing: border-box;
46
- box-sizing: border-box;
47
- }
48
- #ctf .ctf-header a{
49
- float: left;
50
- display: block;
51
- min-width: 100%\9;
52
- }
53
- /* Header avatar */
54
- #ctf .ctf-header-img{
55
- float: left;
56
- position: relative;
57
- width: 48px;
58
- margin: 0 0 0 -100% !important;
59
- overflow: hidden;
60
-
61
- -moz-border-radius: 40px;
62
- -webkit-border-radius: 40px;
63
- border-radius: 40px;
64
- }
65
- #ctf .ctf-header-img img{
66
- display: block;
67
- float: left;
68
- margin: 0 !important;
69
- padding: 0 !important;
70
- border: none !important;
71
-
72
- -moz-border-radius: 40px;
73
- -webkit-border-radius: 40px;
74
- border-radius: 40px;
75
- }
76
- #ctf .ctf-author-avatar {
77
- clear: both;
78
- margin: 3px 6px 0 -53px;
79
- }
80
- #ctf .ctf-author-name {
81
- margin-left: 6px;
82
- }
83
- #ctf .ctf-author-box {
84
- margin-left: 53px;
85
- }
86
- /* Header avatar hover */
87
- #ctf .ctf-header-img-hover,
88
- #ctf .ctf-header-generic-icon{
89
- display: none;
90
- position: absolute;
91
- width: 100%;
92
- top: 0;
93
- bottom: 0;
94
- left: 0;
95
- text-align: center;
96
- color: #fff;
97
- background: #1b95e0;
98
- background: rgba(27,149,224,0.85);
99
-
100
- -moz-border-radius: 40px;
101
- -webkit-border-radius: 40px;
102
- border-radius: 40px;
103
- z-index: 999;
104
- }
105
- #ctf .ctf-header-img-hover .fa,
106
- #ctf .ctf-header-generic-icon .fa,
107
- #ctf .ctf-header-img-hover svg,
108
- #ctf .ctf-header-generic-icon svg{
109
- position: absolute;
110
- top: 50%;
111
- left: 50%;
112
- margin-top: -10px;
113
- margin-left: -8px;
114
- font-size: 20px;
115
- z-index: 1000;
116
- }
117
- #ctf .ctf-header-img-hover svg,
118
- #ctf .ctf-header-generic-icon svg {
119
- margin-top: -10px;
120
- margin-left: -10px;
121
- }
122
- /* Header text */
123
- #ctf .ctf-header-text{
124
- float: left;
125
- width: 100%;
126
- padding-top: 5px;
127
- }
128
- #ctf .ctf-no-bio .ctf-header-text{
129
- padding-top: 13px;
130
- }
131
- #ctf .ctf-header a{
132
- text-decoration: none;
133
- }
134
- #ctf .ctf-header-text .ctf-header-user,
135
- #ctf .ctf-header-text p{
136
- float: left;
137
- clear: both;
138
- width: auto;
139
- margin: 0 0 0 60px !important;
140
- padding: 0 !important;
141
- }
142
- #ctf .ctf-header-name{
143
- font-weight: bold;
144
- margin-right: 6px;
145
- }
146
- #ctf .ctf-header-text .ctf-header-bio{
147
- padding: 0 !important;
148
- word-break: break-word;
149
- }
150
- #ctf .ctf-header-text .ctf-verified{
151
- margin-right: 5px;
152
- }
153
- /* Follow */
154
- #ctf .ctf-header .ctf-header-follow{
155
- display: inline-block;
156
- padding: 3px 6px;
157
- margin: 0 0 0 2px;
158
- position: relative;
159
- top: -2px;
160
- background: #1b95e0;
161
- color: #fff;
162
- font-size: 11px;
163
-
164
- -moz-border-radius: 3px;
165
- -webkit-border-radius: 3px;
166
- border-radius: 3px;
167
-
168
- -moz-transition: background 0.6s ease-in-out;
169
- -webkit-transition: background 0.6s ease-in-out;
170
- -o-transition: background 0.6s ease-in-out;
171
- transition: background 0.6s ease-in-out;
172
- }
173
- #ctf .ctf-header .ctf-header-follow .fa,
174
- #ctf .ctf-header .ctf-header-follow svg{
175
- margin-right: 3px;
176
- font-size: 12px;
177
- }
178
- #ctf .ctf-header a:hover .ctf-header-follow{
179
- background: #0c7abf;
180
-
181
- -moz-transition: background 0.2s ease-in-out;
182
- -webkit-transition: background 0.2s ease-in-out;
183
- -o-transition: background 0.2s ease-in-out;
184
- transition: background 0.2s ease-in-out;
185
- }
186
- /* Generic header version */
187
- #ctf .ctf-header-type-generic .ctf-header-img{
188
- color: #fff;
189
- width: 48px;
190
- height: 48px;
191
- position: relative;
192
- }
193
- #ctf .ctf-header-type-generic .ctf-header-link:hover .ctf-header-generic-icon{
194
- background: #0c7abf;
195
- }
196
- #ctf .ctf-header-type-generic .ctf-header-no-bio{
197
- padding-top: 9px !important;
198
- font-size: 16px;
199
- font-weight: bold;
200
- }
201
- #ctf .ctf-header-type-generic .ctf-header-generic-icon{
202
- display: block;
203
- color: #fff;
204
- background: #1b95e0;
205
-
206
- -moz-transition: background 0.2s ease-in-out;
207
- -webkit-transition: background 0.2s ease-in-out;
208
- -o-transition: background 0.2s ease-in-out;
209
- transition: background 0.2s ease-in-out;
210
- }
211
- #ctf .ctf-header-type-generic:hover .ctf-header-generic-icon{
212
- display: block;
213
- opacity: 1;
214
- }
215
- #ctf .ctf-header .ctf-header-user{
216
- font-size: 16px;
217
- line-height: 1.3;
218
- -ms-word-wrap: break-word;
219
- word-break: break-word;
220
- }
221
- #ctf .ctf-header p,
222
- #ctf .ctf-header .ctf-header-screenname{
223
- font-size: 13px;
224
- line-height: 1.3;
225
- }
226
-
227
- /* screenreader */
228
-
229
- .ctf-screenreader{
230
- text-indent: -9999px !important;
231
- display: inline-block !important;
232
- width: 0 !important;
233
- height: 0 !important;
234
- line-height: 0 !important;
235
- text-align: left !important;
236
- }
237
-
238
- /* No bio */
239
- #ctf .ctf-header-text.sbi-no-info .ctf-header-user{
240
- padding-top: 9px !important;
241
- }
242
- #ctf .ctf-header-text.sbi-no-bio .ctf-header-counts{
243
- clear: both;
244
- }
245
-
246
- /* Context */
247
- #ctf .ctf-context {
248
- /*float: left;*/
249
- width: 100%;
250
- padding-left: 42px;
251
- margin-bottom: 2px;
252
- }
253
- #ctf .ctf-with-logo .ctf-context {
254
- padding-right: 30px;
255
- }
256
- #ctf .ctf-retweet-icon{
257
- display: inline-block;
258
- font-size: inherit;
259
- line-height: 1;
260
- margin: 0 5px 0 0;
261
- -moz-border-radius: 3px;
262
- -webkit-border-radius: 3px;
263
- border-radius: 3px;
264
- }
265
- #ctf.ctf-super-narrow .ctf-retweet-icon{
266
- margin: 0 5px 0 0;
267
- }
268
- #ctf .ctf-reply-icon{
269
- display: inline-block;
270
- font-size: inherit;
271
- padding: 1px 3px 3px 3px;
272
- line-height: 1;
273
- margin: 0 3px 0 0;
274
- }
275
-
276
- /* Author */
277
- #ctf .ctf-author-box{
278
- margin-left: 56px;
279
- }
280
- #ctf .ctf-author-avatar{
281
- width: 48px;
282
- height: 48px;
283
- margin: 3px 6px 0 -56px;
284
- float: left;
285
- overflow: hidden;
286
- border: none;
287
-
288
- -moz-border-radius: 5px;
289
- -webkit-border-radius: 5px;
290
- border-radius: 5px;
291
- }
292
- #ctf .ctf-author-avatar img{
293
- margin: 0 !important;
294
- padding: 0;
295
- }
296
- #ctf .ctf-author-name,
297
- #ctf .ctf-author-screenname,
298
- #ctf .ctf-verified,
299
- #ctf .ctf-tweet-meta{
300
- margin-left: 4px;
301
- margin-right: 4px;
302
- display: inline-block;
303
- }
304
- #ctf .ctf-screename-sep{
305
- margin: 0 2px;
306
- display: inline-block;
307
- }
308
- #ctf .ctf-author-name{
309
- font-weight: bold !important;
310
- margin-top: 4px;
311
- }
312
- #ctf .ctf-author-box-link{
313
- color: black;
314
- text-decoration: none;
315
- display: inline;
316
- line-height: 1.5 !important;
317
- }
318
- #ctf .ctf-verified,
319
- #ctf .ctf-quoted-verified {
320
- color: #77c7f7;
321
- margin-left: 0;
322
- }
323
- #ctf .ctf-quoted-verified {
324
- margin-right: 4px;
325
- }
326
- #ctf .ctf-tweet-meta{
327
- display: inline-block;
328
- }
329
- #ctf .ctf-replied-to-text a {
330
- font-size: inherit;
331
- color: inherit;
332
- font-weight: inherit;
333
- }
334
- #ctf p.ctf-tweet-text,
335
- #ctf p.ctf-media-link {
336
- padding: 0 !important;
337
- margin: 0 !important;
338
- line-height: 1.4;
339
- }
340
- #ctf .ctf-tweet-content,
341
- #ctf .ctf-tweet-actions{
342
- margin-left: 58px;
343
- margin-top: 3px;
344
- max-width: 100%;
345
- }
346
-
347
- #ctf .ctf-corner-logo {
348
- display: inline-block;
349
- float: right;
350
- color: #1b95e0;
351
- font-size: 24px;
352
- }
353
- #ctf.ctf-narrow .ctf-corner-logo {
354
- font-size: 20px;
355
- }
356
- /* No avatar */
357
- #ctf .ctf-hide-avatar .ctf-context,
358
- #ctf .ctf-hide-avatar .ctf-tweet-content,
359
- #ctf .ctf-hide-avatar .ctf-tweet-actions,
360
- #ctf .ctf-hide-avatar .ctf-quoted-tweet,
361
- #ctf .ctf-hide-avatar .ctf-author-box,
362
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-context,
363
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-content,
364
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-actions,
365
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-quoted-tweet,
366
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-author-box,
367
- #ctf .ctf-hide-avatar .ctf-author-name,
368
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-context,
369
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-content,
370
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-actions,
371
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-quoted-tweet,
372
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-author-box{
373
- margin-left: 0;
374
- }
375
- #ctf .ctf-hide-avatar .ctf-context {
376
- padding-left: 0;
377
- }
378
- #ctf .ctf-header-img.ctf-no-consent .ctf-header-img-hover,
379
- #ctf .ctf-header-img.ctf-no-consent {
380
- display: block;
381
- opacity: 1;
382
- height: 48px;
383
- }
384
-
385
-
386
- /*Quoted tweets*/
387
- #ctf .ctf-quoted-tweet{
388
- display: block;
389
- /*float: left;*/
390
- clear: both;
391
- margin: 7px 0 5px 58px;
392
- padding: 8px 12px;
393
- border: 1px solid #ddd;
394
- border: 1px solid rgba(0,0,0,0.1);
395
- text-decoration: none;
396
-
397
- -moz-border-radius: 5px;
398
- -webkit-border-radius: 5px;
399
- border-radius: 5px;
400
-
401
- -moz-transition: background 0.1s ease-in-out;
402
- -webkit-transition: background 0.1s ease-in-out;
403
- -o-transition: background 0.1s ease-in-out;
404
- transition: background 0.1s ease-in-out;
405
- }
406
- #ctf .ctf-quoted-tweet:hover,
407
- #ctf .ctf-quoted-tweet:focus{
408
- background: #eee;
409
- background: rgba(0,0,0,0.05);
410
- border: 1px solid #ddd;
411
- border: 1px solid rgba(0,0,0,0.05);
412
-
413
- -moz-transition: background 0.1s ease-in-out;
414
- -webkit-transition: background 0.1s ease-in-out;
415
- -o-transition: background 0.1s ease-in-out;
416
- transition: background 0.1s ease-in-out;
417
- }
418
- #ctf .ctf-quoted-author-name{
419
- font-weight: bold;
420
- margin-right: 4px;
421
- }
422
- #ctf .ctf-quoted-tweet p{
423
- margin: 2px 0;
424
- }
425
-
426
- /* Actions */
427
- #ctf .ctf-tweet-actions a{
428
- padding: 2px 10px 2px 5px;
429
- float: left;
430
- }
431
- #ctf .ctf-tweet-actions a.ctf-reply,
432
- #ctf .ctf-tweet-actions a.ctf-retweet,
433
- #ctf .ctf-tweet-actions a.ctf-like{
434
- color: #bbb;
435
- color: rgba(0,0,0,0.35);
436
- text-decoration: none;
437
- border: none;
438
- -moz-transition: color 0.1s ease-in-out;
439
- -webkit-transition: color 0.1s ease-in-out;
440
- -o-transition: color 0.1s ease-in-out;
441
- transition: color 0.1s ease-in-out;
442
- }
443
- #ctf .ctf-tweet-actions a svg,
444
- #ctf .ctf-tweet-actions a span.fa{
445
- width: 1.1em;
446
- height: 1.1em;
447
- font-size: 1.1em;
448
- }
449
- #ctf .ctf-tweet-actions a span.fa.fa-retweet {
450
- width: 1.2em;
451
- height: 1.2em;
452
- font-size: 1.2em;
453
- }
454
- #ctf .ctf-tweet-actions .ctf-reply{
455
- margin-left: -5px;
456
- }
457
- #ctf .ctf-tweet-actions a:hover,
458
- #ctf .ctf-tweet-actions a:focus{
459
- -moz-transition: color 0.1s ease-in-out;
460
- -webkit-transition: color 0.1s ease-in-out;
461
- -o-transition: color 0.1s ease-in-out;
462
- transition: color 0.1s ease-in-out;
463
- }
464
- #ctf .ctf-tweet-actions .ctf-reply:hover,
465
- #ctf .ctf-tweet-actions .ctf-reply:focus{
466
- color: #E34F0E !important;
467
- }
468
- #ctf .ctf-tweet-actions .ctf-retweet:hover,
469
- #ctf .ctf-tweet-actions .ctf-retweet:focus{
470
- color: #19cf86 !important;
471
- }
472
- #ctf .ctf-tweet-actions .ctf-like:hover,
473
- #ctf .ctf-tweet-actions .ctf-like:focus{
474
- color: #e81c4f !important;
475
- }
476
- #ctf .ctf-action-count {
477
- float: right;
478
- padding-left: 3px;
479
- font-size: .9em;
480
- margin-top: .25em;
481
- line-height: 1.2em;
482
- }
483
- #ctf .ctf-twitterlink {
484
- margin-top: .3em;
485
- line-height: 1.2em;
486
- font-size: .8em;
487
- }
488
- #ctf .ctf-tweet-text-media-wrap,
489
- #ctf .ctf-quoted-tweet-text-media-wrap{
490
- margin-left: 5px;
491
- padding: 4px;
492
- display: inline-block;
493
- border: 1px solid #ddd;
494
- border: 1px solid rgba(0,0,0,0.15);
495
-
496
- color: inherit;
497
- text-decoration: none;
498
- line-height: 1;
499
-
500
- -moz-border-radius: 2px;
501
- -webkit-border-radius: 2px;
502
- border-radius: 2px;
503
-
504
- -moz-transition: background 0.1s ease-in-out;
505
- -webkit-transition: background 0.1s ease-in-out;
506
- -o-transition: background 0.1s ease-in-out;
507
- transition: background 0.1s ease-in-out;
508
- }
509
- #ctf .ctf-tweet-text-media-wrap:hover,
510
- #ctf .ctf-tweet-text-media-wrap:focus{
511
- background: #eee;
512
- background: rgba(0,0,0,0.05);
513
- border: 1px solid #ddd;
514
- border: 1px solid rgba(0,0,0,0.1);
515
-
516
- -moz-transition: background 0.1s ease-in-out;
517
- -webkit-transition: background 0.1s ease-in-out;
518
- -o-transition: background 0.1s ease-in-out;
519
- transition: background 0.1s ease-in-out;
520
- }
521
- .ctf-multi-media-icon .ctf-tweet-text-media,
522
- .ctf-quoted-tweet-text .ctf-tweet-text-media{
523
- margin-left: 3px;
524
- }
525
-
526
- /* Load more */
527
- #ctf #ctf-more{
528
- display: block;
529
- border: none;
530
- background: #eee;
531
- background: rgba(0,0,0,0.05);
532
- width: 100%;
533
- min-height: 30px;
534
- padding: 5px 0;
535
- margin: 0;
536
- position: relative;
537
- text-align: center;
538
- box-sizing: border-box;
539
- outline: none;
540
- text-decoration: none;
541
-
542
- -moz-border-radius: 5px;
543
- -webkit-border-radius: 5px;
544
- border-radius: 5px;
545
-
546
- -moz-transition: background 0.1s ease-in-out;
547
- -webkit-transition: background 0.1s ease-in-out;
548
- -o-transition: background 0.1s ease-in-out;
549
- transition: background 0.1s ease-in-out;
550
- }
551
- #ctf #ctf-more:hover{
552
- background: #ddd;
553
- background: rgba(0,0,0,0.1);
554
- -moz-transition: background 0.1s ease-in-out;
555
- -webkit-transition: background 0.1s ease-in-out;
556
- -o-transition: background 0.1s ease-in-out;
557
- transition: background 0.1s ease-in-out;
558
- }
559
- #ctf #ctf-more.ctf-loading span{
560
- -moz-transition: opacity 0.5s ease-in-out;
561
- -webkit-transition: opacity 0.5s ease-in-out;
562
- -o-transition: opacity 0.5s ease-in-out;
563
- transition: opacity 0.5s ease-in-out;
564
- }
565
- #ctf #ctf-more.ctf-loading span{
566
- filter: alpha(opacity=0);
567
- opacity: 0;
568
- }
569
- #ctf .ctf-loader {
570
- width: 20px;
571
- height: 20px;
572
-
573
- position: absolute;
574
- top: 50%;
575
- left: 50%;
576
- margin: -10px 0 0 -10px;
577
- background-color: unset;
578
- background-color: rgba(0,0,0,0.5);
579
-
580
- border-radius: 100%;
581
- -webkit-animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
582
- animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
583
- }
584
- @-webkit-keyframes ctf-sk-scaleout {
585
- 0% { -webkit-transform: scale(0) }
586
- 100% {
587
- -webkit-transform: scale(1.0);
588
- opacity: 0;
589
- }
590
- }
591
- @keyframes ctf-sk-scaleout {
592
- 0% {
593
- -webkit-transform: scale(0);
594
- transform: scale(0);
595
- } 100% {
596
- -webkit-transform: scale(1.0);
597
- transform: scale(1.0);
598
- opacity: 0;
599
- }
600
- }
601
-
602
- /* Credit link */
603
- .ctf-credit-link{
604
- width: 100%;
605
- clear: both;
606
- padding: 5px 0;
607
- margin-top: 5px;
608
- text-align: center;
609
- font-size: 12px;
610
- }
611
- .ctf-credit-link .fa,
612
- .ctf-credit-link svg {
613
- margin-right: 4px;
614
- }
615
-
616
- /* SVGs */
617
- #ctf svg { box-sizing: unset; }
618
- #ctf svg:not(:root).svg-inline--fa,
619
- #ctf_lightbox svg:not(:root).svg-inline--fa{
620
- box-sizing: unset;
621
- overflow: visible;
622
- width: 1em;
623
- }
624
- #ctf .svg-inline--fa.fa-w-16,
625
- #ctf .svg-inline--fa.fa-w-16{
626
- width: 16px;
627
- }
628
- #ctf .svg-inline--fa.fa-w-20 {
629
- width: 20px;
630
- }
631
- #ctf .ctf-context .ctf-retweet-icon svg{
632
- width: 1.2em;
633
- height: 1.2em;
634
- }
635
- #ctf .svg-inline--fa,
636
- #ctf_lightbox .svg-inline--fa{
637
- display: inline-block;
638
- font-size: inherit;
639
- height: 1em;
640
- overflow: visible;
641
- vertical-align: -.125em;
642
- }
643
-
644
- #ctf #ctf .ctf-photo-hover span.fa-arrows-alt,
645
- #ctf .ctf-photo-hover svg.fa-arrows-alt {
646
- display: none;
647
- }
648
-
649
- /* Default styles */
650
- #ctf.ctf-styles .ctf-author-screenname,
651
- #ctf.ctf-styles .ctf-screename-sep,
652
- #ctf.ctf-styles .ctf-context,
653
- #ctf.ctf-styles .ctf-tweet-meta{
654
- font-size: 12px;
655
- }
656
-
657
- /* Errors */
658
- #ctf .ctf-error{
659
- width: 100%;
660
- text-align: center;
661
- }
662
- #ctf .ctf-error p,
663
- #ctf .ctf-error iframe,
664
- #ctf .ctf-error code{
665
- padding: 0;
666
- margin: 0 2px;
667
- }
668
- #ctf .ctf-error-user p{
669
- padding-bottom: 5px;
670
- }
671
- #ctf .ctf-error-user{
672
- width: 100%;
673
- }
674
- #ctf .ctf-error-admin{
675
- margin-top: 15px;
676
- display: inline-block;
677
- }
678
- #ctf .ctf-error code{
679
- display: block;
680
- padding: 4px;
681
- }
682
- .ctf_smash_error{
683
- border: 1px solid #ddd;
684
- background: #eee;
685
- color: #333;
686
- margin: 10px 0 0;
687
- padding: 10px 15px;
688
- font-size: 13px;
689
- text-align: center;
690
- clear: both;
691
-
692
- -moz-border-radius: 4px;
693
- -webkit-border-radius: 4px;
694
- border-radius: 4px;
695
- }
696
- .ctf_smash_error p{
697
- padding: 5px 0 !important;
698
- margin: 0 !important;
699
- line-height: 1.3 !important;
700
- }
701
- .ctf_smash_error span{
702
- font-size: 12px;
703
- }
704
-
705
- /* Out of tweets */
706
- #ctf .ctf-out-of-tweets{
707
- width: 100%;
708
- }
709
- #ctf .ctf-out-of-tweets{
710
- display: block;
711
- border: 1px solid rgba(0,0,0,0.1);
712
- background: none;
713
- background: none;
714
- width: 100%;
715
- min-height: 30px;
716
- padding: 5px 0;
717
- margin: 0;
718
- position: relative;
719
- text-align: center;
720
- box-sizing: border-box;
721
-
722
- -moz-border-radius: 5px;
723
- -webkit-border-radius: 5px;
724
- border-radius: 5px;
725
- }
726
- #ctf .ctf-out-of-tweets p,
727
- #ctf .ctf-out-of-tweets iframe{
728
- margin: 0 2px;
729
- padding: 0;
730
- }
731
- #ctf .ctf-out-of-tweets p{
732
- padding: 3px;
733
- display: none;
734
- }
735
-
736
- /* Mobile / Narrow */
737
- #ctf.ctf-narrow .ctf-author-avatar,
738
- #ctf.ctf-narrow .ctf-author-avatar img{
739
- width: 38px;
740
- height: 38px;
741
- }
742
- #ctf.ctf-narrow .ctf-tweet-content,
743
- #ctf.ctf-narrow .ctf-tweet-actions,
744
- #ctf.ctf-narrow .ctf-quoted-tweet{
745
- margin-left: 48px;
746
- }
747
- #ctf.ctf-narrow .ctf-context {
748
- margin-left: 49px;
749
- }
750
- #ctf.ctf-narrow .ctf-author-box{
751
- margin-left: 46px;
752
- }
753
- #ctf.ctf-narrow .ctf-author-avatar {
754
- margin-left: -46px;
755
- }
756
- #ctf.ctf-narrow .ctf-tweet-content{
757
- padding: 3px 0 5px 0;
758
- }
759
- #ctf.ctf-narrow .ctf-tweet-actions {
760
- float: left;
761
- clear: both;
762
- }
763
-
764
- /* Super narrow */
765
- #ctf.ctf-super-narrow .ctf-context,
766
- #ctf.ctf-super-narrow .ctf-tweet-content,
767
- #ctf.ctf-super-narrow .ctf-tweet-actions,
768
- #ctf.ctf-super-narrow .ctf-quoted-tweet {
769
- margin-left: 0;
770
- }
771
- #ctf.ctf-super-narrow .ctf-author-box{
772
- min-height: 32px;
773
- margin-bottom: 2px;
774
- }
775
- #ctf.ctf-super-narrow .ctf-author-avatar,
776
- #ctf.ctf-super-narrow .ctf-author-avatar img{
777
- width: 32px;
778
- height: 32px;
779
- }
780
- #ctf.ctf-super-narrow .ctf-author-box{
781
- margin-left: 38px;
782
- float: left;
783
- clear: both;
784
- }
785
- #ctf.ctf-super-narrow .ctf-author-avatar {
786
- margin-left: -38px;
787
- margin-top: 0;
788
- }
789
- #ctf.ctf-super-narrow .ctf-author-screenname,
790
- #ctf.ctf-super-narrow .ctf-screename-sep {
791
- display: none;
792
- }
793
- #ctf.ctf-super-narrow .ctf-context {
794
- padding-bottom: 5px;
795
- }
796
- #ctf.ctf-super-narrow .ctf-author-name{
797
- display: inline-block;
798
- }
799
- #ctf.ctf-super-narrow .ctf-tweet-content{
800
- float: left;
801
- clear: both;
802
- }
803
- #ctf.ctf-super-narrow .ctf-credit-link{
804
- font-size: 10px;
805
- }
806
- #ctf.ctf-super-narrow .ctf-header-bio{
807
- margin: 0 !important;
808
- padding-top: 5px;
809
- }
810
- #ctf.ctf-super-narrow .ctf-header-user{
811
- min-height: 48px;
812
- }
813
- #ctf.ctf-super-narrow .ctf-header-text{
814
- padding-top: 10px;
815
- }
816
- #ctf.ctf-super-narrow .ctf-header-follow{
817
- margin-top: 5px;
818
- }
819
- #ctf.ctf-super-narrow .ctf-more span{
820
- display: block;
821
- margin-top: 3px;
822
- }
823
-
824
- /* On mobile make the min-width 100% */
825
- @media all and (max-width: 640px){
826
- #ctf.ctf-width-resp{
827
- width: 100% !important;
828
- }
829
  }
1
+
2
+ /* General */
3
+ #ctf{
4
+ overflow-y: auto;
5
+ -ms-overflow-y: auto;
6
+ }
7
+ #ctf .ctf-item{
8
+ padding: 15px 5px;
9
+ border-top: 1px solid #ddd;
10
+ border-top: 1px solid rgba(0,0,0,0.1);
11
+ overflow: hidden;
12
+ }
13
+ #ctf .ctf-item:first-child{
14
+ border-top: none;
15
+ }
16
+ #ctf .ctf_remaining {
17
+ display: none;
18
+ }
19
+ #ctf .ctf_more{
20
+ padding: 1px;
21
+ border: 1px solid transparent;
22
+ border-radius: 3px;
23
+ display: inline-block;
24
+ line-height: 1;
25
+ }
26
+ #ctf .ctf_more:hover {
27
+ cursor: pointer;
28
+ background: rgba(0,0,0,0.05);
29
+ border: 1px solid rgba(0,0,0,0.1);
30
+ text-decoration: none;
31
+ }
32
+
33
+ /* Header */
34
+ #ctf .ctf-header{
35
+ float: left;
36
+ clear: both;
37
+ margin: 0;
38
+ padding: 5px 5px 15px 5px;
39
+ line-height: 1.2;
40
+ width: 100%;
41
+ border-bottom: 1px solid #ddd;
42
+ border-bottom: 1px solid rgba(0,0,0,0.1);
43
+
44
+ -webkit-box-sizing: border-box;
45
+ -moz-box-sizing: border-box;
46
+ box-sizing: border-box;
47
+ }
48
+ #ctf .ctf-header a{
49
+ float: left;
50
+ display: block;
51
+ min-width: 100%\9;
52
+ }
53
+ /* Header avatar */
54
+ #ctf .ctf-header-img{
55
+ float: left;
56
+ position: relative;
57
+ width: 48px;
58
+ margin: 0 0 0 -100% !important;
59
+ overflow: hidden;
60
+
61
+ -moz-border-radius: 40px;
62
+ -webkit-border-radius: 40px;
63
+ border-radius: 40px;
64
+ }
65
+ #ctf .ctf-header-img img{
66
+ display: block;
67
+ float: left;
68
+ margin: 0 !important;
69
+ padding: 0 !important;
70
+ border: none !important;
71
+
72
+ -moz-border-radius: 40px;
73
+ -webkit-border-radius: 40px;
74
+ border-radius: 40px;
75
+ }
76
+ #ctf .ctf-author-avatar {
77
+ clear: both;
78
+ margin: 3px 6px 0 -53px;
79
+ }
80
+ #ctf .ctf-author-name {
81
+ margin-left: 6px;
82
+ }
83
+ #ctf .ctf-author-box {
84
+ margin-left: 53px;
85
+ }
86
+ /* Header avatar hover */
87
+ #ctf .ctf-header-img-hover,
88
+ #ctf .ctf-header-generic-icon{
89
+ display: none;
90
+ position: absolute;
91
+ width: 100%;
92
+ top: 0;
93
+ bottom: 0;
94
+ left: 0;
95
+ text-align: center;
96
+ color: #fff;
97
+ background: #1b95e0;
98
+ background: rgba(27,149,224,0.85);
99
+
100
+ -moz-border-radius: 40px;
101
+ -webkit-border-radius: 40px;
102
+ border-radius: 40px;
103
+ z-index: 999;
104
+ }
105
+ #ctf .ctf-header-img-hover .fa,
106
+ #ctf .ctf-header-generic-icon .fa,
107
+ #ctf .ctf-header-img-hover svg,
108
+ #ctf .ctf-header-generic-icon svg{
109
+ position: absolute;
110
+ top: 50%;
111
+ left: 50%;
112
+ margin-top: -10px;
113
+ margin-left: -8px;
114
+ font-size: 20px;
115
+ z-index: 1000;
116
+ }
117
+ #ctf .ctf-header-img-hover svg,
118
+ #ctf .ctf-header-generic-icon svg {
119
+ margin-top: -10px;
120
+ margin-left: -10px;
121
+ }
122
+ /* Header text */
123
+ #ctf .ctf-header-text{
124
+ float: left;
125
+ width: 100%;
126
+ padding-top: 5px;
127
+ }
128
+ #ctf .ctf-no-bio .ctf-header-text{
129
+ padding-top: 13px;
130
+ }
131
+ #ctf .ctf-header a{
132
+ text-decoration: none;
133
+ }
134
+ #ctf .ctf-header-text .ctf-header-user,
135
+ #ctf .ctf-header-text p{
136
+ float: left;
137
+ clear: both;
138
+ width: auto;
139
+ margin: 0 0 0 60px !important;
140
+ padding: 0 !important;
141
+ }
142
+ #ctf .ctf-header-name{
143
+ font-weight: bold;
144
+ margin-right: 6px;
145
+ }
146
+ #ctf .ctf-header-text .ctf-header-bio{
147
+ padding: 0 !important;
148
+ word-break: break-word;
149
+ }
150
+ #ctf .ctf-header-text .ctf-verified{
151
+ margin-right: 5px;
152
+ }
153
+ /* Follow */
154
+ #ctf .ctf-header .ctf-header-follow{
155
+ display: inline-block;
156
+ padding: 3px 6px;
157
+ margin: 0 0 0 2px;
158
+ position: relative;
159
+ top: -2px;
160
+ background: #1b95e0;
161
+ color: #fff;
162
+ font-size: 11px;
163
+
164
+ -moz-border-radius: 3px;
165
+ -webkit-border-radius: 3px;
166
+ border-radius: 3px;
167
+
168
+ -moz-transition: background 0.6s ease-in-out;
169
+ -webkit-transition: background 0.6s ease-in-out;
170
+ -o-transition: background 0.6s ease-in-out;
171
+ transition: background 0.6s ease-in-out;
172
+ }
173
+ #ctf .ctf-header .ctf-header-follow .fa,
174
+ #ctf .ctf-header .ctf-header-follow svg{
175
+ margin-right: 3px;
176
+ font-size: 12px;
177
+ }
178
+ #ctf .ctf-header a:hover .ctf-header-follow{
179
+ background: #0c7abf;
180
+
181
+ -moz-transition: background 0.2s ease-in-out;
182
+ -webkit-transition: background 0.2s ease-in-out;
183
+ -o-transition: background 0.2s ease-in-out;
184
+ transition: background 0.2s ease-in-out;
185
+ }
186
+ /* Generic header version */
187
+ #ctf .ctf-header-type-generic .ctf-header-img{
188
+ color: #fff;
189
+ width: 48px;
190
+ height: 48px;
191
+ position: relative;
192
+ }
193
+ #ctf .ctf-header-type-generic .ctf-header-link:hover .ctf-header-generic-icon{
194
+ background: #0c7abf;
195
+ }
196
+ #ctf .ctf-header-type-generic .ctf-header-no-bio{
197
+ padding-top: 9px !important;
198
+ font-size: 16px;
199
+ font-weight: bold;
200
+ }
201
+ #ctf .ctf-header-type-generic .ctf-header-generic-icon{
202
+ display: block;
203
+ color: #fff;
204
+ background: #1b95e0;
205
+
206
+ -moz-transition: background 0.2s ease-in-out;
207
+ -webkit-transition: background 0.2s ease-in-out;
208
+ -o-transition: background 0.2s ease-in-out;
209
+ transition: background 0.2s ease-in-out;
210
+ }
211
+ #ctf .ctf-header-type-generic:hover .ctf-header-generic-icon{
212
+ display: block;
213
+ opacity: 1;
214
+ }
215
+ #ctf .ctf-header .ctf-header-user{
216
+ font-size: 16px;
217
+ line-height: 1.3;
218
+ -ms-word-wrap: break-word;
219
+ word-break: break-word;
220
+ }
221
+ #ctf .ctf-header p,
222
+ #ctf .ctf-header .ctf-header-screenname{
223
+ font-size: 13px;
224
+ line-height: 1.3;
225
+ }
226
+
227
+ /* screenreader */
228
+
229
+ .ctf-screenreader{
230
+ text-indent: -9999px !important;
231
+ display: inline-block !important;
232
+ width: 0 !important;
233
+ height: 0 !important;
234
+ line-height: 0 !important;
235
+ text-align: left !important;
236
+ }
237
+
238
+ /* No bio */
239
+ #ctf .ctf-header-text.sbi-no-info .ctf-header-user{
240
+ padding-top: 9px !important;
241
+ }
242
+ #ctf .ctf-header-text.sbi-no-bio .ctf-header-counts{
243
+ clear: both;
244
+ }
245
+
246
+ /* Context */
247
+ #ctf .ctf-context {
248
+ /*float: left;*/
249
+ width: 100%;
250
+ padding-left: 42px;
251
+ margin-bottom: 2px;
252
+ }
253
+ #ctf .ctf-with-logo .ctf-context {
254
+ padding-right: 30px;
255
+ }
256
+ #ctf .ctf-retweet-icon{
257
+ display: inline-block;
258
+ font-size: inherit;
259
+ line-height: 1;
260
+ margin: 0 5px 0 0;
261
+ -moz-border-radius: 3px;
262
+ -webkit-border-radius: 3px;
263
+ border-radius: 3px;
264
+ }
265
+ #ctf.ctf-super-narrow .ctf-retweet-icon{
266
+ margin: 0 5px 0 0;
267
+ }
268
+ #ctf .ctf-reply-icon{
269
+ display: inline-block;
270
+ font-size: inherit;
271
+ padding: 1px 3px 3px 3px;
272
+ line-height: 1;
273
+ margin: 0 3px 0 0;
274
+ }
275
+
276
+ /* Author */
277
+ #ctf .ctf-author-box{
278
+ margin-left: 56px;
279
+ }
280
+ #ctf .ctf-author-avatar{
281
+ width: 48px;
282
+ height: 48px;
283
+ margin: 3px 6px 0 -56px;
284
+ float: left;
285
+ overflow: hidden;
286
+ border: none;
287
+
288
+ -moz-border-radius: 5px;
289
+ -webkit-border-radius: 5px;
290
+ border-radius: 5px;
291
+ }
292
+ #ctf .ctf-author-avatar img{
293
+ margin: 0 !important;
294
+ padding: 0;
295
+ }
296
+ #ctf .ctf-author-name,
297
+ #ctf .ctf-author-screenname,
298
+ #ctf .ctf-verified,
299
+ #ctf .ctf-tweet-meta{
300
+ margin-left: 4px;
301
+ margin-right: 4px;
302
+ display: inline-block;
303
+ }
304
+ #ctf .ctf-screename-sep{
305
+ margin: 0 2px;
306
+ display: inline-block;
307
+ }
308
+ #ctf .ctf-author-name{
309
+ font-weight: bold !important;
310
+ margin-top: 4px;
311
+ }
312
+ #ctf .ctf-author-box-link{
313
+ color: black;
314
+ text-decoration: none;
315
+ display: inline;
316
+ line-height: 1.5 !important;
317
+ }
318
+ #ctf .ctf-verified,
319
+ #ctf .ctf-quoted-verified {
320
+ color: #77c7f7;
321
+ margin-left: 0;
322
+ }
323
+ #ctf .ctf-quoted-verified {
324
+ margin-right: 4px;
325
+ }
326
+ #ctf .ctf-tweet-meta{
327
+ display: inline-block;
328
+ }
329
+ #ctf .ctf-replied-to-text a {
330
+ font-size: inherit;
331
+ color: inherit;
332
+ font-weight: inherit;
333
+ }
334
+ #ctf p.ctf-tweet-text,
335
+ #ctf p.ctf-media-link {
336
+ padding: 0 !important;
337
+ margin: 0 !important;
338
+ line-height: 1.4;
339
+ }
340
+ #ctf .ctf-tweet-content,
341
+ #ctf .ctf-tweet-actions{
342
+ margin-left: 58px;
343
+ margin-top: 3px;
344
+ max-width: 100%;
345
+ }
346
+
347
+ #ctf .ctf-corner-logo {
348
+ display: inline-block;
349
+ float: right;
350
+ color: #1b95e0;
351
+ font-size: 24px;
352
+ }
353
+ #ctf.ctf-narrow .ctf-corner-logo {
354
+ font-size: 20px;
355
+ }
356
+ /* No avatar */
357
+ #ctf .ctf-hide-avatar .ctf-context,
358
+ #ctf .ctf-hide-avatar .ctf-tweet-content,
359
+ #ctf .ctf-hide-avatar .ctf-tweet-actions,
360
+ #ctf .ctf-hide-avatar .ctf-quoted-tweet,
361
+ #ctf .ctf-hide-avatar .ctf-author-box,
362
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-context,
363
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-content,
364
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-actions,
365
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-quoted-tweet,
366
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-author-box,
367
+ #ctf .ctf-hide-avatar .ctf-author-name,
368
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-context,
369
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-content,
370
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-actions,
371
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-quoted-tweet,
372
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-author-box{
373
+ margin-left: 0;
374
+ }
375
+ #ctf .ctf-hide-avatar .ctf-context {
376
+ padding-left: 0;
377
+ }
378
+ #ctf .ctf-header-img.ctf-no-consent .ctf-header-img-hover,
379
+ #ctf .ctf-header-img.ctf-no-consent {
380
+ display: block;
381
+ opacity: 1;
382
+ height: 48px;
383
+ }
384
+
385
+
386
+ /*Quoted tweets*/
387
+ #ctf .ctf-quoted-tweet{
388
+ display: block;
389
+ /*float: left;*/
390
+ clear: both;
391
+ margin: 7px 0 5px 58px;
392
+ padding: 8px 12px;
393
+ border: 1px solid #ddd;
394
+ border: 1px solid rgba(0,0,0,0.1);
395
+ text-decoration: none;
396
+
397
+ -moz-border-radius: 5px;
398
+ -webkit-border-radius: 5px;
399
+ border-radius: 5px;
400
+
401
+ -moz-transition: background 0.1s ease-in-out;
402
+ -webkit-transition: background 0.1s ease-in-out;
403
+ -o-transition: background 0.1s ease-in-out;
404
+ transition: background 0.1s ease-in-out;
405
+ }
406
+ #ctf .ctf-quoted-tweet:hover,
407
+ #ctf .ctf-quoted-tweet:focus{
408
+ background: #eee;
409
+ background: rgba(0,0,0,0.05);
410
+ border: 1px solid #ddd;
411
+ border: 1px solid rgba(0,0,0,0.05);
412
+
413
+ -moz-transition: background 0.1s ease-in-out;
414
+ -webkit-transition: background 0.1s ease-in-out;
415
+ -o-transition: background 0.1s ease-in-out;
416
+ transition: background 0.1s ease-in-out;
417
+ }
418
+ #ctf .ctf-quoted-author-name{
419
+ font-weight: bold;
420
+ margin-right: 4px;
421
+ }
422
+ #ctf .ctf-quoted-tweet p{
423
+ margin: 2px 0;
424
+ }
425
+
426
+ /* Actions */
427
+ #ctf .ctf-tweet-actions a{
428
+ padding: 2px 10px 2px 5px;
429
+ float: left;
430
+ }
431
+ #ctf .ctf-tweet-actions a.ctf-reply,
432
+ #ctf .ctf-tweet-actions a.ctf-retweet,
433
+ #ctf .ctf-tweet-actions a.ctf-like{
434
+ color: #bbb;
435
+ color: rgba(0,0,0,0.35);
436
+ text-decoration: none;
437
+ border: none;
438
+ -moz-transition: color 0.1s ease-in-out;
439
+ -webkit-transition: color 0.1s ease-in-out;
440
+ -o-transition: color 0.1s ease-in-out;
441
+ transition: color 0.1s ease-in-out;
442
+ }
443
+ #ctf .ctf-tweet-actions a svg,
444
+ #ctf .ctf-tweet-actions a span.fa{
445
+ width: 1.1em;
446
+ height: 1.1em;
447
+ font-size: 1.1em;
448
+ }
449
+ #ctf .ctf-tweet-actions a span.fa.fa-retweet {
450
+ width: 1.2em;
451
+ height: 1.2em;
452
+ font-size: 1.2em;
453
+ }
454
+ #ctf .ctf-tweet-actions .ctf-reply{
455
+ margin-left: -5px;
456
+ }
457
+ #ctf .ctf-tweet-actions a:hover,
458
+ #ctf .ctf-tweet-actions a:focus{
459
+ -moz-transition: color 0.1s ease-in-out;
460
+ -webkit-transition: color 0.1s ease-in-out;
461
+ -o-transition: color 0.1s ease-in-out;
462
+ transition: color 0.1s ease-in-out;
463
+ }
464
+ #ctf .ctf-tweet-actions .ctf-reply:hover,
465
+ #ctf .ctf-tweet-actions .ctf-reply:focus{
466
+ color: #E34F0E !important;
467
+ }
468
+ #ctf .ctf-tweet-actions .ctf-retweet:hover,
469
+ #ctf .ctf-tweet-actions .ctf-retweet:focus{
470
+ color: #19cf86 !important;
471
+ }
472
+ #ctf .ctf-tweet-actions .ctf-like:hover,
473
+ #ctf .ctf-tweet-actions .ctf-like:focus{
474
+ color: #e81c4f !important;
475
+ }
476
+ #ctf .ctf-action-count {
477
+ float: right;
478
+ padding-left: 3px;
479
+ font-size: .9em;
480
+ margin-top: .25em;
481
+ line-height: 1.2em;
482
+ }
483
+ #ctf .ctf-twitterlink {
484
+ margin-top: .3em;
485
+ line-height: 1.2em;
486
+ font-size: .8em;
487
+ }
488
+ #ctf .ctf-tweet-text-media-wrap,
489
+ #ctf .ctf-quoted-tweet-text-media-wrap{
490
+ margin-left: 5px;
491
+ padding: 4px;
492
+ display: inline-block;
493
+ border: 1px solid #ddd;
494
+ border: 1px solid rgba(0,0,0,0.15);
495
+
496
+ color: inherit;
497
+ text-decoration: none;
498
+ line-height: 1;
499
+
500
+ -moz-border-radius: 2px;
501
+ -webkit-border-radius: 2px;
502
+ border-radius: 2px;
503
+
504
+ -moz-transition: background 0.1s ease-in-out;
505
+ -webkit-transition: background 0.1s ease-in-out;
506
+ -o-transition: background 0.1s ease-in-out;
507
+ transition: background 0.1s ease-in-out;
508
+ }
509
+ #ctf .ctf-tweet-text-media-wrap:hover,
510
+ #ctf .ctf-tweet-text-media-wrap:focus{
511
+ background: #eee;
512
+ background: rgba(0,0,0,0.05);
513
+ border: 1px solid #ddd;
514
+ border: 1px solid rgba(0,0,0,0.1);
515
+
516
+ -moz-transition: background 0.1s ease-in-out;
517
+ -webkit-transition: background 0.1s ease-in-out;
518
+ -o-transition: background 0.1s ease-in-out;
519
+ transition: background 0.1s ease-in-out;
520
+ }
521
+ .ctf-multi-media-icon .ctf-tweet-text-media,
522
+ .ctf-quoted-tweet-text .ctf-tweet-text-media{
523
+ margin-left: 3px;
524
+ }
525
+
526
+ /* Load more */
527
+ #ctf #ctf-more{
528
+ display: block;
529
+ border: none;
530
+ background: #eee;
531
+ background: rgba(0,0,0,0.05);
532
+ width: 100%;
533
+ min-height: 30px;
534
+ padding: 5px 0;
535
+ margin: 0;
536
+ position: relative;
537
+ text-align: center;
538
+ box-sizing: border-box;
539
+ outline: none;
540
+ text-decoration: none;
541
+
542
+ -moz-border-radius: 5px;
543
+ -webkit-border-radius: 5px;
544
+ border-radius: 5px;
545
+
546
+ -moz-transition: background 0.1s ease-in-out;
547
+ -webkit-transition: background 0.1s ease-in-out;
548
+ -o-transition: background 0.1s ease-in-out;
549
+ transition: background 0.1s ease-in-out;
550
+ }
551
+ #ctf #ctf-more:hover{
552
+ background: #ddd;
553
+ background: rgba(0,0,0,0.1);
554
+ -moz-transition: background 0.1s ease-in-out;
555
+ -webkit-transition: background 0.1s ease-in-out;
556
+ -o-transition: background 0.1s ease-in-out;
557
+ transition: background 0.1s ease-in-out;
558
+ }
559
+ #ctf #ctf-more.ctf-loading span{
560
+ -moz-transition: opacity 0.5s ease-in-out;
561
+ -webkit-transition: opacity 0.5s ease-in-out;
562
+ -o-transition: opacity 0.5s ease-in-out;
563
+ transition: opacity 0.5s ease-in-out;
564
+ }
565
+ #ctf #ctf-more.ctf-loading span{
566
+ filter: alpha(opacity=0);
567
+ opacity: 0;
568
+ }
569
+ #ctf .ctf-loader {
570
+ width: 20px;
571
+ height: 20px;
572
+
573
+ position: absolute;
574
+ top: 50%;
575
+ left: 50%;
576
+ margin: -10px 0 0 -10px;
577
+ background-color: unset;
578
+ background-color: rgba(0,0,0,0.5);
579
+
580
+ border-radius: 100%;
581
+ -webkit-animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
582
+ animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
583
+ }
584
+ @-webkit-keyframes ctf-sk-scaleout {
585
+ 0% { -webkit-transform: scale(0) }
586
+ 100% {
587
+ -webkit-transform: scale(1.0);
588
+ opacity: 0;
589
+ }
590
+ }
591
+ @keyframes ctf-sk-scaleout {
592
+ 0% {
593
+ -webkit-transform: scale(0);
594
+ transform: scale(0);
595
+ } 100% {
596
+ -webkit-transform: scale(1.0);
597
+ transform: scale(1.0);
598
+ opacity: 0;
599
+ }
600
+ }
601
+
602
+ /* Credit link */
603
+ .ctf-credit-link{
604
+ width: 100%;
605
+ clear: both;
606
+ padding: 5px 0;
607
+ margin-top: 5px;
608
+ text-align: center;
609
+ font-size: 12px;
610
+ }
611
+ .ctf-credit-link .fa,
612
+ .ctf-credit-link svg {
613
+ margin-right: 4px;
614
+ }
615
+
616
+ /* SVGs */
617
+ #ctf svg { box-sizing: unset; }
618
+ #ctf svg:not(:root).svg-inline--fa,
619
+ #ctf_lightbox svg:not(:root).svg-inline--fa{
620
+ box-sizing: unset;
621
+ overflow: visible;
622
+ width: 1em;
623
+ }
624
+ #ctf .svg-inline--fa.fa-w-16,
625
+ #ctf .svg-inline--fa.fa-w-16{
626
+ width: 16px;
627
+ }
628
+ #ctf .svg-inline--fa.fa-w-20 {
629
+ width: 20px;
630
+ }
631
+ #ctf .ctf-context .ctf-retweet-icon svg{
632
+ width: 1.2em;
633
+ height: 1.2em;
634
+ }
635
+ #ctf .svg-inline--fa,
636
+ #ctf_lightbox .svg-inline--fa{
637
+ display: inline-block;
638
+ font-size: inherit;
639
+ height: 1em;
640
+ overflow: visible;
641
+ vertical-align: -.125em;
642
+ }
643
+
644
+ #ctf #ctf .ctf-photo-hover span.fa-arrows-alt,
645
+ #ctf .ctf-photo-hover svg.fa-arrows-alt {
646
+ display: none;
647
+ }
648
+
649
+ /* Default styles */
650
+ #ctf.ctf-styles .ctf-author-screenname,
651
+ #ctf.ctf-styles .ctf-screename-sep,
652
+ #ctf.ctf-styles .ctf-context,
653
+ #ctf.ctf-styles .ctf-tweet-meta{
654
+ font-size: 12px;
655
+ }
656
+
657
+ /* Errors */
658
+ #ctf .ctf-error{
659
+ width: 100%;
660
+ text-align: center;
661
+ }
662
+ #ctf .ctf-error p,
663
+ #ctf .ctf-error iframe,
664
+ #ctf .ctf-error code{
665
+ padding: 0;
666
+ margin: 0 2px;
667
+ }
668
+ #ctf .ctf-error-user p{
669
+ padding-bottom: 5px;
670
+ }
671
+ #ctf .ctf-error-user{
672
+ width: 100%;
673
+ }
674
+ #ctf .ctf-error-admin{
675
+ margin-top: 15px;
676
+ display: inline-block;
677
+ }
678
+ #ctf .ctf-error code{
679
+ display: block;
680
+ padding: 4px;
681
+ }
682
+ .ctf_smash_error{
683
+ border: 1px solid #ddd;
684
+ background: #eee;
685
+ color: #333;
686
+ margin: 10px 0 0;
687
+ padding: 10px 15px;
688
+ font-size: 13px;
689
+ text-align: center;
690
+ clear: both;
691
+
692
+ -moz-border-radius: 4px;
693
+ -webkit-border-radius: 4px;
694
+ border-radius: 4px;
695
+ }
696
+ .ctf_smash_error p{
697
+ padding: 5px 0 !important;
698
+ margin: 0 !important;
699
+ line-height: 1.3 !important;
700
+ }
701
+ .ctf_smash_error span{
702
+ font-size: 12px;
703
+ }
704
+
705
+ /* Out of tweets */
706
+ #ctf .ctf-out-of-tweets{
707
+ width: 100%;
708
+ }
709
+ #ctf .ctf-out-of-tweets{
710
+ display: block;
711
+ border: 1px solid rgba(0,0,0,0.1);
712
+ background: none;
713
+ background: none;
714
+ width: 100%;
715
+ min-height: 30px;
716
+ padding: 5px 0;
717
+ margin: 0;
718
+ position: relative;
719
+ text-align: center;
720
+ box-sizing: border-box;
721
+
722
+ -moz-border-radius: 5px;
723
+ -webkit-border-radius: 5px;
724
+ border-radius: 5px;
725
+ }
726
+ #ctf .ctf-out-of-tweets p,
727
+ #ctf .ctf-out-of-tweets iframe{
728
+ margin: 0 2px;
729
+ padding: 0;
730
+ }
731
+ #ctf .ctf-out-of-tweets p{
732
+ padding: 3px;
733
+ display: none;
734
+ }
735
+
736
+ /* Mobile / Narrow */
737
+ #ctf.ctf-narrow .ctf-author-avatar,
738
+ #ctf.ctf-narrow .ctf-author-avatar img{
739
+ width: 38px;
740
+ height: 38px;
741
+ }
742
+ #ctf.ctf-narrow .ctf-tweet-content,
743
+ #ctf.ctf-narrow .ctf-tweet-actions,
744
+ #ctf.ctf-narrow .ctf-quoted-tweet{
745
+ margin-left: 48px;
746
+ }
747
+ #ctf.ctf-narrow .ctf-context {
748
+ margin-left: 49px;
749
+ }
750
+ #ctf.ctf-narrow .ctf-author-box{
751
+ margin-left: 46px;
752
+ }
753
+ #ctf.ctf-narrow .ctf-author-avatar {
754
+ margin-left: -46px;
755
+ }
756
+ #ctf.ctf-narrow .ctf-tweet-content{
757
+ padding: 3px 0 5px 0;
758
+ }
759
+ #ctf.ctf-narrow .ctf-tweet-actions {
760
+ float: left;
761
+ clear: both;
762
+ }
763
+
764
+ /* Super narrow */
765
+ #ctf.ctf-super-narrow .ctf-context,
766
+ #ctf.ctf-super-narrow .ctf-tweet-content,
767
+ #ctf.ctf-super-narrow .ctf-tweet-actions,
768
+ #ctf.ctf-super-narrow .ctf-quoted-tweet {
769
+ margin-left: 0;
770
+ }
771
+ #ctf.ctf-super-narrow .ctf-author-box{
772
+ min-height: 32px;
773
+ margin-bottom: 2px;
774
+ }
775
+ #ctf.ctf-super-narrow .ctf-author-avatar,
776
+ #ctf.ctf-super-narrow .ctf-author-avatar img{
777
+ width: 32px;
778
+ height: 32px;
779
+ }
780
+ #ctf.ctf-super-narrow .ctf-author-box{
781
+ margin-left: 38px;
782
+ float: left;
783
+ clear: both;
784
+ }
785
+ #ctf.ctf-super-narrow .ctf-author-avatar {
786
+ margin-left: -38px;
787
+ margin-top: 0;
788
+ }
789
+ #ctf.ctf-super-narrow .ctf-author-screenname,
790
+ #ctf.ctf-super-narrow .ctf-screename-sep {
791
+ display: none;
792
+ }
793
+ #ctf.ctf-super-narrow .ctf-context {
794
+ padding-bottom: 5px;
795
+ }
796
+ #ctf.ctf-super-narrow .ctf-author-name{
797
+ display: inline-block;
798
+ }
799
+ #ctf.ctf-super-narrow .ctf-tweet-content{
800
+ float: left;
801
+ clear: both;
802
+ }
803
+ #ctf.ctf-super-narrow .ctf-credit-link{
804
+ font-size: 10px;
805
+ }
806
+ #ctf.ctf-super-narrow .ctf-header-bio{
807
+ margin: 0 !important;
808
+ padding-top: 5px;
809
+ }
810
+ #ctf.ctf-super-narrow .ctf-header-user{
811
+ min-height: 48px;
812
+ }
813
+ #ctf.ctf-super-narrow .ctf-header-text{
814
+ padding-top: 10px;
815
+ }
816
+ #ctf.ctf-super-narrow .ctf-header-follow{
817
+ margin-top: 5px;
818
+ }
819
+ #ctf.ctf-super-narrow .ctf-more span{
820
+ display: block;
821
+ margin-top: 3px;
822
+ }
823
+
824
+ /* On mobile make the min-width 100% */
825
+ @media all and (max-width: 640px){
826
+ #ctf.ctf-width-resp{
827
+ width: 100% !important;
828
+ }
829
  }
custom-twitter-feed.php CHANGED
@@ -1,732 +1,732 @@
1
- <?php
2
- /*
3
- Plugin Name: Custom Twitter Feeds
4
- Plugin URI: http://smashballoon.com/custom-twitter-feeds
5
- Description: Customizable Twitter feeds for your website
6
- Version: 1.8.1
7
- Author: Smash Balloon
8
- Author URI: http://smashballoon.com/
9
- Text Domain: custom-twitter-feeds
10
- */
11
- /*
12
- Copyright 2021 Smash Balloon LLC (email : hey@smashballoon.com)
13
- This program is free software; you can redistribute it and/or modify
14
- it under the terms of the GNU General Public License as published by
15
- the Free Software Foundation; either version 2 of the License, or
16
- (at your option) any later version.
17
- This program is distributed in the hope that it will be useful,
18
- but WITHOUT ANY WARRANTY; without even the implied warranty of
19
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
- GNU General Public License for more details.
21
- You should have received a copy of the GNU General Public License
22
- along with this program; if not, write to the Free Software
23
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
- */
25
-
26
- define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
27
- define( 'CTF_VERSION', '1.8.1' );
28
- define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
- define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.min.js?ver=' . CTF_VERSION , __FILE__ ) );
30
- define( 'OAUTH_PROCESSOR_URL', 'https://api.smashballoon.com/twitter-login.php?return_uri=' );
31
- // Plugin Folder Path.
32
- if ( ! defined( 'CTF_PLUGIN_DIR' ) ) {
33
- define( 'CTF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
34
- }
35
- // Plugin Folder URL.
36
- if ( ! defined( 'CTF_PLUGIN_URL' ) ) {
37
- define( 'CTF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
38
- }
39
- // Db version.
40
- if ( ! defined( 'CTF_DBVERSION' ) ) {
41
- define( 'CTF_DBVERSION', '1.0.1' );
42
- }
43
- if ( ! defined( 'CTF_FEED_LOCATOR' ) ) {
44
- define( 'CTF_FEED_LOCATOR', 'ctf_feed_locator' );
45
- }
46
-
47
- if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
48
-
49
- require_once( CTF_URL . '/inc/widget.php' );
50
-
51
- require_once( CTF_URL . '/inc/admin-hooks.php' );
52
-
53
- function ctf_plugin_init() {
54
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/blocks/class-ctf-blocks.php';
55
-
56
- $ctf_blocks = new CTF_Blocks();
57
-
58
- if ( $ctf_blocks->allow_load() ) {
59
- $ctf_blocks->load();
60
- }
61
-
62
- include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-feed-locator.php';
63
- include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-tracking.php';
64
- include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-gdpr-integrations.php';
65
-
66
- if ( is_admin() ) {
67
- if ( version_compare( PHP_VERSION, '5.3.0' ) >= 0
68
- && version_compare( get_bloginfo('version'), '4.6' , '>' ) ) {
69
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-ctf-notifications.php';
70
- $ctf_notifications = new CTF_Notifications();
71
- $ctf_notifications->init();
72
-
73
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-ctf-new-user.php';
74
- $ctf_new_user = new CTF_New_User();
75
- $ctf_new_user->init();
76
-
77
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/addon-functions.php';
78
- }
79
- }
80
- }
81
-
82
- add_action( 'plugins_loaded', 'ctf_plugin_init' );
83
-
84
- function ctf_update_settings() {
85
- $existing_deprecated_options = get_option( 'ctf_configure' );
86
- $existing_options = get_option( 'ctf_options' );
87
-
88
- update_option( 'ctf_version', CTF_VERSION );
89
-
90
- if ( ! empty( $existing_deprecated_options ) && empty( $existing_options ) ) {
91
- $merged_options = $existing_deprecated_options;
92
- $merged_options = array_merge( $merged_options, get_option( 'ctf_customize', array() ) );
93
- $merged_options = array_merge( $merged_options, get_option( 'ctf_style', array() ) );
94
-
95
- update_option( 'ctf_options', $merged_options );
96
- }
97
- }
98
-
99
- function ctf_check_for_db_updates() {
100
-
101
- $db_ver = get_option( 'ctf_db_version', 0 );
102
-
103
- if ( (float) $db_ver < 1.0 ) {
104
-
105
- global $wp_roles;
106
- $wp_roles->add_cap( 'administrator', 'manage_custom_twitter_feeds_options' );
107
-
108
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
109
-
110
- if ( ! isset( $ctf_statuses_option['first_install'] ) ) {
111
-
112
- $options_set = get_option( 'ctf_options', false );
113
-
114
- if ( $options_set ) {
115
- $ctf_statuses_option['first_install'] = 'from_update';
116
- } else {
117
- $ctf_statuses_option['first_install'] = time();
118
- }
119
-
120
- $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
121
-
122
- if ( $ctf_rating_notice_option === 'dismissed' ) {
123
- $ctf_statuses_option['rating_notice_dismissed'] = time();
124
- }
125
-
126
- $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
127
-
128
- if ( $ctf_rating_notice_waiting === false
129
- && $ctf_rating_notice_option === false ) {
130
- $time = 2 * WEEK_IN_SECONDS;
131
- set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', $time );
132
- update_option( 'ctf_rating_notice', 'pending', false );
133
- }
134
-
135
- update_option( 'ctf_statuses', $ctf_statuses_option, false );
136
-
137
- }
138
-
139
- update_option( 'ctf_db_version', CTF_DBVERSION );
140
- }
141
-
142
- if ( version_compare( $db_ver, '1.0.1', '<' ) ) {
143
- include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-feed-locator.php';
144
-
145
- CTF_Feed_Locator::create_table();
146
-
147
- update_option( 'ctf_db_version', CTF_DBVERSION );
148
- }
149
-
150
- }
151
- add_action( 'wp_loaded', 'ctf_check_for_db_updates' );
152
-
153
-
154
- /**
155
- * include the admin files only if in the admin area
156
- */
157
- if ( is_admin() ) {
158
-
159
- $ctf_version = get_option( 'ctf_version', false );
160
-
161
- if ( ! $ctf_version ) {
162
- ctf_update_settings();
163
- }
164
- require_once( CTF_URL . '/inc/CtfAdmin.php' );
165
- require_once( CTF_URL . '/inc/notices.php' );
166
-
167
- $admin = new CtfAdmin;
168
- }
169
-
170
- /**
171
- * Generates the Twitter feed wherever the shortcode is placed
172
- *
173
- * @param $atts array shortcode arguments
174
- *
175
- * @return string
176
- */
177
- function ctf_init( $atts ) {
178
-
179
- include_once( CTF_URL . '/inc/CtfFeed.php' );
180
- wp_enqueue_script( 'ctf_scripts' );
181
-
182
- $twitter_feed = CtfFeed::init( $atts );
183
- /*
184
- echo '<pre>';
185
- var_dump( $twitter_feed->tweet_set);
186
- echo '</pre>'; */
187
- // if there is an error, display the error html, otherwise the feed
188
- if ( ! $twitter_feed->tweet_set || $twitter_feed->missing_credentials ) {
189
- return $twitter_feed->getErrorHtml();
190
- } else {
191
- $twitter_feed->maybeCacheTweets();
192
-
193
- $feed_html = $twitter_feed->getFeedOpeningHtml();
194
- $feed_html .= $twitter_feed->getTweetSetHtml();
195
- $feed_html .= $twitter_feed->getFeedClosingHtml();
196
-
197
- return $feed_html;
198
- }
199
- }
200
- add_shortcode( 'custom-twitter-feed', 'ctf_init' );
201
- add_shortcode( 'custom-twitter-feeds', 'ctf_init' );
202
-
203
- /**
204
- * Called via ajax to get more posts after the "load more" button is clicked
205
- */
206
- function ctf_get_more_posts() {
207
- $shortcode_data = json_decode( str_replace( '\"', '"', sanitize_text_field( $_POST['shortcode_data'] ) ), true ); // necessary to unescape quotes
208
- $last_id_data = isset( $_POST['last_id_data'] ) ? sanitize_text_field( $_POST['last_id_data'] ) : '';
209
- $num_needed = isset( $_POST['num_needed'] ) ? (int)$_POST['num_needed'] : 0;
210
- $ids_to_remove = isset( $_POST['ids_to_remove'] ) ? $_POST['ids_to_remove'] : array();
211
- $is_pagination = empty( $last_id_data ) ? 0 : 1;
212
- $persistent_index = isset( $_POST['persistent_index'] ) ? sanitize_text_field( $_POST['persistent_index'] ) : '';
213
-
214
- include_once( CTF_URL . '/inc/CtfFeed.php' );
215
-
216
- $twitter_feed = CtfFeed::init( $shortcode_data, $last_id_data, $num_needed, $ids_to_remove, $persistent_index );
217
-
218
- if ( ! $twitter_feed->feed_options['persistentcache'] ) {
219
- $twitter_feed->maybeCacheTweets();
220
- }
221
-
222
- $atts = $shortcode_data;
223
-
224
- $feed_id = isset( $_POST['feed_id'] ) ? sanitize_text_field( $_POST['feed_id'] ) : 'unknown';
225
- $location = isset( $_POST['location'] ) && in_array( $_POST['location'], array( 'header', 'footer', 'sidebar', 'content' ), true ) ? sanitize_text_field( $_POST['location'] ) : 'unknown';
226
- $post_id = isset( $_POST['post_id'] ) && $_POST['post_id'] !== 'unknown' ? (int)$_POST['post_id'] : 'unknown';
227
- $feed_details = array(
228
- 'feed_id' => $feed_id,
229
- 'atts' => $atts,
230
- 'location' => array(
231
- 'post_id' => $post_id,
232
- 'html' => $location
233
- )
234
- );
235
-
236
- ctf_do_background_tasks( $feed_details );
237
-
238
- echo $twitter_feed->getTweetSetHtml( $is_pagination );
239
-
240
- die();
241
- }
242
- add_action( 'wp_ajax_nopriv_ctf_get_more_posts', 'ctf_get_more_posts' );
243
- add_action( 'wp_ajax_ctf_get_more_posts', 'ctf_get_more_posts' );
244
-
245
- function ctf_do_locator() {
246
- if ( ! isset( $_POST['feed_id'] ) || strpos( $_POST['feed_id'], 'ctf' ) === false ) {
247
- die( 'invalid feed ID');
248
- }
249
-
250
- $feed_id = sanitize_text_field( $_POST['feed_id'] );
251
-
252
- $atts_raw = isset( $_POST['atts'] ) ? json_decode( stripslashes( $_POST['atts'] ), true ) : array();
253
- if ( is_array( $atts_raw ) ) {
254
- array_map( 'sanitize_text_field', $atts_raw );
255
- } else {
256
- $atts_raw = array();
257
- }
258
- $atts = $atts_raw; // now sanitized
259
-
260
- $location = isset( $_POST['location'] ) && in_array( $_POST['location'], array( 'header', 'footer', 'sidebar', 'content' ), true ) ? sanitize_text_field( $_POST['location'] ) : 'unknown';
261
- $post_id = isset( $_POST['post_id'] ) && $_POST['post_id'] !== 'unknown' ? (int)$_POST['post_id'] : 'unknown';
262
- $feed_details = array(
263
- 'feed_id' => $feed_id,
264
- 'atts' => $atts,
265
- 'location' => array(
266
- 'post_id' => $post_id,
267
- 'html' => $location
268
- )
269
- );
270
-
271
- ctf_do_background_tasks( $feed_details );
272
-
273
- wp_die( 'locating success' );
274
- }
275
- add_action( 'wp_ajax_ctf_do_locator', 'ctf_do_locator' );
276
- add_action( 'wp_ajax_nopriv_ctf_do_locator', 'ctf_do_locator' );
277
-
278
- function ctf_do_background_tasks( $feed_details ) {
279
- $locator = new CTF_Feed_Locator( $feed_details );
280
- $locator->add_or_update_entry();
281
- if ( $locator->should_clear_old_locations() ) {
282
- $locator->delete_old_locations();
283
- }
284
- }
285
-
286
- function ctf_plugin_action_links( $links ) {
287
- $links[] = '<a href="'. esc_url( get_admin_url( null, 'admin.php?page=custom-twitter-feeds' ) ) .'">' . __( 'Settings' ) . '</a>';
288
- return $links;
289
- }
290
- add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), 'ctf_plugin_action_links' );
291
-
292
- function ctf_json_encode( $thing ) {
293
- if ( function_exists( 'wp_json_encode' ) ) {
294
- return wp_json_encode( $thing );
295
- } else {
296
- return json_encode( $thing );
297
- }
298
- }
299
-
300
- /**
301
- * the html output is controlled by the user selecting which portions of tweets to show
302
- *
303
- * @param $part string part of the feed in the html
304
- * @param $feed_options array options that contain what parts of the tweet to show
305
- * @return bool whether or not to show the tweet
306
- */
307
- function ctf_show( $part, $feed_options ) {
308
- $tweet_excludes = isset( $feed_options['tweet_excludes'] ) ? $feed_options['tweet_excludes'] : '';
309
- $tweet_includes = isset( $feed_options['tweet_includes'] ) ? $feed_options['tweet_includes'] : '';
310
-
311
- // if part is in the array of excluded parts or not in the array of included parts, don't show
312
- if ( ! empty( $tweet_excludes ) ) {
313
- return ( in_array( $part, $tweet_excludes ) === false );
314
- } else {
315
- return ( in_array( $part, $tweet_includes ) === true );
316
- }
317
- }
318
-
319
- /**
320
- * this function returns the properly formatted date string based on user input
321
- *
322
- * @param $raw_date string the date from the Twitter api
323
- * @param $feed_options array options for the feed that contain date formatting settings
324
- * @param $utc_offset int offset in seconds for the time display based on timezone
325
- * @return string formatted date
326
- */
327
- function ctf_get_formatted_date( $raw_date, $feed_options, $utc_offset ) {
328
- include_once( CTF_URL . '/inc/CtfDateTime.php' );
329
-
330
- $options = get_option( 'ctf_options' );
331
- $timezone = isset( $options['timezone'] ) ? $options['timezone'] : 'default';
332
- // use php DateTimeZone class to handle the date formatting and offsets
333
- $date_obj = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
334
-
335
- if( $timezone != 'default' ) {
336
- $date_obj->setTimeZone( new DateTimeZone( $timezone ) );
337
- $utc_offset = $date_obj->getOffset();
338
- }
339
-
340
- $tz_offset_timestamp = $date_obj->getTimestamp() + $utc_offset;
341
-
342
- // use the custom date format if set, otherwise use from the selected defaults
343
- if ( ! empty( $feed_options['datecustom'] ) ){
344
- $date_str = date_i18n( $feed_options['datecustom'], $tz_offset_timestamp );
345
- } else {
346
-
347
- switch ( $feed_options['dateformat'] ) {
348
-
349
- case '2':
350
- $date_str = date_i18n( 'F j', $tz_offset_timestamp );
351
- break;
352
- case '3':
353
- $date_str = date_i18n( 'F j, Y', $tz_offset_timestamp );
354
- break;
355
- case '4':
356
- $date_str = date_i18n( 'm.d', $tz_offset_timestamp );
357
- break;
358
- case '5':
359
- $date_str = date_i18n( 'm.d.y', $tz_offset_timestamp );
360
- break;
361
- default:
362
-
363
- // default format is similar to Twitter
364
- $ctf_minute = ! empty( $feed_options['mtime'] ) ? $feed_options['mtime'] : 'm';
365
- $ctf_hour = ! empty( $feed_options['htime'] ) ? $feed_options['htime'] : 'h';
366
- $ctf_now_str = ! empty( $feed_options['nowtime'] ) ? $feed_options['nowtime'] : 'now';
367
-
368
- $now = time() + $utc_offset;
369
-
370
- $difference = $now - $tz_offset_timestamp;
371
-
372
- if ( $difference < 60 ) {
373
- $date_str = $ctf_now_str;
374
- } elseif ( $difference < 60*60 ) {
375
- $date_str = round( $difference/60 ) . $ctf_minute;
376
- } elseif ( $difference < 60*60*24 ) {
377
- $date_str = round( $difference/3600 ) . $ctf_hour;
378
- } else {
379
- $one_year_from_date = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
380
- $one_year_from_date->modify('+1 year');
381
- $one_year_from_date_timestamp = $one_year_from_date->getTimestamp();
382
- if ( $now > $one_year_from_date_timestamp ) {
383
- $date_str = date_i18n( 'j M Y', $tz_offset_timestamp );
384
- } else {
385
- $date_str = date_i18n( 'j M', $tz_offset_timestamp );
386
- }
387
- }
388
- break;
389
- }
390
-
391
- }
392
-
393
- return $date_str;
394
- }
395
-
396
- function ctf_maybe_shorten_text( $string, $feed_settings ) {
397
- $limit = $feed_settings['textlength'];
398
- if ( strlen( $string ) <= $limit || $limit == 280 ) {
399
- return $string;
400
- }
401
- $parts = preg_split( '/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE );
402
- $parts_count = count( $parts );
403
- $length = 0;
404
- $last_part = 0;
405
- for ( ; $last_part < $parts_count; ++$last_part ) {
406
- $length += strlen( $parts[ $last_part ] );
407
- if ( $length > $limit ) { break; }
408
- }
409
- $last_part = $last_part !== 0 ? $last_part - 1 : 0;
410
- $parts[ $last_part ] = $parts[ $last_part ] . '<a href="#" class="ctf_more">...</a><span class="ctf_remaining">';
411
- $return = implode( ' ', $parts ).'</span>';
412
- return $return;
413
- }
414
- add_filter( 'ctf_tweet_text', 'ctf_maybe_shorten_text', 10, 2 );
415
-
416
- function ctf_replace_urls( $string, $feed_settings, $post ) {
417
-
418
- if ( $feed_settings['shorturls'] ) {
419
- return $string;
420
- }
421
-
422
- if ( isset( $post['entities']['urls'][0] ) ) {
423
- foreach ( $post['entities']['urls'] as $url ) {
424
- if ( isset( $url['url'] ) ) {
425
- $string = str_replace( $url['url'], $url['expanded_url'], $string );
426
- }
427
- }
428
- }
429
-
430
- return $string;
431
- }
432
- add_filter( 'ctf_tweet_text', 'ctf_replace_urls', 9, 3 );
433
- add_filter( 'ctf_quoted_tweet_text', 'ctf_replace_urls', 9, 3 );
434
-
435
- function ctf_get_fa_el( $icon ) {
436
- $options = get_option( 'ctf_options' );
437
- $font_method = 'svg';
438
-
439
- $elems = array(
440
- 'fa-arrows-alt' => array(
441
- 'icon' => '<span class="fa fa-arrows-alt"></span>',
442
- 'svg' => '<svg class="svg-inline--fa fa-arrows-alt fa-w-16" aria-hidden="true" aria-label="expand" data-fa-processed="" data-prefix="fa" data-icon="arrows-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M352.201 425.775l-79.196 79.196c-9.373 9.373-24.568 9.373-33.941 0l-79.196-79.196c-15.119-15.119-4.411-40.971 16.971-40.97h51.162L228 284H127.196v51.162c0 21.382-25.851 32.09-40.971 16.971L7.029 272.937c-9.373-9.373-9.373-24.569 0-33.941L86.225 159.8c15.119-15.119 40.971-4.411 40.971 16.971V228H228V127.196h-51.23c-21.382 0-32.09-25.851-16.971-40.971l79.196-79.196c9.373-9.373 24.568-9.373 33.941 0l79.196 79.196c15.119 15.119 4.411 40.971-16.971 40.971h-51.162V228h100.804v-51.162c0-21.382 25.851-32.09 40.97-16.971l79.196 79.196c9.373 9.373 9.373 24.569 0 33.941L425.773 352.2c-15.119 15.119-40.971 4.411-40.97-16.971V284H284v100.804h51.23c21.382 0 32.09 25.851 16.971 40.971z"></path></svg>'
443
- ),
444
- 'fa-check-circle' => array(
445
- 'icon' => '<span class="fa fa-check-circle"></span>',
446
- 'svg' => '<svg class="svg-inline--fa fa-check-circle fa-w-16" aria-hidden="true" aria-label="verified" data-fa-processed="" data-prefix="fa" data-icon="check-circle" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg>'
447
- ),
448
- 'fa-reply' => array(
449
- 'icon' => '<span class="fa fa-reply"></span>',
450
- 'svg' => '<svg class="svg-inline--fa fa-w-16" viewBox="0 0 24 24" aria-label="reply" role="img" xmlns="http://www.w3.org/2000/svg"><g><path fill="currentColor" d="M14.046 2.242l-4.148-.01h-.002c-4.374 0-7.8 3.427-7.8 7.802 0 4.098 3.186 7.206 7.465 7.37v3.828c0 .108.044.286.12.403.142.225.384.347.632.347.138 0 .277-.038.402-.118.264-.168 6.473-4.14 8.088-5.506 1.902-1.61 3.04-3.97 3.043-6.312v-.017c-.006-4.367-3.43-7.787-7.8-7.788zm3.787 12.972c-1.134.96-4.862 3.405-6.772 4.643V16.67c0-.414-.335-.75-.75-.75h-.396c-3.66 0-6.318-2.476-6.318-5.886 0-3.534 2.768-6.302 6.3-6.302l4.147.01h.002c3.532 0 6.3 2.766 6.302 6.296-.003 1.91-.942 3.844-2.514 5.176z"></path></g></svg>'
451
- ),
452
- 'fa-retweet' => array(
453
- 'icon' => '<span class="fa fa-retweet"></span>',
454
- 'svg' => '<svg class="svg-inline--fa fa-w-16" viewBox="0 0 24 24" aria-hidden="true" aria-label="retweet" role="img"><path fill="currentColor" d="M23.77 15.67c-.292-.293-.767-.293-1.06 0l-2.22 2.22V7.65c0-2.068-1.683-3.75-3.75-3.75h-5.85c-.414 0-.75.336-.75.75s.336.75.75.75h5.85c1.24 0 2.25 1.01 2.25 2.25v10.24l-2.22-2.22c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l3.5 3.5c.145.147.337.22.53.22s.383-.072.53-.22l3.5-3.5c.294-.292.294-.767 0-1.06zm-10.66 3.28H7.26c-1.24 0-2.25-1.01-2.25-2.25V6.46l2.22 2.22c.148.147.34.22.532.22s.384-.073.53-.22c.293-.293.293-.768 0-1.06l-3.5-3.5c-.293-.294-.768-.294-1.06 0l-3.5 3.5c-.294.292-.294.767 0 1.06s.767.293 1.06 0l2.22-2.22V16.7c0 2.068 1.683 3.75 3.75 3.75h5.85c.414 0 .75-.336.75-.75s-.337-.75-.75-.75z"></path></svg>'
455
- ),
456
- 'fa-heart' => array(
457
- 'icon' => '<span class="fa fa-heart"></span>',
458
- 'svg' => '<svg class="svg-inline--fa fa-w-16" viewBox="0 0 24 24" aria-hidden="true" aria-label="like" role="img" xmlns="http://www.w3.org/2000/svg"><g><path fill="currentColor" d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.814-1.148 2.354-2.73 4.645-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.376-7.454 13.11-10.037 13.157H12zM7.354 4.225c-2.08 0-3.903 1.988-3.903 4.255 0 5.74 7.034 11.596 8.55 11.658 1.518-.062 8.55-5.917 8.55-11.658 0-2.267-1.823-4.255-3.903-4.255-2.528 0-3.94 2.936-3.952 2.965-.23.562-1.156.562-1.387 0-.014-.03-1.425-2.965-3.954-2.965z"></path></g></svg>'
459
- ),
460
- 'fa-twitter' => array(
461
- 'icon' => '<span class="fa fab fa-twitter"></span>',
462
- 'svg' => '<svg class="svg-inline--fa fa-twitter fa-w-16" aria-hidden="true" aria-label="twitter logo" data-fa-processed="" data-prefix="fab" data-icon="twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>'
463
- ),
464
- 'fa-user' => array(
465
- 'icon' => '<span class="fa fa-user"></span>',
466
- 'svg' => '<svg class="svg-inline--fa fa-user fa-w-16" aria-hidden="true" aria-label="followers" data-fa-processed="" data-prefix="fa" data-icon="user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M96 160C96 71.634 167.635 0 256 0s160 71.634 160 160-71.635 160-160 160S96 248.366 96 160zm304 192h-28.556c-71.006 42.713-159.912 42.695-230.888 0H112C50.144 352 0 402.144 0 464v24c0 13.255 10.745 24 24 24h464c13.255 0 24-10.745 24-24v-24c0-61.856-50.144-112-112-112z"></path></svg>'
467
- ),
468
- 'ctf_playbtn' => array(
469
- 'icon' => '',
470
- 'svg' => '<svg aria-label="play button" style="color: rgba(255,255,255,1)" class="svg-inline--fa fa-play fa-w-14 ctf_playbtn" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="play" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"></path></svg>'
471
- ),
472
- 'fa-file-video-o' => array(
473
- 'icon' => '<span class="fa fa-file-video-o ctf-tweet-text-media" aria-hidden="true"></span>',
474
- 'svg' => '<svg aria-hidden="true" aria-label="video in tweet" focusable="false" data-prefix="far" data-icon="file-video" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" class="svg-inline--fa fa-file-video fa-w-12 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M369.941 97.941l-83.882-83.882A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v416c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48V131.882a48 48 0 0 0-14.059-33.941zM332.118 128H256V51.882L332.118 128zM48 464V48h160v104c0 13.255 10.745 24 24 24h104v288H48zm228.687-211.303L224 305.374V268c0-11.046-8.954-20-20-20H100c-11.046 0-20 8.954-20 20v104c0 11.046 8.954 20 20 20h104c11.046 0 20-8.954 20-20v-37.374l52.687 52.674C286.704 397.318 304 390.28 304 375.986V264.011c0-14.311-17.309-21.319-27.313-11.314z" class=""></path></svg>'
475
- ),
476
- 'fa-picture-o' => array(
477
- 'icon' => '<span class="fa fa-picture-o ctf-tweet-text-media" aria-hidden="true"></span>',
478
- 'svg' => '<svg aria-hidden="true" aria-label="images in tweet" focusable="false" data-prefix="far" data-icon="image" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-image fa-w-16 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z" class=""></path></svg>'
479
- ),
480
- );
481
-
482
- if ( $font_method !== 'fontfile' ){
483
- return $elems[ $icon ]['svg'];
484
- }
485
-
486
- return $elems[ $icon ]['icon'];
487
- }
488
-
489
- /**
490
- * Called via ajax to automatically save access token and access token secret
491
- * retrieved with the big blue button
492
- */
493
- function ctf_auto_save_tokens() {
494
- if ( current_user_can( 'edit_posts' ) ) {
495
- wp_cache_delete ( 'alloptions', 'options' );
496
-
497
- $options = get_option( 'ctf_options', array() );
498
-
499
- $options['access_token'] = sanitize_text_field( $_POST['access_token'] );
500
- $options['access_token_secret'] = sanitize_text_field( $_POST['access_token_secret'] );
501
-
502
- update_option( 'ctf_options', $options );
503
- delete_transient( 'ctf_reauthenticate' );
504
- die();
505
- }
506
- die();
507
- }
508
- add_action( 'wp_ajax_ctf_auto_save_tokens', 'ctf_auto_save_tokens' );
509
-
510
- /**
511
- * manually clears the cached tweets in case of error or user preference
512
- *
513
- * @return mixed bool whether or not it was successful
514
- */
515
- function ctf_clear_cache() {
516
-
517
- //Delete all transients
518
- global $wpdb;
519
- $table_name = $wpdb->prefix . "options";
520
- $result = $wpdb->query("
521
- DELETE
522
- FROM $table_name
523
- WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
524
- ");
525
- $wpdb->query("
526
- DELETE
527
- FROM $table_name
528
- WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
529
- ");
530
-
531
- }
532
- add_action( 'ctf_cron_job', 'ctf_clear_cache' );
533
-
534
- function ctf_clear_cache_admin() {
535
-
536
- //Delete all transients
537
- global $wpdb;
538
- $table_name = $wpdb->prefix . "options";
539
- $result = $wpdb->query("
540
- DELETE
541
- FROM $table_name
542
- WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
543
- ");
544
- $wpdb->query("
545
- DELETE
546
- FROM $table_name
547
- WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
548
- ");
549
-
550
- }
551
- add_action( 'wp_ajax_ctf_clear_cache_admin', 'ctf_clear_cache_admin' );
552
-
553
- /**
554
- * manually clears the persistent cached tweets
555
- *
556
- * @return mixed bool whether or not it was successful
557
- */
558
-
559
- function ctf_clear_persistent_cache() {
560
- if ( current_user_can( 'edit_posts' ) ) {
561
- //Delete all persistent caches (start with ctf_!)
562
- global $wpdb;
563
- $table_name = $wpdb->prefix . "options";
564
- $result = $wpdb->query("
565
- DELETE
566
- FROM $table_name
567
- WHERE `option_name` LIKE ('%ctf\_\!%')
568
- ");
569
- delete_option( 'ctf_cache_list' );
570
- return $result;
571
- } else {
572
- return false;
573
- }
574
-
575
- die();
576
- }
577
- add_action( 'wp_ajax_ctf_clear_persistent_cache', 'ctf_clear_persistent_cache' );
578
-
579
- function ctf_activate() {
580
- // set usage tracking to false if fresh install.
581
- $usage_tracking = get_option( 'ctf_usage_tracking', false );
582
-
583
- if ( ! is_array( $usage_tracking ) ) {
584
- $usage_tracking = array(
585
- 'enabled' => false,
586
- 'last_send' => 0
587
- );
588
-
589
- update_option( 'ctf_usage_tracking', $usage_tracking, false );
590
- }
591
- }
592
- register_activation_hook( __FILE__, 'ctf_activate' );
593
-
594
- /**
595
- * clear the cache and unschedule an cron jobs when deactivated
596
- */
597
- function ctf_deactivate() {
598
- ctf_clear_cache();
599
-
600
- wp_clear_scheduled_hook( 'ctf_cron_job' );
601
- }
602
- register_deactivation_hook( __FILE__, 'ctf_deactivate' );
603
-
604
- /**
605
- * Loads the javascript for the plugin front-end. Also localizes the admin-ajax file location for use in ajax calls
606
- */
607
- function ctf_scripts_and_styles( $enqueue = false ) {
608
- $options = get_option( 'ctf_options' );
609
- $not_ajax_theme = (! isset( $options['ajax_theme'] ) || ! $options['ajax_theme']);
610
- $font_method = 'svg';
611
-
612
- $loacalize_args = array(
613
- 'ajax_url' => admin_url( 'admin-ajax.php' )
614
- );
615
-
616
- wp_enqueue_style( 'ctf_styles', plugins_url( '/css/ctf-styles.min.css', __FILE__ ), array(), CTF_VERSION );
617
-
618
-
619
- if ( $not_ajax_theme ) {
620
- wp_register_script( 'ctf_scripts', plugins_url( '/js/ctf-scripts.min.js', __FILE__ ), array( 'jquery' ), CTF_VERSION, true );
621
- wp_localize_script( 'ctf_scripts', 'ctf', $loacalize_args );
622
- } else {
623
- wp_localize_script( 'jquery', 'ctf', $loacalize_args );
624
- }
625
-
626
- if ( $enqueue ) {
627
- wp_enqueue_style( 'ctf_styles' );
628
- wp_enqueue_script( 'ctf_scripts' );
629
- }
630
- }
631
- add_action( 'wp_enqueue_scripts', 'ctf_scripts_and_styles' );
632
-
633
- /**
634
- * outputs the custom js from the "Customize" tab on the Settings page
635
- */
636
- function ctf_custom_js() {
637
- $options = get_option( 'ctf_options' );
638
- $ctf_custom_js = isset( $options[ 'custom_js' ] ) ? $options[ 'custom_js' ] : '';
639
-
640
- if ( ! empty( $ctf_custom_js ) ) {
641
- ?>
642
- <!-- Custom Twitter Feeds JS -->
643
- <script type="text/javascript">
644
- <?php echo "window.ctf_custom_js = function($){" . stripslashes( $ctf_custom_js ) . "}\r\n"; ?>
645
- </script>
646
- <?php
647
- }
648
- }
649
- add_action( 'wp_footer', 'ctf_custom_js' );
650
-
651
- /**
652
- * outputs the custom css from the "Customize" tab on the Settings page
653
- */
654
- function ctf_custom_css() {
655
- $options = get_option( 'ctf_options' );
656
- $ctf_custom_css = isset( $options[ 'custom_css' ] ) ? $options[ 'custom_css' ] : '';
657
-
658
- if ( ! empty( $ctf_custom_css ) ) {
659
- ?>
660
- <!-- Custom Twitter Feeds CSS -->
661
- <style type="text/css">
662
- <?php echo stripslashes( $ctf_custom_css ) . "\r\n"; ?>
663
- </style>
664
- <?php
665
- }
666
- }
667
- add_action( 'wp_head', 'ctf_custom_css' );
668
-
669
- /**
670
- * Some CSS and JS needed in the admin area as well
671
- */
672
- function ctf_admin_scripts_and_styles() {
673
- wp_enqueue_style( 'ctf_admin_styles', plugins_url( '/css/ctf-admin-styles.css', __FILE__ ), array(), CTF_VERSION );
674
- wp_enqueue_style( 'sb-font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' );
675
- wp_enqueue_script( 'ctf_admin_scripts', plugins_url( '/js/ctf-admin-scripts.js', __FILE__ ) , array( 'jquery' ), CTF_VERSION, false );
676
- wp_localize_script( 'ctf_admin_scripts', 'ctf', array(
677
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
678
- 'sb_nonce' => wp_create_nonce( 'ctf-smash-balloon' )
679
- )
680
- );
681
- $strings = array(
682
- 'addon_activate' => esc_html__( 'Activate', 'custom-twitter-feeds' ),
683
- 'addon_activated' => esc_html__( 'Activated', 'custom-twitter-feeds' ),
684
- 'addon_active' => esc_html__( 'Active', 'custom-twitter-feeds' ),
685
- 'addon_deactivate' => esc_html__( 'Deactivate', 'custom-twitter-feeds' ),
686
- 'addon_inactive' => esc_html__( 'Inactive', 'custom-twitter-feeds' ),
687
- 'addon_install' => esc_html__( 'Install Addon', 'custom-twitter-feeds' ),
688
- 'addon_error' => esc_html__( 'Could not install addon. Please download from wpforms.com and install manually.', 'custom-twitter-feeds' ),
689
- 'plugin_error' => esc_html__( 'Could not install a plugin. Please download from WordPress.org and install manually.', 'custom-twitter-feeds' ),
690
- 'addon_search' => esc_html__( 'Searching Addons', 'custom-twitter-feeds' ),
691
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
692
- 'cancel' => esc_html__( 'Cancel', 'custom-twitter-feeds' ),
693
- 'close' => esc_html__( 'Close', 'custom-twitter-feeds' ),
694
- 'nonce' => wp_create_nonce( 'ctf-admin' ),
695
- 'almost_done' => esc_html__( 'Almost Done', 'custom-twitter-feeds' ),
696
- 'oops' => esc_html__( 'Oops!', 'custom-twitter-feeds' ),
697
- 'ok' => esc_html__( 'OK', 'custom-twitter-feeds' ),
698
- 'plugin_install_activate_btn' => esc_html__( 'Install and Activate', 'custom-twitter-feeds' ),
699
- 'plugin_install_activate_confirm' => esc_html__( 'needs to be installed and activated to import its forms. Would you like us to install and activate it for you?', 'custom-twitter-feeds' ),
700
- 'plugin_activate_btn' => esc_html__( 'Activate', 'custom-twitter-feeds' ),
701
- );
702
- $strings = apply_filters( 'ctf_admin_strings', $strings );
703
-
704
- wp_localize_script(
705
- 'ctf_admin_scripts',
706
- 'ctf_admin_strings',
707
- $strings
708
- );
709
- wp_enqueue_style( 'wp-color-picker' );
710
- wp_enqueue_script( 'wp-color-picker' );
711
- wp_enqueue_script(
712
- 'jquery-matchheight',
713
- CTF_PLUGIN_URL . 'js/jquery.matchHeight-min.js',
714
- array( 'jquery' ),
715
- '0.7.0',
716
- false
717
- );
718
- }
719
- add_action( 'admin_enqueue_scripts', 'ctf_admin_scripts_and_styles' );
720
-
721
-
722
- function ctf_is_pro_version() {
723
- return defined( 'CTF_STORE_URL' );
724
- }
725
-
726
-
727
- function ctf_get_database_settings() {
728
- $options = get_option( 'ctf_options', array() );
729
-
730
- return $options;
731
-
732
  }
1
+ <?php
2
+ /*
3
+ Plugin Name: Custom Twitter Feeds
4
+ Plugin URI: http://smashballoon.com/custom-twitter-feeds
5
+ Description: Customizable Twitter feeds for your website
6
+ Version: 1.8.2
7
+ Author: Smash Balloon
8
+ Author URI: http://smashballoon.com/
9
+ Text Domain: custom-twitter-feeds
10
+ */
11
+ /*
12
+ Copyright 2021 Smash Balloon LLC (email : hey@smashballoon.com)
13
+ This program is free software; you can redistribute it and/or modify
14
+ it under the terms of the GNU General Public License as published by
15
+ the Free Software Foundation; either version 2 of the License, or
16
+ (at your option) any later version.
17
+ This program is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU General Public License for more details.
21
+ You should have received a copy of the GNU General Public License
22
+ along with this program; if not, write to the Free Software
23
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
+ */
25
+
26
+ define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
27
+ define( 'CTF_VERSION', '1.8.2' );
28
+ define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
+ define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.min.js?ver=' . CTF_VERSION , __FILE__ ) );
30
+ define( 'OAUTH_PROCESSOR_URL', 'https://api.smashballoon.com/twitter-login.php?return_uri=' );
31
+ // Plugin Folder Path.
32
+ if ( ! defined( 'CTF_PLUGIN_DIR' ) ) {
33
+ define( 'CTF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
34
+ }
35
+ // Plugin Folder URL.
36
+ if ( ! defined( 'CTF_PLUGIN_URL' ) ) {
37
+ define( 'CTF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
38
+ }
39
+ // Db version.
40
+ if ( ! defined( 'CTF_DBVERSION' ) ) {
41
+ define( 'CTF_DBVERSION', '1.0.1' );
42
+ }
43
+ if ( ! defined( 'CTF_FEED_LOCATOR' ) ) {
44
+ define( 'CTF_FEED_LOCATOR', 'ctf_feed_locator' );
45
+ }
46
+
47
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
48
+
49
+ require_once( CTF_URL . '/inc/widget.php' );
50
+
51
+ require_once( CTF_URL . '/inc/admin-hooks.php' );
52
+
53
+ function ctf_plugin_init() {
54
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/blocks/class-ctf-blocks.php';
55
+
56
+ $ctf_blocks = new CTF_Blocks();
57
+
58
+ if ( $ctf_blocks->allow_load() ) {
59
+ $ctf_blocks->load();
60
+ }
61
+
62
+ include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-feed-locator.php';
63
+ include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-tracking.php';
64
+ include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-gdpr-integrations.php';
65
+
66
+ if ( is_admin() ) {
67
+ if ( version_compare( PHP_VERSION, '5.3.0' ) >= 0
68
+ && version_compare( get_bloginfo('version'), '4.6' , '>' ) ) {
69
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-ctf-notifications.php';
70
+ $ctf_notifications = new CTF_Notifications();
71
+ $ctf_notifications->init();
72
+
73
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-ctf-new-user.php';
74
+ $ctf_new_user = new CTF_New_User();
75
+ $ctf_new_user->init();
76
+
77
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/addon-functions.php';
78
+ }
79
+ }
80
+ }
81
+
82
+ add_action( 'plugins_loaded', 'ctf_plugin_init' );
83
+
84
+ function ctf_update_settings() {
85
+ $existing_deprecated_options = get_option( 'ctf_configure' );
86
+ $existing_options = get_option( 'ctf_options' );
87
+
88
+ update_option( 'ctf_version', CTF_VERSION );
89
+
90
+ if ( ! empty( $existing_deprecated_options ) && empty( $existing_options ) ) {
91
+ $merged_options = $existing_deprecated_options;
92
+ $merged_options = array_merge( $merged_options, get_option( 'ctf_customize', array() ) );
93
+ $merged_options = array_merge( $merged_options, get_option( 'ctf_style', array() ) );
94
+
95
+ update_option( 'ctf_options', $merged_options );
96
+ }
97
+ }
98
+
99
+ function ctf_check_for_db_updates() {
100
+
101
+ $db_ver = get_option( 'ctf_db_version', 0 );
102
+
103
+ if ( (float) $db_ver < 1.0 ) {
104
+
105
+ global $wp_roles;
106
+ $wp_roles->add_cap( 'administrator', 'manage_custom_twitter_feeds_options' );
107
+
108
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
109
+
110
+ if ( ! isset( $ctf_statuses_option['first_install'] ) ) {
111
+
112
+ $options_set = get_option( 'ctf_options', false );
113
+
114
+ if ( $options_set ) {
115
+ $ctf_statuses_option['first_install'] = 'from_update';
116
+ } else {
117
+ $ctf_statuses_option['first_install'] = time();
118
+ }
119
+
120
+ $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
121
+
122
+ if ( $ctf_rating_notice_option === 'dismissed' ) {
123
+ $ctf_statuses_option['rating_notice_dismissed'] = time();
124
+ }
125
+
126
+ $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
127
+
128
+ if ( $ctf_rating_notice_waiting === false
129
+ && $ctf_rating_notice_option === false ) {
130
+ $time = 2 * WEEK_IN_SECONDS;
131
+ set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', $time );
132
+ update_option( 'ctf_rating_notice', 'pending', false );
133
+ }
134
+
135
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
136
+
137
+ }
138
+
139
+ update_option( 'ctf_db_version', CTF_DBVERSION );
140
+ }
141
+
142
+ if ( version_compare( $db_ver, '1.0.1', '<' ) ) {
143
+ include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-feed-locator.php';
144
+
145
+ CTF_Feed_Locator::create_table();
146
+
147
+ update_option( 'ctf_db_version', CTF_DBVERSION );
148
+ }
149
+
150
+ }
151
+ add_action( 'wp_loaded', 'ctf_check_for_db_updates' );
152
+
153
+
154
+ /**
155
+ * include the admin files only if in the admin area
156
+ */
157
+ if ( is_admin() ) {
158
+
159
+ $ctf_version = get_option( 'ctf_version', false );
160
+
161
+ if ( ! $ctf_version ) {
162
+ ctf_update_settings();
163
+ }
164
+ require_once( CTF_URL . '/inc/CtfAdmin.php' );
165
+ require_once( CTF_URL . '/inc/notices.php' );
166
+
167
+ $admin = new CtfAdmin;
168
+ }
169
+
170
+ /**
171
+ * Generates the Twitter feed wherever the shortcode is placed
172
+ *
173
+ * @param $atts array shortcode arguments
174
+ *
175
+ * @return string
176
+ */
177
+ function ctf_init( $atts ) {
178
+
179
+ include_once( CTF_URL . '/inc/CtfFeed.php' );
180
+ wp_enqueue_script( 'ctf_scripts' );
181
+
182
+ $twitter_feed = CtfFeed::init( $atts );
183
+ /*
184
+ echo '<pre>';
185
+ var_dump( $twitter_feed->tweet_set);
186
+ echo '</pre>'; */
187
+ // if there is an error, display the error html, otherwise the feed
188
+ if ( ! $twitter_feed->tweet_set || $twitter_feed->missing_credentials ) {
189
+ return $twitter_feed->getErrorHtml();
190
+ } else {
191
+ $twitter_feed->maybeCacheTweets();
192
+
193
+ $feed_html = $twitter_feed->getFeedOpeningHtml();
194
+ $feed_html .= $twitter_feed->getTweetSetHtml();
195
+ $feed_html .= $twitter_feed->getFeedClosingHtml();
196
+
197
+ return $feed_html;
198
+ }
199
+ }
200
+ add_shortcode( 'custom-twitter-feed', 'ctf_init' );
201
+ add_shortcode( 'custom-twitter-feeds', 'ctf_init' );
202
+
203
+ /**
204
+ * Called via ajax to get more posts after the "load more" button is clicked
205
+ */
206
+ function ctf_get_more_posts() {
207
+ $shortcode_data = json_decode( str_replace( '\"', '"', sanitize_text_field( $_POST['shortcode_data'] ) ), true ); // necessary to unescape quotes
208
+ $last_id_data = isset( $_POST['last_id_data'] ) ? sanitize_text_field( $_POST['last_id_data'] ) : '';
209
+ $num_needed = isset( $_POST['num_needed'] ) ? (int)$_POST['num_needed'] : 0;
210
+ $ids_to_remove = isset( $_POST['ids_to_remove'] ) ? $_POST['ids_to_remove'] : array();
211
+ $is_pagination = empty( $last_id_data ) ? 0 : 1;
212
+ $persistent_index = isset( $_POST['persistent_index'] ) ? sanitize_text_field( $_POST['persistent_index'] ) : '';
213
+
214
+ include_once( CTF_URL . '/inc/CtfFeed.php' );
215
+
216
+ $twitter_feed = CtfFeed::init( $shortcode_data, $last_id_data, $num_needed, $ids_to_remove, $persistent_index );
217
+
218
+ if ( ! $twitter_feed->feed_options['persistentcache'] ) {
219
+ $twitter_feed->maybeCacheTweets();
220
+ }
221
+
222
+ $atts = $shortcode_data;
223
+
224
+ $feed_id = isset( $_POST['feed_id'] ) ? sanitize_text_field( $_POST['feed_id'] ) : 'unknown';
225
+ $location = isset( $_POST['location'] ) && in_array( $_POST['location'], array( 'header', 'footer', 'sidebar', 'content' ), true ) ? sanitize_text_field( $_POST['location'] ) : 'unknown';
226
+ $post_id = isset( $_POST['post_id'] ) && $_POST['post_id'] !== 'unknown' ? (int)$_POST['post_id'] : 'unknown';
227
+ $feed_details = array(
228
+ 'feed_id' => $feed_id,
229
+ 'atts' => $atts,
230
+ 'location' => array(
231
+ 'post_id' => $post_id,
232
+ 'html' => $location
233
+ )
234
+ );
235
+
236
+ ctf_do_background_tasks( $feed_details );
237
+
238
+ echo $twitter_feed->getTweetSetHtml( $is_pagination );
239
+
240
+ die();
241
+ }
242
+ add_action( 'wp_ajax_nopriv_ctf_get_more_posts', 'ctf_get_more_posts' );
243
+ add_action( 'wp_ajax_ctf_get_more_posts', 'ctf_get_more_posts' );
244
+
245
+ function ctf_do_locator() {
246
+ if ( ! isset( $_POST['feed_id'] ) || strpos( $_POST['feed_id'], 'ctf' ) === false ) {
247
+ die( 'invalid feed ID');
248
+ }
249
+
250
+ $feed_id = sanitize_text_field( $_POST['feed_id'] );
251
+
252
+ $atts_raw = isset( $_POST['atts'] ) ? json_decode( stripslashes( $_POST['atts'] ), true ) : array();
253
+ if ( is_array( $atts_raw ) ) {
254
+ array_map( 'sanitize_text_field', $atts_raw );
255
+ } else {
256
+ $atts_raw = array();
257
+ }
258
+ $atts = $atts_raw; // now sanitized
259
+
260
+ $location = isset( $_POST['location'] ) && in_array( $_POST['location'], array( 'header', 'footer', 'sidebar', 'content' ), true ) ? sanitize_text_field( $_POST['location'] ) : 'unknown';
261
+ $post_id = isset( $_POST['post_id'] ) && $_POST['post_id'] !== 'unknown' ? (int)$_POST['post_id'] : 'unknown';
262
+ $feed_details = array(
263
+ 'feed_id' => $feed_id,
264
+ 'atts' => $atts,
265
+ 'location' => array(
266
+ 'post_id' => $post_id,
267
+ 'html' => $location
268
+ )
269
+ );
270
+
271
+ ctf_do_background_tasks( $feed_details );
272
+
273
+ wp_die( 'locating success' );
274
+ }
275
+ add_action( 'wp_ajax_ctf_do_locator', 'ctf_do_locator' );
276
+ add_action( 'wp_ajax_nopriv_ctf_do_locator', 'ctf_do_locator' );
277
+
278
+ function ctf_do_background_tasks( $feed_details ) {
279
+ $locator = new CTF_Feed_Locator( $feed_details );
280
+ $locator->add_or_update_entry();
281
+ if ( $locator->should_clear_old_locations() ) {
282
+ $locator->delete_old_locations();
283
+ }
284
+ }
285
+
286
+ function ctf_plugin_action_links( $links ) {
287
+ $links[] = '<a href="'. esc_url( get_admin_url( null, 'admin.php?page=custom-twitter-feeds' ) ) .'">' . __( 'Settings' ) . '</a>';
288
+ return $links;
289
+ }
290
+ add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), 'ctf_plugin_action_links' );
291
+
292
+ function ctf_json_encode( $thing ) {
293
+ if ( function_exists( 'wp_json_encode' ) ) {
294
+ return wp_json_encode( $thing );
295
+ } else {
296
+ return json_encode( $thing );
297
+ }
298
+ }
299
+
300
+ /**
301
+ * the html output is controlled by the user selecting which portions of tweets to show
302
+ *
303
+ * @param $part string part of the feed in the html
304
+ * @param $feed_options array options that contain what parts of the tweet to show
305
+ * @return bool whether or not to show the tweet
306
+ */
307
+ function ctf_show( $part, $feed_options ) {
308
+ $tweet_excludes = isset( $feed_options['tweet_excludes'] ) ? $feed_options['tweet_excludes'] : '';
309
+ $tweet_includes = isset( $feed_options['tweet_includes'] ) ? $feed_options['tweet_includes'] : '';
310
+
311
+ // if part is in the array of excluded parts or not in the array of included parts, don't show
312
+ if ( ! empty( $tweet_excludes ) ) {
313
+ return ( in_array( $part, $tweet_excludes ) === false );
314
+ } else {
315
+ return ( in_array( $part, $tweet_includes ) === true );
316
+ }
317
+ }
318
+
319
+ /**
320
+ * this function returns the properly formatted date string based on user input
321
+ *
322
+ * @param $raw_date string the date from the Twitter api
323
+ * @param $feed_options array options for the feed that contain date formatting settings
324
+ * @param $utc_offset int offset in seconds for the time display based on timezone
325
+ * @return string formatted date
326
+ */
327
+ function ctf_get_formatted_date( $raw_date, $feed_options, $utc_offset ) {
328
+ include_once( CTF_URL . '/inc/CtfDateTime.php' );
329
+
330
+ $options = get_option( 'ctf_options' );
331
+ $timezone = isset( $options['timezone'] ) ? $options['timezone'] : 'default';
332
+ // use php DateTimeZone class to handle the date formatting and offsets
333
+ $date_obj = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
334
+
335
+ if( $timezone != 'default' ) {
336
+ $date_obj->setTimeZone( new DateTimeZone( $timezone ) );
337
+ $utc_offset = $date_obj->getOffset();
338
+ }
339
+
340
+ $tz_offset_timestamp = $date_obj->getTimestamp() + $utc_offset;
341
+
342
+ // use the custom date format if set, otherwise use from the selected defaults
343
+ if ( ! empty( $feed_options['datecustom'] ) ){
344
+ $date_str = date_i18n( $feed_options['datecustom'], $tz_offset_timestamp );
345
+ } else {
346
+
347
+ switch ( $feed_options['dateformat'] ) {
348
+
349
+ case '2':
350
+ $date_str = date_i18n( 'F j', $tz_offset_timestamp );
351
+ break;
352
+ case '3':
353
+ $date_str = date_i18n( 'F j, Y', $tz_offset_timestamp );
354
+ break;
355
+ case '4':
356
+ $date_str = date_i18n( 'm.d', $tz_offset_timestamp );
357
+ break;
358
+ case '5':
359
+ $date_str = date_i18n( 'm.d.y', $tz_offset_timestamp );
360
+ break;
361
+ default:
362
+
363
+ // default format is similar to Twitter
364
+ $ctf_minute = ! empty( $feed_options['mtime'] ) ? $feed_options['mtime'] : 'm';
365
+ $ctf_hour = ! empty( $feed_options['htime'] ) ? $feed_options['htime'] : 'h';
366
+ $ctf_now_str = ! empty( $feed_options['nowtime'] ) ? $feed_options['nowtime'] : 'now';
367
+
368
+ $now = time() + $utc_offset;
369
+
370
+ $difference = $now - $tz_offset_timestamp;
371
+
372
+ if ( $difference < 60 ) {
373
+ $date_str = $ctf_now_str;
374
+ } elseif ( $difference < 60*60 ) {
375
+ $date_str = round( $difference/60 ) . $ctf_minute;
376
+ } elseif ( $difference < 60*60*24 ) {
377
+ $date_str = round( $difference/3600 ) . $ctf_hour;
378
+ } else {
379
+ $one_year_from_date = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
380
+ $one_year_from_date->modify('+1 year');
381
+ $one_year_from_date_timestamp = $one_year_from_date->getTimestamp();
382
+ if ( $now > $one_year_from_date_timestamp ) {
383
+ $date_str = date_i18n( 'j M Y', $tz_offset_timestamp );
384
+ } else {
385
+ $date_str = date_i18n( 'j M', $tz_offset_timestamp );
386
+ }
387
+ }
388
+ break;
389
+ }
390
+
391
+ }
392
+
393
+ return $date_str;
394
+ }
395
+
396
+ function ctf_maybe_shorten_text( $string, $feed_settings ) {
397
+ $limit = $feed_settings['textlength'];
398
+ if ( strlen( $string ) <= $limit || $limit == 280 ) {
399
+ return $string;
400
+ }
401
+ $parts = preg_split( '/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE );
402
+ $parts_count = count( $parts );
403
+ $length = 0;
404
+ $last_part = 0;
405
+ for ( ; $last_part < $parts_count; ++$last_part ) {
406
+ $length += strlen( $parts[ $last_part ] );
407
+ if ( $length > $limit ) { break; }
408
+ }
409
+ $last_part = $last_part !== 0 ? $last_part - 1 : 0;
410
+ $parts[ $last_part ] = $parts[ $last_part ] . '<a href="#" class="ctf_more">...</a><span class="ctf_remaining">';
411
+ $return = implode( ' ', $parts ).'</span>';
412
+ return $return;
413
+ }
414
+ add_filter( 'ctf_tweet_text', 'ctf_maybe_shorten_text', 10, 2 );
415
+
416
+ function ctf_replace_urls( $string, $feed_settings, $post ) {
417
+
418
+ if ( $feed_settings['shorturls'] ) {
419
+ return $string;
420
+ }
421
+
422
+ if ( isset( $post['entities']['urls'][0] ) ) {
423
+ foreach ( $post['entities']['urls'] as $url ) {
424
+ if ( isset( $url['url'] ) ) {
425
+ $string = str_replace( $url['url'], $url['expanded_url'], $string );
426
+ }
427
+ }
428
+ }
429
+
430
+ return $string;
431
+ }
432
+ add_filter( 'ctf_tweet_text', 'ctf_replace_urls', 9, 3 );
433
+ add_filter( 'ctf_quoted_tweet_text', 'ctf_replace_urls', 9, 3 );
434
+
435
+ function ctf_get_fa_el( $icon ) {
436
+ $options = get_option( 'ctf_options' );
437
+ $font_method = 'svg';
438
+
439
+ $elems = array(
440
+ 'fa-arrows-alt' => array(
441
+ 'icon' => '<span class="fa fa-arrows-alt"></span>',
442
+ 'svg' => '<svg class="svg-inline--fa fa-arrows-alt fa-w-16" aria-hidden="true" aria-label="expand" data-fa-processed="" data-prefix="fa" data-icon="arrows-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M352.201 425.775l-79.196 79.196c-9.373 9.373-24.568 9.373-33.941 0l-79.196-79.196c-15.119-15.119-4.411-40.971 16.971-40.97h51.162L228 284H127.196v51.162c0 21.382-25.851 32.09-40.971 16.971L7.029 272.937c-9.373-9.373-9.373-24.569 0-33.941L86.225 159.8c15.119-15.119 40.971-4.411 40.971 16.971V228H228V127.196h-51.23c-21.382 0-32.09-25.851-16.971-40.971l79.196-79.196c9.373-9.373 24.568-9.373 33.941 0l79.196 79.196c15.119 15.119 4.411 40.971-16.971 40.971h-51.162V228h100.804v-51.162c0-21.382 25.851-32.09 40.97-16.971l79.196 79.196c9.373 9.373 9.373 24.569 0 33.941L425.773 352.2c-15.119 15.119-40.971 4.411-40.97-16.971V284H284v100.804h51.23c21.382 0 32.09 25.851 16.971 40.971z"></path></svg>'
443
+ ),
444
+ 'fa-check-circle' => array(
445
+ 'icon' => '<span class="fa fa-check-circle"></span>',
446
+ 'svg' => '<svg class="svg-inline--fa fa-check-circle fa-w-16" aria-hidden="true" aria-label="verified" data-fa-processed="" data-prefix="fa" data-icon="check-circle" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg>'
447
+ ),
448
+ 'fa-reply' => array(
449
+ 'icon' => '<span class="fa fa-reply"></span>',
450
+ 'svg' => '<svg class="svg-inline--fa fa-w-16" viewBox="0 0 24 24" aria-label="reply" role="img" xmlns="http://www.w3.org/2000/svg"><g><path fill="currentColor" d="M14.046 2.242l-4.148-.01h-.002c-4.374 0-7.8 3.427-7.8 7.802 0 4.098 3.186 7.206 7.465 7.37v3.828c0 .108.044.286.12.403.142.225.384.347.632.347.138 0 .277-.038.402-.118.264-.168 6.473-4.14 8.088-5.506 1.902-1.61 3.04-3.97 3.043-6.312v-.017c-.006-4.367-3.43-7.787-7.8-7.788zm3.787 12.972c-1.134.96-4.862 3.405-6.772 4.643V16.67c0-.414-.335-.75-.75-.75h-.396c-3.66 0-6.318-2.476-6.318-5.886 0-3.534 2.768-6.302 6.3-6.302l4.147.01h.002c3.532 0 6.3 2.766 6.302 6.296-.003 1.91-.942 3.844-2.514 5.176z"></path></g></svg>'
451
+ ),
452
+ 'fa-retweet' => array(
453
+ 'icon' => '<span class="fa fa-retweet"></span>',
454
+ 'svg' => '<svg class="svg-inline--fa fa-w-16" viewBox="0 0 24 24" aria-hidden="true" aria-label="retweet" role="img"><path fill="currentColor" d="M23.77 15.67c-.292-.293-.767-.293-1.06 0l-2.22 2.22V7.65c0-2.068-1.683-3.75-3.75-3.75h-5.85c-.414 0-.75.336-.75.75s.336.75.75.75h5.85c1.24 0 2.25 1.01 2.25 2.25v10.24l-2.22-2.22c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l3.5 3.5c.145.147.337.22.53.22s.383-.072.53-.22l3.5-3.5c.294-.292.294-.767 0-1.06zm-10.66 3.28H7.26c-1.24 0-2.25-1.01-2.25-2.25V6.46l2.22 2.22c.148.147.34.22.532.22s.384-.073.53-.22c.293-.293.293-.768 0-1.06l-3.5-3.5c-.293-.294-.768-.294-1.06 0l-3.5 3.5c-.294.292-.294.767 0 1.06s.767.293 1.06 0l2.22-2.22V16.7c0 2.068 1.683 3.75 3.75 3.75h5.85c.414 0 .75-.336.75-.75s-.337-.75-.75-.75z"></path></svg>'
455
+ ),
456
+ 'fa-heart' => array(
457
+ 'icon' => '<span class="fa fa-heart"></span>',
458
+ 'svg' => '<svg class="svg-inline--fa fa-w-16" viewBox="0 0 24 24" aria-hidden="true" aria-label="like" role="img" xmlns="http://www.w3.org/2000/svg"><g><path fill="currentColor" d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.814-1.148 2.354-2.73 4.645-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.376-7.454 13.11-10.037 13.157H12zM7.354 4.225c-2.08 0-3.903 1.988-3.903 4.255 0 5.74 7.034 11.596 8.55 11.658 1.518-.062 8.55-5.917 8.55-11.658 0-2.267-1.823-4.255-3.903-4.255-2.528 0-3.94 2.936-3.952 2.965-.23.562-1.156.562-1.387 0-.014-.03-1.425-2.965-3.954-2.965z"></path></g></svg>'
459
+ ),
460
+ 'fa-twitter' => array(
461
+ 'icon' => '<span class="fa fab fa-twitter"></span>',
462
+ 'svg' => '<svg class="svg-inline--fa fa-twitter fa-w-16" aria-hidden="true" aria-label="twitter logo" data-fa-processed="" data-prefix="fab" data-icon="twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>'
463
+ ),
464
+ 'fa-user' => array(
465
+ 'icon' => '<span class="fa fa-user"></span>',
466
+ 'svg' => '<svg class="svg-inline--fa fa-user fa-w-16" aria-hidden="true" aria-label="followers" data-fa-processed="" data-prefix="fa" data-icon="user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M96 160C96 71.634 167.635 0 256 0s160 71.634 160 160-71.635 160-160 160S96 248.366 96 160zm304 192h-28.556c-71.006 42.713-159.912 42.695-230.888 0H112C50.144 352 0 402.144 0 464v24c0 13.255 10.745 24 24 24h464c13.255 0 24-10.745 24-24v-24c0-61.856-50.144-112-112-112z"></path></svg>'
467
+ ),
468
+ 'ctf_playbtn' => array(
469
+ 'icon' => '',
470
+ 'svg' => '<svg aria-label="play button" style="color: rgba(255,255,255,1)" class="svg-inline--fa fa-play fa-w-14 ctf_playbtn" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="play" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"></path></svg>'
471
+ ),
472
+ 'fa-file-video-o' => array(
473
+ 'icon' => '<span class="fa fa-file-video-o ctf-tweet-text-media" aria-hidden="true"></span>',
474
+ 'svg' => '<svg aria-hidden="true" aria-label="video in tweet" focusable="false" data-prefix="far" data-icon="file-video" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" class="svg-inline--fa fa-file-video fa-w-12 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M369.941 97.941l-83.882-83.882A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v416c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48V131.882a48 48 0 0 0-14.059-33.941zM332.118 128H256V51.882L332.118 128zM48 464V48h160v104c0 13.255 10.745 24 24 24h104v288H48zm228.687-211.303L224 305.374V268c0-11.046-8.954-20-20-20H100c-11.046 0-20 8.954-20 20v104c0 11.046 8.954 20 20 20h104c11.046 0 20-8.954 20-20v-37.374l52.687 52.674C286.704 397.318 304 390.28 304 375.986V264.011c0-14.311-17.309-21.319-27.313-11.314z" class=""></path></svg>'
475
+ ),
476
+ 'fa-picture-o' => array(
477
+ 'icon' => '<span class="fa fa-picture-o ctf-tweet-text-media" aria-hidden="true"></span>',
478
+ 'svg' => '<svg aria-hidden="true" aria-label="images in tweet" focusable="false" data-prefix="far" data-icon="image" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-image fa-w-16 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z" class=""></path></svg>'
479
+ ),
480
+ );
481
+
482
+ if ( $font_method !== 'fontfile' ){
483
+ return $elems[ $icon ]['svg'];
484
+ }
485
+
486
+ return $elems[ $icon ]['icon'];
487
+ }
488
+
489
+ /**
490
+ * Called via ajax to automatically save access token and access token secret
491
+ * retrieved with the big blue button
492
+ */
493
+ function ctf_auto_save_tokens() {
494
+ if ( current_user_can( 'edit_posts' ) ) {
495
+ wp_cache_delete ( 'alloptions', 'options' );
496
+
497
+ $options = get_option( 'ctf_options', array() );
498
+
499
+ $options['access_token'] = sanitize_text_field( $_POST['access_token'] );
500
+ $options['access_token_secret'] = sanitize_text_field( $_POST['access_token_secret'] );
501
+
502
+ update_option( 'ctf_options', $options );
503
+ delete_transient( 'ctf_reauthenticate' );
504
+ die();
505
+ }
506
+ die();
507
+ }
508
+ add_action( 'wp_ajax_ctf_auto_save_tokens', 'ctf_auto_save_tokens' );
509
+
510
+ /**
511
+ * manually clears the cached tweets in case of error or user preference
512
+ *
513
+ * @return mixed bool whether or not it was successful
514
+ */
515
+ function ctf_clear_cache() {
516
+
517
+ //Delete all transients
518
+ global $wpdb;
519
+ $table_name = $wpdb->prefix . "options";
520
+ $result = $wpdb->query("
521
+ DELETE
522
+ FROM $table_name
523
+ WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
524
+ ");
525
+ $wpdb->query("
526
+ DELETE
527
+ FROM $table_name
528
+ WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
529
+ ");
530
+
531
+ }
532
+ add_action( 'ctf_cron_job', 'ctf_clear_cache' );
533
+
534
+ function ctf_clear_cache_admin() {
535
+
536
+ //Delete all transients
537
+ global $wpdb;
538
+ $table_name = $wpdb->prefix . "options";
539
+ $result = $wpdb->query("
540
+ DELETE
541
+ FROM $table_name
542
+ WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
543
+ ");
544
+ $wpdb->query("
545
+ DELETE
546
+ FROM $table_name
547
+ WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
548
+ ");
549
+
550
+ }
551
+ add_action( 'wp_ajax_ctf_clear_cache_admin', 'ctf_clear_cache_admin' );
552
+
553
+ /**
554
+ * manually clears the persistent cached tweets
555
+ *
556
+ * @return mixed bool whether or not it was successful
557
+ */
558
+
559
+ function ctf_clear_persistent_cache() {
560
+ if ( current_user_can( 'edit_posts' ) ) {
561
+ //Delete all persistent caches (start with ctf_!)
562
+ global $wpdb;
563
+ $table_name = $wpdb->prefix . "options";
564
+ $result = $wpdb->query("
565
+ DELETE
566
+ FROM $table_name
567
+ WHERE `option_name` LIKE ('%ctf\_\!%')
568
+ ");
569
+ delete_option( 'ctf_cache_list' );
570
+ return $result;
571
+ } else {
572
+ return false;
573
+ }
574
+
575
+ die();
576
+ }
577
+ add_action( 'wp_ajax_ctf_clear_persistent_cache', 'ctf_clear_persistent_cache' );
578
+
579
+ function ctf_activate() {
580
+ // set usage tracking to false if fresh install.
581
+ $usage_tracking = get_option( 'ctf_usage_tracking', false );
582
+
583
+ if ( ! is_array( $usage_tracking ) ) {
584
+ $usage_tracking = array(
585
+ 'enabled' => false,
586
+ 'last_send' => 0
587
+ );
588
+
589
+ update_option( 'ctf_usage_tracking', $usage_tracking, false );
590
+ }
591
+ }
592
+ register_activation_hook( __FILE__, 'ctf_activate' );
593
+
594
+ /**
595
+ * clear the cache and unschedule an cron jobs when deactivated
596
+ */
597
+ function ctf_deactivate() {
598
+ ctf_clear_cache();
599
+
600
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
601
+ }
602
+ register_deactivation_hook( __FILE__, 'ctf_deactivate' );
603
+
604
+ /**
605
+ * Loads the javascript for the plugin front-end. Also localizes the admin-ajax file location for use in ajax calls
606
+ */
607
+ function ctf_scripts_and_styles( $enqueue = false ) {
608
+ $options = get_option( 'ctf_options' );
609
+ $not_ajax_theme = (! isset( $options['ajax_theme'] ) || ! $options['ajax_theme']);
610
+ $font_method = 'svg';
611
+
612
+ $loacalize_args = array(
613
+ 'ajax_url' => admin_url( 'admin-ajax.php' )
614
+ );
615
+
616
+ wp_enqueue_style( 'ctf_styles', plugins_url( '/css/ctf-styles.min.css', __FILE__ ), array(), CTF_VERSION );
617
+
618
+
619
+ if ( $not_ajax_theme ) {
620
+ wp_register_script( 'ctf_scripts', plugins_url( '/js/ctf-scripts.min.js', __FILE__ ), array( 'jquery' ), CTF_VERSION, true );
621
+ wp_localize_script( 'ctf_scripts', 'ctf', $loacalize_args );
622
+ } else {
623
+ wp_localize_script( 'jquery', 'ctf', $loacalize_args );
624
+ }
625
+
626
+ if ( $enqueue ) {
627
+ wp_enqueue_style( 'ctf_styles' );
628
+ wp_enqueue_script( 'ctf_scripts' );
629
+ }
630
+ }
631
+ add_action( 'wp_enqueue_scripts', 'ctf_scripts_and_styles' );
632
+
633
+ /**
634
+ * outputs the custom js from the "Customize" tab on the Settings page
635
+ */
636
+ function ctf_custom_js() {
637
+ $options = get_option( 'ctf_options' );
638
+ $ctf_custom_js = isset( $options[ 'custom_js' ] ) ? $options[ 'custom_js' ] : '';
639
+
640
+ if ( ! empty( $ctf_custom_js ) ) {
641
+ ?>
642
+ <!-- Custom Twitter Feeds JS -->
643
+ <script type="text/javascript">
644
+ <?php echo "window.ctf_custom_js = function($){" . stripslashes( $ctf_custom_js ) . "}\r\n"; ?>
645
+ </script>
646
+ <?php
647
+ }
648
+ }
649
+ add_action( 'wp_footer', 'ctf_custom_js' );
650
+
651
+ /**
652
+ * outputs the custom css from the "Customize" tab on the Settings page
653
+ */
654
+ function ctf_custom_css() {
655
+ $options = get_option( 'ctf_options' );
656
+ $ctf_custom_css = isset( $options[ 'custom_css' ] ) ? $options[ 'custom_css' ] : '';
657
+
658
+ if ( ! empty( $ctf_custom_css ) ) {
659
+ ?>
660
+ <!-- Custom Twitter Feeds CSS -->
661
+ <style type="text/css">
662
+ <?php echo stripslashes( $ctf_custom_css ) . "\r\n"; ?>
663
+ </style>
664
+ <?php
665
+ }
666
+ }
667
+ add_action( 'wp_head', 'ctf_custom_css' );
668
+
669
+ /**
670
+ * Some CSS and JS needed in the admin area as well
671
+ */
672
+ function ctf_admin_scripts_and_styles() {
673
+ wp_enqueue_style( 'ctf_admin_styles', plugins_url( '/css/ctf-admin-styles.css', __FILE__ ), array(), CTF_VERSION );
674
+ wp_enqueue_style( 'sb-font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' );
675
+ wp_enqueue_script( 'ctf_admin_scripts', plugins_url( '/js/ctf-admin-scripts.js', __FILE__ ) , array( 'jquery' ), CTF_VERSION, false );
676
+ wp_localize_script( 'ctf_admin_scripts', 'ctf', array(
677
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
678
+ 'sb_nonce' => wp_create_nonce( 'ctf-smash-balloon' )
679
+ )
680
+ );
681
+ $strings = array(
682
+ 'addon_activate' => esc_html__( 'Activate', 'custom-twitter-feeds' ),
683
+ 'addon_activated' => esc_html__( 'Activated', 'custom-twitter-feeds' ),
684
+ 'addon_active' => esc_html__( 'Active', 'custom-twitter-feeds' ),
685
+ 'addon_deactivate' => esc_html__( 'Deactivate', 'custom-twitter-feeds' ),
686
+ 'addon_inactive' => esc_html__( 'Inactive', 'custom-twitter-feeds' ),
687
+ 'addon_install' => esc_html__( 'Install Addon', 'custom-twitter-feeds' ),
688
+ 'addon_error' => esc_html__( 'Could not install addon. Please download from wpforms.com and install manually.', 'custom-twitter-feeds' ),
689
+ 'plugin_error' => esc_html__( 'Could not install a plugin. Please download from WordPress.org and install manually.', 'custom-twitter-feeds' ),
690
+ 'addon_search' => esc_html__( 'Searching Addons', 'custom-twitter-feeds' ),
691
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
692
+ 'cancel' => esc_html__( 'Cancel', 'custom-twitter-feeds' ),
693
+ 'close' => esc_html__( 'Close', 'custom-twitter-feeds' ),
694
+ 'nonce' => wp_create_nonce( 'ctf-admin' ),
695
+ 'almost_done' => esc_html__( 'Almost Done', 'custom-twitter-feeds' ),
696
+ 'oops' => esc_html__( 'Oops!', 'custom-twitter-feeds' ),
697
+ 'ok' => esc_html__( 'OK', 'custom-twitter-feeds' ),
698
+ 'plugin_install_activate_btn' => esc_html__( 'Install and Activate', 'custom-twitter-feeds' ),
699
+ 'plugin_install_activate_confirm' => esc_html__( 'needs to be installed and activated to import its forms. Would you like us to install and activate it for you?', 'custom-twitter-feeds' ),
700
+ 'plugin_activate_btn' => esc_html__( 'Activate', 'custom-twitter-feeds' ),
701
+ );
702
+ $strings = apply_filters( 'ctf_admin_strings', $strings );
703
+
704
+ wp_localize_script(
705
+ 'ctf_admin_scripts',
706
+ 'ctf_admin_strings',
707
+ $strings
708
+ );
709
+ wp_enqueue_style( 'wp-color-picker' );
710
+ wp_enqueue_script( 'wp-color-picker' );
711
+ wp_enqueue_script(
712
+ 'jquery-matchheight',
713
+ CTF_PLUGIN_URL . 'js/jquery.matchHeight-min.js',
714
+ array( 'jquery' ),
715
+ '0.7.0',
716
+ false
717
+ );
718
+ }
719
+ add_action( 'admin_enqueue_scripts', 'ctf_admin_scripts_and_styles' );
720
+
721
+
722
+ function ctf_is_pro_version() {
723
+ return defined( 'CTF_STORE_URL' );
724
+ }
725
+
726
+
727
+ function ctf_get_database_settings() {
728
+ $options = get_option( 'ctf_options', array() );
729
+
730
+ return $options;
731
+
732
  }
img/ctf-icon.jpg DELETED
Binary file
inc/CtfAdmin.php CHANGED
@@ -1,2081 +1,2081 @@
1
- <?php
2
- /**
3
- * Class CtfAdmin
4
- *
5
- * Uses the Settings API to create easily customizable settings pages and tabs
6
- */
7
-
8
- // Don't load directly
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- die( '-1' );
11
- }
12
-
13
- class CtfAdmin
14
- {
15
- public function __construct()
16
- {
17
- add_action( 'admin_menu', array( $this, 'add_menu' ) );
18
- add_action( 'admin_init', array( $this, 'options_page_init' ) );
19
- }
20
-
21
- public function add_menu()
22
- {
23
- add_menu_page(
24
- 'Twitter Feeds',
25
- 'Twitter Feeds',
26
- 'manage_options',
27
- 'custom-twitter-feeds',
28
- array( $this, 'create_options_page' ),
29
- '',
30
- 99
31
- );
32
-
33
- add_submenu_page(
34
- 'custom-twitter-feeds',
35
- 'Customize',
36
- 'Customize',
37
- 'manage_options',
38
- 'custom-twitter-feeds-customize',
39
- array( $this, 'create_submenu_page_customize' )
40
- );
41
-
42
- add_submenu_page(
43
- 'custom-twitter-feeds',
44
- 'Style',
45
- 'Style',
46
- 'manage_options',
47
- 'custom-twitter-feeds-style',
48
- array( $this, 'create_submenu_page_style' )
49
- );
50
-
51
- //Show a Instagram plugin menu item if it isn't already installed
52
- if( !is_plugin_active( 'instagram-feed/instagram-feed.php' ) && !is_plugin_active( 'instagram-feed-pro/instagram-feed.php' ) ){
53
- add_submenu_page(
54
- 'custom-twitter-feeds',
55
- __( 'Instagram Feed', 'ctf' ),
56
- '<span class="ctf_get_sbi"><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="instagram" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-instagram fa-w-14 fa-2x"><path fill="currentColor" d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z" class=""></path></svg>' . __( 'Instagram Feed', 'ctf' ) . '</span>',
57
- 'manage_options',
58
- 'admin.php?page=custom-twitter-feeds&tab=more',
59
- ''
60
- );
61
- }
62
-
63
- //Show a Instagram plugin menu item if it isn't already installed
64
- if( !is_plugin_active( 'custom-facebook-feed/custom-facebook-feed.php' ) && !is_plugin_active( 'custom-facebook-feed-pro/custom-facebook-feed.php' ) ){
65
- add_submenu_page(
66
- 'custom-twitter-feeds',
67
- __( 'Facebook Feed', 'ctf' ),
68
- '<span class="ctf_get_cff"><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="facebook" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-facebook fa-w-16 fa-2x"><path fill="currentColor" d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z" class=""></path></svg>' . __( 'Facebook Feed', 'ctf' ) . '</span>',
69
- 'manage_options',
70
- 'admin.php?page=custom-twitter-feeds&tab=more',
71
- ''
72
- );
73
- }
74
-
75
- //Show a YouTube plugin menu item if it isn't already installed
76
- if( !is_plugin_active( 'feeds-for-youtube/youtube-feed.php' ) && !is_plugin_active( 'youtube-feed-pro/youtube-feed.php' ) ){
77
- add_submenu_page(
78
- 'custom-twitter-feeds',
79
- __( 'YouTube Feed', 'ctf' ),
80
- '<span class="ctf_get_yt"><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="youtube" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class="svg-inline--fa fa-youtube fa-w-18 fa-2x"><path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z" class=""></path></svg>' . __( 'YouTube Feed', 'ctf' ) . '</span>',
81
- 'manage_options',
82
- 'admin.php?page=custom-twitter-feeds&tab=more',
83
- ''
84
- );
85
- }
86
-
87
- add_submenu_page(
88
- 'custom-twitter-feeds',
89
- __( 'Social Wall', 'ctf' ),
90
- '<span><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="th" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-th fa-w-16 fa-2x"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z" class=""></path></svg>' . __( 'Social Wall', 'ctf' ) . '</span>',
91
- 'manage_options',
92
- 'ctf-sw',
93
- array( $this, 'social_wall_page' )
94
- );
95
-
96
- add_submenu_page(
97
- 'custom-twitter-feeds',
98
- __( 'Try the Pro Demo', 'ctf' ),
99
- __( '<span class="ctf_get_pro">Try the Pro Demo</span>', 'custom-twitter-feeds' ),
100
- 'manage_options',
101
- 'https://smashballoon.com/custom-twitter-feeds/demo/?utm_campaign=twitter-free&utm_source=menu-link&utm_medium=upgrade-link',
102
- ''
103
- );
104
-
105
- }
106
-
107
-
108
- public static function get_active_tab( $tab = '' )
109
- {
110
- switch ( $tab ) {
111
- case 'customize':
112
- return 'customize';
113
- case 'style':
114
- return 'style';
115
- case 'display':
116
- return 'display';
117
- case 'support':
118
- return 'support';
119
- case 'allfeeds':
120
- return 'allfeeds';
121
- case 'more':
122
- return 'more';
123
- default:
124
- return 'configure';
125
- }
126
- }
127
-
128
- public function create_options_page()
129
- {
130
- require_once CTF_URL . '/views/admin/main.php';
131
- }
132
-
133
- public function create_submenu_page_customize()
134
- {
135
- $tab = 'customize';
136
-
137
- require_once CTF_URL . '/views/admin/main.php';
138
- }
139
-
140
- public function create_submenu_page_style()
141
- {
142
- $tab = 'style';
143
-
144
- require_once CTF_URL . '/views/admin/main.php';
145
- }
146
-
147
-
148
- public function social_wall_page() {
149
- require_once CTF_URL .'views/admin/social-wall.php';
150
- }
151
-
152
- public function general_section_text()
153
- {
154
- // no explanation needed
155
- }
156
-
157
- public function access_token_button()
158
- {
159
- $this->the_admin_access_token_configure_html( $_GET );
160
- $options = get_option( 'ctf_options' );
161
- $option_checked = ( isset( $options['have_own_tokens'] ) ) ? $options['have_own_tokens'] : false;
162
- ?>
163
- <input name="<?php echo 'ctf_options'.'[have_own_tokens]'; ?>" id="ctf_have_own_tokens" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
164
- <label for="ctf_have_own_tokens" class="ctf_checkbox"><?php _e( 'Or, manually enter my own Twitter app information' ); ?></label>
165
- <span class="ctf-tooltip-wrap">
166
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
167
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Check this box if you would like to manually enter the information from your own <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/?utm_campaign=twitter-free&utm_source=settings&utm_medium=createownapp" target="_blank">Twitter app</a>', 'custom-twitter-feeds' ); ?>.</p>
168
- </span>
169
- <?php
170
- }
171
-
172
- /**
173
- * generates the html for the access token retrieving button
174
- *
175
- * @param $access_token_data array the $_GET data if it exists
176
- */
177
- private function the_admin_access_token_configure_html( $access_token_data ) {
178
- ?>
179
-
180
- <div id="ctf_config">
181
-
182
- <?php if ( isset( $access_token_data['oauth_token'] ) ) : ?>
183
- <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
184
- <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=buttonnotworking" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
185
-
186
- <input type="hidden" id="ctf-retrieved-access-token" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token'] ) ); ?>">
187
- <input type="hidden" id="ctf-retrieved-access-token-secret" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token_secret'] ) ); ?>">
188
- <input type="hidden" id="ctf-retrieved-default-screen-name" value="<?php echo esc_html( sanitize_text_field( $access_token_data['screen_name'] ) ); ?>">
189
-
190
- <?php elseif ( isset( $access_token_data['error'] ) && ! isset( $access_token_data['oauth_token'] ) ) : ?>
191
-
192
- <p class="ctf_error_notice"><?php _e( 'There was an error with retrieving your access tokens. Please <a href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank">use this tool</a> to get your access token and secret.' ); ?></p><br>
193
- <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
194
- <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
195
-
196
- <?php else : ?>
197
-
198
- <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
199
- <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=buttonnotworking" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
200
-
201
- <?php endif; ?>
202
-
203
- </div>
204
- <?php
205
- }
206
-
207
- public function options_page_init()
208
- {
209
- /*
210
- * "Configure" Tab
211
- */
212
-
213
- register_setting(
214
- 'ctf_options', // name of the option that gets called in "get_option()"
215
- 'ctf_options', // matches the options name
216
- array( $this, 'validate_ctf_options' ) // callback function to validate and clean data
217
- );
218
-
219
- add_settings_section(
220
- 'ctf_options_connect', // matches the section name
221
- 'Configuration',
222
- array( $this, 'access_token_button' ), // callback function to explain the section
223
- 'ctf_options_connect' // matches the section name
224
- );
225
-
226
- // Consumer Key
227
- $this->create_settings_field( array(
228
- 'name' => 'consumer_key',
229
- 'title' => '<label for="ctf_consumer_key">Consumer Key</label>', // label for the input field
230
- 'callback' => 'default_text', // name of the function that outputs the html
231
- 'page' => 'ctf_options_connect', // matches the section name
232
- 'section' => 'ctf_options_connect', // matches the section name
233
- 'option' => 'ctf_options', // matches the options name
234
- 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
235
- 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/?utm_campaign=twitter-free&utm_source=settings&utm_medium=createapp" target="_blank">here</a>', // what is this? text
236
- 'size' => '27'
237
- ) );
238
-
239
- // Consumer Secret
240
- $this->create_settings_field( array(
241
- 'name' => 'consumer_secret',
242
- 'title' => '<label for="ctf_consumer_secret">Consumer Secret</label>', // label for the input field
243
- 'callback' => 'default_text', // name of the function that outputs the html
244
- 'page' => 'ctf_options_connect', // matches the section name
245
- 'section' => 'ctf_options_connect', // matches the section name
246
- 'option' => 'ctf_options', // matches the options name
247
- 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
248
- 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/?utm_campaign=twitter-free&utm_source=settings&utm_medium=createapp" target="_blank">here</a>', // what is this? text
249
- 'size' => '57'
250
- ) );
251
-
252
- // Access Token
253
- $this->create_settings_field( array(
254
- 'name' => 'access_token',
255
- 'title' => '<label for="ctf_access_token">Access Token</label>', // label for the input field
256
- 'callback' => 'default_text', // name of the function that outputs the html
257
- 'page' => 'ctf_options_connect', // matches the section name
258
- 'section' => 'ctf_options_connect', // matches the section name
259
- 'option' => 'ctf_options', // matches the options name
260
- 'class' => 'ctf-toggle-access', // class for the wrapper and input field
261
- 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
262
- 'size' => '57'
263
- ) );
264
-
265
- // Access Token Secret
266
- $this->create_settings_field( array(
267
- 'name' => 'access_token_secret',
268
- 'title' => '<label for="ctf_access_token_secret">Access Token Secret</label>', // label for the input field
269
- 'callback' => 'access_token_secret', // name of the function that outputs the html
270
- 'page' => 'ctf_options_connect', // matches the section name
271
- 'section' => 'ctf_options_connect', // matches the section name
272
- 'option' => 'ctf_options', // matches the options name
273
- 'class' => 'ctf-toggle-access', // class for the wrapper and input field
274
- 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
275
- 'size' => '57'
276
- ));
277
-
278
- add_settings_section(
279
- 'ctf_options_feed_settings', // matches the section name
280
- 'Feed Settings',
281
- array( $this, 'general_section_text' ), // callback function to explain the section
282
- 'ctf_options_feed_settings' // matches the section name
283
- );
284
-
285
- $locator_html = '';
286
- if ( CTF_Feed_Locator::count_unique() > 1 ) {
287
- $locator_html .= '<div class="ctf_locations_link">';
288
- $locator_html .= '<a href="?page=custom-twitter-feeds&amp;tab=allfeeds"><svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="search" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-search fa-w-16 fa-2x"><path fill="currentColor" d="M508.5 468.9L387.1 347.5c-2.3-2.3-5.3-3.5-8.5-3.5h-13.2c31.5-36.5 50.6-84 50.6-136C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c52 0 99.5-19.1 136-50.6v13.2c0 3.2 1.3 6.2 3.5 8.5l121.4 121.4c4.7 4.7 12.3 4.7 17 0l22.6-22.6c4.7-4.7 4.7-12.3 0-17zM208 368c-88.4 0-160-71.6-160-160S119.6 48 208 48s160 71.6 160 160-71.6 160-160 160z" class=""></path></svg> ' . __( 'Feed Finder', 'custom-twitter-feeds' ) . '</a>';
289
- $locator_html .= '</div>';
290
- }
291
-
292
- // User Timeline Radio
293
- $this->create_settings_field( array(
294
- 'name' => 'usertimeline',
295
- 'title' => '<label for="ctf_feed_type">Select a Feed Type</label><code class="ctf_shortcode">type
296
- Eg: screenname=gopro
297
- Eg: home=true
298
- Eg: hashtag=#cats</code>' . $locator_html, // label for the input field
299
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
300
- 'page' => 'ctf_options_feed_settings', // matches the section name
301
- 'section' => 'ctf_options_feed_settings', // matches the section name
302
- 'option' => 'ctf_options', // matches the options name
303
- 'class' => 'ctf-radio', // class for the wrapper and input field
304
- 'whatis' => "Select this option and enter any screen name to create a user timeline feed", // what is this? text
305
- 'label' => "User Timeline:",
306
- 'has_input' => true,
307
- 'has_replies' => true
308
- ));
309
-
310
- // Search Radio
311
- $search_label = apply_filters( 'ctf_admin_search_label', '' );
312
- $search_whatis = apply_filters( 'ctf_admin_search_whatis', '' );
313
- $this->create_settings_field( array(
314
- 'name' => 'search',
315
- 'title' => '<label></label>', // label for the input field
316
- 'callback' => 'feed_settings_radio_search', // name of the function that outputs the html
317
- 'page' => 'ctf_options_feed_settings', // matches the section name
318
- 'section' => 'ctf_options_feed_settings', // matches the section name
319
- 'option' => 'ctf_options', // matches the options name
320
- 'class' => 'ctf-radio', // class for the wrapper and input field
321
- 'whatis' => $search_whatis, // what is this? text
322
- 'label' => $search_label,
323
- 'has_input' => true,
324
- 'note_after_input' => __( '(only recent tweets initially)', 'custom-twitter-feeds' ),
325
- 'extra' => true
326
- ) );
327
-
328
- // Home Timeline Radio
329
- $this->create_settings_field( array(
330
- 'name' => 'hometimeline',
331
- 'title' => '<label></label>', // label for the input field
332
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
333
- 'page' => 'ctf_options_feed_settings', // matches the section name
334
- 'section' => 'ctf_options_feed_settings', // matches the section name
335
- 'option' => 'ctf_options', // matches the options name
336
- 'class' => 'ctf-radio', // class for the wrapper and input field
337
- 'whatis' => 'Select this option to display tweets from yourself and those you follow', // what is this? text
338
- 'label' => "Home Timeline",
339
- 'has_input' => false,
340
- 'has_replies' => true
341
- ));
342
-
343
- do_action( 'ctf_admin_endpoints', $this );
344
-
345
- // Number of Tweets
346
- $this->create_settings_field( array(
347
- 'name' => 'num',
348
- 'title' => '<label for="ctf_num">How Many Tweets to Display</label><code class="ctf_shortcode">num
349
- Eg: num=10</code>', // label for the input field
350
- 'callback' => 'default_text', // name of the function that outputs the html
351
- 'page' => 'ctf_options_feed_settings', // matches the section name
352
- 'section' => 'ctf_options_feed_settings', // matches the section name
353
- 'option' => 'ctf_options', // matches the options name
354
- 'class' => 'small-text', // class for the wrapper and input field
355
- 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
356
- 'type' => 'number', // input field "type" attribute
357
- 'default' => 5
358
- ));
359
-
360
- // time unit for cache
361
- $this->create_settings_field( array(
362
- 'name' => 'cache_time',
363
- 'title' => '<label for="ctf_cache_time">How Many Tweets to Display</label>', // label for the input field
364
- 'callback' => 'default_text', // name of the function that outputs the html
365
- 'page' => 'ctf_options_feed_settings', // matches the section name
366
- 'section' => 'ctf_options_feed_settings', // matches the section name
367
- 'option' => 'ctf_options', // matches the options name
368
- 'class' => 'small-text', // class for the wrapper and input field
369
- 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
370
- 'type' => 'number' // input field "type" attribute
371
- ));
372
-
373
- // check for new tweets
374
- $this->create_settings_field( array(
375
- 'name' => 'cache_time',
376
- 'title' => '<label for="ctf_cache_time">Check for new tweets every</label>', // label for the input field
377
- 'callback' => 'cache_time', // name of the function that outputs the html
378
- 'page' => 'ctf_options_feed_settings', // matches the section name
379
- 'section' => 'ctf_options_feed_settings', // matches the section name
380
- 'option' => 'ctf_options', // matches the options name
381
- 'class' => 'short-text', // class for the wrapper and input field
382
- 'whatis' => "Your Tweets are temporarily cached by the plugin in your WordPress database. You can choose how long the posts should be cached for. If you set the time to 1 hour then the plugin will clear the cache after that length of time and check Instagram for posts again" // what is this? text
383
- ) );
384
-
385
- // preserve settings
386
- $this->create_settings_field( array(
387
- 'name' => 'preserve_settings',
388
- 'title' => '<label for="ctf_preserve_settings">Preserve settings when plugin is removed</label>', // label for the input field
389
- 'callback' => 'default_checkbox', // name of the function that outputs the html
390
- 'page' => 'ctf_options_feed_settings', // matches the section name
391
- 'section' => 'ctf_options_feed_settings', // matches the section name
392
- 'option' => 'ctf_options', // matches the options name
393
- 'class' => '',
394
- 'whatis' => "When removing the plugin your settings are automatically erased. Checking this box will prevent any settings from being deleted. This means that you can uninstall and reinstall the plugin without losing your settings"
395
- ));
396
-
397
- /*
398
- * "Customize" tab
399
- */
400
-
401
- add_settings_section(
402
- 'ctf_options_general', // matches the section name
403
- 'General',
404
- array( $this, 'general_section_text' ), // callback function to explain the section
405
- 'ctf_options_general' // matches the section name
406
- );
407
-
408
- // width
409
- $this->create_settings_field( array(
410
- 'name' => 'width',
411
- 'title' => '<label for="ctf_width">Width of Feed</label><code class="ctf_shortcode">width
412
- Eg: width=500</code>', // label for the input field
413
- 'callback' => 'width_and_height_settings', // name of the function that outputs the html
414
- 'page' => 'ctf_options_general', // matches the section name
415
- 'section' => 'ctf_options_general', // matches the section name
416
- 'option' => 'ctf_options', // matches the options name
417
- 'class' => 'small-text',
418
- 'default' => '100',
419
- 'default_unit' => '%'
420
- ));
421
-
422
- // height
423
- $this->create_settings_field( array(
424
- 'name' => 'height',
425
- 'title' => '<label for="ctf_height">Height of Feed</label><code class="ctf_shortcode">height
426
- Eg: height=1000</code>', // label for the input field
427
- 'callback' => 'width_and_height_settings', // name of the function that outputs the html
428
- 'page' => 'ctf_options_general', // matches the section name
429
- 'section' => 'ctf_options_general', // matches the section name
430
- 'option' => 'ctf_options', // matches the options name
431
- 'class' => 'small-text',
432
- 'default_unit' => 'px'
433
- ));
434
-
435
- // class
436
- $this->create_settings_field( array(
437
- 'name' => 'class',
438
- 'title' => '<label for="ctf_class">Add Custom CSS Class</label><code class="ctf_shortcode">class
439
- Eg: class="my-class"</code>', // label for the input field
440
- 'callback' => 'default_text', // name of the function that outputs the html
441
- 'page' => 'ctf_options_general', // matches the section name
442
- 'section' => 'ctf_options_general', // matches the section name
443
- 'option' => 'ctf_options', // matches the options name
444
- 'class' => 'default-text',
445
- 'type' => 'text',
446
- 'whatis' => "You can add your own CSS classes to the feed here. To add multiple classes separate each with a space, Eg. classone classtwo classthree"
447
- ));
448
-
449
- add_settings_section(
450
- 'ctf_options_layout', // matches the section name
451
- 'Layout',
452
- array( $this, 'general_section_text' ), // callback function to explain the section
453
- 'ctf_options_layout' // matches the section name
454
- );
455
-
456
- $settings = get_option( 'ctf_options', array() );
457
- $layout = 'list';
458
- $layout_selections = array(
459
- 'layout' => $layout,
460
- 'carouselcols' => isset( $settings['carouselcols'] ) ? $settings['carouselcols'] : 3,
461
- 'carouselmobilecols' => isset( $settings['carouselmobilecols'] ) ? $settings['carouselmobilecols'] : 1,
462
- 'carouselarrows' => isset( $settings['carouselarrows'] ) ? $settings['carouselarrows'] : 'onhover',
463
- 'carouselpag' => isset( $settings['carouselpag'] ) ? $settings['carouselpag'] : true,
464
- 'carouselheight' => isset( $settings['carouselheight'] ) ? $settings['carouselheight'] : 'tallest',
465
- 'carouselautoplay' => isset( $settings['carouselautoplay'] ) ? $settings['carouselautoplay'] : false,
466
- 'carouseltime' => isset( $settings['carouseltime'] ) ? $settings['carouseltime'] : '5000',
467
- 'carouselloop' => isset( $settings['carouselloop'] ) ? $settings['carouselloop'] : 'infinite',
468
- 'masonrycols' => isset( $settings['masonrycols'] ) ? $settings['masonrycols'] : 3,
469
- 'masonrymobilecols' => isset( $settings['masonrymobilecols'] ) ? $settings['masonrymobilecols'] : 1,
470
- );
471
-
472
- $this->create_settings_field( array(
473
- 'name' => 'class',
474
- 'title' => '<label for="ctf_layout">Layout Type</label><code class="ctf_shortcode">layout
475
- Eg: layout="masonry"</code>', // label for the input field
476
- 'callback' => 'layout', // name of the function that outputs the html
477
- 'page' => 'ctf_options_layout', // matches the section name
478
- 'section' => 'ctf_options_layout', // matches the section name
479
- 'option' => 'ctf_options', // matches the options name
480
- 'class' => 'default-text ctf_pro',
481
- 'type' => 'text',
482
- 'layout_selections' => $layout_selections,
483
- 'whatis' => ""
484
- ));
485
-
486
- add_settings_section(
487
- 'ctf_options_showandhide', // matches the section name
488
- 'Show/Hide',
489
- array( $this, 'general_section_text' ), // callback function to explain the section
490
- 'ctf_options_showandhide' // matches the section name
491
- );
492
-
493
- // show/hide
494
- $show_hide_list = array(
495
- array( 'include_retweeter', 'Retweeted text' ),
496
- array( 'include_avatar', 'Avatar image' ),
497
- array( 'include_author', 'Author name' ),
498
- array( 'include_logo', 'Twitter logo' ),
499
- array( 'include_text', 'Tweet text' ),
500
- array( 'include_media_placeholder', 'Media placeholder' ),
501
- array( 'include_date', 'Date' ),
502
- array( 'include_actions', 'Tweet actions (reply, retweet, like)' ),
503
- array( 'include_twitterlink', '"Twitter" link' ),
504
- array( 'include_linkbox', 'Quoted tweet box' )
505
- );
506
- $show_hide_list = apply_filters( 'ctf_admin_show_hide_list', $show_hide_list );
507
-
508
- $this->create_settings_field( array(
509
- 'name' => 'showandhide',
510
- 'title' => '<label>Include the Following in Tweets <em>(when applicable)</em></label><code class="ctf_shortcode">include exclude
511
- Eg: include=author,date
512
- Eg: exclude=actions
513
- Options: avatar, author,
514
- logo, text, placeholder,
515
- date, actions, linkbox </code>', // label for the input field
516
- 'callback' => 'include_exclude_checkbox', // name of the function that outputs the html
517
- 'page' => 'ctf_options_showandhide', // matches the section name
518
- 'section' => 'ctf_options_showandhide', // matches the section name
519
- 'option' => 'ctf_options', // matches the options name
520
- 'fields' => $show_hide_list,
521
- 'class' => ''
522
- ));
523
-
524
- // show header
525
- $this->create_settings_field( array(
526
- 'name' => 'showheader',
527
- 'title' => '<label for="ctf_showheader">Show Header</label><code class="ctf_shortcode">showheader
528
- Eg: showheader=true</code>', // label for the input field
529
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
530
- 'page' => 'ctf_options_showandhide', // matches the section name
531
- 'section' => 'ctf_options_showandhide', // matches the section name
532
- 'option' => 'ctf_options', // matches the options name
533
- 'class' => '',
534
- 'whatis' => "The header is displayed above your tweets with some basic information about the feed"
535
- ));
536
-
537
- // load more button
538
- $this->create_settings_field( array(
539
- 'name' => 'showbutton',
540
- 'title' => '<label for="ctf_showbutton">Show the "Load More" Button</label><code class="ctf_shortcode">showbutton
541
- Eg: showbutton=true</code>', // label for the input field
542
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
543
- 'page' => 'ctf_options_showandhide', // matches the section name
544
- 'section' => 'ctf_options_showandhide', // matches the section name
545
- 'option' => 'ctf_options', // matches the options name
546
- 'class' => '',
547
- 'whatis' => "Show the Load More Button",
548
- ));
549
-
550
-
551
- // credit ctf
552
- $this->create_settings_field( array(
553
- 'name' => 'creditctf',
554
- 'title' => '<label for="ctf_creditctf">Add Custom Twitter Feeds Credit</label><code class="ctf_shortcode">creditctf
555
- Eg: creditctf=true</code>', // label for the input field
556
- 'callback' => 'default_checkbox', // name of the function that outputs the html
557
- 'page' => 'ctf_options_showandhide', // matches the section name
558
- 'section' => 'ctf_options_showandhide', // matches the section name
559
- 'option' => 'ctf_options', // matches the options name
560
- 'class' => '',
561
- 'whatis' => "Help us keep this plugin great! Add a link below your feed to credit Custom Twitter Feeds by Smash Balloon"
562
- ));
563
-
564
- do_action( 'ctf_admin_customize_option', $this );
565
-
566
- add_settings_section(
567
- 'ctf_options_misc', // matches the section name
568
- 'Misc',
569
- array( $this, 'general_section_text' ), // callback function to explain the section
570
- 'ctf_options_misc' // matches the section name
571
- );
572
-
573
- // Custom CSS
574
- $this->create_settings_field( array(
575
- 'name' => 'custom_css',
576
- 'title' => '<label for="ctf_custom_css">Custom CSS</label>', // label for the input field
577
- 'callback' => 'custom_code', // name of the function that outputs the html
578
- 'page' => 'ctf_options_misc', // matches the section name
579
- 'section' => 'ctf_options_misc', // matches the section name
580
- 'option' => 'ctf_options', // matches the options name
581
- 'class' => 'default-text', // class for the wrapper and input field
582
- 'description' => 'Enter your own custom CSS in the box below'
583
- ));
584
-
585
- // Custom JS
586
- $this->create_settings_field( array(
587
- 'name' => 'custom_js',
588
- 'title' => '<label for="ctf_custom_js">Custom Javascript*</label>', // label for the input field
589
- 'callback' => 'custom_code', // name of the function that outputs the html
590
- 'page' => 'ctf_options_misc', // matches the section name
591
- 'section' => 'ctf_options_misc', // matches the section name
592
- 'option' => 'ctf_options', // matches the options name
593
- 'class' => 'default-text', // class for the wrapper and input field
594
- 'description' => 'Enter your own custom Javascript/JQuery in the box below',
595
- 'extra' => '*will be fired every time more tweets are loaded'
596
- ));
597
-
598
- add_settings_section(
599
- 'ctf_options_gdpr', // matches the section name
600
- 'GDPR',
601
- array( $this, 'general_section_text' ), // callback function to explain the section
602
- 'ctf_options_gdpr' // matches the section name
603
- );
604
-
605
- $this->create_settings_field( array(
606
- 'name' => 'gdpr',
607
- 'title' => '<label for="ctf_gdpr">Enable GDPR Settings</label>', // label for the input field
608
- 'callback' => 'gdpr', // name of the function that outputs the html
609
- 'page' => 'ctf_options_gdpr', // matches the section name
610
- 'section' => 'ctf_options_gdpr', // matches the section name
611
- 'option' => 'ctf_options', // matches the options name
612
- 'class' => 'default-text', // class for the wrapper and input field
613
- ));
614
-
615
- add_settings_section(
616
- 'ctf_options_advanced', // matches the section name
617
- 'Advanced',
618
- array( $this, 'general_section_text' ), // callback function to explain the section
619
- 'ctf_options_advanced' // matches the section name
620
- );
621
-
622
- // ajax theme
623
- $this->create_settings_field( array(
624
- 'name' => 'ajax_theme',
625
- 'title' => '<label for="ctf_ajax_theme">Are you using an Ajax powered theme?</label>', // label for the input field
626
- 'callback' => 'default_checkbox', // name of the function that outputs the html
627
- 'page' => 'ctf_options_advanced', // matches the section name
628
- 'section' => 'ctf_options_advanced', // matches the section name
629
- 'option' => 'ctf_options', // matches the options name
630
- 'class' => '',
631
- 'whatis' => "When navigating your site, if your theme uses Ajax to load content into your pages (meaning your page doesn't refresh) then check this setting. If you're not sure then please check with the theme author"
632
- ));
633
-
634
- // Request Method
635
- $this->create_settings_field( array(
636
- 'name' => 'request_method',
637
- 'title' => '<label for="ctf_request_method">Request Method</label>', // label for the input field
638
- 'callback' => 'default_select', // name of the function that outputs the html
639
- 'page' => 'ctf_options_advanced', // matches the section name
640
- 'section' => 'ctf_options_advanced', // matches the section name
641
- 'option' => 'ctf_options', // matches the options name
642
- 'class' => 'default-text', // class for the wrapper and input field
643
- 'fields' => array(
644
- 1 => array( 'auto', 'Auto' ),
645
- 2 => array( 'curl', 'cURL' ),
646
- 3 => array( 'file_get_contents', 'file_get_contents()' ),
647
- 4 => array( 'wp_http', 'WP_Http' )
648
- ),
649
- 'whatis' => "Explicitly set the request method. You would only want to change this if you are unable to connect to the Twitter API" // what is this? text
650
- ) );
651
-
652
- // force cache to clear on interval
653
- $this->create_settings_field( array(
654
- 'name' => 'cron_cache_clear',
655
- 'title' => '<label for="ctf_cron_cache_clear">Force cache to clear on interval</label>', // label for the input field
656
- 'callback' => 'default_select', // name of the function that outputs the html
657
- 'page' => 'ctf_options_advanced', // matches the section name
658
- 'section' => 'ctf_options_advanced', // matches the section name
659
- 'option' => 'ctf_options', // matches the options name
660
- 'class' => 'default-text', // class for the wrapper and input field
661
- 'fields' => array(
662
- 1 => array( 'unset', '-' ),
663
- 2 => array( 'yes', 'Yes' ),
664
- 3 => array( 'no', 'No' )
665
- ),
666
- 'whatis' => "If you're experiencing an issue with the plugin not auto-updating then you can set this to 'Yes' to run a scheduled event behind the scenes which forces the plugin cache to clear on a regular basis and retrieve new data from Twitter" // what is this? text
667
- ) );
668
-
669
- // tweet multiplier
670
- $this->create_settings_field( array(
671
- 'name' => 'multiplier',
672
- 'title' => '<label for="ctf_multiplier">Tweet Multiplier</label><code class="ctf_shortcode">multiplier
673
- Eg: multiplier=1.5</code>', // label for the input field
674
- 'callback' => 'default_text', // name of the function that outputs the html
675
- 'page' => 'ctf_options_advanced', // matches the section name
676
- 'section' => 'ctf_options_advanced', // matches the section name
677
- 'option' => 'ctf_options', // matches the options name
678
- 'class' => 'small-text', // class for the wrapper and input field
679
- 'whatis' => "If your feed excludes reply tweets (this is automatic in hashtag/search feeds), the correct number of tweets may not show up. Increasing this number will increase the number of tweets retrieved but will also increase the load time for the feed as well", // what is this? text
680
- 'type' => 'number', // input field "type" attribute
681
- 'min' => 1,
682
- 'max' => 3,
683
- 'step' => 'any',
684
- 'default' => 1.25
685
- ));
686
-
687
- $this->create_settings_field( array(
688
- 'name' => 'persistent',
689
- 'title' => '<label for="ctf_multiplier">Clear Persistent Cache</label>', // label for the input field
690
- 'callback' => 'clear_persistent_cache_button', // name of the function that outputs the html
691
- 'page' => 'ctf_options_advanced', // matches the section name
692
- 'section' => 'ctf_options_advanced', // matches the section name
693
- 'option' => 'ctf_options', // matches the options name
694
- 'class' => 'small-text' // class for the wrapper and input field
695
- ));
696
-
697
- // persistent cache
698
- $this->create_settings_field( array(
699
- 'name' => 'persistentcache',
700
- 'title' => '<label for="ctf_persistentcache">Persistent cache enabled by default</label><code class="ctf_shortcode">persistentcache
701
- Eg: persistentcache=false</code>', // label for the input field
702
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
703
- 'page' => 'ctf_options_advanced', // matches the section name
704
- 'section' => 'ctf_options_advanced', // matches the section name
705
- 'option' => 'ctf_options', // matches the options name
706
- 'class' => '',
707
- 'whatis' => "Checking this box will make all Search and Hashtag feeds have a permanent cache saved in the database by default of up to 150 tweets. Tweets will be available for the feed even after the 7 day limit though numbers of retweets and likes will not update"
708
- ));
709
-
710
- $this->create_settings_field( array(
711
- 'name' => 'selfreplies',
712
- 'title' => '<label for="ctf_selfreplies">Always include replies to self in the feed</label><code class="ctf_shortcode">autores
713
- Eg: selfreplies=true</code>', // label for the input field
714
- 'callback' => 'default_checkbox', // name of the function that outputs the html
715
- 'page' => 'ctf_options_advanced', // matches the section name
716
- 'section' => 'ctf_options_advanced', // matches the section name
717
- 'option' => 'ctf_options', // matches the options name
718
- 'class' => '',
719
- 'whatis' => "Twitter considers @mentions of your own account and replies to your own account's tweets as \"reply\" tweets. Enable this setting to include these type of tweets in your feed"
720
- ));
721
-
722
- $this->create_settings_field( array(
723
- 'name' => 'disableintents',
724
- 'title' => '<label for="ctf_disableintents">Disable Twitter intents JS</label><code class="ctf_shortcode">disableintents
725
- Eg: disableintents=true</code>', // label for the input field
726
- 'callback' => 'default_checkbox', // name of the function that outputs the html
727
- 'page' => 'ctf_options_advanced', // matches the section name
728
- 'section' => 'ctf_options_advanced', // matches the section name
729
- 'option' => 'ctf_options', // matches the options name
730
- 'class' => '',
731
- 'whatis' => "Twitter provides JavaScript that allows visitors of your site to reply to, retweet, and like tweets without leaving your site. This can be disabled using this setting"
732
- ));
733
-
734
- $this->create_settings_field( array(
735
- 'name' => 'shorturls',
736
- 'title' => '<label for="ctf_shorturls">Use shortened urls</label>', // label for the input field
737
- 'callback' => 'default_checkbox', // name of the function that outputs the html
738
- 'page' => 'ctf_options_advanced', // matches the section name
739
- 'section' => 'ctf_options_advanced', // matches the section name
740
- 'option' => 'ctf_options', // matches the options name
741
- 'class' => '',
742
- 'whatis' => "Twitter provides shortened versions of links in tweets. Enable this setting to use them instead of the full URLs"
743
- ));
744
-
745
- /**
746
- * "Style" tab
747
- */
748
-
749
- add_settings_section(
750
- 'ctf_options_general_style', // matches the section name
751
- 'General',
752
- array( $this, 'general_section_text' ), // callback function to explain the section
753
- 'ctf_options_general_style' // matches the section name
754
- );
755
-
756
- // background color
757
- $this->create_settings_field( array(
758
- 'name' => 'bgcolor',
759
- 'title' => '<label for="ctf_bgcolor">Feed Background Color</label><code class="ctf_shortcode">bgcolor
760
- Eg: bgcolor=#eee</code>', // label for the input field
761
- 'callback' => 'default_color', // name of the function that outputs the html
762
- 'page' => 'ctf_options_general_style', // matches the section name
763
- 'section' => 'ctf_options_general_style', // matches the section name
764
- 'option' => 'ctf_options', // matches the options name
765
- 'class' => '',
766
- 'whatis' => "The background color of the feed"
767
- ));
768
-
769
- // tweet background color
770
- $this->create_settings_field( array(
771
- 'name' => 'tweetbgcolor',
772
- 'title' => '<label for="ctf_tweetbgcolor">Tweet Background Color</label><code class="ctf_shortcode">tweetbgcolor
773
- Eg: tweetbgcolor=#eee</code>', // label for the input field
774
- 'callback' => 'default_color', // name of the function that outputs the html
775
- 'page' => 'ctf_options_general_style', // matches the section name
776
- 'section' => 'ctf_options_general_style', // matches the section name
777
- 'option' => 'ctf_options', // matches the options name
778
- 'class' => '',
779
- 'whatis' => "The background color of each tweet"
780
- ));
781
-
782
- add_settings_section(
783
- 'ctf_options_header', // matches the section name
784
- 'Header',
785
- array( $this, 'general_section_text' ), // callback function to explain the section
786
- 'ctf_options_header' // matches the section name
787
- );
788
-
789
- // show bio
790
- $this->create_settings_field( array(
791
- 'name' => 'showbio',
792
- 'title' => '<label for="ctf_showbio">Show Bio</label><code class="ctf_shortcode">showbio
793
- Eg: showbio=false</code>', // label for the input field
794
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
795
- 'page' => 'ctf_options_header', // matches the section name
796
- 'section' => 'ctf_options_header', // matches the section name
797
- 'option' => 'ctf_options', // matches the options name
798
- 'class' => 'default-text', // class for the wrapper and input field
799
- 'whatis' => "Show the bio text description on the header of the feed"
800
- ));
801
-
802
- // header background color
803
- $this->create_settings_field( array(
804
- 'name' => 'headerbgcolor',
805
- 'title' => '<label for="ctf_headerbgcolor">Header Background Color</label><code class="ctf_shortcode">headerbgcolor
806
- Eg: headerbgcolor=#ee0</code>', // label for the input field
807
- 'callback' => 'default_color', // name of the function that outputs the html
808
- 'page' => 'ctf_options_header', // matches the section name
809
- 'section' => 'ctf_options_header', // matches the section name
810
- 'option' => 'ctf_options', // matches the options name
811
- 'class' => ''
812
- ));
813
-
814
- // header text color
815
- $this->create_settings_field( array(
816
- 'name' => 'headertextcolor',
817
- 'title' => '<label for="ctf_headertextcolor">Header Text Color</label><code class="ctf_shortcode">headertextcolor
818
- Eg: headertextcolor=#444</code>', // label for the input field
819
- 'callback' => 'default_color', // name of the function that outputs the html
820
- 'page' => 'ctf_options_header', // matches the section name
821
- 'section' => 'ctf_options_header', // matches the section name
822
- 'option' => 'ctf_options', // matches the options name
823
- 'class' => ''
824
- ));
825
-
826
-
827
- // custom header text
828
- $this->create_settings_field( array(
829
- 'name' => 'headertext',
830
- 'title' => '<label for="ctf_headertext">Custom Header Text</label><code class="ctf_shortcode">headertext
831
- Eg: headertext="Tweets from @SmashBalloon"</code>', // label for the input field
832
- 'callback' => 'default_text', // name of the function that outputs the html
833
- 'page' => 'ctf_options_header', // matches the section name
834
- 'section' => 'ctf_options_header', // matches the section name
835
- 'option' => 'ctf_options', // matches the options name
836
- 'class' => 'default-text', // class for the wrapper and input field
837
- 'whatis' => 'This will replace the default text displayed inside the optional header of the feed' // "what is this?" text
838
- ));
839
-
840
- add_settings_section(
841
- 'ctf_options_date', // matches the section name
842
- 'Date',
843
- array( $this, 'general_section_text' ), // callback function to explain the section
844
- 'ctf_options_date' // matches the section name
845
- );
846
-
847
- // Timezone
848
- $this->create_settings_field( array(
849
- 'name' => 'timezone',
850
- 'title' => '<label for="ctf_timezone">Timezone</label>', // label for the input field
851
- 'callback' => 'feed_settings_timezone', // name of the function that outputs the html
852
- 'page' => 'ctf_options_date', // matches the section name
853
- 'section' => 'ctf_options_date', // matches the section name
854
- 'option' => 'ctf_options', // matches the options name
855
- 'class' => 'default-text', // class for the wrapper and input field
856
- 'whatis' => "Select a timezone for displaying date and timestamps of tweets" // what is this? text
857
- ));
858
-
859
- // Date Format
860
- $this->create_settings_field( array(
861
- 'name' => 'dateformat',
862
- 'title' => '<label for="ctf_date_format">Date Format</label><code class="ctf_shortcode">dateformat
863
- Eg: dateformat=3</code>', // label for the input field
864
- 'callback' => 'customize_date_format', // name of the function that outputs the html
865
- 'page' => 'ctf_options_date', // matches the section name
866
- 'section' => 'ctf_options_date', // matches the section name
867
- 'option' => 'ctf_options', // matches the options name
868
- 'class' => 'default-text', // class for the wrapper and input field
869
- 'whatis' => "Select the format you would like for dates in tweets" // what is this? text
870
- ));
871
-
872
- // Custom Date Format
873
- $this->create_settings_field( array(
874
- 'name' => 'datecustom',
875
- 'title' => '<label for="ctf_custom_date_format">Custom Format</label><code class="ctf_shortcode">datecustom
876
- Eg: datecustom="D M jS, Y"</code>', // label for the input field
877
- 'callback' => 'customize_custom_date_format', // name of the function that outputs the html
878
- 'page' => 'ctf_options_date', // matches the section name
879
- 'section' => 'ctf_options_date', // matches the section name
880
- 'option' => 'ctf_options', // matches the options name
881
- 'class' => 'default-text', // class for the wrapper and input field
882
- ));
883
-
884
- // Custom Time Translations
885
- $this->create_settings_field( array(
886
- 'name' => 'custom_time_translations',
887
- 'title' => '<label>Custom Time Translations</label><code class="ctf_shortcode">mtime, htime,
888
- nowtime
889
- Eg: mtime="M"
890
- Eg: htime="S"
891
- Eg: nowtime="Jetzt"</code>', // label for the input field
892
- 'callback' => 'customize_custom_time_translations', // name of the function that outputs the html
893
- 'page' => 'ctf_options_date', // matches the section name
894
- 'section' => 'ctf_options_date', // matches the section name
895
- 'option' => 'ctf_options', // matches the options name
896
- 'class' => 'default-text', // class for the wrapper and input field
897
- ));
898
-
899
- // date Text Size
900
- $this->create_settings_field( array(
901
- 'name' => 'datetextsize',
902
- 'title' => '<label for="ctf_datetextsize">Date Text Size</label><code class="ctf_shortcode">datetextsize
903
- Eg: datetextsize=16</code>', // label for the input field
904
- 'callback' => 'text_size', // name of the function that outputs the html
905
- 'page' => 'ctf_options_date', // matches the section name
906
- 'section' => 'ctf_options_date', // matches the section name
907
- 'option' => 'ctf_options', // matches the options name
908
- 'class' => 'default-text', // class for the wrapper and input field
909
- ));
910
-
911
- // date text weight
912
- $this->create_settings_field( array(
913
- 'name' => 'datetextweight',
914
- 'title' => '<label for="ctf_datetextweight">Date Text Weight</label><code class="ctf_shortcode">datetextweight
915
- Eg: datetextweight=bold</code>', // label for the input field
916
- 'callback' => 'text_weight', // name of the function that outputs the html
917
- 'page' => 'ctf_options_date', // matches the section name
918
- 'section' => 'ctf_options_date', // matches the section name
919
- 'option' => 'ctf_options', // matches the options name
920
- 'class' => '',
921
- ));
922
-
923
- add_settings_section(
924
- 'ctf_options_author', // matches the section name
925
- 'Author',
926
- array( $this, 'general_section_text' ), // callback function to explain the section
927
- 'ctf_options_author' // matches the section name
928
- );
929
-
930
- // Author Text Size
931
- $this->create_settings_field( array(
932
- 'name' => 'authortextsize',
933
- 'title' => '<label for="ctf_authortextsize">Author Text Size</label><code class="ctf_shortcode">authortextsize
934
- Eg: authortextsize=16</code>', // label for the input field
935
- 'callback' => 'text_size', // name of the function that outputs the html
936
- 'page' => 'ctf_options_author', // matches the section name
937
- 'section' => 'ctf_options_author', // matches the section name
938
- 'option' => 'ctf_options', // matches the options name
939
- 'class' => 'default-text', // class for the wrapper and input field
940
- ));
941
-
942
- // author text weight
943
- $this->create_settings_field( array(
944
- 'name' => 'authortextweight',
945
- 'title' => '<label for="ctf_authortextcolor">Author Text Weight</label><code class="ctf_shortcode">authortextweight
946
- Eg: authortextweight=bold</code>', // label for the input field
947
- 'callback' => 'text_weight', // name of the function that outputs the html
948
- 'page' => 'ctf_options_author', // matches the section name
949
- 'section' => 'ctf_options_author', // matches the section name
950
- 'option' => 'ctf_options', // matches the options name
951
- 'class' => '',
952
- ));
953
-
954
- $this->create_settings_field( array(
955
- 'name' => 'logosize',
956
- 'title' => '<label for="ctf_logotextsize">Twitter Logo Size</label><code class="ctf_shortcode">logosize
957
- Eg: logosize=16</code>', // label for the input field
958
- 'callback' => 'text_size', // name of the function that outputs the html
959
- 'page' => 'ctf_options_author', // matches the section name
960
- 'section' => 'ctf_options_author', // matches the section name
961
- 'option' => 'ctf_options', // matches the options name
962
- 'class' => 'default-text', // class for the wrapper and input field
963
- ));
964
-
965
- $this->create_settings_field( array(
966
- 'name' => 'logocolor',
967
- 'title' => '<label for="ctf_logocolor">Logo Color</label><code class="ctf_shortcode">logocolor
968
- Eg: logocolor=#333</code>', // label for the input field
969
- 'callback' => 'default_color', // name of the function that outputs the html
970
- 'page' => 'ctf_options_author', // matches the section name
971
- 'section' => 'ctf_options_author', // matches the section name
972
- 'option' => 'ctf_options', // matches the options name
973
- 'class' => '',
974
- ));
975
-
976
- add_settings_section(
977
- 'ctf_options_text', // matches the section name
978
- 'Tweet Text',
979
- array( $this, 'general_section_text' ), // callback function to explain the section
980
- 'ctf_options_text' // matches the section name
981
- );
982
-
983
- // Tweet Text Size
984
- $this->create_settings_field( array(
985
- 'name' => 'tweettextsize',
986
- 'title' => '<label for="ctf_tweettextsize">Tweet Text Size</label><code class="ctf_shortcode">tweettextsize
987
- Eg: tweettextsize=16</code>', // label for the input field
988
- 'callback' => 'text_size', // name of the function that outputs the html
989
- 'page' => 'ctf_options_text', // matches the section name
990
- 'section' => 'ctf_options_text', // matches the section name
991
- 'option' => 'ctf_options', // matches the options name
992
- 'class' => 'default-text', // class for the wrapper and input field
993
- ));
994
-
995
- // tweet text weight
996
- $this->create_settings_field( array(
997
- 'name' => 'tweettextweight',
998
- 'title' => '<label for="ctf_tweettextweight">Tweet Text Weight</label><code class="ctf_shortcode">tweettextweight
999
- Eg: tweettextweight=bold</code>', // label for the input field
1000
- 'callback' => 'text_weight', // name of the function that outputs the html
1001
- 'page' => 'ctf_options_text', // matches the section name
1002
- 'section' => 'ctf_options_text', // matches the section name
1003
- 'option' => 'ctf_options', // matches the options name
1004
- 'class' => '',
1005
- ));
1006
-
1007
- // text color
1008
- $this->create_settings_field( array(
1009
- 'name' => 'textcolor',
1010
- 'title' => '<label for="ctf_textcolor">Text Color</label><code class="ctf_shortcode">textcolor
1011
- Eg: textcolor=#333</code>', // label for the input field
1012
- 'callback' => 'default_color', // name of the function that outputs the html
1013
- 'page' => 'ctf_options_text', // matches the section name
1014
- 'section' => 'ctf_options_text', // matches the section name
1015
- 'option' => 'ctf_options', // matches the options name
1016
- 'class' => '',
1017
- ));
1018
-
1019
- $this->create_settings_field( array(
1020
- 'name' => 'textlength',
1021
- 'title' => '<label for="ctf_textlength">Text Length</label><code class="ctf_shortcode">textlength
1022
- Eg: textlength=150</code>', // label for the input field
1023
- 'callback' => 'default_text', // name of the function that outputs the html
1024
- 'page' => 'ctf_options_text', // matches the section name
1025
- 'section' => 'ctf_options_text', // matches the section name
1026
- 'option' => 'ctf_options', // matches the options name
1027
- 'class' => '',
1028
- 'default' => 280,
1029
- 'min' => 20,
1030
- 'max' => 280,
1031
- 'step' => 1,
1032
- 'example' => 'characters',
1033
- 'type' => 'number',
1034
- 'whatis' => 'The number of characters of text to display in the tweet text. An ellipsis link will be added to allow the user to reveal more text if desired',
1035
- ));
1036
-
1037
- // custom retweeted text
1038
- $this->create_settings_field( array(
1039
- 'name' => 'retweetedtext',
1040
- 'title' => '<label for="ctf_retweetedtext">Translation for "Retweeted"</label><code class="ctf_shortcode">retweetedtext
1041
- Eg: retweetedtext="retuiteó"</code>', // label for the input field
1042
- 'callback' => 'default_text', // name of the function that outputs the html
1043
- 'page' => 'ctf_options_text', // matches the section name
1044
- 'section' => 'ctf_options_text', // matches the section name
1045
- 'option' => 'ctf_options', // matches the options name
1046
- 'class' => 'default-text', // class for the wrapper and input field
1047
- 'whatis' => 'This will replace the default text displayed for retweeted texts',
1048
- 'default' => 'Retweeted'// "what is this?" text
1049
- ));
1050
-
1051
- add_settings_section(
1052
- 'ctf_options_links', // matches the section name
1053
- 'Links',
1054
- array( $this, 'general_section_text' ), // callback function to explain the section
1055
- 'ctf_options_links' // matches the section name
1056
- );
1057
-
1058
- // disable links
1059
- $this->create_settings_field( array(
1060
- 'name' => 'disablelinks',
1061
- 'title' => '<label for="ctf_disablelinks">Disable Links in Tweet Text</label><code class="ctf_shortcode">disablelinks
1062
- Eg: disablelinks=true</code>', // label for the input field
1063
- 'callback' => 'default_checkbox', // name of the function that outputs the html
1064
- 'page' => 'ctf_options_links', // matches the section name
1065
- 'section' => 'ctf_options_links', // matches the section name
1066
- 'option' => 'ctf_options', // matches the options name
1067
- 'class' => '',
1068
- 'whatis' => "By default, links, hashtags, and mentions are turned into links inside the tweet text"
1069
- ));
1070
-
1071
- // link text to twitter
1072
- $this->create_settings_field( array(
1073
- 'name' => 'linktexttotwitter',
1074
- 'title' => '<label for="ctf_linktexttotwitter">Link Tweet Text to Twitter</label><code class="ctf_shortcode">linktexttotwitter
1075
- Eg: linktexttotwitter=true</code>', // label for the input field
1076
- 'callback' => 'default_checkbox', // name of the function that outputs the html
1077
- 'page' => 'ctf_options_links', // matches the section name
1078
- 'section' => 'ctf_options_links', // matches the section name
1079
- 'option' => 'ctf_options', // matches the options name
1080
- 'class' => '',
1081
- 'whatis' => "Clicking on the text of the tweet will link to the tweet on Twitter"
1082
- ));
1083
-
1084
- // link text color
1085
- $this->create_settings_field( array(
1086
- 'name' => 'linktextcolor',
1087
- 'title' => '<label for="ctf_linktextcolor">Links in Tweets Text Color</label><code class="ctf_shortcode">linktextcolor
1088
- Eg: linktextcolor=#00e</code>', // label for the input field
1089
- 'callback' => 'default_color', // name of the function that outputs the html
1090
- 'page' => 'ctf_options_links', // matches the section name
1091
- 'section' => 'ctf_options_links', // matches the section name
1092
- 'option' => 'ctf_options', // matches the options name
1093
- 'class' => '',
1094
- ));
1095
-
1096
- add_settings_section(
1097
- 'ctf_options_quoted', // matches the section name
1098
- 'Retweet Boxes',
1099
- array( $this, 'general_section_text' ), // callback function to explain the section
1100
- 'ctf_options_quoted' // matches the section name
1101
- );
1102
-
1103
- // quoted author Size
1104
- $this->create_settings_field( array(
1105
- 'name' => 'quotedauthorsize',
1106
- 'title' => '<label for="ctf_quotedauthorsize">Quoted Author Size</label><code class="ctf_shortcode">quotedauthorsize
1107
- Eg: quotedauthorsize=16</code>', // label for the input field
1108
- 'callback' => 'text_size', // name of the function that outputs the html
1109
- 'page' => 'ctf_options_quoted', // matches the section name
1110
- 'section' => 'ctf_options_quoted', // matches the section name
1111
- 'option' => 'ctf_options', // matches the options name
1112
- 'class' => 'default-text', // class for the wrapper and input field
1113
- ));
1114
-
1115
- // quoted author weight
1116
- $this->create_settings_field( array(
1117
- 'name' => 'quotedauthorweight',
1118
- 'title' => '<label for="ctf_quotedauthorweight">Quoted Author Weight</label><code class="ctf_shortcode">quotedauthorweight
1119
- Eg: quotedauthorweight=bold</code>', // label for the input field
1120
- 'callback' => 'text_weight', // name of the function that outputs the html
1121
- 'page' => 'ctf_options_quoted', // matches the section name
1122
- 'section' => 'ctf_options_quoted', // matches the section name
1123
- 'option' => 'ctf_options', // matches the options name
1124
- 'class' => '',
1125
- ));
1126
-
1127
- add_settings_section(
1128
- 'ctf_options_actions', // matches the section name
1129
- 'Tweets Actions',
1130
- array( $this, 'general_section_text' ), // callback function to explain the section
1131
- 'ctf_options_actions' // matches the section name
1132
- );
1133
-
1134
- // icon Size
1135
- $this->create_settings_field( array(
1136
- 'name' => 'iconsize',
1137
- 'title' => '<label for="ctf_iconsize">Icon Size</label><code class="ctf_shortcode">iconsize
1138
- Eg: iconsize=16</code>', // label for the input field
1139
- 'callback' => 'text_size', // name of the function that outputs the html
1140
- 'page' => 'ctf_options_actions', // matches the section name
1141
- 'section' => 'ctf_options_actions', // matches the section name
1142
- 'option' => 'ctf_options', // matches the options name
1143
- 'class' => 'default-text', // class for the wrapper and input field
1144
- ));
1145
-
1146
- // icon color
1147
- $this->create_settings_field( array(
1148
- 'name' => 'iconcolor',
1149
- 'title' => '<label for="ctf_iconcolor">Icon Color</label><code class="ctf_shortcode">iconcolor
1150
- Eg: iconcolor=green</code>', // label for the input field
1151
- 'callback' => 'default_color', // name of the function that outputs the html
1152
- 'page' => 'ctf_options_actions', // matches the section name
1153
- 'section' => 'ctf_options_actions', // matches the section name
1154
- 'option' => 'ctf_options', // matches the options name
1155
- 'class' => '',
1156
- ));
1157
-
1158
-
1159
- // view on twitter text
1160
- $this->create_settings_field( array(
1161
- 'name' => 'twitterlinktext',
1162
- 'title' => '<label for="ctf_twitterlinktext">Custom Text for "Twitter" Link</label><code class="ctf_shortcode">twitterlinktext
1163
- Eg: twitterlinktext="View this Tweet"</code>', // label for the input field
1164
- 'callback' => 'default_text', // name of the function that outputs the html
1165
- 'page' => 'ctf_options_actions', // matches the section name
1166
- 'section' => 'ctf_options_actions', // matches the section name
1167
- 'option' => 'ctf_options', // matches the options name
1168
- 'class' => 'default-text', // class for the wrapper and input field
1169
- 'default' => 'Twitter'
1170
- ));
1171
-
1172
- add_settings_section(
1173
- 'ctf_options_load', // matches the section name
1174
- '"Load More" Button',
1175
- array( $this, 'general_section_text' ), // callback function to explain the section
1176
- 'ctf_options_load' // matches the section name
1177
- );
1178
-
1179
- // button background color
1180
- $this->create_settings_field( array(
1181
- 'name' => 'buttoncolor',
1182
- 'title' => '<label for="ctf_buttoncolor">Button Background Color</label><code class="ctf_shortcode">buttoncolor
1183
- Eg: buttoncolor=#f33</code>', // label for the input field
1184
- 'callback' => 'default_color', // name of the function that outputs the html
1185
- 'page' => 'ctf_options_load', // matches the section name
1186
- 'section' => 'ctf_options_load', // matches the section name
1187
- 'option' => 'ctf_options', // matches the options name
1188
- 'class' => '',
1189
- 'whatis' => "The color of the background of the load more button"
1190
- ));
1191
-
1192
- // button text color
1193
- $this->create_settings_field( array(
1194
- 'name' => 'buttontextcolor',
1195
- 'title' => '<label for="ctf_buttontextcolor">Button Text Color</label><code class="ctf_shortcode">buttontextcolor
1196
- Eg: buttontextcolor=#444</code>', // label for the input field
1197
- 'callback' => 'default_color', // name of the function that outputs the html
1198
- 'page' => 'ctf_options_load', // matches the section name
1199
- 'section' => 'ctf_options_load', // matches the section name
1200
- 'option' => 'ctf_options', // matches the options name
1201
- 'class' => '',
1202
- 'whatis' => "The color of the text of the load more button"
1203
- ));
1204
-
1205
- // button text
1206
- $this->create_settings_field( array(
1207
- 'name' => 'buttontext',
1208
- 'title' => '<label for="ctf_buttontext">Button Text</label><code class="ctf_shortcode">buttontext
1209
- Eg: buttontext="More"</code>', // label for the input field
1210
- 'callback' => 'default_text', // name of the function that outputs the html
1211
- 'page' => 'ctf_options_load', // matches the section name
1212
- 'section' => 'ctf_options_load', // matches the section name
1213
- 'option' => 'ctf_options', // matches the options name
1214
- 'class' => 'default-text', // class for the wrapper and input field
1215
- 'default' => 'Load More...'
1216
- ));
1217
-
1218
- do_action( 'ctf_admin_style_option', $this );
1219
- }
1220
-
1221
- public function create_settings_field( $args=array() )
1222
- {
1223
- add_settings_field(
1224
- $args['name'],
1225
- $args['title'],
1226
- array( $this, $args['callback'] ),
1227
- $args['page'],
1228
- $args['section'],
1229
- $args
1230
- );
1231
- }
1232
-
1233
- public function default_text( $args )
1234
- {
1235
- $options = get_option( $args['option'] );
1236
- $default = isset( $args['default'] ) ? $args['default'] : '';
1237
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1238
- $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1239
- $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1240
- $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1241
- $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1242
- $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1243
- ?>
1244
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1245
- <?php if ( isset( $args['example'] ) ) : ?>
1246
- <span><?php echo $args['example']; ?></span>
1247
- <?php endif; ?>
1248
- <?php if ( isset( $args['whatis'] ) ) : ?>
1249
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1250
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1251
- <?php endif; ?>
1252
- <?php
1253
- }
1254
-
1255
- public function default_select( $args )
1256
- {
1257
- $options = get_option( $args['option'] );
1258
- $selected = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1259
- ?>
1260
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>">
1261
- <?php foreach ( $args['fields'] as $field ) : ?>
1262
- <option value="<?php echo $field[0]; ?>" id="ctf-<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php if( $selected == $field[0] ) { echo ' selected'; } ?>><?php _e( $field[1], 'custom-twitter-feeds' ); ?></option>
1263
- <?php endforeach; ?>
1264
- </select>
1265
- <?php if ( isset( $args['whatis'] ) ) : ?>
1266
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1267
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1268
- <?php endif; ?>
1269
- <?php
1270
- }
1271
-
1272
- public function default_color( $args )
1273
- {
1274
- $options = get_option( $args['option'] );
1275
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1276
- ?>
1277
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" value="#<?php esc_attr_e( str_replace('#', '', $option_string ) ); ?>" class="ctf-colorpicker" />
1278
- <?php
1279
- }
1280
-
1281
- public function default_checkbox( $args )
1282
- {
1283
- $options = get_option( $args['option'] );
1284
- $option_checked = ( isset( $options[ $args['name'] ] ) ) ? $options[ $args['name'] ] : false;
1285
- ?>
1286
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1287
- <?php if ( isset( $args['whatis'] ) ) : ?>
1288
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1289
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1290
- <?php endif; ?>
1291
- <?php
1292
- }
1293
-
1294
- public function reverse_checkbox( $args )
1295
- {
1296
- $options = get_option( $args['option'] );
1297
- $option_checked = isset( $options[ $args['name'] ] ) ? $options[ $args['name'] ] : true;
1298
- ?>
1299
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1300
- <?php if ( isset( $args['whatis'] ) ) : ?>
1301
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1302
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1303
- <?php endif; ?>
1304
- <?php
1305
- }
1306
-
1307
- public function access_token_secret( $args )
1308
- {
1309
- $options = get_option( $args['option'] );
1310
- $default = isset( $args['default'] ) ? $args['default'] : '';
1311
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1312
- $option_checked = ( isset( $options['use_own_consumer'] ) ) ? $options['use_own_consumer'] : false;
1313
- $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1314
- $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1315
- $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1316
- $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1317
- $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1318
- ?>
1319
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1320
- <?php if ( isset( $args['example'] ) ) : ?>
1321
- <span><?php echo $args['example']; ?></span>
1322
- <?php endif; ?>
1323
-
1324
- <?php if ( isset( $args['whatis'] ) ) : ?>
1325
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1326
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1327
- <?php endif; ?>
1328
-
1329
- <?php
1330
- }
1331
-
1332
- public function feed_settings_radio( $args )
1333
- {
1334
- $options = get_option( $args['option'] );
1335
- $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1336
- $show_replies = ( isset( $options[ $args['name'].'_includereplies' ] ) ) ? $options[ $args['name'].'_includereplies' ] : false;
1337
- $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1338
- ?>
1339
- <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1340
- <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php _e( $args['label'], 'custom-twitter-feeds' ); ?></label>
1341
- <?php if ( $args['has_input'] ) : ?>
1342
- <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1343
- <?php endif; ?>
1344
- <?php if ( isset( $args['whatis'] ) ) : ?>
1345
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1346
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1347
- <?php endif; ?>
1348
- <?php if ( $args['has_replies'] ) : ?>
1349
- <span class="ctf_include_replies_toggle ctf_pro">
1350
- <input name="<?php echo $args['option'].'['.$args['name'].'_includereplies]'; ?>" id="ctf_include_replies" type="checkbox" <?php if ( $show_replies ) echo "checked"; ?> />
1351
- <label class="ctf-radio-label" for="ctf_include_replies"><?php _e( 'Include replies', 'custom-twitter-feeds' ); ?></label>
1352
- <?php do_action( 'ctf_admin_upgrade_note' ); ?>
1353
- </span>
1354
- <?php endif; ?>
1355
- <?php
1356
- do_action( 'ctf_admin_feed_settings_radio_extra', $args );
1357
- }
1358
-
1359
- public function feed_settings_radio_search( $args )
1360
- {
1361
- $options = get_option( $args['option'] );
1362
- $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1363
- $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1364
- ?>
1365
- <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1366
- <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php echo $args['label']; ?></label>
1367
- <?php if ( $args['has_input'] ) : ?>
1368
- <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1369
- <?php endif; ?>
1370
- <?php if ( isset( $args['note_after_input'] ) ) : ?>
1371
- <span class="ctf-note-after-input"><?php echo esc_attr( $args['note_after_input'] ); ?></span>
1372
- <?php endif; ?>
1373
- <?php if ( isset( $args['whatis'] ) ) : ?>
1374
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1375
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1376
- <?php endif; ?>
1377
- <?php
1378
- do_action( 'ctf_admin_feed_settings_search_extra' );
1379
- }
1380
-
1381
- public function width_and_height_settings( $args )
1382
- {
1383
- $options = get_option( $args['option'] );
1384
- $default = isset( $args['default'] ) ? $args['default'] : '';
1385
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1386
- $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : $args['default_unit'];
1387
- ?>
1388
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1389
- <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>" id="ctf_<?php echo $args['name'].'_unit'; ?>">
1390
- <option value="px" <?php if ( $selected == "px" ) echo 'selected="selected"' ?> >px</option>
1391
- <option value="%" <?php if ( $selected == "%" ) echo 'selected="selected"' ?> >%</option>
1392
- </select>
1393
-
1394
- <?php if ( $args['name'] == 'width' ) :
1395
- $checked = ( isset( $options[ $args['name'] . '_mobile_no_fixed' ] ) ) ? esc_attr( $options[ $args['name'] . '_mobile_no_fixed' ] ) : false; ?>
1396
- <div id="ctf_width_options">
1397
- <input name="<?php echo $args['option'].'[width_mobile_no_fixed]'; ?>" type="checkbox" id="ctf_width_mobile_no_fixed" <?php if ( $checked == true ) { echo "checked"; }?> /><label for="ctf_width_mobile_no_fixed"><?php _e('Set to be 100% width on mobile?', 'custom-twitter-feeds'); ?></label>
1398
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><?php _e('What does this mean?', 'custom-facebook-feed'); ?></a>
1399
- <p class="ctf-tooltip ctf-more-info"><?php _e("If you set a width on the feed then this will be used on mobile as well as desktop. Check this setting to set the feed width to be 100% on mobile so that it is responsive.", 'custom-twitter-feeds'); ?></p>
1400
- </div>
1401
- <?php endif; ?>
1402
- <?php
1403
- }
1404
-
1405
- public function cache_time( $args )
1406
- {
1407
- $min_cache_time = 3600;
1408
- $options = get_option( $args['option'] );
1409
- $unrestricted = isset( $options['have_own_tokens'] ) && $options['have_own_tokens'];
1410
- if ( $unrestricted ) {
1411
- $min_cache_time = 1;
1412
- }
1413
- $default = 3;
1414
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1415
-
1416
- $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1417
- $actual_time = (int)$option_string * (int)$selected;
1418
- $show_notice = false;
1419
- if ( get_transient( 'ct_feed_forced_cache_time_raise' ) ) {
1420
- $show_notice = true;
1421
- }
1422
- if ( $actual_time < $min_cache_time ) {
1423
- set_transient( 'ct_feed_forced_cache_time_raise', 'true', 60 * 60 * 48 );
1424
- $show_notice = true;
1425
- $option_string = max( 1, $min_cache_time / 3600 );
1426
- $selected = "3600";
1427
- } else {
1428
- $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1429
- }
1430
-
1431
- ?>
1432
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1433
- <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>">
1434
- <?php if ( $unrestricted ) : ?>
1435
- <option value="60" <?php if ( $selected == "60" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Minutes' ); ?></option>
1436
- <?php endif; ?>
1437
- <option value="3600" <?php if ( $selected == "3600" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Hours' ); ?></option>
1438
- <option value="86400" <?php if ( $selected == "86400" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Days' ); ?></option>
1439
- </select>&nbsp;
1440
- <input id="ctf-clear-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cache' ); ?>" />
1441
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1442
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your Twitter feeds', 'custom-twitter-feeds' ); ?>.</p>
1443
- <?php if ( $show_notice ) : ?>
1444
- <p class="ctf-more-info"><?php _e( 'Due to changes in Twitter\'s API usage policy, the minimum caching time for the plugin is 1 hour. To check for Tweets more often either create your own Twitter Developer app or upgrade to the Pro version.', 'custom-twitter-feeds' ); ?></p>
1445
- <?php endif; ?>
1446
- <?php
1447
- }
1448
-
1449
- public function customize_date_format( $args )
1450
- {
1451
- $options = get_option( $args['option'] );
1452
- $ctf_date_formatting = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1453
- $original = strtotime( '2016-02-25T17:30:00+0000' );
1454
- ?>
1455
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1456
- <option value="1" <?php if ( $ctf_date_formatting == "1" ) echo 'selected="selected"'; ?> ><?php _e( '2h / 25 Feb' ); ?></option>
1457
- <option value="2" <?php if ( $ctf_date_formatting == "2" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j', $original ); ?></option>
1458
- <option value="3" <?php if ( $ctf_date_formatting == "3" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j, Y', $original ); ?></option>
1459
- <option value="4" <?php if ( $ctf_date_formatting == "4" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d', $original ); ?></option>
1460
- <option value="5" <?php if ( $ctf_date_formatting == "5" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d.y', $original ); ?></option>
1461
- </select>
1462
- <?php if ( isset( $args['whatis'] ) ) : ?>
1463
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1464
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1465
- <?php endif; ?>
1466
- <?php
1467
- }
1468
-
1469
- public function customize_custom_date_format( $args )
1470
- {
1471
- $options = get_option( $args['option'] );
1472
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1473
- ?>
1474
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="10" placeholder="Eg. F jS, Y" />
1475
- <a href="https://smashballoon.com/custom-twitter-feeds/docs/date/" class="cff-external-link" target="_blank"><?php _e( 'Examples' , 'custom-twitter-feeds'); ?></a>
1476
- <?php
1477
- }
1478
-
1479
- public function customize_custom_time_translations( $args )
1480
- {
1481
- $options = get_option( $args['option'] );
1482
- $option_m = ( isset( $options['mtime'] ) ) ? esc_attr( $options['mtime'] ) : '';
1483
- $option_h = ( isset( $options['htime'] ) ) ? esc_attr( $options['htime'] ) : '';
1484
- $option_now = ( isset( $options['nowtime'] ) ) ? esc_attr( $options['nowtime'] ) : '';
1485
-
1486
- ?>
1487
- <input name="<?php echo $args['option'].'[mtime]'; ?>" id="ctf_translate_minute" type="text" value="<?php esc_attr_e( $option_m ); ?>" size="5" />
1488
- <label for=ctf_translate_minute"><?php _e( 'translation for "m" (minutes)', 'custom-twitter-feeds' ); ?></label><br>
1489
- <input name="<?php echo $args['option'].'[htime]'; ?>" id="ctf_translate_hour" type="text" value="<?php esc_attr_e( $option_h ); ?>" size="5" />
1490
- <label for=ctf_translate_hour"><?php _e( 'translation for "h" (hours)', 'custom-twitter-feeds' ); ?></label><br>
1491
- <input name="<?php echo $args['option'].'[nowtime]'; ?>" id="ctf_translate_now" type="text" value="<?php esc_attr_e( $option_now ); ?>" size="5" />
1492
- <label for=ctf_translate_now"><?php _e( 'translation for "now"', 'custom-twitter-feeds' ); ?></label><br>
1493
- <?php
1494
- }
1495
-
1496
- public function include_exclude_checkbox( $args )
1497
- {
1498
- $options = get_option( $args['option'] );
1499
- foreach ( $args['fields'] as $field ) {
1500
- $option_checked = isset( $options[$field[0]] ) ? $options[$field[0]] : true;
1501
- ?>
1502
- <input name="<?php echo $args['option'] . '[' . $field[0] . ']'; ?>"
1503
- id="ctf_<?php echo $field[0]; ?>" type="checkbox"
1504
- <?php if ( $option_checked ) {
1505
- echo "checked";
1506
- } ?> />
1507
- <label for=ctf_<?php echo $field[0]; ?>"><?php _e( $field[1], 'custom-twitter-feeds' ); ?></label><br>
1508
- <?php
1509
- } // end foreach
1510
- }
1511
-
1512
- public function text_size( $args )
1513
- {
1514
- $options = get_option( $args['option'] );
1515
- $ctf_text_size = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1516
- ?>
1517
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1518
- <option value="inherit" <?php if ( $ctf_text_size == "inherit" ) echo 'selected="selected"' ?> >Inherit</option>
1519
- <option value="10" <?php if ( $ctf_text_size == "10" ) echo 'selected="selected"' ?> >10px</option>
1520
- <option value="11" <?php if ( $ctf_text_size == "11" ) echo 'selected="selected"' ?> >11px</option>
1521
- <option value="12" <?php if ( $ctf_text_size == "12" ) echo 'selected="selected"' ?> >12px</option>
1522
- <option value="13" <?php if ( $ctf_text_size == "13" ) echo 'selected="selected"' ?> >13px</option>
1523
- <option value="14" <?php if ( $ctf_text_size == "14" ) echo 'selected="selected"' ?> >14px</option>
1524
- <option value="16" <?php if ( $ctf_text_size == "16" ) echo 'selected="selected"' ?> >16px</option>
1525
- <option value="18" <?php if ( $ctf_text_size == "18" ) echo 'selected="selected"' ?> >18px</option>
1526
- <option value="20" <?php if ( $ctf_text_size == "20" ) echo 'selected="selected"' ?> >20px</option>
1527
- <option value="24" <?php if ( $ctf_text_size == "24" ) echo 'selected="selected"' ?> >24px</option>
1528
- <option value="28" <?php if ( $ctf_text_size == "28" ) echo 'selected="selected"' ?> >28px</option>
1529
- <option value="32" <?php if ( $ctf_text_size == "32" ) echo 'selected="selected"' ?> >32px</option>
1530
- <option value="36" <?php if ( $ctf_text_size == "36" ) echo 'selected="selected"' ?> >36px</option>
1531
- <option value="42" <?php if ( $ctf_text_size == "42" ) echo 'selected="selected"' ?> >42px</option>
1532
- <option value="48" <?php if ( $ctf_text_size == "48" ) echo 'selected="selected"' ?> >48px</option>
1533
- <option value="54" <?php if ( $ctf_text_size == "54" ) echo 'selected="selected"' ?> >54px</option>
1534
- <option value="60" <?php if ( $ctf_text_size == "60" ) echo 'selected="selected"' ?> >60px</option>
1535
- </select>
1536
- <?php if ( isset( $args['whatis'] ) ) : ?>
1537
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1538
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1539
- <?php endif; ?>
1540
- <?php
1541
- }
1542
-
1543
- public function text_weight( $args )
1544
- {
1545
- $options = get_option( $args['option'] );
1546
- $ctf_text_weight = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1547
- ?>
1548
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1549
- <option value="inherit" <?php if ( $ctf_text_weight == "inherit" ) echo 'selected="selected"'; ?> >Inherit</option>
1550
- <option value="normal" <?php if ( $ctf_text_weight == "normal" ) echo 'selected="selected"'; ?> >Normal</option>
1551
- <option value="bold" <?php if ( $ctf_text_weight == "bold" ) echo 'selected="selected"'; ?> >Bold</option>
1552
- </select>
1553
- <?php if ( isset( $args['whatis'] ) ) : ?>
1554
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1555
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1556
- <?php endif; ?>
1557
- <?php
1558
- }
1559
-
1560
- public function feed_settings_timezone( $args )
1561
- {
1562
- $options = get_option( $args['option'] );
1563
- $ctf_timezone = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1564
- ?>
1565
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 300px;">
1566
- <option value="default" <?php if( $ctf_timezone == "default" ) echo 'selected="selected"' ?> ><?php _e( 'default from Twitter' ) ?></option>
1567
- <option value="Pacific/Midway" <?php if( $ctf_timezone == "Pacific/Midway" ) echo 'selected="selected"' ?> ><?php _e( '(GMT11:00) Midway Island, Samoa' ) ?></option>
1568
- <option value="America/Adak" <?php if( $ctf_timezone == "America/Adak" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) HawaiiAleutian' ) ?></option>
1569
- <option value="Etc/GMT+10" <?php if( $ctf_timezone == "Etc/GMT+10" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) Hawaii' ) ?></option>
1570
- <option value="Pacific/Marquesas" <?php if( $ctf_timezone == "Pacific/Marquesas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:30) Marquesas Islands' ) ?></option>
1571
- <option value="Pacific/Gambier" <?php if( $ctf_timezone == "Pacific/Gambier" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Gambier Islands' ) ?></option>
1572
- <option value="America/Anchorage" <?php if( $ctf_timezone == "America/Anchorage" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Alaska' ) ?></option>
1573
- <option value="America/Ensenada" <?php if( $ctf_timezone == "America/Ensenada" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Tijuana, Baja California' ) ?></option>
1574
- <option value="Etc/GMT+8" <?php if( $ctf_timezone == "Etc/GMT+8" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pitcairn Islands' ) ?></option>
1575
- <option value="America/Los_Angeles" <?php if( $ctf_timezone == "America/Los_Angeles" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pacific Time (US & Canada)' ) ?></option>
1576
- <option value="America/Denver" <?php if( $ctf_timezone == "America/Denver" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Mountain Time (US & Canada)' ) ?></option>
1577
- <option value="America/Chihuahua" <?php if( $ctf_timezone == "America/Chihuahua" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Chihuahua, La Paz, Mazatlan' ) ?></option>
1578
- <option value="America/Dawson_Creek" <?php if( $ctf_timezone == "America/Dawson_Creek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Arizona' ) ?></option>
1579
- <option value="America/Belize" <?php if( $ctf_timezone == "America/Belize" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Saskatchewan, Central America' ) ?></option>
1580
- <option value="America/Cancun" <?php if( $ctf_timezone == "America/Cancun" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Guadalajara, Mexico City, Monterrey' ) ?></option>
1581
- <option value="Chile/EasterIsland" <?php if( $ctf_timezone == "Chile/EasterIsland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Easter Island' ) ?></option>
1582
- <option value="America/Chicago" <?php if( $ctf_timezone == "America/Chicago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Central Time (US & Canada)' ) ?></option>
1583
- <option value="America/New_York" <?php if( $ctf_timezone == "America/New_York" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Eastern Time (US & Canada)' ) ?></option>
1584
- <option value="America/Havana" <?php if( $ctf_timezone == "America/Havana" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Cuba' ) ?></option>
1585
- <option value="America/Bogota" <?php if( $ctf_timezone == "America/Bogota" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Bogota, Lima, Quito, Rio Branco' ) ?></option>
1586
- <option value="America/Caracas" <?php if( $ctf_timezone == "America/Caracas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:30) Caracas' ) ?></option>
1587
- <option value="America/Santiago" <?php if( $ctf_timezone == "America/Santiago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Santiago' ) ?></option>
1588
- <option value="America/La_Paz" <?php if( $ctf_timezone == "America/La_Paz" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) La Paz' ) ?></option>
1589
- <option value="Atlantic/Stanley" <?php if( $ctf_timezone == "Atlantic/Stanley" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Faukland Islands' ) ?></option>
1590
- <option value="America/Campo_Grande" <?php if( $ctf_timezone == "America/Campo_Grande" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Brazil' ) ?></option>
1591
- <option value="America/Goose_Bay" <?php if( $ctf_timezone == "America/Goose_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Goose Bay)' ) ?></option>
1592
- <option value="America/Glace_Bay" <?php if( $ctf_timezone == "America/Glace_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Canada)' ) ?></option>
1593
- <option value="America/St_Johns" <?php if( $ctf_timezone == "America/St_Johns" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:30) Newfoundland' ) ?></option>
1594
- <option value="America/Araguaina" <?php if( $ctf_timezone == "America/Araguaina" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) UTC3' ) ?></option>
1595
- <option value="America/Montevideo" <?php if( $ctf_timezone == "America/Montevideo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Montevideo' ) ?></option>
1596
- <option value="America/Miquelon" <?php if( $ctf_timezone == "America/Miquelon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Miquelon, St. Pierre' ) ?></option>
1597
- <option value="America/Godthab" <?php if( $ctf_timezone == "America/Godthab" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Greenland' ) ?></option>
1598
- <option value="America/Argentina/Buenos_Aires" <?php if( $ctf_timezone == "America/Argentina/Buenos_Aires" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Buenos Aires' ) ?></option>
1599
- <option value="America/Sao_Paulo" <?php if( $ctf_timezone == "America/Sao_Paulo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Brasilia' ) ?></option>
1600
- <option value="America/Noronha" <?php if( $ctf_timezone == "America/Noronha" ) echo 'selected="selected"' ?> ><?php _e( '(GMT02:00) MidAtlantic' ) ?></option>
1601
- <option value="Atlantic/Cape_Verde" <?php if( $ctf_timezone == "Atlantic/Cape_Verde" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Cape Verde Is.' ) ?></option>
1602
- <option value="Atlantic/Azores" <?php if( $ctf_timezone == "Atlantic/Azores" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Azores' ) ?></option>
1603
- <option value="Europe/Belfast" <?php if( $ctf_timezone == "Europe/Belfast" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Belfast' ) ?></option>
1604
- <option value="Europe/Dublin" <?php if( $ctf_timezone == "Europe/Dublin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Dublin' ) ?></option>
1605
- <option value="Europe/Lisbon" <?php if( $ctf_timezone == "Europe/Lisbon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Lisbon' ) ?></option>
1606
- <option value="Europe/London" <?php if( $ctf_timezone == "Europe/London" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : London' ) ?></option>
1607
- <option value="Africa/Abidjan" <?php if( $ctf_timezone == "Africa/Abidjan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Monrovia, Reykjavik' ) ?></option>
1608
- <option value="Europe/Amsterdam" <?php if( $ctf_timezone == "Europe/Amsterdam" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' ) ?></option>
1609
- <option value="Europe/Belgrade" <?php if( $ctf_timezone == "Europe/Belgrade" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' ) ?></option>
1610
- <option value="Europe/Brussels" <?php if( $ctf_timezone == "Europe/Brussels" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Brussels, Copenhagen, Madrid, Paris' ) ?></option>
1611
- <option value="Africa/Algiers" <?php if( $ctf_timezone == "Africa/Algiers" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) West Central Africa' ) ?></option>
1612
- <option value="Africa/Windhoek" <?php if( $ctf_timezone == "Africa/Windhoek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Windhoek' ) ?></option>
1613
- <option value="Asia/Beirut" <?php if( $ctf_timezone == "Asia/Beirut" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Beirut' ) ?></option>
1614
- <option value="Africa/Cairo" <?php if( $ctf_timezone == "Africa/Cairo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Cairo' ) ?></option>
1615
- <option value="Asia/Gaza" <?php if( $ctf_timezone == "Asia/Gaza" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Gaza' ) ?></option>
1616
- <option value="Africa/Blantyre" <?php if( $ctf_timezone == "Africa/Blantyre" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Harare, Pretoria' ) ?></option>
1617
- <option value="Asia/Jerusalem" <?php if( $ctf_timezone == "Asia/Jerusalem" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Jerusalem' ) ?></option>
1618
- <option value="Europe/Minsk" <?php if( $ctf_timezone == "Europe/Minsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Minsk' ) ?></option>
1619
- <option value="Asia/Damascus" <?php if( $ctf_timezone == "Asia/Damascus" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Syria' ) ?></option>
1620
- <option value="Europe/Moscow" <?php if( $ctf_timezone == "Europe/Moscow" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Moscow, St. Petersburg, Volgograd' ) ?></option>
1621
- <option value="Africa/Addis_Ababa" <?php if( $ctf_timezone == "Africa/Addis_Ababa" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Nairobi' ) ?></option>
1622
- <option value="Asia/Tehran" <?php if( $ctf_timezone == "Asia/Tehran" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:30) Tehran' ) ?></option>
1623
- <option value="Asia/Dubai" <?php if( $ctf_timezone == "Asia/Dubai" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Abu Dhabi, Muscat' ) ?></option>
1624
- <option value="Asia/Yerevan" <?php if( $ctf_timezone == "Asia/Yerevan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Yerevan' ) ?></option>
1625
- <option value="Asia/Kabul" <?php if( $ctf_timezone == "Asia/Kabul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:30) Kabul' ) ?></option>
1626
- <option value="Asia/Yekaterinburg" <?php if( $ctf_timezone == "Asia/Yekaterinburg" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Ekaterinburg' ) ?></option>
1627
- <option value="Asia/Tashkent" <?php if( $ctf_timezone == "Asia/Tashkent" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Tashkent' ) ?></option>
1628
- <option value="Asia/Kolkata" <?php if( $ctf_timezone == "Asia/Kolkata" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi' ) ?></option>
1629
- <option value="Asia/Katmandu" <?php if( $ctf_timezone == "Asia/Katmandu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:45) Kathmandu' ) ?></option>
1630
- <option value="Asia/Dhaka" <?php if( $ctf_timezone == "Asia/Dhaka" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Astana, Dhaka' ) ?></option>
1631
- <option value="Asia/Novosibirsk" <?php if( $ctf_timezone == "Asia/Novosibirsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Novosibirsk' ) ?></option>
1632
- <option value="Asia/Rangoon" <?php if( $ctf_timezone == "Asia/Rangoon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:30) Yangon (Rangoon)' ) ?></option>
1633
- <option value="Asia/Bangkok" <?php if( $ctf_timezone == "Asia/Bangkok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Bangkok, Hanoi, Jakarta' ) ?></option>
1634
- <option value="Asia/Krasnoyarsk" <?php if( $ctf_timezone == "Asia/Krasnoyarsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Krasnoyarsk' ) ?></option>
1635
- <option value="Asia/Hong_Kong" <?php if( $ctf_timezone == "Asia/Hong_Kong" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi' ) ?></option>
1636
- <option value="Asia/Irkutsk" <?php if( $ctf_timezone == "Asia/Irkutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Irkutsk, Ulaan Bataar' ) ?></option>
1637
- <option value="Australia/Perth" <?php if( $ctf_timezone == "Australia/Perth" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Perth' ) ?></option>
1638
- <option value="Australia/Eucla" <?php if( $ctf_timezone == "Australia/Eucla" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:45) Eucla' ) ?></option>
1639
- <option value="Asia/Tokyo" <?php if( $ctf_timezone == "Asia/Tokyo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Osaka, Sapporo, Tokyo' ) ?></option>
1640
- <option value="Asia/Seoul" <?php if( $ctf_timezone == "Asia/Seoul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Seoul' ) ?></option>
1641
- <option value="Asia/Yakutsk" <?php if( $ctf_timezone == "Asia/Yakutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Yakutsk' ) ?></option>
1642
- <option value="Australia/Adelaide" <?php if( $ctf_timezone == "Australia/Adelaide" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Adelaide' ) ?></option>
1643
- <option value="Australia/Darwin" <?php if( $ctf_timezone == "Australia/Darwin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Darwin' ) ?></option>
1644
- <option value="Australia/Brisbane" <?php if( $ctf_timezone == "Australia/Brisbane" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Brisbane' ) ?></option>
1645
- <option value="Australia/Hobart" <?php if( $ctf_timezone == "Australia/Hobart" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Sydney' ) ?></option>
1646
- <option value="Asia/Vladivostok" <?php if( $ctf_timezone == "Asia/Vladivostok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Vladivostok' ) ?></option>
1647
- <option value="Australia/Lord_Howe" <?php if( $ctf_timezone == "Australia/Lord_Howe" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:30) Lord Howe Island' ) ?></option>
1648
- <option value="Etc/GMT11" <?php if( $ctf_timezone == "Etc/GMT11" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Solomon Is., New Caledonia' ) ?></option>
1649
- <option value="Asia/Magadan" <?php if( $ctf_timezone == "Asia/Magadan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Magadan' ) ?></option>
1650
- <option value="Pacific/Norfolk" <?php if( $ctf_timezone == "Pacific/Norfolk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:30) Norfolk Island' ) ?></option>
1651
- <option value="Asia/Anadyr" <?php if( $ctf_timezone == "Asia/Anadyr" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Anadyr, Kamchatka' ) ?></option>
1652
- <option value="Pacific/Auckland" <?php if( $ctf_timezone == "Pacific/Auckland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Auckland, Wellington' ) ?></option>
1653
- <option value="Etc/GMT12" <?php if( $ctf_timezone == "Etc/GMT12" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Fiji, Kamchatka, Marshall Is.' ) ?></option>
1654
- <option value="Pacific/Chatham" <?php if( $ctf_timezone == "Pacific/Chatham" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:45) Chatham Islands' ) ?></option>
1655
- <option value="Pacific/Tongatapu" <?php if( $ctf_timezone == "Pacific/Tongatapu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+13:00) Nuku\'alofa' ) ?></option>
1656
- <option value="Pacific/Kiritimati" <?php if( $ctf_timezone == "Pacific/Kiritimati" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+14:00) Kiritimati' ) ?></option>
1657
- </select>
1658
- <?php if ( isset( $args['whatis'] ) ) : ?>
1659
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1660
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1661
- <?php endif; ?>
1662
- <?php
1663
- }
1664
-
1665
- public function layout( $args ) {
1666
- $selected_type = $args['layout_selections']['layout'];
1667
- $layout_types = array(
1668
- 'list' => __( 'List', 'instagram-feed' ),
1669
- 'carousel' => __( 'Carousel', 'instagram-feed' ),
1670
- 'masonry' => __( 'Masonry', 'instagram-feed' ),
1671
- );
1672
- $layout_images = array(
1673
- 'list' => CTF_PLUGIN_URL . 'img/list.png',
1674
- 'carousel' => CTF_PLUGIN_URL . 'img/carousel.png',
1675
- 'masonry' => CTF_PLUGIN_URL . 'img/masonry.png',
1676
- );
1677
- ?>
1678
-
1679
- <?php foreach( $layout_types as $layout_type => $label ) : ?>
1680
- <div class="ctf_layout_cell <?php if($selected_type === $layout_type) echo "ctf_layout_selected"; ?>">
1681
- <input class="ctf_layout_type" id="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>" name="<?php echo $args['option'].'[layout]'; ?>" type="radio" value="<?php esc_attr_e( $layout_type ); ?>" <?php if ( $selected_type === $layout_type ) echo 'checked'; ?>/><label for="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>"><span class="ctf_label"><?php echo esc_html( $label ); ?></span><img src="<?php echo $layout_images[ $layout_type ]; ?>" /></label>
1682
-
1683
- <a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=layout" target="_blank" class="ctf_lock"><i class="fa fa-rocket"></i>Pro</a>
1684
- </div>
1685
- <?php endforeach; ?>
1686
- <div class="ctf_layout_options_wrap">
1687
- <div class="ctf_layout_settings ctf_layout_type_list">
1688
- <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('A full-width list of tweets.'); ?></span>
1689
- </div>
1690
- <div class="ctf_layout_settings ctf_layout_type_masonry">
1691
- <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Masonry layout option</a></p>
1692
- <div class="ctf_layout_setting">
1693
- <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Tweets in columns with no empty space between them.'); ?></span>
1694
- </div>
1695
- <div class="ctf_layout_setting">
1696
- <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrycols
1697
- Eg: masonrycols=4</code>
1698
- <br />
1699
- <select name="<?php echo $args['option'].'[masonrycols]'; ?>" id="ctf_masonrycols">
1700
- <?php
1701
- $cols_options = array(1,2,3,4,5,6);
1702
- foreach ( $cols_options as $option ) :
1703
- ?>
1704
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrycols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1705
- <?php endforeach; ?>
1706
- </select>
1707
- </div>
1708
- <div class="ctf_layout_setting">
1709
- <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrymobilecols
1710
- Eg: masonrymobilecols=2</code>
1711
- <br />
1712
- <select name="<?php echo $args['option'].'[masonrymobilecols]'; ?>" id="ctf_masonrymobilecols">
1713
- <?php
1714
- $cols_options = array(1,2);
1715
- foreach ( $cols_options as $option ) :
1716
- ?>
1717
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrymobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1718
- <?php endforeach; ?>
1719
- </select>
1720
- </div>
1721
- </div>
1722
- <div class="ctf_layout_settings ctf_layout_type_carousel">
1723
- <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Carousel layout option</a></p>
1724
- <div class="ctf_layout_setting">
1725
- <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Posts are displayed in a slideshow carousel.', 'instagram-feed'); ?></span>
1726
- </div>
1727
- <div class="ctf_layout_setting">
1728
- <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselcols
1729
- Eg: carouselcols=4</code>
1730
- <br />
1731
- <select name="<?php echo $args['option'].'[carouselcols]'; ?>" id="ctf_carouselcols">
1732
- <?php
1733
- $cols_options = array(1,2,3,4,5,6);
1734
- foreach ( $cols_options as $option ) :
1735
- ?>
1736
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselcols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1737
- <?php endforeach; ?>
1738
- </select>
1739
- </div>
1740
- <div class="ctf_layout_setting">
1741
- <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselmobilecols
1742
- Eg: carouselmobilecols=2</code>
1743
- <br />
1744
- <select name="<?php echo $args['option'].'[carouselmobilecols]'; ?>" id="ctf_carouselmobilecols">
1745
- <?php
1746
- $cols_options = array(1,2);
1747
- foreach ( $cols_options as $option ) :
1748
- ?>
1749
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselmobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1750
- <?php endforeach; ?>
1751
- </select>
1752
- </div>
1753
- <div class="ctf_layout_setting">
1754
- <label><?php _e('Loop Type', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselloop
1755
- Eg: carouselloop=rewind
1756
- carouselloop=infinity</code>
1757
- <br />
1758
- <select name="<?php echo $args['option'].'[carouselloop]'; ?>" id="ctf_carousel_loop">
1759
- <option value="none" <?php if($args['layout_selections']['carouselloop'] == "none") echo 'selected="selected"' ?> ><?php _e( 'None', 'instagram-feed'); ?></option>
1760
- <option value="rewind" <?php if($args['layout_selections']['carouselloop'] == "rewind") echo 'selected="selected"' ?> ><?php _e( 'Rewind', 'instagram-feed'); ?></option>
1761
- <option value="infinity" <?php if($args['layout_selections']['carouselloop'] == "infinity") echo 'selected="selected"' ?> ><?php _e( 'Infinity', 'instagram-feed'); ?></option>
1762
- </select>
1763
- </div>
1764
- <div class="ctf_layout_setting">
1765
- <label><?php _e('Navigation Arrows', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1766
- Eg: carouselarrows=below</code>
1767
- <br />
1768
- <select name="<?php echo $args['option'].'[carouselarrows]'; ?>" id="ctf_carousel_loop">
1769
- <option value="onhover" <?php if($args['layout_selections']['carouselarrows'] == "onhover") echo 'selected="selected"' ?> ><?php _e( 'Show on Hover', 'instagram-feed'); ?></option>
1770
- <option value="below" <?php if($args['layout_selections']['carouselarrows'] == "below") echo 'selected="selected"' ?> ><?php _e( 'Show below feed', 'instagram-feed'); ?></option>
1771
- <option value="hide" <?php if($args['layout_selections']['carouselarrows'] == "hide") echo 'selected="selected"' ?> ><?php _e( 'Hide arrows', 'instagram-feed'); ?></option>
1772
- </select>
1773
- </div>
1774
- <div class="ctf_layout_setting">
1775
- <label><?php _e('Carousel Height', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1776
- Eg: carouselarrows=below</code>
1777
- <br />
1778
- <select name="<?php echo $args['option'].'[carouselheight]'; ?>" id="ctf_carousel_loop">
1779
- <option value="tallest" <?php if($args['layout_selections']['carouselheight'] == "tallest") echo 'selected="selected"' ?> ><?php _e( 'Always set to tallest post', 'instagram-feed'); ?></option>
1780
- <option value="clickexpand" <?php if($args['layout_selections']['carouselheight'] == "clickexpand") echo 'selected="selected"' ?> ><?php _e( 'Set to shortest post, button to expand', 'instagram-feed'); ?></option>
1781
- <option value="auto'" <?php if($args['layout_selections']['carouselheight'] == "auto'") echo 'selected="selected"' ?> ><?php _e( 'Automatically set to post height (forces single column)', 'instagram-feed'); ?></option>
1782
- </select>
1783
- </div>
1784
- <div class="ctf_layout_setting">
1785
- <input type="checkbox" name="<?php echo $args['option'].'[carouselautoplay]'; ?>" id="ctf_carousel_autoplay" <?php if($args['layout_selections']['carouselautoplay'] == true) echo 'checked="checked"' ?> />
1786
- <label><?php _e("Enable Autoplay", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselautoplay
1787
- Eg: carouselautoplay=true</code>
1788
- </div>
1789
- <div class="ctf_layout_setting">
1790
- <label><?php _e("Interval Time", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouseltime
1791
- Eg: carouseltime=8000</code>
1792
- <br />
1793
- <input name="<?php echo $args['option'].'[carouseltime]'; ?>" type="text" value="<?php esc_attr_e( $args['layout_selections']['carouseltime'] ); ?>" size="6" /><?php _e("miliseconds", 'instagram-feed'); ?>
1794
- </div>
1795
- </div>
1796
-
1797
- </div>
1798
- <?php
1799
- }
1800
-
1801
- public function custom_code( $args )
1802
- {
1803
- $options = get_option( $args['option'] );
1804
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1805
- ?>
1806
- <p><?php _e( $args['description'], 'custom-twitter-feeds' ) ; ?></p>
1807
- <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="7"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
1808
- <?php if ( isset( $args['extra'] ) ) { _e( '<p class="ctf_note">'.$args['extra'].'</p>', 'custom-twitter-feeds' ); } ?>
1809
- <?php
1810
- }
1811
-
1812
- public function gdpr( $args ) {
1813
- $options = get_option( $args['option'] );
1814
- $gdpr = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1815
- $select_options = array(
1816
- array(
1817
- 'label' => __( 'Automatic', 'custom-twitter-feeds' ),
1818
- 'value' => 'auto'
1819
- ),
1820
- array(
1821
- 'label' => __( 'Yes', 'custom-twitter-feeds' ),
1822
- 'value' => 'yes'
1823
- ),
1824
- array(
1825
- 'label' => __( 'No', 'custom-twitter-feeds' ),
1826
- 'value' => 'no'
1827
- )
1828
- )
1829
- ?>
1830
- <?php
1831
- $gdpr_list = "<ul class='ctf-list'>
1832
- <li>" . __( 'Avatars will be replaced with a Twitter logo in the feed header.', 'custom-twitter-feeds' ) . "</li>
1833
- <li>" . __( 'Avatars will not display in Tweets.', 'custom-twitter-feeds' ) . "</li>
1834
- <li>" . __( 'Twitter intents (for replying, retweeting, and liking) will be plain links.', 'custom-twitter-feeds' ) . "</li>
1835
- </ul>";
1836
- ?>
1837
- <div>
1838
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_gdpr_setting">
1839
- <?php foreach ( $select_options as $select_option ) :
1840
- $selected = $select_option['value'] === $gdpr ? ' selected' : '';
1841
- ?>
1842
- <option value="<?php echo esc_attr( $select_option['value'] ); ?>"<?php echo $selected; ?> ><?php echo esc_html( $select_option['label'] ); ?></option>
1843
- <?php endforeach; ?>
1844
- </select>
1845
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><?php _e('What does this mean?', 'custom-twitter-feeds'); ?></a>
1846
- <div class="ctf-tooltip ctf-more-info gdpr_tooltip">
1847
-
1848
- <p><span><?php _e("Yes", 'custom-twitter-feeds' ); ?>:</span> <?php _e("Enabling this setting prevents all images and videos from being loaded directly from Twitter's servers (CDN) to prevent any requests to external websites in your browser. To accommodate this, some features of the plugin will be disabled or limited.", 'custom-twitter-feeds' ); ?> <a href="JavaScript:void(0);" class="ctf_show_gdpr_list"><?php _e( 'What will be limited?', 'custom-twitter-feeds' ); ?></a></p>
1849
-
1850
- <?php echo "<div class='ctf_gdpr_list'>" . $gdpr_list . '</div>'; ?>
1851
-
1852
-
1853
- <p><span><?php _e("No", 'custom-twitter-feeds' ); ?>:</span> <?php _e("The plugin will still make some requests to load and display images and videos directly from Twitter.", 'custom-twitter-feeds' ); ?></p>
1854
-
1855
-
1856
- <p><span><?php _e("Automatic", 'custom-twitter-feeds' ); ?>:</span> <?php echo sprintf( __( 'The plugin will only load images and videos directly from Twitter if consent has been given by one of these integrated %s', 'custom-twitter-feeds' ), '<a href="https://smashballoon.com/doc/gdpr-plugin-list/?twitter" target="_blank" rel="noopener">' . __( 'GDPR cookie plugins', 'custom-twitter-feeds' ) . '</a>' ); ?></p>
1857
-
1858
- <p><?php echo sprintf( __( '%s to learn more about GDPR compliance in the Custom Twitter Feed plugin.', 'custom-twitter-feeds' ), '<a href="https://smashballoon.com/doc/custom-twitter-feeds-gdpr-compliance/?twitter" target="_blank" rel="noopener">'. __( 'Click here', 'custom-twitter-feeds' ).'</a>' ); ?></p>
1859
- </div>
1860
- </div>
1861
-
1862
- <?php if ( ! CTF_GDPR_Integrations::gdpr_tests_successful( isset( $_GET['retest'] ) ) ) :
1863
- $errors = CTF_GDPR_Integrations::gdpr_tests_error_message();
1864
- ?>
1865
- <div class="ctf-box ctf_gdpr_error">
1866
- <div class="ctf-box-setting">
1867
- <p>
1868
- <strong><?php _e( 'Error:', 'custom-twitter-feeds' ); ?></strong> <?php _e("Due to a configuration issue on your web server, the GDPR setting is unable to be enabled. Please see below for more information.", 'custom-twitter-feeds' ); ?></p>
1869
- <p>
1870
- <?php echo $errors; ?>
1871
- </p>
1872
- </div>
1873
- </div>
1874
- <?php else: ?>
1875
-
1876
- <div class="ctf_gdpr_auto">
1877
- <?php if ( CTF_GDPR_Integrations::gdpr_plugins_active() ) :
1878
- $active_plugin = CTF_GDPR_Integrations::gdpr_plugins_active();
1879
- ?>
1880
- <div class="ctf_gdpr_plugin_active">
1881
- <div class="ctf_active">
1882
- <p>
1883
- <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check-circle" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-check-circle fa-w-16 fa-2x"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z" class=""></path></svg>
1884
- <b><?php echo sprintf( __( '%s detected', 'custom-twitter-feeds' ), $active_plugin ); ?></b>
1885
- <br />
1886
- <?php _e( 'Some Custom Twitter Feed features will be limited for visitors to ensure GDPR compliance until they give consent.', 'custom-twitter-feeds' ); ?>
1887
- <a href="JavaScript:void(0);" class="ctf_show_gdpr_list"><?php _e( 'What will be limited?', 'custom-twitter-feeds' ); ?></a>
1888
- </p>
1889
- <?php echo "<div class='ctf_gdpr_list'>" . $gdpr_list . '</div>'; ?>
1890
- </div>
1891
-
1892
- </div>
1893
- <?php else: ?>
1894
- <div class="ctf-box">
1895
- <div class="ctf-box-setting">
1896
- <p><?php _e( 'No GDPR consent plugin detected. Install a compatible <a href="https://smashballoon.com/doc/gdpr-plugin-list/?twitter">GDPR consent plugin</a>, or manually enable the setting above to display a GDPR compliant version of the feed to all visitors.', 'custom-twitter-feeds' ); ?></p>
1897
- </div>
1898
- </div>
1899
- <?php endif; ?>
1900
- </div>
1901
-
1902
- <div class="ctf-box ctf_gdpr_yes">
1903
- <div class="ctf-box-setting">
1904
- <p><?php _e( "No requests will be made to third-party websites. To accommodate this, some features of the plugin will be limited:", 'custom-twitter-feeds' ); ?></p>
1905
- <?php echo $gdpr_list; ?>
1906
- </div>
1907
- </div>
1908
-
1909
- <div class="ctf-box ctf_gdpr_no">
1910
- <div class="ctf-box-setting">
1911
- <p><?php _e( "The plugin will function as normal and load images and videos directly from Twitter.", 'custom-twitter-feeds' ); ?></p>
1912
- </div>
1913
- </div>
1914
-
1915
- <?php endif;
1916
- }
1917
-
1918
- public function clear_persistent_cache_button( $args ) {
1919
- ?>
1920
- <input id="ctf-clear-persistent-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Persistent Caches' ); ?>" />
1921
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1922
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for search and hashtag feeds that have persistent caches', 'custom-twitter-feeds' ); ?>.</p>
1923
- <?php
1924
- }
1925
-
1926
- public function validate_ctf_options( $input )
1927
- {
1928
- if ( isset( $input['tab'] ) && ! isset( $_POST['just_tokens'] ) ) {
1929
- wp_cache_delete ( 'alloptions', 'options' );
1930
-
1931
- $ctf_options = get_option( 'ctf_options', array() );
1932
-
1933
- if ( $input['tab'] === 'configure' && isset( $input['usertimeline_text'] ) ) {
1934
-
1935
- $feed_types = apply_filters( 'ctf_admin_feed_type_list', '' );
1936
- $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1937
- $ctf_options['have_own_tokens'] = false;
1938
- $ctf_options['use_own_consumer'] = false;
1939
- $ctf_options['preserve_settings'] = false;
1940
- $ctf_options['usertimeline_includereplies'] = false;
1941
- $ctf_options['hometimeline_includereplies'] = false;
1942
- $ctf_options['mentionstimeline_includereplies'] = false;
1943
-
1944
- foreach ( $input as $key => $val ) {
1945
- if ( $key == 'search_text' || $key == 'usertimeline_text' || $key == 'hashtag_text' ) {
1946
- $ctf_options[$key] = apply_filters( 'ctf_admin_validate_' . $key, $val );
1947
- } elseif ( $key == 'ajax_theme' || $key == 'use_own_consumer' || $key == 'have_own_tokens' || $key == 'preserve_settings' ||
1948
- $key == 'usertimeline_includereplies' || $key == 'hometimeline_includereplies' || $key == 'mentionstimeline_includereplies' ) {
1949
- if ( $val != 'on' ) {
1950
- $ctf_options[$key] = false;
1951
- } else {
1952
- $ctf_options[$key] = true;
1953
- }
1954
- } else {
1955
- $ctf_options[$key] = sanitize_text_field( $val );
1956
- }
1957
- }
1958
-
1959
- $ctf_options['includereplies'] = apply_filters( 'ctf_admin_set_include_replies', $ctf_options );
1960
-
1961
- // delete feeds cached in transients
1962
- ctf_clear_cache();
1963
- delete_transient( 'ctf_reauthenticate' );
1964
-
1965
- // process force cache to clear on interval
1966
- $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
1967
- $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
1968
-
1969
- if ( $cron_clear_cache == 'no' ) {
1970
- wp_clear_scheduled_hook( 'ctf_cron_job' );
1971
- } elseif ( $cron_clear_cache == 'yes' ) {
1972
- //Clear the existing cron event
1973
- wp_clear_scheduled_hook( 'ctf_cron_job' );
1974
-
1975
- //Set the event schedule based on what the caching time is set to
1976
- if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
1977
- $ctf_cron_schedule = 'twicedaily';
1978
- } elseif ( $cache_time_unit == 86400 ) {
1979
- $ctf_cron_schedule = 'daily';
1980
- } else {
1981
- $ctf_cron_schedule = 'hourly';
1982
- }
1983
-
1984
- wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1985
- }
1986
- } elseif ( $input['tab'] === 'customize' && isset( $input['class'] ) ) {
1987
-
1988
- $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1989
- $checkbox_settings = array( 'width_mobile_no_fixed', 'include_retweeter', 'include_avatar', 'include_author', 'include_logo', 'include_text', 'include_media_placeholder',
1990
- 'include_date', 'include_actions', 'include_twitterlink', 'ajax_theme', 'include_linkbox', 'creditctf', 'showbutton', 'showheader', 'persistentcache', 'selfreplies',
1991
- 'disableintents', 'disableawesome', 'shorturls' );
1992
- $checkbox_settings = apply_filters( 'ctf_admin_customize_checkbox_settings', $checkbox_settings );
1993
- $leave_spaces = array( 'headertext', 'translate_minute', 'translate_hour', 'custom_css', 'custom_js' );
1994
-
1995
- foreach ( $checkbox_settings as $checkbox_setting ) {
1996
- $ctf_options[$checkbox_setting] = 0;
1997
- }
1998
-
1999
- foreach ( $input as $key => $val ) {
2000
- if ( in_array( $key, $checkbox_settings ) ) {
2001
- if ( $val != 'on' ) {
2002
- $ctf_options[$key] = false;
2003
- } else {
2004
- $ctf_options[$key] = true;
2005
- }
2006
- } else {
2007
- if ( in_array( $key, $leave_spaces ) ) {
2008
- $ctf_options[$key] = $val;
2009
- } else {
2010
- $ctf_options[$key] = sanitize_text_field( $val );
2011
- }
2012
- }
2013
- }
2014
-
2015
- // delete feeds cached in transients
2016
- ctf_clear_cache();
2017
-
2018
- // process force cache to clear on interval
2019
- $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
2020
- $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
2021
-
2022
- if ( $cron_clear_cache == 'no' ) {
2023
- wp_clear_scheduled_hook( 'ctf_cron_job' );
2024
- } elseif ( $cron_clear_cache == 'yes' ) {
2025
- //Clear the existing cron event
2026
- wp_clear_scheduled_hook( 'ctf_cron_job' );
2027
-
2028
- //Set the event schedule based on what the caching time is set to
2029
- if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
2030
- $ctf_cron_schedule = 'twicedaily';
2031
- } elseif ( $cache_time_unit == 86400 ) {
2032
- $ctf_cron_schedule = 'daily';
2033
- } else {
2034
- $ctf_cron_schedule = 'hourly';
2035
- }
2036
-
2037
- wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
2038
- }
2039
- } elseif ( $input['tab'] === 'style' && isset( $input['headertext'] ) ) {
2040
- $checkbox_settings = array( 'showbio', 'disablelinks', 'linktexttotwitter' );
2041
- $leave_spaces = array( 'headertext' );
2042
-
2043
- foreach ( $checkbox_settings as $checkbox_setting ) {
2044
- $ctf_options[$checkbox_setting] = false;
2045
- }
2046
-
2047
- foreach ( $input as $key => $val ) {
2048
- if ( in_array( $key, $checkbox_settings ) ) {
2049
- if ( $val != 'on' ) {
2050
- $ctf_options[$key] = false;
2051
- } else {
2052
- $ctf_options[$key] = true;
2053
- }
2054
- } else {
2055
- if ( in_array( $key, $leave_spaces ) ) {
2056
- $ctf_options[$key] = $val;
2057
- } else {
2058
- $ctf_options[$key] = sanitize_text_field( $val );
2059
- }
2060
- }
2061
- }
2062
- }
2063
-
2064
- return $ctf_options;
2065
- } elseif ( isset( $input['access_token'] ) ) {
2066
- wp_cache_delete ( 'alloptions', 'options' );
2067
-
2068
- $new = get_option( 'ctf_options', array() );
2069
- $new['access_token'] = $input['access_token'];
2070
- $new['access_token_secret'] = $input['access_token_secret'];
2071
-
2072
- return $new;
2073
-
2074
- }
2075
- wp_cache_delete ( 'alloptions', 'options' );
2076
-
2077
- $new = get_option( 'ctf_options', array() );
2078
-
2079
- return $new;
2080
- }
2081
  }
1
+ <?php
2
+ /**
3
+ * Class CtfAdmin
4
+ *
5
+ * Uses the Settings API to create easily customizable settings pages and tabs
6
+ */
7
+
8
+ // Don't load directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ die( '-1' );
11
+ }
12
+
13
+ class CtfAdmin
14
+ {
15
+ public function __construct()
16
+ {
17
+ add_action( 'admin_menu', array( $this, 'add_menu' ) );
18
+ add_action( 'admin_init', array( $this, 'options_page_init' ) );
19
+ }
20
+
21
+ public function add_menu()
22
+ {
23
+ add_menu_page(
24
+ 'Twitter Feeds',
25
+ 'Twitter Feeds',
26
+ 'manage_options',
27
+ 'custom-twitter-feeds',
28
+ array( $this, 'create_options_page' ),
29
+ '',
30
+ 99
31
+ );
32
+
33
+ add_submenu_page(
34
+ 'custom-twitter-feeds',
35
+ 'Customize',
36
+ 'Customize',
37
+ 'manage_options',
38
+ 'custom-twitter-feeds-customize',
39
+ array( $this, 'create_submenu_page_customize' )
40
+ );
41
+
42
+ add_submenu_page(
43
+ 'custom-twitter-feeds',
44
+ 'Style',
45
+ 'Style',
46
+ 'manage_options',
47
+ 'custom-twitter-feeds-style',
48
+ array( $this, 'create_submenu_page_style' )
49
+ );
50
+
51
+ //Show a Instagram plugin menu item if it isn't already installed
52
+ if( !is_plugin_active( 'instagram-feed/instagram-feed.php' ) && !is_plugin_active( 'instagram-feed-pro/instagram-feed.php' ) ){
53
+ add_submenu_page(
54
+ 'custom-twitter-feeds',
55
+ __( 'Instagram Feed', 'ctf' ),
56
+ '<span class="ctf_get_sbi"><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="instagram" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-instagram fa-w-14 fa-2x"><path fill="currentColor" d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z" class=""></path></svg>' . __( 'Instagram Feed', 'ctf' ) . '</span>',
57
+ 'manage_options',
58
+ 'admin.php?page=custom-twitter-feeds&tab=more',
59
+ ''
60
+ );
61
+ }
62
+
63
+ //Show a Instagram plugin menu item if it isn't already installed
64
+ if( !is_plugin_active( 'custom-facebook-feed/custom-facebook-feed.php' ) && !is_plugin_active( 'custom-facebook-feed-pro/custom-facebook-feed.php' ) ){
65
+ add_submenu_page(
66
+ 'custom-twitter-feeds',
67
+ __( 'Facebook Feed', 'ctf' ),
68
+ '<span class="ctf_get_cff"><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="facebook" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-facebook fa-w-16 fa-2x"><path fill="currentColor" d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z" class=""></path></svg>' . __( 'Facebook Feed', 'ctf' ) . '</span>',
69
+ 'manage_options',
70
+ 'admin.php?page=custom-twitter-feeds&tab=more',
71
+ ''
72
+ );
73
+ }
74
+
75
+ //Show a YouTube plugin menu item if it isn't already installed
76
+ if( !is_plugin_active( 'feeds-for-youtube/youtube-feed.php' ) && !is_plugin_active( 'youtube-feed-pro/youtube-feed.php' ) ){
77
+ add_submenu_page(
78
+ 'custom-twitter-feeds',
79
+ __( 'YouTube Feed', 'ctf' ),
80
+ '<span class="ctf_get_yt"><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="youtube" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class="svg-inline--fa fa-youtube fa-w-18 fa-2x"><path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z" class=""></path></svg>' . __( 'YouTube Feed', 'ctf' ) . '</span>',
81
+ 'manage_options',
82
+ 'admin.php?page=custom-twitter-feeds&tab=more',
83
+ ''
84
+ );
85
+ }
86
+
87
+ add_submenu_page(
88
+ 'custom-twitter-feeds',
89
+ __( 'Social Wall', 'ctf' ),
90
+ '<span><svg style="height: 14px; margin: 0 8px 0 0; position: relative; top: 2px;" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="th" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-th fa-w-16 fa-2x"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z" class=""></path></svg>' . __( 'Social Wall', 'ctf' ) . '</span>',
91
+ 'manage_options',
92
+ 'ctf-sw',
93
+ array( $this, 'social_wall_page' )
94
+ );
95
+
96
+ add_submenu_page(
97
+ 'custom-twitter-feeds',
98
+ __( 'Try the Pro Demo', 'ctf' ),
99
+ __( '<span class="ctf_get_pro">Try the Pro Demo</span>', 'custom-twitter-feeds' ),
100
+ 'manage_options',
101
+ 'https://smashballoon.com/custom-twitter-feeds/demo/?utm_campaign=twitter-free&utm_source=menu-link&utm_medium=upgrade-link',
102
+ ''
103
+ );
104
+
105
+ }
106
+
107
+
108
+ public static function get_active_tab( $tab = '' )
109
+ {
110
+ switch ( $tab ) {
111
+ case 'customize':
112
+ return 'customize';
113
+ case 'style':
114
+ return 'style';
115
+ case 'display':
116
+ return 'display';
117
+ case 'support':
118
+ return 'support';
119
+ case 'allfeeds':
120
+ return 'allfeeds';
121
+ case 'more':
122
+ return 'more';
123
+ default:
124
+ return 'configure';
125
+ }
126
+ }
127
+
128
+ public function create_options_page()
129
+ {
130
+ require_once CTF_URL . '/views/admin/main.php';
131
+ }
132
+
133
+ public function create_submenu_page_customize()
134
+ {
135
+ $tab = 'customize';
136
+
137
+ require_once CTF_URL . '/views/admin/main.php';
138
+ }
139
+
140
+ public function create_submenu_page_style()
141
+ {
142
+ $tab = 'style';
143
+
144
+ require_once CTF_URL . '/views/admin/main.php';
145
+ }
146
+
147
+
148
+ public function social_wall_page() {
149
+ require_once CTF_URL .'views/admin/social-wall.php';
150
+ }
151
+
152
+ public function general_section_text()
153
+ {
154
+ // no explanation needed
155
+ }
156
+
157
+ public function access_token_button()
158
+ {
159
+ $this->the_admin_access_token_configure_html( $_GET );
160
+ $options = get_option( 'ctf_options' );
161
+ $option_checked = ( isset( $options['have_own_tokens'] ) ) ? $options['have_own_tokens'] : false;
162
+ ?>
163
+ <input name="<?php echo 'ctf_options'.'[have_own_tokens]'; ?>" id="ctf_have_own_tokens" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
164
+ <label for="ctf_have_own_tokens" class="ctf_checkbox"><?php _e( 'Or, manually enter my own Twitter app information' ); ?></label>
165
+ <span class="ctf-tooltip-wrap">
166
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
167
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Check this box if you would like to manually enter the information from your own <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/?utm_campaign=twitter-free&utm_source=settings&utm_medium=createownapp" target="_blank">Twitter app</a>', 'custom-twitter-feeds' ); ?>.</p>
168
+ </span>
169
+ <?php
170
+ }
171
+
172
+ /**
173
+ * generates the html for the access token retrieving button
174
+ *
175
+ * @param $access_token_data array the $_GET data if it exists
176
+ */
177
+ private function the_admin_access_token_configure_html( $access_token_data ) {
178
+ ?>
179
+
180
+ <div id="ctf_config">
181
+
182
+ <?php if ( isset( $access_token_data['oauth_token'] ) ) : ?>
183
+ <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
184
+ <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=buttonnotworking" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
185
+
186
+ <input type="hidden" id="ctf-retrieved-access-token" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token'] ) ); ?>">
187
+ <input type="hidden" id="ctf-retrieved-access-token-secret" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token_secret'] ) ); ?>">
188
+ <input type="hidden" id="ctf-retrieved-default-screen-name" value="<?php echo esc_html( sanitize_text_field( $access_token_data['screen_name'] ) ); ?>">
189
+
190
+ <?php elseif ( isset( $access_token_data['error'] ) && ! isset( $access_token_data['oauth_token'] ) ) : ?>
191
+
192
+ <p class="ctf_error_notice"><?php _e( 'There was an error with retrieving your access tokens. Please <a href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank">use this tool</a> to get your access token and secret.' ); ?></p><br>
193
+ <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
194
+ <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
195
+
196
+ <?php else : ?>
197
+
198
+ <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
199
+ <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=buttonnotworking" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
200
+
201
+ <?php endif; ?>
202
+
203
+ </div>
204
+ <?php
205
+ }
206
+
207
+ public function options_page_init()
208
+ {
209
+ /*
210
+ * "Configure" Tab
211
+ */
212
+
213
+ register_setting(
214
+ 'ctf_options', // name of the option that gets called in "get_option()"
215
+ 'ctf_options', // matches the options name
216
+ array( $this, 'validate_ctf_options' ) // callback function to validate and clean data
217
+ );
218
+
219
+ add_settings_section(
220
+ 'ctf_options_connect', // matches the section name
221
+ 'Configuration',
222
+ array( $this, 'access_token_button' ), // callback function to explain the section
223
+ 'ctf_options_connect' // matches the section name
224
+ );
225
+
226
+ // Consumer Key
227
+ $this->create_settings_field( array(
228
+ 'name' => 'consumer_key',
229
+ 'title' => '<label for="ctf_consumer_key">Consumer Key</label>', // label for the input field
230
+ 'callback' => 'default_text', // name of the function that outputs the html
231
+ 'page' => 'ctf_options_connect', // matches the section name
232
+ 'section' => 'ctf_options_connect', // matches the section name
233
+ 'option' => 'ctf_options', // matches the options name
234
+ 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
235
+ 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/?utm_campaign=twitter-free&utm_source=settings&utm_medium=createapp" target="_blank">here</a>', // what is this? text
236
+ 'size' => '27'
237
+ ) );
238
+
239
+ // Consumer Secret
240
+ $this->create_settings_field( array(
241
+ 'name' => 'consumer_secret',
242
+ 'title' => '<label for="ctf_consumer_secret">Consumer Secret</label>', // label for the input field
243
+ 'callback' => 'default_text', // name of the function that outputs the html
244
+ 'page' => 'ctf_options_connect', // matches the section name
245
+ 'section' => 'ctf_options_connect', // matches the section name
246
+ 'option' => 'ctf_options', // matches the options name
247
+ 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
248
+ 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/?utm_campaign=twitter-free&utm_source=settings&utm_medium=createapp" target="_blank">here</a>', // what is this? text
249
+ 'size' => '57'
250
+ ) );
251
+
252
+ // Access Token
253
+ $this->create_settings_field( array(
254
+ 'name' => 'access_token',
255
+ 'title' => '<label for="ctf_access_token">Access Token</label>', // label for the input field
256
+ 'callback' => 'default_text', // name of the function that outputs the html
257
+ 'page' => 'ctf_options_connect', // matches the section name
258
+ 'section' => 'ctf_options_connect', // matches the section name
259
+ 'option' => 'ctf_options', // matches the options name
260
+ 'class' => 'ctf-toggle-access', // class for the wrapper and input field
261
+ 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
262
+ 'size' => '57'
263
+ ) );
264
+
265
+ // Access Token Secret
266
+ $this->create_settings_field( array(
267
+ 'name' => 'access_token_secret',
268
+ 'title' => '<label for="ctf_access_token_secret">Access Token Secret</label>', // label for the input field
269
+ 'callback' => 'access_token_secret', // name of the function that outputs the html
270
+ 'page' => 'ctf_options_connect', // matches the section name
271
+ 'section' => 'ctf_options_connect', // matches the section name
272
+ 'option' => 'ctf_options', // matches the options name
273
+ 'class' => 'ctf-toggle-access', // class for the wrapper and input field
274
+ 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
275
+ 'size' => '57'
276
+ ));
277
+
278
+ add_settings_section(
279
+ 'ctf_options_feed_settings', // matches the section name
280
+ 'Feed Settings',
281
+ array( $this, 'general_section_text' ), // callback function to explain the section
282
+ 'ctf_options_feed_settings' // matches the section name
283
+ );
284
+
285
+ $locator_html = '';
286
+ if ( CTF_Feed_Locator::count_unique() > 1 ) {
287
+ $locator_html .= '<div class="ctf_locations_link">';
288
+ $locator_html .= '<a href="?page=custom-twitter-feeds&amp;tab=allfeeds"><svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="search" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-search fa-w-16 fa-2x"><path fill="currentColor" d="M508.5 468.9L387.1 347.5c-2.3-2.3-5.3-3.5-8.5-3.5h-13.2c31.5-36.5 50.6-84 50.6-136C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c52 0 99.5-19.1 136-50.6v13.2c0 3.2 1.3 6.2 3.5 8.5l121.4 121.4c4.7 4.7 12.3 4.7 17 0l22.6-22.6c4.7-4.7 4.7-12.3 0-17zM208 368c-88.4 0-160-71.6-160-160S119.6 48 208 48s160 71.6 160 160-71.6 160-160 160z" class=""></path></svg> ' . __( 'Feed Finder', 'custom-twitter-feeds' ) . '</a>';
289
+ $locator_html .= '</div>';
290
+ }
291
+
292
+ // User Timeline Radio
293
+ $this->create_settings_field( array(
294
+ 'name' => 'usertimeline',
295
+ 'title' => '<label for="ctf_feed_type">Select a Feed Type</label><code class="ctf_shortcode">type
296
+ Eg: screenname=gopro
297
+ Eg: home=true
298
+ Eg: hashtag=#cats</code>' . $locator_html, // label for the input field
299
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
300
+ 'page' => 'ctf_options_feed_settings', // matches the section name
301
+ 'section' => 'ctf_options_feed_settings', // matches the section name
302
+ 'option' => 'ctf_options', // matches the options name
303
+ 'class' => 'ctf-radio', // class for the wrapper and input field
304
+ 'whatis' => "Select this option and enter any screen name to create a user timeline feed", // what is this? text
305
+ 'label' => "User Timeline:",
306
+ 'has_input' => true,
307
+ 'has_replies' => true
308
+ ));
309
+
310
+ // Search Radio
311
+ $search_label = apply_filters( 'ctf_admin_search_label', '' );
312
+ $search_whatis = apply_filters( 'ctf_admin_search_whatis', '' );
313
+ $this->create_settings_field( array(
314
+ 'name' => 'search',
315
+ 'title' => '<label></label>', // label for the input field
316
+ 'callback' => 'feed_settings_radio_search', // name of the function that outputs the html
317
+ 'page' => 'ctf_options_feed_settings', // matches the section name
318
+ 'section' => 'ctf_options_feed_settings', // matches the section name
319
+ 'option' => 'ctf_options', // matches the options name
320
+ 'class' => 'ctf-radio', // class for the wrapper and input field
321
+ 'whatis' => $search_whatis, // what is this? text
322
+ 'label' => $search_label,
323
+ 'has_input' => true,
324
+ 'note_after_input' => __( '(only recent tweets initially)', 'custom-twitter-feeds' ),
325
+ 'extra' => true
326
+ ) );
327
+
328
+ // Home Timeline Radio
329
+ $this->create_settings_field( array(
330
+ 'name' => 'hometimeline',
331
+ 'title' => '<label></label>', // label for the input field
332
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
333
+ 'page' => 'ctf_options_feed_settings', // matches the section name
334
+ 'section' => 'ctf_options_feed_settings', // matches the section name
335
+ 'option' => 'ctf_options', // matches the options name
336
+ 'class' => 'ctf-radio', // class for the wrapper and input field
337
+ 'whatis' => 'Select this option to display tweets from yourself and those you follow', // what is this? text
338
+ 'label' => "Home Timeline",
339
+ 'has_input' => false,
340
+ 'has_replies' => true
341
+ ));
342
+
343
+ do_action( 'ctf_admin_endpoints', $this );
344
+
345
+ // Number of Tweets
346
+ $this->create_settings_field( array(
347
+ 'name' => 'num',
348
+ 'title' => '<label for="ctf_num">How Many Tweets to Display</label><code class="ctf_shortcode">num
349
+ Eg: num=10</code>', // label for the input field
350
+ 'callback' => 'default_text', // name of the function that outputs the html
351
+ 'page' => 'ctf_options_feed_settings', // matches the section name
352
+ 'section' => 'ctf_options_feed_settings', // matches the section name
353
+ 'option' => 'ctf_options', // matches the options name
354
+ 'class' => 'small-text', // class for the wrapper and input field
355
+ 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
356
+ 'type' => 'number', // input field "type" attribute
357
+ 'default' => 5
358
+ ));
359
+
360
+ // time unit for cache
361
+ $this->create_settings_field( array(
362
+ 'name' => 'cache_time',
363
+ 'title' => '<label for="ctf_cache_time">How Many Tweets to Display</label>', // label for the input field
364
+ 'callback' => 'default_text', // name of the function that outputs the html
365
+ 'page' => 'ctf_options_feed_settings', // matches the section name
366
+ 'section' => 'ctf_options_feed_settings', // matches the section name
367
+ 'option' => 'ctf_options', // matches the options name
368
+ 'class' => 'small-text', // class for the wrapper and input field
369
+ 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
370
+ 'type' => 'number' // input field "type" attribute
371
+ ));
372
+
373
+ // check for new tweets
374
+ $this->create_settings_field( array(
375
+ 'name' => 'cache_time',
376
+ 'title' => '<label for="ctf_cache_time">Check for new tweets every</label>', // label for the input field
377
+ 'callback' => 'cache_time', // name of the function that outputs the html
378
+ 'page' => 'ctf_options_feed_settings', // matches the section name
379
+ 'section' => 'ctf_options_feed_settings', // matches the section name
380
+ 'option' => 'ctf_options', // matches the options name
381
+ 'class' => 'short-text', // class for the wrapper and input field
382
+ 'whatis' => "Your Tweets are temporarily cached by the plugin in your WordPress database. You can choose how long the posts should be cached for. If you set the time to 1 hour then the plugin will clear the cache after that length of time and check Instagram for posts again" // what is this? text
383
+ ) );
384
+
385
+ // preserve settings
386
+ $this->create_settings_field( array(
387
+ 'name' => 'preserve_settings',
388
+ 'title' => '<label for="ctf_preserve_settings">Preserve settings when plugin is removed</label>', // label for the input field
389
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
390
+ 'page' => 'ctf_options_feed_settings', // matches the section name
391
+ 'section' => 'ctf_options_feed_settings', // matches the section name
392
+ 'option' => 'ctf_options', // matches the options name
393
+ 'class' => '',
394
+ 'whatis' => "When removing the plugin your settings are automatically erased. Checking this box will prevent any settings from being deleted. This means that you can uninstall and reinstall the plugin without losing your settings"
395
+ ));
396
+
397
+ /*
398
+ * "Customize" tab
399
+ */
400
+
401
+ add_settings_section(
402
+ 'ctf_options_general', // matches the section name
403
+ 'General',
404
+ array( $this, 'general_section_text' ), // callback function to explain the section
405
+ 'ctf_options_general' // matches the section name
406
+ );
407
+
408
+ // width
409
+ $this->create_settings_field( array(
410
+ 'name' => 'width',
411
+ 'title' => '<label for="ctf_width">Width of Feed</label><code class="ctf_shortcode">width
412
+ Eg: width=500</code>', // label for the input field
413
+ 'callback' => 'width_and_height_settings', // name of the function that outputs the html
414
+ 'page' => 'ctf_options_general', // matches the section name
415
+ 'section' => 'ctf_options_general', // matches the section name
416
+ 'option' => 'ctf_options', // matches the options name
417
+ 'class' => 'small-text',
418
+ 'default' => '100',
419
+ 'default_unit' => '%'
420
+ ));
421
+
422
+ // height
423
+ $this->create_settings_field( array(
424
+ 'name' => 'height',
425
+ 'title' => '<label for="ctf_height">Height of Feed</label><code class="ctf_shortcode">height
426
+ Eg: height=1000</code>', // label for the input field
427
+ 'callback' => 'width_and_height_settings', // name of the function that outputs the html
428
+ 'page' => 'ctf_options_general', // matches the section name
429
+ 'section' => 'ctf_options_general', // matches the section name
430
+ 'option' => 'ctf_options', // matches the options name
431
+ 'class' => 'small-text',
432
+ 'default_unit' => 'px'
433
+ ));
434
+
435
+ // class
436
+ $this->create_settings_field( array(
437
+ 'name' => 'class',
438
+ 'title' => '<label for="ctf_class">Add Custom CSS Class</label><code class="ctf_shortcode">class
439
+ Eg: class="my-class"</code>', // label for the input field
440
+ 'callback' => 'default_text', // name of the function that outputs the html
441
+ 'page' => 'ctf_options_general', // matches the section name
442
+ 'section' => 'ctf_options_general', // matches the section name
443
+ 'option' => 'ctf_options', // matches the options name
444
+ 'class' => 'default-text',
445
+ 'type' => 'text',
446
+ 'whatis' => "You can add your own CSS classes to the feed here. To add multiple classes separate each with a space, Eg. classone classtwo classthree"
447
+ ));
448
+
449
+ add_settings_section(
450
+ 'ctf_options_layout', // matches the section name
451
+ 'Layout',
452
+ array( $this, 'general_section_text' ), // callback function to explain the section
453
+ 'ctf_options_layout' // matches the section name
454
+ );
455
+
456
+ $settings = get_option( 'ctf_options', array() );
457
+ $layout = 'list';
458
+ $layout_selections = array(
459
+ 'layout' => $layout,
460
+ 'carouselcols' => isset( $settings['carouselcols'] ) ? $settings['carouselcols'] : 3,
461
+ 'carouselmobilecols' => isset( $settings['carouselmobilecols'] ) ? $settings['carouselmobilecols'] : 1,
462
+ 'carouselarrows' => isset( $settings['carouselarrows'] ) ? $settings['carouselarrows'] : 'onhover',
463
+ 'carouselpag' => isset( $settings['carouselpag'] ) ? $settings['carouselpag'] : true,
464
+ 'carouselheight' => isset( $settings['carouselheight'] ) ? $settings['carouselheight'] : 'tallest',
465
+ 'carouselautoplay' => isset( $settings['carouselautoplay'] ) ? $settings['carouselautoplay'] : false,
466
+ 'carouseltime' => isset( $settings['carouseltime'] ) ? $settings['carouseltime'] : '5000',
467
+ 'carouselloop' => isset( $settings['carouselloop'] ) ? $settings['carouselloop'] : 'infinite',
468
+ 'masonrycols' => isset( $settings['masonrycols'] ) ? $settings['masonrycols'] : 3,
469
+ 'masonrymobilecols' => isset( $settings['masonrymobilecols'] ) ? $settings['masonrymobilecols'] : 1,
470
+ );
471
+
472
+ $this->create_settings_field( array(
473
+ 'name' => 'class',
474
+ 'title' => '<label for="ctf_layout">Layout Type</label><code class="ctf_shortcode">layout
475
+ Eg: layout="masonry"</code>', // label for the input field
476
+ 'callback' => 'layout', // name of the function that outputs the html
477
+ 'page' => 'ctf_options_layout', // matches the section name
478
+ 'section' => 'ctf_options_layout', // matches the section name
479
+ 'option' => 'ctf_options', // matches the options name
480
+ 'class' => 'default-text ctf_pro',
481
+ 'type' => 'text',
482
+ 'layout_selections' => $layout_selections,
483
+ 'whatis' => ""
484
+ ));
485
+
486
+ add_settings_section(
487
+ 'ctf_options_showandhide', // matches the section name
488
+ 'Show/Hide',
489
+ array( $this, 'general_section_text' ), // callback function to explain the section
490
+ 'ctf_options_showandhide' // matches the section name
491
+ );
492
+
493
+ // show/hide
494
+ $show_hide_list = array(
495
+ array( 'include_retweeter', 'Retweeted text' ),
496
+ array( 'include_avatar', 'Avatar image' ),
497
+ array( 'include_author', 'Author name' ),
498
+ array( 'include_logo', 'Twitter logo' ),
499
+ array( 'include_text', 'Tweet text' ),
500
+ array( 'include_media_placeholder', 'Media placeholder' ),
501
+ array( 'include_date', 'Date' ),
502
+ array( 'include_actions', 'Tweet actions (reply, retweet, like)' ),
503
+ array( 'include_twitterlink', '"Twitter" link' ),
504
+ array( 'include_linkbox', 'Quoted tweet box' )
505
+ );
506
+ $show_hide_list = apply_filters( 'ctf_admin_show_hide_list', $show_hide_list );
507
+
508
+ $this->create_settings_field( array(
509
+ 'name' => 'showandhide',
510
+ 'title' => '<label>Include the Following in Tweets <em>(when applicable)</em></label><code class="ctf_shortcode">include exclude
511
+ Eg: include=author,date
512
+ Eg: exclude=actions
513
+ Options: avatar, author,
514
+ logo, text, placeholder,
515
+ date, actions, linkbox </code>', // label for the input field
516
+ 'callback' => 'include_exclude_checkbox', // name of the function that outputs the html
517
+ 'page' => 'ctf_options_showandhide', // matches the section name
518
+ 'section' => 'ctf_options_showandhide', // matches the section name
519
+ 'option' => 'ctf_options', // matches the options name
520
+ 'fields' => $show_hide_list,
521
+ 'class' => ''
522
+ ));
523
+
524
+ // show header
525
+ $this->create_settings_field( array(
526
+ 'name' => 'showheader',
527
+ 'title' => '<label for="ctf_showheader">Show Header</label><code class="ctf_shortcode">showheader
528
+ Eg: showheader=true</code>', // label for the input field
529
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
530
+ 'page' => 'ctf_options_showandhide', // matches the section name
531
+ 'section' => 'ctf_options_showandhide', // matches the section name
532
+ 'option' => 'ctf_options', // matches the options name
533
+ 'class' => '',
534
+ 'whatis' => "The header is displayed above your tweets with some basic information about the feed"
535
+ ));
536
+
537
+ // load more button
538
+ $this->create_settings_field( array(
539
+ 'name' => 'showbutton',
540
+ 'title' => '<label for="ctf_showbutton">Show the "Load More" Button</label><code class="ctf_shortcode">showbutton
541
+ Eg: showbutton=true</code>', // label for the input field
542
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
543
+ 'page' => 'ctf_options_showandhide', // matches the section name
544
+ 'section' => 'ctf_options_showandhide', // matches the section name
545
+ 'option' => 'ctf_options', // matches the options name
546
+ 'class' => '',
547
+ 'whatis' => "Show the Load More Button",
548
+ ));
549
+
550
+
551
+ // credit ctf
552
+ $this->create_settings_field( array(
553
+ 'name' => 'creditctf',
554
+ 'title' => '<label for="ctf_creditctf">Add Custom Twitter Feeds Credit</label><code class="ctf_shortcode">creditctf
555
+ Eg: creditctf=true</code>', // label for the input field
556
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
557
+ 'page' => 'ctf_options_showandhide', // matches the section name
558
+ 'section' => 'ctf_options_showandhide', // matches the section name
559
+ 'option' => 'ctf_options', // matches the options name
560
+ 'class' => '',
561
+ 'whatis' => "Help us keep this plugin great! Add a link below your feed to credit Custom Twitter Feeds by Smash Balloon"
562
+ ));
563
+
564
+ do_action( 'ctf_admin_customize_option', $this );
565
+
566
+ add_settings_section(
567
+ 'ctf_options_misc', // matches the section name
568
+ 'Misc',
569
+ array( $this, 'general_section_text' ), // callback function to explain the section
570
+ 'ctf_options_misc' // matches the section name
571
+ );
572
+
573
+ // Custom CSS
574
+ $this->create_settings_field( array(
575
+ 'name' => 'custom_css',
576
+ 'title' => '<label for="ctf_custom_css">Custom CSS</label>', // label for the input field
577
+ 'callback' => 'custom_code', // name of the function that outputs the html
578
+ 'page' => 'ctf_options_misc', // matches the section name
579
+ 'section' => 'ctf_options_misc', // matches the section name
580
+ 'option' => 'ctf_options', // matches the options name
581
+ 'class' => 'default-text', // class for the wrapper and input field
582
+ 'description' => 'Enter your own custom CSS in the box below'
583
+ ));
584
+
585
+ // Custom JS
586
+ $this->create_settings_field( array(
587
+ 'name' => 'custom_js',
588
+ 'title' => '<label for="ctf_custom_js">Custom Javascript*</label>', // label for the input field
589
+ 'callback' => 'custom_code', // name of the function that outputs the html
590
+ 'page' => 'ctf_options_misc', // matches the section name
591
+ 'section' => 'ctf_options_misc', // matches the section name
592
+ 'option' => 'ctf_options', // matches the options name
593
+ 'class' => 'default-text', // class for the wrapper and input field
594
+ 'description' => 'Enter your own custom Javascript/JQuery in the box below',
595
+ 'extra' => '*will be fired every time more tweets are loaded'
596
+ ));
597
+
598
+ add_settings_section(
599
+ 'ctf_options_gdpr', // matches the section name
600
+ 'GDPR',
601
+ array( $this, 'general_section_text' ), // callback function to explain the section
602
+ 'ctf_options_gdpr' // matches the section name
603
+ );
604
+
605
+ $this->create_settings_field( array(
606
+ 'name' => 'gdpr',
607
+ 'title' => '<label for="ctf_gdpr">Enable GDPR Settings</label>', // label for the input field
608
+ 'callback' => 'gdpr', // name of the function that outputs the html
609
+ 'page' => 'ctf_options_gdpr', // matches the section name
610
+ 'section' => 'ctf_options_gdpr', // matches the section name
611
+ 'option' => 'ctf_options', // matches the options name
612
+ 'class' => 'default-text', // class for the wrapper and input field
613
+ ));
614
+
615
+ add_settings_section(
616
+ 'ctf_options_advanced', // matches the section name
617
+ 'Advanced',
618
+ array( $this, 'general_section_text' ), // callback function to explain the section
619
+ 'ctf_options_advanced' // matches the section name
620
+ );
621
+
622
+ // ajax theme
623
+ $this->create_settings_field( array(
624
+ 'name' => 'ajax_theme',
625
+ 'title' => '<label for="ctf_ajax_theme">Are you using an Ajax powered theme?</label>', // label for the input field
626
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
627
+ 'page' => 'ctf_options_advanced', // matches the section name
628
+ 'section' => 'ctf_options_advanced', // matches the section name
629
+ 'option' => 'ctf_options', // matches the options name
630
+ 'class' => '',
631
+ 'whatis' => "When navigating your site, if your theme uses Ajax to load content into your pages (meaning your page doesn't refresh) then check this setting. If you're not sure then please check with the theme author"
632
+ ));
633
+
634
+ // Request Method
635
+ $this->create_settings_field( array(
636
+ 'name' => 'request_method',
637
+ 'title' => '<label for="ctf_request_method">Request Method</label>', // label for the input field
638
+ 'callback' => 'default_select', // name of the function that outputs the html
639
+ 'page' => 'ctf_options_advanced', // matches the section name
640
+ 'section' => 'ctf_options_advanced', // matches the section name
641
+ 'option' => 'ctf_options', // matches the options name
642
+ 'class' => 'default-text', // class for the wrapper and input field
643
+ 'fields' => array(
644
+ 1 => array( 'auto', 'Auto' ),
645
+ 2 => array( 'curl', 'cURL' ),
646
+ 3 => array( 'file_get_contents', 'file_get_contents()' ),
647
+ 4 => array( 'wp_http', 'WP_Http' )
648
+ ),
649
+ 'whatis' => "Explicitly set the request method. You would only want to change this if you are unable to connect to the Twitter API" // what is this? text
650
+ ) );
651
+
652
+ // force cache to clear on interval
653
+ $this->create_settings_field( array(
654
+ 'name' => 'cron_cache_clear',
655
+ 'title' => '<label for="ctf_cron_cache_clear">Force cache to clear on interval</label>', // label for the input field
656
+ 'callback' => 'default_select', // name of the function that outputs the html
657
+ 'page' => 'ctf_options_advanced', // matches the section name
658
+ 'section' => 'ctf_options_advanced', // matches the section name
659
+ 'option' => 'ctf_options', // matches the options name
660
+ 'class' => 'default-text', // class for the wrapper and input field
661
+ 'fields' => array(
662
+ 1 => array( 'unset', '-' ),
663
+ 2 => array( 'yes', 'Yes' ),
664
+ 3 => array( 'no', 'No' )
665
+ ),
666
+ 'whatis' => "If you're experiencing an issue with the plugin not auto-updating then you can set this to 'Yes' to run a scheduled event behind the scenes which forces the plugin cache to clear on a regular basis and retrieve new data from Twitter" // what is this? text
667
+ ) );
668
+
669
+ // tweet multiplier
670
+ $this->create_settings_field( array(
671
+ 'name' => 'multiplier',
672
+ 'title' => '<label for="ctf_multiplier">Tweet Multiplier</label><code class="ctf_shortcode">multiplier
673
+ Eg: multiplier=1.5</code>', // label for the input field
674
+ 'callback' => 'default_text', // name of the function that outputs the html
675
+ 'page' => 'ctf_options_advanced', // matches the section name
676
+ 'section' => 'ctf_options_advanced', // matches the section name
677
+ 'option' => 'ctf_options', // matches the options name
678
+ 'class' => 'small-text', // class for the wrapper and input field
679
+ 'whatis' => "If your feed excludes reply tweets (this is automatic in hashtag/search feeds), the correct number of tweets may not show up. Increasing this number will increase the number of tweets retrieved but will also increase the load time for the feed as well", // what is this? text
680
+ 'type' => 'number', // input field "type" attribute
681
+ 'min' => 1,
682
+ 'max' => 3,
683
+ 'step' => 'any',
684
+ 'default' => 1.25
685
+ ));
686
+
687
+ $this->create_settings_field( array(
688
+ 'name' => 'persistent',
689
+ 'title' => '<label for="ctf_multiplier">Clear Persistent Cache</label>', // label for the input field
690
+ 'callback' => 'clear_persistent_cache_button', // name of the function that outputs the html
691
+ 'page' => 'ctf_options_advanced', // matches the section name
692
+ 'section' => 'ctf_options_advanced', // matches the section name
693
+ 'option' => 'ctf_options', // matches the options name
694
+ 'class' => 'small-text' // class for the wrapper and input field
695
+ ));
696
+
697
+ // persistent cache
698
+ $this->create_settings_field( array(
699
+ 'name' => 'persistentcache',
700
+ 'title' => '<label for="ctf_persistentcache">Persistent cache enabled by default</label><code class="ctf_shortcode">persistentcache
701
+ Eg: persistentcache=false</code>', // label for the input field
702
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
703
+ 'page' => 'ctf_options_advanced', // matches the section name
704
+ 'section' => 'ctf_options_advanced', // matches the section name
705
+ 'option' => 'ctf_options', // matches the options name
706
+ 'class' => '',
707
+ 'whatis' => "Checking this box will make all Search and Hashtag feeds have a permanent cache saved in the database by default of up to 150 tweets. Tweets will be available for the feed even after the 7 day limit though numbers of retweets and likes will not update"
708
+ ));
709
+
710
+ $this->create_settings_field( array(
711
+ 'name' => 'selfreplies',
712
+ 'title' => '<label for="ctf_selfreplies">Always include replies to self in the feed</label><code class="ctf_shortcode">autores
713
+ Eg: selfreplies=true</code>', // label for the input field
714
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
715
+ 'page' => 'ctf_options_advanced', // matches the section name
716
+ 'section' => 'ctf_options_advanced', // matches the section name
717
+ 'option' => 'ctf_options', // matches the options name
718
+ 'class' => '',
719
+ 'whatis' => "Twitter considers @mentions of your own account and replies to your own account's tweets as \"reply\" tweets. Enable this setting to include these type of tweets in your feed"
720
+ ));
721
+
722
+ $this->create_settings_field( array(
723
+ 'name' => 'disableintents',
724
+ 'title' => '<label for="ctf_disableintents">Disable Twitter intents JS</label><code class="ctf_shortcode">disableintents
725
+ Eg: disableintents=true</code>', // label for the input field
726
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
727
+ 'page' => 'ctf_options_advanced', // matches the section name
728
+ 'section' => 'ctf_options_advanced', // matches the section name
729
+ 'option' => 'ctf_options', // matches the options name
730
+ 'class' => '',
731
+ 'whatis' => "Twitter provides JavaScript that allows visitors of your site to reply to, retweet, and like tweets without leaving your site. This can be disabled using this setting"
732
+ ));
733
+
734
+ $this->create_settings_field( array(
735
+ 'name' => 'shorturls',
736
+ 'title' => '<label for="ctf_shorturls">Use shortened urls</label>', // label for the input field
737
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
738
+ 'page' => 'ctf_options_advanced', // matches the section name
739
+ 'section' => 'ctf_options_advanced', // matches the section name
740
+ 'option' => 'ctf_options', // matches the options name
741
+ 'class' => '',
742
+ 'whatis' => "Twitter provides shortened versions of links in tweets. Enable this setting to use them instead of the full URLs"
743
+ ));
744
+
745
+ /**
746
+ * "Style" tab
747
+ */
748
+
749
+ add_settings_section(
750
+ 'ctf_options_general_style', // matches the section name
751
+ 'General',
752
+ array( $this, 'general_section_text' ), // callback function to explain the section
753
+ 'ctf_options_general_style' // matches the section name
754
+ );
755
+
756
+ // background color
757
+ $this->create_settings_field( array(
758
+ 'name' => 'bgcolor',
759
+ 'title' => '<label for="ctf_bgcolor">Feed Background Color</label><code class="ctf_shortcode">bgcolor
760
+ Eg: bgcolor=#eee</code>', // label for the input field
761
+ 'callback' => 'default_color', // name of the function that outputs the html
762
+ 'page' => 'ctf_options_general_style', // matches the section name
763
+ 'section' => 'ctf_options_general_style', // matches the section name
764
+ 'option' => 'ctf_options', // matches the options name
765
+ 'class' => '',
766
+ 'whatis' => "The background color of the feed"
767
+ ));
768
+
769
+ // tweet background color
770
+ $this->create_settings_field( array(
771
+ 'name' => 'tweetbgcolor',
772
+ 'title' => '<label for="ctf_tweetbgcolor">Tweet Background Color</label><code class="ctf_shortcode">tweetbgcolor
773
+ Eg: tweetbgcolor=#eee</code>', // label for the input field
774
+ 'callback' => 'default_color', // name of the function that outputs the html
775
+ 'page' => 'ctf_options_general_style', // matches the section name
776
+ 'section' => 'ctf_options_general_style', // matches the section name
777
+ 'option' => 'ctf_options', // matches the options name
778
+ 'class' => '',
779
+ 'whatis' => "The background color of each tweet"
780
+ ));
781
+
782
+ add_settings_section(
783
+ 'ctf_options_header', // matches the section name
784
+ 'Header',
785
+ array( $this, 'general_section_text' ), // callback function to explain the section
786
+ 'ctf_options_header' // matches the section name
787
+ );
788
+
789
+ // show bio
790
+ $this->create_settings_field( array(
791
+ 'name' => 'showbio',
792
+ 'title' => '<label for="ctf_showbio">Show Bio</label><code class="ctf_shortcode">showbio
793
+ Eg: showbio=false</code>', // label for the input field
794
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
795
+ 'page' => 'ctf_options_header', // matches the section name
796
+ 'section' => 'ctf_options_header', // matches the section name
797
+ 'option' => 'ctf_options', // matches the options name
798
+ 'class' => 'default-text', // class for the wrapper and input field
799
+ 'whatis' => "Show the bio text description on the header of the feed"
800
+ ));
801
+
802
+ // header background color
803
+ $this->create_settings_field( array(
804
+ 'name' => 'headerbgcolor',
805
+ 'title' => '<label for="ctf_headerbgcolor">Header Background Color</label><code class="ctf_shortcode">headerbgcolor
806
+ Eg: headerbgcolor=#ee0</code>', // label for the input field
807
+ 'callback' => 'default_color', // name of the function that outputs the html
808
+ 'page' => 'ctf_options_header', // matches the section name
809
+ 'section' => 'ctf_options_header', // matches the section name
810
+ 'option' => 'ctf_options', // matches the options name
811
+ 'class' => ''
812
+ ));
813
+
814
+ // header text color
815
+ $this->create_settings_field( array(
816
+ 'name' => 'headertextcolor',
817
+ 'title' => '<label for="ctf_headertextcolor">Header Text Color</label><code class="ctf_shortcode">headertextcolor
818
+ Eg: headertextcolor=#444</code>', // label for the input field
819
+ 'callback' => 'default_color', // name of the function that outputs the html
820
+ 'page' => 'ctf_options_header', // matches the section name
821
+ 'section' => 'ctf_options_header', // matches the section name
822
+ 'option' => 'ctf_options', // matches the options name
823
+ 'class' => ''
824
+ ));
825
+
826
+
827
+ // custom header text
828
+ $this->create_settings_field( array(
829
+ 'name' => 'headertext',
830
+ 'title' => '<label for="ctf_headertext">Custom Header Text</label><code class="ctf_shortcode">headertext
831
+ Eg: headertext="Tweets from @SmashBalloon"</code>', // label for the input field
832
+ 'callback' => 'default_text', // name of the function that outputs the html
833
+ 'page' => 'ctf_options_header', // matches the section name
834
+ 'section' => 'ctf_options_header', // matches the section name
835
+ 'option' => 'ctf_options', // matches the options name
836
+ 'class' => 'default-text', // class for the wrapper and input field
837
+ 'whatis' => 'This will replace the default text displayed inside the optional header of the feed' // "what is this?" text
838
+ ));
839
+
840
+ add_settings_section(
841
+ 'ctf_options_date', // matches the section name
842
+ 'Date',
843
+ array( $this, 'general_section_text' ), // callback function to explain the section
844
+ 'ctf_options_date' // matches the section name
845
+ );
846
+
847
+ // Timezone
848
+ $this->create_settings_field( array(
849
+ 'name' => 'timezone',
850
+ 'title' => '<label for="ctf_timezone">Timezone</label>', // label for the input field
851
+ 'callback' => 'feed_settings_timezone', // name of the function that outputs the html
852
+ 'page' => 'ctf_options_date', // matches the section name
853
+ 'section' => 'ctf_options_date', // matches the section name
854
+ 'option' => 'ctf_options', // matches the options name
855
+ 'class' => 'default-text', // class for the wrapper and input field
856
+ 'whatis' => "Select a timezone for displaying date and timestamps of tweets" // what is this? text
857
+ ));
858
+
859
+ // Date Format
860
+ $this->create_settings_field( array(
861
+ 'name' => 'dateformat',
862
+ 'title' => '<label for="ctf_date_format">Date Format</label><code class="ctf_shortcode">dateformat
863
+ Eg: dateformat=3</code>', // label for the input field
864
+ 'callback' => 'customize_date_format', // name of the function that outputs the html
865
+ 'page' => 'ctf_options_date', // matches the section name
866
+ 'section' => 'ctf_options_date', // matches the section name
867
+ 'option' => 'ctf_options', // matches the options name
868
+ 'class' => 'default-text', // class for the wrapper and input field
869
+ 'whatis' => "Select the format you would like for dates in tweets" // what is this? text
870
+ ));
871
+
872
+ // Custom Date Format
873
+ $this->create_settings_field( array(
874
+ 'name' => 'datecustom',
875
+ 'title' => '<label for="ctf_custom_date_format">Custom Format</label><code class="ctf_shortcode">datecustom
876
+ Eg: datecustom="D M jS, Y"</code>', // label for the input field
877
+ 'callback' => 'customize_custom_date_format', // name of the function that outputs the html
878
+ 'page' => 'ctf_options_date', // matches the section name
879
+ 'section' => 'ctf_options_date', // matches the section name
880
+ 'option' => 'ctf_options', // matches the options name
881
+ 'class' => 'default-text', // class for the wrapper and input field
882
+ ));
883
+
884
+ // Custom Time Translations
885
+ $this->create_settings_field( array(
886
+ 'name' => 'custom_time_translations',
887
+ 'title' => '<label>Custom Time Translations</label><code class="ctf_shortcode">mtime, htime,
888
+ nowtime
889
+ Eg: mtime="M"
890
+ Eg: htime="S"
891
+ Eg: nowtime="Jetzt"</code>', // label for the input field
892
+ 'callback' => 'customize_custom_time_translations', // name of the function that outputs the html
893
+ 'page' => 'ctf_options_date', // matches the section name
894
+ 'section' => 'ctf_options_date', // matches the section name
895
+ 'option' => 'ctf_options', // matches the options name
896
+ 'class' => 'default-text', // class for the wrapper and input field
897
+ ));
898
+
899
+ // date Text Size
900
+ $this->create_settings_field( array(
901
+ 'name' => 'datetextsize',
902
+ 'title' => '<label for="ctf_datetextsize">Date Text Size</label><code class="ctf_shortcode">datetextsize
903
+ Eg: datetextsize=16</code>', // label for the input field
904
+ 'callback' => 'text_size', // name of the function that outputs the html
905
+ 'page' => 'ctf_options_date', // matches the section name
906
+ 'section' => 'ctf_options_date', // matches the section name
907
+ 'option' => 'ctf_options', // matches the options name
908
+ 'class' => 'default-text', // class for the wrapper and input field
909
+ ));
910
+
911
+ // date text weight
912
+ $this->create_settings_field( array(
913
+ 'name' => 'datetextweight',
914
+ 'title' => '<label for="ctf_datetextweight">Date Text Weight</label><code class="ctf_shortcode">datetextweight
915
+ Eg: datetextweight=bold</code>', // label for the input field
916
+ 'callback' => 'text_weight', // name of the function that outputs the html
917
+ 'page' => 'ctf_options_date', // matches the section name
918
+ 'section' => 'ctf_options_date', // matches the section name
919
+ 'option' => 'ctf_options', // matches the options name
920
+ 'class' => '',
921
+ ));
922
+
923
+ add_settings_section(
924
+ 'ctf_options_author', // matches the section name
925
+ 'Author',
926
+ array( $this, 'general_section_text' ), // callback function to explain the section
927
+ 'ctf_options_author' // matches the section name
928
+ );
929
+
930
+ // Author Text Size
931
+ $this->create_settings_field( array(
932
+ 'name' => 'authortextsize',
933
+ 'title' => '<label for="ctf_authortextsize">Author Text Size</label><code class="ctf_shortcode">authortextsize
934
+ Eg: authortextsize=16</code>', // label for the input field
935
+ 'callback' => 'text_size', // name of the function that outputs the html
936
+ 'page' => 'ctf_options_author', // matches the section name
937
+ 'section' => 'ctf_options_author', // matches the section name
938
+ 'option' => 'ctf_options', // matches the options name
939
+ 'class' => 'default-text', // class for the wrapper and input field
940
+ ));
941
+
942
+ // author text weight
943
+ $this->create_settings_field( array(
944
+ 'name' => 'authortextweight',
945
+ 'title' => '<label for="ctf_authortextcolor">Author Text Weight</label><code class="ctf_shortcode">authortextweight
946
+ Eg: authortextweight=bold</code>', // label for the input field
947
+ 'callback' => 'text_weight', // name of the function that outputs the html
948
+ 'page' => 'ctf_options_author', // matches the section name
949
+ 'section' => 'ctf_options_author', // matches the section name
950
+ 'option' => 'ctf_options', // matches the options name
951
+ 'class' => '',
952
+ ));
953
+
954
+ $this->create_settings_field( array(
955
+ 'name' => 'logosize',
956
+ 'title' => '<label for="ctf_logotextsize">Twitter Logo Size</label><code class="ctf_shortcode">logosize
957
+ Eg: logosize=16</code>', // label for the input field
958
+ 'callback' => 'text_size', // name of the function that outputs the html
959
+ 'page' => 'ctf_options_author', // matches the section name
960
+ 'section' => 'ctf_options_author', // matches the section name
961
+ 'option' => 'ctf_options', // matches the options name
962
+ 'class' => 'default-text', // class for the wrapper and input field
963
+ ));
964
+
965
+ $this->create_settings_field( array(
966
+ 'name' => 'logocolor',
967
+ 'title' => '<label for="ctf_logocolor">Logo Color</label><code class="ctf_shortcode">logocolor
968
+ Eg: logocolor=#333</code>', // label for the input field
969
+ 'callback' => 'default_color', // name of the function that outputs the html
970
+ 'page' => 'ctf_options_author', // matches the section name
971
+ 'section' => 'ctf_options_author', // matches the section name
972
+ 'option' => 'ctf_options', // matches the options name
973
+ 'class' => '',
974
+ ));
975
+
976
+ add_settings_section(
977
+ 'ctf_options_text', // matches the section name
978
+ 'Tweet Text',
979
+ array( $this, 'general_section_text' ), // callback function to explain the section
980
+ 'ctf_options_text' // matches the section name
981
+ );
982
+
983
+ // Tweet Text Size
984
+ $this->create_settings_field( array(
985
+ 'name' => 'tweettextsize',
986
+ 'title' => '<label for="ctf_tweettextsize">Tweet Text Size</label><code class="ctf_shortcode">tweettextsize
987
+ Eg: tweettextsize=16</code>', // label for the input field
988
+ 'callback' => 'text_size', // name of the function that outputs the html
989
+ 'page' => 'ctf_options_text', // matches the section name
990
+ 'section' => 'ctf_options_text', // matches the section name
991
+ 'option' => 'ctf_options', // matches the options name
992
+ 'class' => 'default-text', // class for the wrapper and input field
993
+ ));
994
+
995
+ // tweet text weight
996
+ $this->create_settings_field( array(
997
+ 'name' => 'tweettextweight',
998
+ 'title' => '<label for="ctf_tweettextweight">Tweet Text Weight</label><code class="ctf_shortcode">tweettextweight
999
+ Eg: tweettextweight=bold</code>', // label for the input field
1000
+ 'callback' => 'text_weight', // name of the function that outputs the html
1001
+ 'page' => 'ctf_options_text', // matches the section name
1002
+ 'section' => 'ctf_options_text', // matches the section name
1003
+ 'option' => 'ctf_options', // matches the options name
1004
+ 'class' => '',
1005
+ ));
1006
+
1007
+ // text color
1008
+ $this->create_settings_field( array(
1009
+ 'name' => 'textcolor',
1010
+ 'title' => '<label for="ctf_textcolor">Text Color</label><code class="ctf_shortcode">textcolor
1011
+ Eg: textcolor=#333</code>', // label for the input field
1012
+ 'callback' => 'default_color', // name of the function that outputs the html
1013
+ 'page' => 'ctf_options_text', // matches the section name
1014
+ 'section' => 'ctf_options_text', // matches the section name
1015
+ 'option' => 'ctf_options', // matches the options name
1016
+ 'class' => '',
1017
+ ));
1018
+
1019
+ $this->create_settings_field( array(
1020
+ 'name' => 'textlength',
1021
+ 'title' => '<label for="ctf_textlength">Text Length</label><code class="ctf_shortcode">textlength
1022
+ Eg: textlength=150</code>', // label for the input field
1023
+ 'callback' => 'default_text', // name of the function that outputs the html
1024
+ 'page' => 'ctf_options_text', // matches the section name
1025
+ 'section' => 'ctf_options_text', // matches the section name
1026
+ 'option' => 'ctf_options', // matches the options name
1027
+ 'class' => '',
1028
+ 'default' => 280,
1029
+ 'min' => 20,
1030
+ 'max' => 280,
1031
+ 'step' => 1,
1032
+ 'example' => 'characters',
1033
+ 'type' => 'number',
1034
+ 'whatis' => 'The number of characters of text to display in the tweet text. An ellipsis link will be added to allow the user to reveal more text if desired',
1035
+ ));
1036
+
1037
+ // custom retweeted text
1038
+ $this->create_settings_field( array(
1039
+ 'name' => 'retweetedtext',
1040
+ 'title' => '<label for="ctf_retweetedtext">Translation for "Retweeted"</label><code class="ctf_shortcode">retweetedtext
1041
+ Eg: retweetedtext="retuiteó"</code>', // label for the input field
1042
+ 'callback' => 'default_text', // name of the function that outputs the html
1043
+ 'page' => 'ctf_options_text', // matches the section name
1044
+ 'section' => 'ctf_options_text', // matches the section name
1045
+ 'option' => 'ctf_options', // matches the options name
1046
+ 'class' => 'default-text', // class for the wrapper and input field
1047
+ 'whatis' => 'This will replace the default text displayed for retweeted texts',
1048
+ 'default' => 'Retweeted'// "what is this?" text
1049
+ ));
1050
+
1051
+ add_settings_section(
1052
+ 'ctf_options_links', // matches the section name
1053
+ 'Links',
1054
+ array( $this, 'general_section_text' ), // callback function to explain the section
1055
+ 'ctf_options_links' // matches the section name
1056
+ );
1057
+
1058
+ // disable links
1059
+ $this->create_settings_field( array(
1060
+ 'name' => 'disablelinks',
1061
+ 'title' => '<label for="ctf_disablelinks">Disable Links in Tweet Text</label><code class="ctf_shortcode">disablelinks
1062
+ Eg: disablelinks=true</code>', // label for the input field
1063
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
1064
+ 'page' => 'ctf_options_links', // matches the section name
1065
+ 'section' => 'ctf_options_links', // matches the section name
1066
+ 'option' => 'ctf_options', // matches the options name
1067
+ 'class' => '',
1068
+ 'whatis' => "By default, links, hashtags, and mentions are turned into links inside the tweet text"
1069
+ ));
1070
+
1071
+ // link text to twitter
1072
+ $this->create_settings_field( array(
1073
+ 'name' => 'linktexttotwitter',
1074
+ 'title' => '<label for="ctf_linktexttotwitter">Link Tweet Text to Twitter</label><code class="ctf_shortcode">linktexttotwitter
1075
+ Eg: linktexttotwitter=true</code>', // label for the input field
1076
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
1077
+ 'page' => 'ctf_options_links', // matches the section name
1078
+ 'section' => 'ctf_options_links', // matches the section name
1079
+ 'option' => 'ctf_options', // matches the options name
1080
+ 'class' => '',
1081
+ 'whatis' => "Clicking on the text of the tweet will link to the tweet on Twitter"
1082
+ ));
1083
+
1084
+ // link text color
1085
+ $this->create_settings_field( array(
1086
+ 'name' => 'linktextcolor',
1087
+ 'title' => '<label for="ctf_linktextcolor">Links in Tweets Text Color</label><code class="ctf_shortcode">linktextcolor
1088
+ Eg: linktextcolor=#00e</code>', // label for the input field
1089
+ 'callback' => 'default_color', // name of the function that outputs the html
1090
+ 'page' => 'ctf_options_links', // matches the section name
1091
+ 'section' => 'ctf_options_links', // matches the section name
1092
+ 'option' => 'ctf_options', // matches the options name
1093
+ 'class' => '',
1094
+ ));
1095
+
1096
+ add_settings_section(
1097
+ 'ctf_options_quoted', // matches the section name
1098
+ 'Retweet Boxes',
1099
+ array( $this, 'general_section_text' ), // callback function to explain the section
1100
+ 'ctf_options_quoted' // matches the section name
1101
+ );
1102
+
1103
+ // quoted author Size
1104
+ $this->create_settings_field( array(
1105
+ 'name' => 'quotedauthorsize',
1106
+ 'title' => '<label for="ctf_quotedauthorsize">Quoted Author Size</label><code class="ctf_shortcode">quotedauthorsize
1107
+ Eg: quotedauthorsize=16</code>', // label for the input field
1108
+ 'callback' => 'text_size', // name of the function that outputs the html
1109
+ 'page' => 'ctf_options_quoted', // matches the section name
1110
+ 'section' => 'ctf_options_quoted', // matches the section name
1111
+ 'option' => 'ctf_options', // matches the options name
1112
+ 'class' => 'default-text', // class for the wrapper and input field
1113
+ ));
1114
+
1115
+ // quoted author weight
1116
+ $this->create_settings_field( array(
1117
+ 'name' => 'quotedauthorweight',
1118
+ 'title' => '<label for="ctf_quotedauthorweight">Quoted Author Weight</label><code class="ctf_shortcode">quotedauthorweight
1119
+ Eg: quotedauthorweight=bold</code>', // label for the input field
1120
+ 'callback' => 'text_weight', // name of the function that outputs the html
1121
+ 'page' => 'ctf_options_quoted', // matches the section name
1122
+ 'section' => 'ctf_options_quoted', // matches the section name
1123
+ 'option' => 'ctf_options', // matches the options name
1124
+ 'class' => '',
1125
+ ));
1126
+
1127
+ add_settings_section(
1128
+ 'ctf_options_actions', // matches the section name
1129
+ 'Tweets Actions',
1130
+ array( $this, 'general_section_text' ), // callback function to explain the section
1131
+ 'ctf_options_actions' // matches the section name
1132
+ );
1133
+
1134
+ // icon Size
1135
+ $this->create_settings_field( array(
1136
+ 'name' => 'iconsize',
1137
+ 'title' => '<label for="ctf_iconsize">Icon Size</label><code class="ctf_shortcode">iconsize
1138
+ Eg: iconsize=16</code>', // label for the input field
1139
+ 'callback' => 'text_size', // name of the function that outputs the html
1140
+ 'page' => 'ctf_options_actions', // matches the section name
1141
+ 'section' => 'ctf_options_actions', // matches the section name
1142
+ 'option' => 'ctf_options', // matches the options name
1143
+ 'class' => 'default-text', // class for the wrapper and input field
1144
+ ));
1145
+
1146
+ // icon color
1147
+ $this->create_settings_field( array(
1148
+ 'name' => 'iconcolor',
1149
+ 'title' => '<label for="ctf_iconcolor">Icon Color</label><code class="ctf_shortcode">iconcolor
1150
+ Eg: iconcolor=green</code>', // label for the input field
1151
+ 'callback' => 'default_color', // name of the function that outputs the html
1152
+ 'page' => 'ctf_options_actions', // matches the section name
1153
+ 'section' => 'ctf_options_actions', // matches the section name
1154
+ 'option' => 'ctf_options', // matches the options name
1155
+ 'class' => '',
1156
+ ));
1157
+
1158
+
1159
+ // view on twitter text
1160
+ $this->create_settings_field( array(
1161
+ 'name' => 'twitterlinktext',
1162
+ 'title' => '<label for="ctf_twitterlinktext">Custom Text for "Twitter" Link</label><code class="ctf_shortcode">twitterlinktext
1163
+ Eg: twitterlinktext="View this Tweet"</code>', // label for the input field
1164
+ 'callback' => 'default_text', // name of the function that outputs the html
1165
+ 'page' => 'ctf_options_actions', // matches the section name
1166
+ 'section' => 'ctf_options_actions', // matches the section name
1167
+ 'option' => 'ctf_options', // matches the options name
1168
+ 'class' => 'default-text', // class for the wrapper and input field
1169
+ 'default' => 'Twitter'
1170
+ ));
1171
+
1172
+ add_settings_section(
1173
+ 'ctf_options_load', // matches the section name
1174
+ '"Load More" Button',
1175
+ array( $this, 'general_section_text' ), // callback function to explain the section
1176
+ 'ctf_options_load' // matches the section name
1177
+ );
1178
+
1179
+ // button background color
1180
+ $this->create_settings_field( array(
1181
+ 'name' => 'buttoncolor',
1182
+ 'title' => '<label for="ctf_buttoncolor">Button Background Color</label><code class="ctf_shortcode">buttoncolor
1183
+ Eg: buttoncolor=#f33</code>', // label for the input field
1184
+ 'callback' => 'default_color', // name of the function that outputs the html
1185
+ 'page' => 'ctf_options_load', // matches the section name
1186
+ 'section' => 'ctf_options_load', // matches the section name
1187
+ 'option' => 'ctf_options', // matches the options name
1188
+ 'class' => '',
1189
+ 'whatis' => "The color of the background of the load more button"
1190
+ ));
1191
+
1192
+ // button text color
1193
+ $this->create_settings_field( array(
1194
+ 'name' => 'buttontextcolor',
1195
+ 'title' => '<label for="ctf_buttontextcolor">Button Text Color</label><code class="ctf_shortcode">buttontextcolor
1196
+ Eg: buttontextcolor=#444</code>', // label for the input field
1197
+ 'callback' => 'default_color', // name of the function that outputs the html
1198
+ 'page' => 'ctf_options_load', // matches the section name
1199
+ 'section' => 'ctf_options_load', // matches the section name
1200
+ 'option' => 'ctf_options', // matches the options name
1201
+ 'class' => '',
1202
+ 'whatis' => "The color of the text of the load more button"
1203
+ ));
1204
+
1205
+ // button text
1206
+ $this->create_settings_field( array(
1207
+ 'name' => 'buttontext',
1208
+ 'title' => '<label for="ctf_buttontext">Button Text</label><code class="ctf_shortcode">buttontext
1209
+ Eg: buttontext="More"</code>', // label for the input field
1210
+ 'callback' => 'default_text', // name of the function that outputs the html
1211
+ 'page' => 'ctf_options_load', // matches the section name
1212
+ 'section' => 'ctf_options_load', // matches the section name
1213
+ 'option' => 'ctf_options', // matches the options name
1214
+ 'class' => 'default-text', // class for the wrapper and input field
1215
+ 'default' => 'Load More...'
1216
+ ));
1217
+
1218
+ do_action( 'ctf_admin_style_option', $this );
1219
+ }
1220
+
1221
+ public function create_settings_field( $args=array() )
1222
+ {
1223
+ add_settings_field(
1224
+ $args['name'],
1225
+ $args['title'],
1226
+ array( $this, $args['callback'] ),
1227
+ $args['page'],
1228
+ $args['section'],
1229
+ $args
1230
+ );
1231
+ }
1232
+
1233
+ public function default_text( $args )
1234
+ {
1235
+ $options = get_option( $args['option'] );
1236
+ $default = isset( $args['default'] ) ? $args['default'] : '';
1237
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1238
+ $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1239
+ $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1240
+ $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1241
+ $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1242
+ $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1243
+ ?>
1244
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1245
+ <?php if ( isset( $args['example'] ) ) : ?>
1246
+ <span><?php echo $args['example']; ?></span>
1247
+ <?php endif; ?>
1248
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1249
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1250
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1251
+ <?php endif; ?>
1252
+ <?php
1253
+ }
1254
+
1255
+ public function default_select( $args )
1256
+ {
1257
+ $options = get_option( $args['option'] );
1258
+ $selected = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1259
+ ?>
1260
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>">
1261
+ <?php foreach ( $args['fields'] as $field ) : ?>
1262
+ <option value="<?php echo $field[0]; ?>" id="ctf-<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php if( $selected == $field[0] ) { echo ' selected'; } ?>><?php _e( $field[1], 'custom-twitter-feeds' ); ?></option>
1263
+ <?php endforeach; ?>
1264
+ </select>
1265
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1266
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1267
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1268
+ <?php endif; ?>
1269
+ <?php
1270
+ }
1271
+
1272
+ public function default_color( $args )
1273
+ {
1274
+ $options = get_option( $args['option'] );
1275
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1276
+ ?>
1277
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" value="#<?php esc_attr_e( str_replace('#', '', $option_string ) ); ?>" class="ctf-colorpicker" />
1278
+ <?php
1279
+ }
1280
+
1281
+ public function default_checkbox( $args )
1282
+ {
1283
+ $options = get_option( $args['option'] );
1284
+ $option_checked = ( isset( $options[ $args['name'] ] ) ) ? $options[ $args['name'] ] : false;
1285
+ ?>
1286
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1287
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1288
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1289
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1290
+ <?php endif; ?>
1291
+ <?php
1292
+ }
1293
+
1294
+ public function reverse_checkbox( $args )
1295
+ {
1296
+ $options = get_option( $args['option'] );
1297
+ $option_checked = isset( $options[ $args['name'] ] ) ? $options[ $args['name'] ] : true;
1298
+ ?>
1299
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1300
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1301
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1302
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1303
+ <?php endif; ?>
1304
+ <?php
1305
+ }
1306
+
1307
+ public function access_token_secret( $args )
1308
+ {
1309
+ $options = get_option( $args['option'] );
1310
+ $default = isset( $args['default'] ) ? $args['default'] : '';
1311
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1312
+ $option_checked = ( isset( $options['use_own_consumer'] ) ) ? $options['use_own_consumer'] : false;
1313
+ $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1314
+ $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1315
+ $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1316
+ $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1317
+ $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1318
+ ?>
1319
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1320
+ <?php if ( isset( $args['example'] ) ) : ?>
1321
+ <span><?php echo $args['example']; ?></span>
1322
+ <?php endif; ?>
1323
+
1324
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1325
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1326
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1327
+ <?php endif; ?>
1328
+
1329
+ <?php
1330
+ }
1331
+
1332
+ public function feed_settings_radio( $args )
1333
+ {
1334
+ $options = get_option( $args['option'] );
1335
+ $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1336
+ $show_replies = ( isset( $options[ $args['name'].'_includereplies' ] ) ) ? $options[ $args['name'].'_includereplies' ] : false;
1337
+ $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1338
+ ?>
1339
+ <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1340
+ <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php _e( $args['label'], 'custom-twitter-feeds' ); ?></label>
1341
+ <?php if ( $args['has_input'] ) : ?>
1342
+ <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1343
+ <?php endif; ?>
1344
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1345
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1346
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1347
+ <?php endif; ?>
1348
+ <?php if ( $args['has_replies'] ) : ?>
1349
+ <span class="ctf_include_replies_toggle ctf_pro">
1350
+ <input name="<?php echo $args['option'].'['.$args['name'].'_includereplies]'; ?>" id="ctf_include_replies" type="checkbox" <?php if ( $show_replies ) echo "checked"; ?> />
1351
+ <label class="ctf-radio-label" for="ctf_include_replies"><?php _e( 'Include replies', 'custom-twitter-feeds' ); ?></label>
1352
+ <?php do_action( 'ctf_admin_upgrade_note' ); ?>
1353
+ </span>
1354
+ <?php endif; ?>
1355
+ <?php
1356
+ do_action( 'ctf_admin_feed_settings_radio_extra', $args );
1357
+ }
1358
+
1359
+ public function feed_settings_radio_search( $args )
1360
+ {
1361
+ $options = get_option( $args['option'] );
1362
+ $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1363
+ $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1364
+ ?>
1365
+ <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1366
+ <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php echo $args['label']; ?></label>
1367
+ <?php if ( $args['has_input'] ) : ?>
1368
+ <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1369
+ <?php endif; ?>
1370
+ <?php if ( isset( $args['note_after_input'] ) ) : ?>
1371
+ <span class="ctf-note-after-input"><?php echo esc_attr( $args['note_after_input'] ); ?></span>
1372
+ <?php endif; ?>
1373
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1374
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1375
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1376
+ <?php endif; ?>
1377
+ <?php
1378
+ do_action( 'ctf_admin_feed_settings_search_extra' );
1379
+ }
1380
+
1381
+ public function width_and_height_settings( $args )
1382
+ {
1383
+ $options = get_option( $args['option'] );
1384
+ $default = isset( $args['default'] ) ? $args['default'] : '';
1385
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1386
+ $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : $args['default_unit'];
1387
+ ?>
1388
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1389
+ <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>" id="ctf_<?php echo $args['name'].'_unit'; ?>">
1390
+ <option value="px" <?php if ( $selected == "px" ) echo 'selected="selected"' ?> >px</option>
1391
+ <option value="%" <?php if ( $selected == "%" ) echo 'selected="selected"' ?> >%</option>
1392
+ </select>
1393
+
1394
+ <?php if ( $args['name'] == 'width' ) :
1395
+ $checked = ( isset( $options[ $args['name'] . '_mobile_no_fixed' ] ) ) ? esc_attr( $options[ $args['name'] . '_mobile_no_fixed' ] ) : false; ?>
1396
+ <div id="ctf_width_options">
1397
+ <input name="<?php echo $args['option'].'[width_mobile_no_fixed]'; ?>" type="checkbox" id="ctf_width_mobile_no_fixed" <?php if ( $checked == true ) { echo "checked"; }?> /><label for="ctf_width_mobile_no_fixed"><?php _e('Set to be 100% width on mobile?', 'custom-twitter-feeds'); ?></label>
1398
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><?php _e('What does this mean?', 'custom-facebook-feed'); ?></a>
1399
+ <p class="ctf-tooltip ctf-more-info"><?php _e("If you set a width on the feed then this will be used on mobile as well as desktop. Check this setting to set the feed width to be 100% on mobile so that it is responsive.", 'custom-twitter-feeds'); ?></p>
1400
+ </div>
1401
+ <?php endif; ?>
1402
+ <?php
1403
+ }
1404
+
1405
+ public function cache_time( $args )
1406
+ {
1407
+ $min_cache_time = 3600;
1408
+ $options = get_option( $args['option'] );
1409
+ $unrestricted = isset( $options['have_own_tokens'] ) && $options['have_own_tokens'];
1410
+ if ( $unrestricted ) {
1411
+ $min_cache_time = 1;
1412
+ }
1413
+ $default = 3;
1414
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1415
+
1416
+ $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1417
+ $actual_time = (int)$option_string * (int)$selected;
1418
+ $show_notice = false;
1419
+ if ( get_transient( 'ct_feed_forced_cache_time_raise' ) ) {
1420
+ $show_notice = true;
1421
+ }
1422
+ if ( $actual_time < $min_cache_time ) {
1423
+ set_transient( 'ct_feed_forced_cache_time_raise', 'true', 60 * 60 * 48 );
1424
+ $show_notice = true;
1425
+ $option_string = max( 1, $min_cache_time / 3600 );
1426
+ $selected = "3600";
1427
+ } else {
1428
+ $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1429
+ }
1430
+
1431
+ ?>
1432
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1433
+ <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>">
1434
+ <?php if ( $unrestricted ) : ?>
1435
+ <option value="60" <?php if ( $selected == "60" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Minutes' ); ?></option>
1436
+ <?php endif; ?>
1437
+ <option value="3600" <?php if ( $selected == "3600" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Hours' ); ?></option>
1438
+ <option value="86400" <?php if ( $selected == "86400" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Days' ); ?></option>
1439
+ </select>&nbsp;
1440
+ <input id="ctf-clear-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cache' ); ?>" />
1441
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1442
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your Twitter feeds', 'custom-twitter-feeds' ); ?>.</p>
1443
+ <?php if ( $show_notice ) : ?>
1444
+ <p class="ctf-more-info"><?php _e( 'Due to changes in Twitter\'s API usage policy, the minimum caching time for the plugin is 1 hour. To check for Tweets more often either create your own Twitter Developer app or upgrade to the Pro version.', 'custom-twitter-feeds' ); ?></p>
1445
+ <?php endif; ?>
1446
+ <?php
1447
+ }
1448
+
1449
+ public function customize_date_format( $args )
1450
+ {
1451
+ $options = get_option( $args['option'] );
1452
+ $ctf_date_formatting = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1453
+ $original = strtotime( '2016-02-25T17:30:00+0000' );
1454
+ ?>
1455
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1456
+ <option value="1" <?php if ( $ctf_date_formatting == "1" ) echo 'selected="selected"'; ?> ><?php _e( '2h / 25 Feb' ); ?></option>
1457
+ <option value="2" <?php if ( $ctf_date_formatting == "2" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j', $original ); ?></option>
1458
+ <option value="3" <?php if ( $ctf_date_formatting == "3" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j, Y', $original ); ?></option>
1459
+ <option value="4" <?php if ( $ctf_date_formatting == "4" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d', $original ); ?></option>
1460
+ <option value="5" <?php if ( $ctf_date_formatting == "5" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d.y', $original ); ?></option>
1461
+ </select>
1462
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1463
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1464
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1465
+ <?php endif; ?>
1466
+ <?php
1467
+ }
1468
+
1469
+ public function customize_custom_date_format( $args )
1470
+ {
1471
+ $options = get_option( $args['option'] );
1472
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1473
+ ?>
1474
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="10" placeholder="Eg. F jS, Y" />
1475
+ <a href="https://smashballoon.com/custom-twitter-feeds/docs/date/" class="cff-external-link" target="_blank"><?php _e( 'Examples' , 'custom-twitter-feeds'); ?></a>
1476
+ <?php
1477
+ }
1478
+
1479
+ public function customize_custom_time_translations( $args )
1480
+ {
1481
+ $options = get_option( $args['option'] );
1482
+ $option_m = ( isset( $options['mtime'] ) ) ? esc_attr( $options['mtime'] ) : '';
1483
+ $option_h = ( isset( $options['htime'] ) ) ? esc_attr( $options['htime'] ) : '';
1484
+ $option_now = ( isset( $options['nowtime'] ) ) ? esc_attr( $options['nowtime'] ) : '';
1485
+
1486
+ ?>
1487
+ <input name="<?php echo $args['option'].'[mtime]'; ?>" id="ctf_translate_minute" type="text" value="<?php esc_attr_e( $option_m ); ?>" size="5" />
1488
+ <label for=ctf_translate_minute"><?php _e( 'translation for "m" (minutes)', 'custom-twitter-feeds' ); ?></label><br>
1489
+ <input name="<?php echo $args['option'].'[htime]'; ?>" id="ctf_translate_hour" type="text" value="<?php esc_attr_e( $option_h ); ?>" size="5" />
1490
+ <label for=ctf_translate_hour"><?php _e( 'translation for "h" (hours)', 'custom-twitter-feeds' ); ?></label><br>
1491
+ <input name="<?php echo $args['option'].'[nowtime]'; ?>" id="ctf_translate_now" type="text" value="<?php esc_attr_e( $option_now ); ?>" size="5" />
1492
+ <label for=ctf_translate_now"><?php _e( 'translation for "now"', 'custom-twitter-feeds' ); ?></label><br>
1493
+ <?php
1494
+ }
1495
+
1496
+ public function include_exclude_checkbox( $args )
1497
+ {
1498
+ $options = get_option( $args['option'] );
1499
+ foreach ( $args['fields'] as $field ) {
1500
+ $option_checked = isset( $options[$field[0]] ) ? $options[$field[0]] : true;
1501
+ ?>
1502
+ <input name="<?php echo $args['option'] . '[' . $field[0] . ']'; ?>"
1503
+ id="ctf_<?php echo $field[0]; ?>" type="checkbox"
1504
+ <?php if ( $option_checked ) {
1505
+ echo "checked";
1506
+ } ?> />
1507
+ <label for=ctf_<?php echo $field[0]; ?>"><?php _e( $field[1], 'custom-twitter-feeds' ); ?></label><br>
1508
+ <?php
1509
+ } // end foreach
1510
+ }
1511
+
1512
+ public function text_size( $args )
1513
+ {
1514
+ $options = get_option( $args['option'] );
1515
+ $ctf_text_size = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1516
+ ?>
1517
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1518
+ <option value="inherit" <?php if ( $ctf_text_size == "inherit" ) echo 'selected="selected"' ?> >Inherit</option>
1519
+ <option value="10" <?php if ( $ctf_text_size == "10" ) echo 'selected="selected"' ?> >10px</option>
1520
+ <option value="11" <?php if ( $ctf_text_size == "11" ) echo 'selected="selected"' ?> >11px</option>
1521
+ <option value="12" <?php if ( $ctf_text_size == "12" ) echo 'selected="selected"' ?> >12px</option>
1522
+ <option value="13" <?php if ( $ctf_text_size == "13" ) echo 'selected="selected"' ?> >13px</option>
1523
+ <option value="14" <?php if ( $ctf_text_size == "14" ) echo 'selected="selected"' ?> >14px</option>
1524
+ <option value="16" <?php if ( $ctf_text_size == "16" ) echo 'selected="selected"' ?> >16px</option>
1525
+ <option value="18" <?php if ( $ctf_text_size == "18" ) echo 'selected="selected"' ?> >18px</option>
1526
+ <option value="20" <?php if ( $ctf_text_size == "20" ) echo 'selected="selected"' ?> >20px</option>
1527
+ <option value="24" <?php if ( $ctf_text_size == "24" ) echo 'selected="selected"' ?> >24px</option>
1528
+ <option value="28" <?php if ( $ctf_text_size == "28" ) echo 'selected="selected"' ?> >28px</option>
1529
+ <option value="32" <?php if ( $ctf_text_size == "32" ) echo 'selected="selected"' ?> >32px</option>
1530
+ <option value="36" <?php if ( $ctf_text_size == "36" ) echo 'selected="selected"' ?> >36px</option>
1531
+ <option value="42" <?php if ( $ctf_text_size == "42" ) echo 'selected="selected"' ?> >42px</option>
1532
+ <option value="48" <?php if ( $ctf_text_size == "48" ) echo 'selected="selected"' ?> >48px</option>
1533
+ <option value="54" <?php if ( $ctf_text_size == "54" ) echo 'selected="selected"' ?> >54px</option>
1534
+ <option value="60" <?php if ( $ctf_text_size == "60" ) echo 'selected="selected"' ?> >60px</option>
1535
+ </select>
1536
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1537
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1538
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1539
+ <?php endif; ?>
1540
+ <?php
1541
+ }
1542
+
1543
+ public function text_weight( $args )
1544
+ {
1545
+ $options = get_option( $args['option'] );
1546
+ $ctf_text_weight = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1547
+ ?>
1548
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1549
+ <option value="inherit" <?php if ( $ctf_text_weight == "inherit" ) echo 'selected="selected"'; ?> >Inherit</option>
1550
+ <option value="normal" <?php if ( $ctf_text_weight == "normal" ) echo 'selected="selected"'; ?> >Normal</option>
1551
+ <option value="bold" <?php if ( $ctf_text_weight == "bold" ) echo 'selected="selected"'; ?> >Bold</option>
1552
+ </select>
1553
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1554
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1555
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1556
+ <?php endif; ?>
1557
+ <?php
1558
+ }
1559
+
1560
+ public function feed_settings_timezone( $args )
1561
+ {
1562
+ $options = get_option( $args['option'] );
1563
+ $ctf_timezone = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1564
+ ?>
1565
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 300px;">
1566
+ <option value="default" <?php if( $ctf_timezone == "default" ) echo 'selected="selected"' ?> ><?php _e( 'default from Twitter' ) ?></option>
1567
+ <option value="Pacific/Midway" <?php if( $ctf_timezone == "Pacific/Midway" ) echo 'selected="selected"' ?> ><?php _e( '(GMT11:00) Midway Island, Samoa' ) ?></option>
1568
+ <option value="America/Adak" <?php if( $ctf_timezone == "America/Adak" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) HawaiiAleutian' ) ?></option>
1569
+ <option value="Etc/GMT+10" <?php if( $ctf_timezone == "Etc/GMT+10" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) Hawaii' ) ?></option>
1570
+ <option value="Pacific/Marquesas" <?php if( $ctf_timezone == "Pacific/Marquesas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:30) Marquesas Islands' ) ?></option>
1571
+ <option value="Pacific/Gambier" <?php if( $ctf_timezone == "Pacific/Gambier" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Gambier Islands' ) ?></option>
1572
+ <option value="America/Anchorage" <?php if( $ctf_timezone == "America/Anchorage" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Alaska' ) ?></option>
1573
+ <option value="America/Ensenada" <?php if( $ctf_timezone == "America/Ensenada" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Tijuana, Baja California' ) ?></option>
1574
+ <option value="Etc/GMT+8" <?php if( $ctf_timezone == "Etc/GMT+8" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pitcairn Islands' ) ?></option>
1575
+ <option value="America/Los_Angeles" <?php if( $ctf_timezone == "America/Los_Angeles" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pacific Time (US & Canada)' ) ?></option>
1576
+ <option value="America/Denver" <?php if( $ctf_timezone == "America/Denver" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Mountain Time (US & Canada)' ) ?></option>
1577
+ <option value="America/Chihuahua" <?php if( $ctf_timezone == "America/Chihuahua" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Chihuahua, La Paz, Mazatlan' ) ?></option>
1578
+ <option value="America/Dawson_Creek" <?php if( $ctf_timezone == "America/Dawson_Creek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Arizona' ) ?></option>
1579
+ <option value="America/Belize" <?php if( $ctf_timezone == "America/Belize" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Saskatchewan, Central America' ) ?></option>
1580
+ <option value="America/Cancun" <?php if( $ctf_timezone == "America/Cancun" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Guadalajara, Mexico City, Monterrey' ) ?></option>
1581
+ <option value="Chile/EasterIsland" <?php if( $ctf_timezone == "Chile/EasterIsland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Easter Island' ) ?></option>
1582
+ <option value="America/Chicago" <?php if( $ctf_timezone == "America/Chicago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Central Time (US & Canada)' ) ?></option>
1583
+ <option value="America/New_York" <?php if( $ctf_timezone == "America/New_York" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Eastern Time (US & Canada)' ) ?></option>
1584
+ <option value="America/Havana" <?php if( $ctf_timezone == "America/Havana" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Cuba' ) ?></option>
1585
+ <option value="America/Bogota" <?php if( $ctf_timezone == "America/Bogota" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Bogota, Lima, Quito, Rio Branco' ) ?></option>
1586
+ <option value="America/Caracas" <?php if( $ctf_timezone == "America/Caracas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:30) Caracas' ) ?></option>
1587
+ <option value="America/Santiago" <?php if( $ctf_timezone == "America/Santiago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Santiago' ) ?></option>
1588
+ <option value="America/La_Paz" <?php if( $ctf_timezone == "America/La_Paz" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) La Paz' ) ?></option>
1589
+ <option value="Atlantic/Stanley" <?php if( $ctf_timezone == "Atlantic/Stanley" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Faukland Islands' ) ?></option>
1590
+ <option value="America/Campo_Grande" <?php if( $ctf_timezone == "America/Campo_Grande" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Brazil' ) ?></option>
1591
+ <option value="America/Goose_Bay" <?php if( $ctf_timezone == "America/Goose_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Goose Bay)' ) ?></option>
1592
+ <option value="America/Glace_Bay" <?php if( $ctf_timezone == "America/Glace_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Canada)' ) ?></option>
1593
+ <option value="America/St_Johns" <?php if( $ctf_timezone == "America/St_Johns" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:30) Newfoundland' ) ?></option>
1594
+ <option value="America/Araguaina" <?php if( $ctf_timezone == "America/Araguaina" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) UTC3' ) ?></option>
1595
+ <option value="America/Montevideo" <?php if( $ctf_timezone == "America/Montevideo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Montevideo' ) ?></option>
1596
+ <option value="America/Miquelon" <?php if( $ctf_timezone == "America/Miquelon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Miquelon, St. Pierre' ) ?></option>
1597
+ <option value="America/Godthab" <?php if( $ctf_timezone == "America/Godthab" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Greenland' ) ?></option>
1598
+ <option value="America/Argentina/Buenos_Aires" <?php if( $ctf_timezone == "America/Argentina/Buenos_Aires" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Buenos Aires' ) ?></option>
1599
+ <option value="America/Sao_Paulo" <?php if( $ctf_timezone == "America/Sao_Paulo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Brasilia' ) ?></option>
1600
+ <option value="America/Noronha" <?php if( $ctf_timezone == "America/Noronha" ) echo 'selected="selected"' ?> ><?php _e( '(GMT02:00) MidAtlantic' ) ?></option>
1601
+ <option value="Atlantic/Cape_Verde" <?php if( $ctf_timezone == "Atlantic/Cape_Verde" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Cape Verde Is.' ) ?></option>
1602
+ <option value="Atlantic/Azores" <?php if( $ctf_timezone == "Atlantic/Azores" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Azores' ) ?></option>
1603
+ <option value="Europe/Belfast" <?php if( $ctf_timezone == "Europe/Belfast" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Belfast' ) ?></option>
1604
+ <option value="Europe/Dublin" <?php if( $ctf_timezone == "Europe/Dublin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Dublin' ) ?></option>
1605
+ <option value="Europe/Lisbon" <?php if( $ctf_timezone == "Europe/Lisbon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Lisbon' ) ?></option>
1606
+ <option value="Europe/London" <?php if( $ctf_timezone == "Europe/London" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : London' ) ?></option>
1607
+ <option value="Africa/Abidjan" <?php if( $ctf_timezone == "Africa/Abidjan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Monrovia, Reykjavik' ) ?></option>
1608
+ <option value="Europe/Amsterdam" <?php if( $ctf_timezone == "Europe/Amsterdam" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' ) ?></option>
1609
+ <option value="Europe/Belgrade" <?php if( $ctf_timezone == "Europe/Belgrade" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' ) ?></option>
1610
+ <option value="Europe/Brussels" <?php if( $ctf_timezone == "Europe/Brussels" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Brussels, Copenhagen, Madrid, Paris' ) ?></option>
1611
+ <option value="Africa/Algiers" <?php if( $ctf_timezone == "Africa/Algiers" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) West Central Africa' ) ?></option>
1612
+ <option value="Africa/Windhoek" <?php if( $ctf_timezone == "Africa/Windhoek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Windhoek' ) ?></option>
1613
+ <option value="Asia/Beirut" <?php if( $ctf_timezone == "Asia/Beirut" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Beirut' ) ?></option>
1614
+ <option value="Africa/Cairo" <?php if( $ctf_timezone == "Africa/Cairo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Cairo' ) ?></option>
1615
+ <option value="Asia/Gaza" <?php if( $ctf_timezone == "Asia/Gaza" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Gaza' ) ?></option>
1616
+ <option value="Africa/Blantyre" <?php if( $ctf_timezone == "Africa/Blantyre" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Harare, Pretoria' ) ?></option>
1617
+ <option value="Asia/Jerusalem" <?php if( $ctf_timezone == "Asia/Jerusalem" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Jerusalem' ) ?></option>
1618
+ <option value="Europe/Minsk" <?php if( $ctf_timezone == "Europe/Minsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Minsk' ) ?></option>
1619
+ <option value="Asia/Damascus" <?php if( $ctf_timezone == "Asia/Damascus" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Syria' ) ?></option>
1620
+ <option value="Europe/Moscow" <?php if( $ctf_timezone == "Europe/Moscow" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Moscow, St. Petersburg, Volgograd' ) ?></option>
1621
+ <option value="Africa/Addis_Ababa" <?php if( $ctf_timezone == "Africa/Addis_Ababa" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Nairobi' ) ?></option>
1622
+ <option value="Asia/Tehran" <?php if( $ctf_timezone == "Asia/Tehran" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:30) Tehran' ) ?></option>
1623
+ <option value="Asia/Dubai" <?php if( $ctf_timezone == "Asia/Dubai" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Abu Dhabi, Muscat' ) ?></option>
1624
+ <option value="Asia/Yerevan" <?php if( $ctf_timezone == "Asia/Yerevan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Yerevan' ) ?></option>
1625
+ <option value="Asia/Kabul" <?php if( $ctf_timezone == "Asia/Kabul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:30) Kabul' ) ?></option>
1626
+ <option value="Asia/Yekaterinburg" <?php if( $ctf_timezone == "Asia/Yekaterinburg" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Ekaterinburg' ) ?></option>
1627
+ <option value="Asia/Tashkent" <?php if( $ctf_timezone == "Asia/Tashkent" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Tashkent' ) ?></option>
1628
+ <option value="Asia/Kolkata" <?php if( $ctf_timezone == "Asia/Kolkata" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi' ) ?></option>
1629
+ <option value="Asia/Katmandu" <?php if( $ctf_timezone == "Asia/Katmandu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:45) Kathmandu' ) ?></option>
1630
+ <option value="Asia/Dhaka" <?php if( $ctf_timezone == "Asia/Dhaka" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Astana, Dhaka' ) ?></option>
1631
+ <option value="Asia/Novosibirsk" <?php if( $ctf_timezone == "Asia/Novosibirsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Novosibirsk' ) ?></option>
1632
+ <option value="Asia/Rangoon" <?php if( $ctf_timezone == "Asia/Rangoon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:30) Yangon (Rangoon)' ) ?></option>
1633
+ <option value="Asia/Bangkok" <?php if( $ctf_timezone == "Asia/Bangkok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Bangkok, Hanoi, Jakarta' ) ?></option>
1634
+ <option value="Asia/Krasnoyarsk" <?php if( $ctf_timezone == "Asia/Krasnoyarsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Krasnoyarsk' ) ?></option>
1635
+ <option value="Asia/Hong_Kong" <?php if( $ctf_timezone == "Asia/Hong_Kong" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi' ) ?></option>
1636
+ <option value="Asia/Irkutsk" <?php if( $ctf_timezone == "Asia/Irkutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Irkutsk, Ulaan Bataar' ) ?></option>
1637
+ <option value="Australia/Perth" <?php if( $ctf_timezone == "Australia/Perth" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Perth' ) ?></option>
1638
+ <option value="Australia/Eucla" <?php if( $ctf_timezone == "Australia/Eucla" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:45) Eucla' ) ?></option>
1639
+ <option value="Asia/Tokyo" <?php if( $ctf_timezone == "Asia/Tokyo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Osaka, Sapporo, Tokyo' ) ?></option>
1640
+ <option value="Asia/Seoul" <?php if( $ctf_timezone == "Asia/Seoul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Seoul' ) ?></option>
1641
+ <option value="Asia/Yakutsk" <?php if( $ctf_timezone == "Asia/Yakutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Yakutsk' ) ?></option>
1642
+ <option value="Australia/Adelaide" <?php if( $ctf_timezone == "Australia/Adelaide" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Adelaide' ) ?></option>
1643
+ <option value="Australia/Darwin" <?php if( $ctf_timezone == "Australia/Darwin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Darwin' ) ?></option>
1644
+ <option value="Australia/Brisbane" <?php if( $ctf_timezone == "Australia/Brisbane" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Brisbane' ) ?></option>
1645
+ <option value="Australia/Hobart" <?php if( $ctf_timezone == "Australia/Hobart" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Sydney' ) ?></option>
1646
+ <option value="Asia/Vladivostok" <?php if( $ctf_timezone == "Asia/Vladivostok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Vladivostok' ) ?></option>
1647
+ <option value="Australia/Lord_Howe" <?php if( $ctf_timezone == "Australia/Lord_Howe" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:30) Lord Howe Island' ) ?></option>
1648
+ <option value="Etc/GMT11" <?php if( $ctf_timezone == "Etc/GMT11" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Solomon Is., New Caledonia' ) ?></option>
1649
+ <option value="Asia/Magadan" <?php if( $ctf_timezone == "Asia/Magadan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Magadan' ) ?></option>
1650
+ <option value="Pacific/Norfolk" <?php if( $ctf_timezone == "Pacific/Norfolk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:30) Norfolk Island' ) ?></option>
1651
+ <option value="Asia/Anadyr" <?php if( $ctf_timezone == "Asia/Anadyr" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Anadyr, Kamchatka' ) ?></option>
1652
+ <option value="Pacific/Auckland" <?php if( $ctf_timezone == "Pacific/Auckland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Auckland, Wellington' ) ?></option>
1653
+ <option value="Etc/GMT12" <?php if( $ctf_timezone == "Etc/GMT12" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Fiji, Kamchatka, Marshall Is.' ) ?></option>
1654
+ <option value="Pacific/Chatham" <?php if( $ctf_timezone == "Pacific/Chatham" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:45) Chatham Islands' ) ?></option>
1655
+ <option value="Pacific/Tongatapu" <?php if( $ctf_timezone == "Pacific/Tongatapu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+13:00) Nuku\'alofa' ) ?></option>
1656
+ <option value="Pacific/Kiritimati" <?php if( $ctf_timezone == "Pacific/Kiritimati" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+14:00) Kiritimati' ) ?></option>
1657
+ </select>
1658
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1659
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1660
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1661
+ <?php endif; ?>
1662
+ <?php
1663
+ }
1664
+
1665
+ public function layout( $args ) {
1666
+ $selected_type = $args['layout_selections']['layout'];
1667
+ $layout_types = array(
1668
+ 'list' => __( 'List', 'instagram-feed' ),
1669
+ 'carousel' => __( 'Carousel', 'instagram-feed' ),
1670
+ 'masonry' => __( 'Masonry', 'instagram-feed' ),
1671
+ );
1672
+ $layout_images = array(
1673
+ 'list' => CTF_PLUGIN_URL . 'img/list.png',
1674
+ 'carousel' => CTF_PLUGIN_URL . 'img/carousel.png',
1675
+ 'masonry' => CTF_PLUGIN_URL . 'img/masonry.png',
1676
+ );
1677
+ ?>
1678
+
1679
+ <?php foreach( $layout_types as $layout_type => $label ) : ?>
1680
+ <div class="ctf_layout_cell <?php if($selected_type === $layout_type) echo "ctf_layout_selected"; ?>">
1681
+ <input class="ctf_layout_type" id="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>" name="<?php echo $args['option'].'[layout]'; ?>" type="radio" value="<?php esc_attr_e( $layout_type ); ?>" <?php if ( $selected_type === $layout_type ) echo 'checked'; ?>/><label for="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>"><span class="ctf_label"><?php echo esc_html( $label ); ?></span><img src="<?php echo $layout_images[ $layout_type ]; ?>" /></label>
1682
+
1683
+ <a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=layout" target="_blank" class="ctf_lock"><i class="fa fa-rocket"></i>Pro</a>
1684
+ </div>
1685
+ <?php endforeach; ?>
1686
+ <div class="ctf_layout_options_wrap">
1687
+ <div class="ctf_layout_settings ctf_layout_type_list">
1688
+ <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('A full-width list of tweets.'); ?></span>
1689
+ </div>
1690
+ <div class="ctf_layout_settings ctf_layout_type_masonry">
1691
+ <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Masonry layout option</a></p>
1692
+ <div class="ctf_layout_setting">
1693
+ <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Tweets in columns with no empty space between them.'); ?></span>
1694
+ </div>
1695
+ <div class="ctf_layout_setting">
1696
+ <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrycols
1697
+ Eg: masonrycols=4</code>
1698
+ <br />
1699
+ <select name="<?php echo $args['option'].'[masonrycols]'; ?>" id="ctf_masonrycols">
1700
+ <?php
1701
+ $cols_options = array(1,2,3,4,5,6);
1702
+ foreach ( $cols_options as $option ) :
1703
+ ?>
1704
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrycols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1705
+ <?php endforeach; ?>
1706
+ </select>
1707
+ </div>
1708
+ <div class="ctf_layout_setting">
1709
+ <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrymobilecols
1710
+ Eg: masonrymobilecols=2</code>
1711
+ <br />
1712
+ <select name="<?php echo $args['option'].'[masonrymobilecols]'; ?>" id="ctf_masonrymobilecols">
1713
+ <?php
1714
+ $cols_options = array(1,2);
1715
+ foreach ( $cols_options as $option ) :
1716
+ ?>
1717
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrymobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1718
+ <?php endforeach; ?>
1719
+ </select>
1720
+ </div>
1721
+ </div>
1722
+ <div class="ctf_layout_settings ctf_layout_type_carousel">
1723
+ <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Carousel layout option</a></p>
1724
+ <div class="ctf_layout_setting">
1725
+ <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Posts are displayed in a slideshow carousel.', 'instagram-feed'); ?></span>
1726
+ </div>
1727
+ <div class="ctf_layout_setting">
1728
+ <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselcols
1729
+ Eg: carouselcols=4</code>
1730
+ <br />
1731
+ <select name="<?php echo $args['option'].'[carouselcols]'; ?>" id="ctf_carouselcols">
1732
+ <?php
1733
+ $cols_options = array(1,2,3,4,5,6);
1734
+ foreach ( $cols_options as $option ) :
1735
+ ?>
1736
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselcols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1737
+ <?php endforeach; ?>
1738
+ </select>
1739
+ </div>
1740
+ <div class="ctf_layout_setting">
1741
+ <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselmobilecols
1742
+ Eg: carouselmobilecols=2</code>
1743
+ <br />
1744
+ <select name="<?php echo $args['option'].'[carouselmobilecols]'; ?>" id="ctf_carouselmobilecols">
1745
+ <?php
1746
+ $cols_options = array(1,2);
1747
+ foreach ( $cols_options as $option ) :
1748
+ ?>
1749
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselmobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1750
+ <?php endforeach; ?>
1751
+ </select>
1752
+ </div>
1753
+ <div class="ctf_layout_setting">
1754
+ <label><?php _e('Loop Type', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselloop
1755
+ Eg: carouselloop=rewind
1756
+ carouselloop=infinity</code>
1757
+ <br />
1758
+ <select name="<?php echo $args['option'].'[carouselloop]'; ?>" id="ctf_carousel_loop">
1759
+ <option value="none" <?php if($args['layout_selections']['carouselloop'] == "none") echo 'selected="selected"' ?> ><?php _e( 'None', 'instagram-feed'); ?></option>
1760
+ <option value="rewind" <?php if($args['layout_selections']['carouselloop'] == "rewind") echo 'selected="selected"' ?> ><?php _e( 'Rewind', 'instagram-feed'); ?></option>
1761
+ <option value="infinity" <?php if($args['layout_selections']['carouselloop'] == "infinity") echo 'selected="selected"' ?> ><?php _e( 'Infinity', 'instagram-feed'); ?></option>
1762
+ </select>
1763
+ </div>
1764
+ <div class="ctf_layout_setting">
1765
+ <label><?php _e('Navigation Arrows', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1766
+ Eg: carouselarrows=below</code>
1767
+ <br />
1768
+ <select name="<?php echo $args['option'].'[carouselarrows]'; ?>" id="ctf_carousel_loop">
1769
+ <option value="onhover" <?php if($args['layout_selections']['carouselarrows'] == "onhover") echo 'selected="selected"' ?> ><?php _e( 'Show on Hover', 'instagram-feed'); ?></option>
1770
+ <option value="below" <?php if($args['layout_selections']['carouselarrows'] == "below") echo 'selected="selected"' ?> ><?php _e( 'Show below feed', 'instagram-feed'); ?></option>
1771
+ <option value="hide" <?php if($args['layout_selections']['carouselarrows'] == "hide") echo 'selected="selected"' ?> ><?php _e( 'Hide arrows', 'instagram-feed'); ?></option>
1772
+ </select>
1773
+ </div>
1774
+ <div class="ctf_layout_setting">
1775
+ <label><?php _e('Carousel Height', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1776
+ Eg: carouselarrows=below</code>
1777
+ <br />
1778
+ <select name="<?php echo $args['option'].'[carouselheight]'; ?>" id="ctf_carousel_loop">
1779
+ <option value="tallest" <?php if($args['layout_selections']['carouselheight'] == "tallest") echo 'selected="selected"' ?> ><?php _e( 'Always set to tallest post', 'instagram-feed'); ?></option>
1780
+ <option value="clickexpand" <?php if($args['layout_selections']['carouselheight'] == "clickexpand") echo 'selected="selected"' ?> ><?php _e( 'Set to shortest post, button to expand', 'instagram-feed'); ?></option>
1781
+ <option value="auto'" <?php if($args['layout_selections']['carouselheight'] == "auto'") echo 'selected="selected"' ?> ><?php _e( 'Automatically set to post height (forces single column)', 'instagram-feed'); ?></option>
1782
+ </select>
1783
+ </div>
1784
+ <div class="ctf_layout_setting">
1785
+ <input type="checkbox" name="<?php echo $args['option'].'[carouselautoplay]'; ?>" id="ctf_carousel_autoplay" <?php if($args['layout_selections']['carouselautoplay'] == true) echo 'checked="checked"' ?> />
1786
+ <label><?php _e("Enable Autoplay", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselautoplay
1787
+ Eg: carouselautoplay=true</code>
1788
+ </div>
1789
+ <div class="ctf_layout_setting">
1790
+ <label><?php _e("Interval Time", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouseltime
1791
+ Eg: carouseltime=8000</code>
1792
+ <br />
1793
+ <input name="<?php echo $args['option'].'[carouseltime]'; ?>" type="text" value="<?php esc_attr_e( $args['layout_selections']['carouseltime'] ); ?>" size="6" /><?php _e("miliseconds", 'instagram-feed'); ?>
1794
+ </div>
1795
+ </div>
1796
+
1797
+ </div>
1798
+ <?php
1799
+ }
1800
+
1801
+ public function custom_code( $args )
1802
+ {
1803
+ $options = get_option( $args['option'] );
1804
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1805
+ ?>
1806
+ <p><?php _e( $args['description'], 'custom-twitter-feeds' ) ; ?></p>
1807
+ <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="7"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
1808
+ <?php if ( isset( $args['extra'] ) ) { _e( '<p class="ctf_note">'.$args['extra'].'</p>', 'custom-twitter-feeds' ); } ?>
1809
+ <?php
1810
+ }
1811
+
1812
+ public function gdpr( $args ) {
1813
+ $options = get_option( $args['option'] );
1814
+ $gdpr = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1815
+ $select_options = array(
1816
+ array(
1817
+ 'label' => __( 'Automatic', 'custom-twitter-feeds' ),
1818
+ 'value' => 'auto'
1819
+ ),
1820
+ array(
1821
+ 'label' => __( 'Yes', 'custom-twitter-feeds' ),
1822
+ 'value' => 'yes'
1823
+ ),
1824
+ array(
1825
+ 'label' => __( 'No', 'custom-twitter-feeds' ),
1826
+ 'value' => 'no'
1827
+ )
1828
+ )
1829
+ ?>
1830
+ <?php
1831
+ $gdpr_list = "<ul class='ctf-list'>
1832
+ <li>" . __( 'Avatars will be replaced with a Twitter logo in the feed header.', 'custom-twitter-feeds' ) . "</li>
1833
+ <li>" . __( 'Avatars will not display in Tweets.', 'custom-twitter-feeds' ) . "</li>
1834
+ <li>" . __( 'Twitter intents (for replying, retweeting, and liking) will be plain links.', 'custom-twitter-feeds' ) . "</li>
1835
+ </ul>";
1836
+ ?>
1837
+ <div>
1838
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_gdpr_setting">
1839
+ <?php foreach ( $select_options as $select_option ) :
1840
+ $selected = $select_option['value'] === $gdpr ? ' selected' : '';
1841
+ ?>
1842
+ <option value="<?php echo esc_attr( $select_option['value'] ); ?>"<?php echo $selected; ?> ><?php echo esc_html( $select_option['label'] ); ?></option>
1843
+ <?php endforeach; ?>
1844
+ </select>
1845
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><?php _e('What does this mean?', 'custom-twitter-feeds'); ?></a>
1846
+ <div class="ctf-tooltip ctf-more-info gdpr_tooltip">
1847
+
1848
+ <p><span><?php _e("Yes", 'custom-twitter-feeds' ); ?>:</span> <?php _e("Enabling this setting prevents all images and videos from being loaded directly from Twitter's servers (CDN) to prevent any requests to external websites in your browser. To accommodate this, some features of the plugin will be disabled or limited.", 'custom-twitter-feeds' ); ?> <a href="JavaScript:void(0);" class="ctf_show_gdpr_list"><?php _e( 'What will be limited?', 'custom-twitter-feeds' ); ?></a></p>
1849
+
1850
+ <?php echo "<div class='ctf_gdpr_list'>" . $gdpr_list . '</div>'; ?>
1851
+
1852
+
1853
+ <p><span><?php _e("No", 'custom-twitter-feeds' ); ?>:</span> <?php _e("The plugin will still make some requests to load and display images and videos directly from Twitter.", 'custom-twitter-feeds' ); ?></p>
1854
+
1855
+
1856
+ <p><span><?php _e("Automatic", 'custom-twitter-feeds' ); ?>:</span> <?php echo sprintf( __( 'The plugin will only load images and videos directly from Twitter if consent has been given by one of these integrated %s', 'custom-twitter-feeds' ), '<a href="https://smashballoon.com/doc/gdpr-plugin-list/?twitter" target="_blank" rel="noopener">' . __( 'GDPR cookie plugins', 'custom-twitter-feeds' ) . '</a>' ); ?></p>
1857
+
1858
+ <p><?php echo sprintf( __( '%s to learn more about GDPR compliance in the Custom Twitter Feed plugin.', 'custom-twitter-feeds' ), '<a href="https://smashballoon.com/doc/custom-twitter-feeds-gdpr-compliance/?twitter" target="_blank" rel="noopener">'. __( 'Click here', 'custom-twitter-feeds' ).'</a>' ); ?></p>
1859
+ </div>
1860
+ </div>
1861
+
1862
+ <?php if ( ! CTF_GDPR_Integrations::gdpr_tests_successful( isset( $_GET['retest'] ) ) ) :
1863
+ $errors = CTF_GDPR_Integrations::gdpr_tests_error_message();
1864
+ ?>
1865
+ <div class="ctf-box ctf_gdpr_error">
1866
+ <div class="ctf-box-setting">
1867
+ <p>
1868
+ <strong><?php _e( 'Error:', 'custom-twitter-feeds' ); ?></strong> <?php _e("Due to a configuration issue on your web server, the GDPR setting is unable to be enabled. Please see below for more information.", 'custom-twitter-feeds' ); ?></p>
1869
+ <p>
1870
+ <?php echo $errors; ?>
1871
+ </p>
1872
+ </div>
1873
+ </div>
1874
+ <?php else: ?>
1875
+
1876
+ <div class="ctf_gdpr_auto">
1877
+ <?php if ( CTF_GDPR_Integrations::gdpr_plugins_active() ) :
1878
+ $active_plugin = CTF_GDPR_Integrations::gdpr_plugins_active();
1879
+ ?>
1880
+ <div class="ctf_gdpr_plugin_active">
1881
+ <div class="ctf_active">
1882
+ <p>
1883
+ <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check-circle" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-check-circle fa-w-16 fa-2x"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z" class=""></path></svg>
1884
+ <b><?php echo sprintf( __( '%s detected', 'custom-twitter-feeds' ), $active_plugin ); ?></b>
1885
+ <br />
1886
+ <?php _e( 'Some Custom Twitter Feed features will be limited for visitors to ensure GDPR compliance until they give consent.', 'custom-twitter-feeds' ); ?>
1887
+ <a href="JavaScript:void(0);" class="ctf_show_gdpr_list"><?php _e( 'What will be limited?', 'custom-twitter-feeds' ); ?></a>
1888
+ </p>
1889
+ <?php echo "<div class='ctf_gdpr_list'>" . $gdpr_list . '</div>'; ?>
1890
+ </div>
1891
+
1892
+ </div>
1893
+ <?php else: ?>
1894
+ <div class="ctf-box">
1895
+ <div class="ctf-box-setting">
1896
+ <p><?php _e( 'No GDPR consent plugin detected. Install a compatible <a href="https://smashballoon.com/doc/gdpr-plugin-list/?twitter">GDPR consent plugin</a>, or manually enable the setting above to display a GDPR compliant version of the feed to all visitors.', 'custom-twitter-feeds' ); ?></p>
1897
+ </div>
1898
+ </div>
1899
+ <?php endif; ?>
1900
+ </div>
1901
+
1902
+ <div class="ctf-box ctf_gdpr_yes">
1903
+ <div class="ctf-box-setting">
1904
+ <p><?php _e( "No requests will be made to third-party websites. To accommodate this, some features of the plugin will be limited:", 'custom-twitter-feeds' ); ?></p>
1905
+ <?php echo $gdpr_list; ?>
1906
+ </div>
1907
+ </div>
1908
+
1909
+ <div class="ctf-box ctf_gdpr_no">
1910
+ <div class="ctf-box-setting">
1911
+ <p><?php _e( "The plugin will function as normal and load images and videos directly from Twitter.", 'custom-twitter-feeds' ); ?></p>
1912
+ </div>
1913
+ </div>
1914
+
1915
+ <?php endif;
1916
+ }
1917
+
1918
+ public function clear_persistent_cache_button( $args ) {
1919
+ ?>
1920
+ <input id="ctf-clear-persistent-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Persistent Caches' ); ?>" />
1921
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1922
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for search and hashtag feeds that have persistent caches', 'custom-twitter-feeds' ); ?>.</p>
1923
+ <?php
1924
+ }
1925
+
1926
+ public function validate_ctf_options( $input )
1927
+ {
1928
+ if ( isset( $input['tab'] ) && ! isset( $_POST['just_tokens'] ) ) {
1929
+ wp_cache_delete ( 'alloptions', 'options' );
1930
+
1931
+ $ctf_options = get_option( 'ctf_options', array() );
1932
+
1933
+ if ( $input['tab'] === 'configure' && isset( $input['usertimeline_text'] ) ) {
1934
+
1935
+ $feed_types = apply_filters( 'ctf_admin_feed_type_list', '' );
1936
+ $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1937
+ $ctf_options['have_own_tokens'] = false;
1938
+ $ctf_options['use_own_consumer'] = false;
1939
+ $ctf_options['preserve_settings'] = false;
1940
+ $ctf_options['usertimeline_includereplies'] = false;
1941
+ $ctf_options['hometimeline_includereplies'] = false;
1942
+ $ctf_options['mentionstimeline_includereplies'] = false;
1943
+
1944
+ foreach ( $input as $key => $val ) {
1945
+ if ( $key == 'search_text' || $key == 'usertimeline_text' || $key == 'hashtag_text' ) {
1946
+ $ctf_options[$key] = apply_filters( 'ctf_admin_validate_' . $key, $val );
1947
+ } elseif ( $key == 'ajax_theme' || $key == 'use_own_consumer' || $key == 'have_own_tokens' || $key == 'preserve_settings' ||
1948
+ $key == 'usertimeline_includereplies' || $key == 'hometimeline_includereplies' || $key == 'mentionstimeline_includereplies' ) {
1949
+ if ( $val != 'on' ) {
1950
+ $ctf_options[$key] = false;
1951
+ } else {
1952
+ $ctf_options[$key] = true;
1953
+ }
1954
+ } else {
1955
+ $ctf_options[$key] = sanitize_text_field( $val );
1956
+ }
1957
+ }
1958
+
1959
+ $ctf_options['includereplies'] = apply_filters( 'ctf_admin_set_include_replies', $ctf_options );
1960
+
1961
+ // delete feeds cached in transients
1962
+ ctf_clear_cache();
1963
+ delete_transient( 'ctf_reauthenticate' );
1964
+
1965
+ // process force cache to clear on interval
1966
+ $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
1967
+ $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
1968
+
1969
+ if ( $cron_clear_cache == 'no' ) {
1970
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
1971
+ } elseif ( $cron_clear_cache == 'yes' ) {
1972
+ //Clear the existing cron event
1973
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
1974
+
1975
+ //Set the event schedule based on what the caching time is set to
1976
+ if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
1977
+ $ctf_cron_schedule = 'twicedaily';
1978
+ } elseif ( $cache_time_unit == 86400 ) {
1979
+ $ctf_cron_schedule = 'daily';
1980
+ } else {
1981
+ $ctf_cron_schedule = 'hourly';
1982
+ }
1983
+
1984
+ wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1985
+ }
1986
+ } elseif ( $input['tab'] === 'customize' && isset( $input['class'] ) ) {
1987
+
1988
+ $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1989
+ $checkbox_settings = array( 'width_mobile_no_fixed', 'include_retweeter', 'include_avatar', 'include_author', 'include_logo', 'include_text', 'include_media_placeholder',
1990
+ 'include_date', 'include_actions', 'include_twitterlink', 'ajax_theme', 'include_linkbox', 'creditctf', 'showbutton', 'showheader', 'persistentcache', 'selfreplies',
1991
+ 'disableintents', 'disableawesome', 'shorturls' );
1992
+ $checkbox_settings = apply_filters( 'ctf_admin_customize_checkbox_settings', $checkbox_settings );
1993
+ $leave_spaces = array( 'headertext', 'translate_minute', 'translate_hour', 'custom_css', 'custom_js' );
1994
+
1995
+ foreach ( $checkbox_settings as $checkbox_setting ) {
1996
+ $ctf_options[$checkbox_setting] = 0;
1997
+ }
1998
+
1999
+ foreach ( $input as $key => $val ) {
2000
+ if ( in_array( $key, $checkbox_settings ) ) {
2001
+ if ( $val != 'on' ) {
2002
+ $ctf_options[$key] = false;
2003
+ } else {
2004
+ $ctf_options[$key] = true;
2005
+ }
2006
+ } else {
2007
+ if ( in_array( $key, $leave_spaces ) ) {
2008
+ $ctf_options[$key] = $val;
2009
+ } else {
2010
+ $ctf_options[$key] = sanitize_text_field( $val );
2011
+ }
2012
+ }
2013
+ }
2014
+
2015
+ // delete feeds cached in transients
2016
+ ctf_clear_cache();
2017
+
2018
+ // process force cache to clear on interval
2019
+ $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
2020
+ $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
2021
+
2022
+ if ( $cron_clear_cache == 'no' ) {
2023
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
2024
+ } elseif ( $cron_clear_cache == 'yes' ) {
2025
+ //Clear the existing cron event
2026
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
2027
+
2028
+ //Set the event schedule based on what the caching time is set to
2029
+ if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
2030
+ $ctf_cron_schedule = 'twicedaily';
2031
+ } elseif ( $cache_time_unit == 86400 ) {
2032
+ $ctf_cron_schedule = 'daily';
2033
+ } else {
2034
+ $ctf_cron_schedule = 'hourly';
2035
+ }
2036
+
2037
+ wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
2038
+ }
2039
+ } elseif ( $input['tab'] === 'style' && isset( $input['headertext'] ) ) {
2040
+ $checkbox_settings = array( 'showbio', 'disablelinks', 'linktexttotwitter' );
2041
+ $leave_spaces = array( 'headertext' );
2042
+
2043
+ foreach ( $checkbox_settings as $checkbox_setting ) {
2044
+ $ctf_options[$checkbox_setting] = false;
2045
+ }
2046
+
2047
+ foreach ( $input as $key => $val ) {
2048
+ if ( in_array( $key, $checkbox_settings ) ) {
2049
+ if ( $val != 'on' ) {
2050
+ $ctf_options[$key] = false;
2051
+ } else {
2052
+ $ctf_options[$key] = true;
2053
+ }
2054
+ } else {
2055
+ if ( in_array( $key, $leave_spaces ) ) {
2056
+ $ctf_options[$key] = $val;
2057
+ } else {
2058
+ $ctf_options[$key] = sanitize_text_field( $val );
2059
+ }
2060
+ }
2061
+ }
2062
+ }
2063
+
2064
+ return $ctf_options;
2065
+ } elseif ( isset( $input['access_token'] ) ) {
2066
+ wp_cache_delete ( 'alloptions', 'options' );
2067
+
2068
+ $new = get_option( 'ctf_options', array() );
2069
+ $new['access_token'] = $input['access_token'];
2070
+ $new['access_token_secret'] = $input['access_token_secret'];
2071
+
2072
+ return $new;
2073
+
2074
+ }
2075
+ wp_cache_delete ( 'alloptions', 'options' );
2076
+
2077
+ $new = get_option( 'ctf_options', array() );
2078
+
2079
+ return $new;
2080
+ }
2081
  }
inc/CtfDateTime.php CHANGED
@@ -1,25 +1,25 @@
1
- <?php
2
- /**
3
- * Class CtfDateTime
4
- *
5
- * Workaround for PHP 5.2
6
- */
7
- // Don't load directly
8
- if ( ! defined( 'ABSPATH' ) ) {
9
- die( '-1' );
10
- }
11
-
12
- class CtfDateTime extends DateTime
13
- {
14
- public function setTimestamp( $timestamp )
15
- {
16
- $date = getdate( ( int ) $timestamp );
17
- $this->setDate( $date['year'] , $date['mon'] , $date['mday'] );
18
- $this->setTime( $date['hours'] , $date['minutes'] , $date['seconds'] );
19
- }
20
-
21
- public function getTimestamp()
22
- {
23
- return $this->format( 'U' );
24
- }
25
- }
1
+ <?php
2
+ /**
3
+ * Class CtfDateTime
4
+ *
5
+ * Workaround for PHP 5.2
6
+ */
7
+ // Don't load directly
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ die( '-1' );
10
+ }
11
+
12
+ class CtfDateTime extends DateTime
13
+ {
14
+ public function setTimestamp( $timestamp )
15
+ {
16
+ $date = getdate( ( int ) $timestamp );
17
+ $this->setDate( $date['year'] , $date['mon'] , $date['mday'] );
18
+ $this->setTime( $date['hours'] , $date['minutes'] , $date['seconds'] );
19
+ }
20
+
21
+ public function getTimestamp()
22
+ {
23
+ return $this->format( 'U' );
24
+ }
25
+ }
inc/CtfFeed.php CHANGED
@@ -1,1848 +1,1848 @@
1
- <?php
2
- /**
3
- * Class CtfFeed
4
- *
5
- * Creates the settings for the feed and outputs the html
6
- */
7
-
8
- // Don't load directly
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- die( '-1' );
11
- }
12
-
13
- class CtfFeed
14
- {
15
- /**
16
- * @var array
17
- */
18
- public $errors = array();
19
-
20
- /**
21
- * @var array
22
- */
23
- protected $atts;
24
-
25
- /**
26
- * @var string
27
- */
28
- protected $last_id_data;
29
-
30
- private $num_needed_input;
31
-
32
- /**
33
- * @var mixed|void
34
- */
35
- protected $db_options;
36
-
37
- /**
38
- * @var array
39
- */
40
- public $feed_options = array();
41
-
42
- /**
43
- * @var mixed|void
44
- */
45
- public $missing_credentials;
46
-
47
- /**
48
- * @var string
49
- */
50
- public $transient_name;
51
-
52
- /**
53
- * @var bool
54
- */
55
- protected $transient_data = false;
56
-
57
- /**
58
- * @var int
59
- */
60
- private $num_tweets_needed;
61
-
62
- private $check_for_duplicates = false;
63
-
64
- /**
65
- * @var array
66
- */
67
- public $tweet_set;
68
-
69
- /**
70
- * @var object
71
- */
72
- public $api_obj;
73
-
74
- /**
75
- * @var string
76
- */
77
- public $feed_html;
78
-
79
- private $persistent_index;
80
-
81
-
82
- /**
83
- * retrieves and sets options that apply to the feed
84
- *
85
- * @param array $atts data from the shortcode
86
- * @param string $last_id_data the last visible tweet on the feed, empty string if first set
87
- * @param int $num_needed_input this number represents the number left to retrieve after the first set
88
- */
89
- public function __construct( $atts, $last_id_data, $num_needed_input )
90
- {
91
- $this->atts = $atts;
92
- $this->last_id_data = $last_id_data;
93
- $this->num_needed_input = $num_needed_input;
94
- $this->db_options = get_option( 'ctf_options', array() );
95
- }
96
-
97
- /**
98
- * creates and returns all of the data needed to generate the output for the feed
99
- *
100
- * @param array $atts data from the shortcode
101
- * @param string $last_id_data the last visible tweet on the feed, empty string if first set
102
- * @param int $num_needed_input this number represents the number left to retrieve after the first set
103
- * @return CtfFeed the complete object for the feed
104
- */
105
- public static function init( $atts, $last_id_data = '', $num_needed_input = 0, $ids_to_remove = array(), $persistent_index = 1 )
106
- {
107
- $feed = new CtfFeed( $atts, $last_id_data, $num_needed_input );
108
- $feed->setFeedOptions();
109
-
110
- $feed->setCacheTypeOption();
111
- if ( $feed->feed_options['persistentcache'] ) {
112
- $feed->persistent_index = $persistent_index;
113
- }
114
-
115
- $feed->setTweetSet();
116
- return $feed;
117
- }
118
-
119
- /**
120
- * creates all of the feed options with shortcode settings having the highest priority
121
- */
122
- protected function setFeedOptions()
123
- {
124
- $this->setFeedTypeAndTermOptions();
125
-
126
- $bool_false = array (
127
- 'have_own_tokens',
128
- 'includereplies',
129
- 'ajax_theme',
130
- 'width_mobile_no_fixed',
131
- 'disablelinks',
132
- 'linktexttotwitter',
133
- 'creditctf',
134
- 'selfreplies',
135
- 'disableintents',
136
- 'shorturls'
137
- );
138
- $this->setStandardBoolOptions( $bool_false, false );
139
-
140
- $this->setAccessTokenAndSecretOptions();
141
- $this->setConsumerKeyAndSecretOptions();
142
-
143
- $db_only = array(
144
- 'request_method'
145
- );
146
- $this->setDatabaseOnlyOptions( $db_only );
147
-
148
- $this->setStandardTextOptions( 'num', 5 );
149
-
150
- $standard_text = array(
151
- 'class',
152
- 'headertext',
153
- 'dateformat',
154
- 'datecustom',
155
- 'mtime',
156
- 'htime',
157
- 'nowtime'
158
- );
159
- $this->setStandardTextOptions( $standard_text, '' );
160
-
161
- $this->setStandardTextOptions( 'retweetedtext', __( 'Retweeted', 'custom-twitter-feeds' ) );
162
- $this->setStandardTextOptions( 'font_method', 'svg' );
163
- $this->setStandardTextOptions( 'multiplier', 1.25 );
164
- $this->setStandardTextOptions( 'twitterlinktext', 'Twitter' );
165
- $this->setStandardTextOptions( 'gdpr', 'auto' );
166
-
167
- $this->setStandardTextOptions( 'buttontext', __( 'Load More...', 'custom-twitter-feeds' ) );
168
- $this->setStandardTextOptions( 'textlength', 280 );
169
- $text_size = array(
170
- 'authortextsize',
171
- 'tweettextsize',
172
- 'datetextsize',
173
- 'quotedauthorsize',
174
- 'iconsize',
175
- 'logosize'
176
- );
177
- $this->setTextSizeOptions( $text_size );
178
-
179
- $text_weight = array(
180
- 'authortextweight',
181
- 'tweettextweight',
182
- 'datetextweight',
183
- 'quotedauthorweight'
184
- );
185
- $this->setStandardStyleProperty( $text_weight, 'font-weight' );
186
-
187
- $text_color = array(
188
- 'headertextcolor',
189
- 'textcolor',
190
- 'linktextcolor',
191
- 'iconcolor',
192
- 'logocolor',
193
- 'buttontextcolor'
194
- );
195
- $this->setStandardStyleProperty( $text_color, 'color' );
196
-
197
- $bg_color = array(
198
- 'bgcolor',
199
- 'tweetbgcolor',
200
- 'headerbgcolor',
201
- 'buttoncolor'
202
- );
203
- $this->setStandardStyleProperty( $bg_color, 'background-color' );
204
-
205
- $bool_true = array(
206
- 'persistentcache',
207
- 'showbutton',
208
- 'showbio',
209
- 'showheader'
210
- );
211
- $this->setStandardBoolOptions( $bool_true, true );
212
-
213
- $this->setDimensionOptions();
214
- $this->setCacheTimeOptions();
215
- $this->setIncludeExcludeOptions();
216
-
217
- if ( CTF_GDPR_Integrations::doing_gdpr( $this->feed_options ) ) {
218
- CTF_GDPR_Integrations::init();
219
- }
220
- }
221
-
222
- /**
223
- * uses the feed options to set the the tweets in the feed by using
224
- * an existing set in a cache or by retrieving them from Twitter
225
- */
226
- protected function setTweetSet()
227
- {
228
- $this->setTransientName();
229
- $success = $this->maybeSetTweetsFromCache();
230
-
231
- if ( ! $success ) {
232
- $this->maybeSetTweetsFromTwitter();
233
- }
234
-
235
- $this->num_tweets_needed = $this->numTweetsNeeded();
236
- }
237
-
238
- /**
239
- * the access token and secret must be set in order for the feed to work
240
- * this function processes the user input and sets a flag if none are entered
241
- */
242
- private function setAccessTokenAndSecretOptions()
243
- {
244
- $this->feed_options['access_token'] = isset( $this->db_options['access_token'] ) && strlen( $this->db_options['access_token'] ) > 30 ? $this->db_options['access_token'] : 'missing';
245
- $this->feed_options['access_token_secret'] = isset( $this->db_options['access_token_secret'] ) && strlen( $this->db_options['access_token_secret'] ) > 30 ? $this->db_options['access_token_secret'] : 'missing';
246
-
247
- // verify that access token and secret have been entered
248
- $this->setMissingCredentials();
249
- }
250
-
251
- /**
252
- * generates the flag if there are missing access tokens
253
- */
254
- private function setMissingCredentials() {
255
- if ( $this->feed_options['access_token'] == 'missing' || $this->feed_options['access_token_secret'] == 'missing' ) {
256
- $this->missing_credentials = true;
257
- } else {
258
- $this->missing_credentials = false;
259
- }
260
- }
261
-
262
- /**
263
- * processes the consumer key and secret options
264
- */
265
- protected function setConsumerKeyAndSecretOptions()
266
- {
267
- if ( $this->feed_options['have_own_tokens'] ) {
268
- $this->feed_options['consumer_key'] = isset( $this->db_options['consumer_key'] ) && strlen( $this->db_options['consumer_key'] ) > 15 ? $this->db_options['consumer_key'] : 'FPYSYWIdyUIQ76Yz5hdYo5r7y';
269
- $this->feed_options['consumer_secret'] = isset( $this->db_options['consumer_secret'] ) && strlen( $this->db_options['consumer_secret'] ) > 30 ? $this->db_options['consumer_secret'] : 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
270
- } else {
271
- $this->feed_options['consumer_key'] ='FPYSYWIdyUIQ76Yz5hdYo5r7y';
272
- $this->feed_options['consumer_secret'] = 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
273
- }
274
- }
275
-
276
- /**
277
- * determines what value to use and saves it for the appropriate key in the feed_options array
278
- *
279
- * @param $options mixed the key or array of keys to be set
280
- * @param $options_page string options page this setting is set on
281
- * @param string $default default value to use if there is no user input
282
- */
283
- public function setDatabaseOnlyOptions( $options, $default = '' )
284
- {
285
- if ( is_array( $options ) ) {
286
- foreach ( $options as $option ) {
287
- $this->feed_options[$option] = isset( $this->db_options[$option] ) && ! empty( $this->db_options[$option] ) ? $this->db_options[$option] : $default;
288
- }
289
- } else {
290
- $this->feed_options[$options] = isset( $this->db_options[$options] ) && ! empty( $this->db_options[$options] ) ? $this->db_options[$options] : $default;
291
- }
292
- }
293
-
294
- /**
295
- * determines what value to use and saves it for the appropriate key in the feed_options array
296
- *
297
- * @param $options mixed the key or array of keys to be set
298
- * @param $options_page string options page this setting is set on
299
- * @param string $default default value to use if there is no user input
300
- */
301
- public function setStandardTextOptions( $options, $default = '' )
302
- {
303
- if ( is_array( $options ) ) {
304
- foreach ( $options as $option ) {
305
- $this->feed_options[$option] = isset( $this->atts[$option] ) ? esc_attr( __( $this->atts[$option], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$option] ) ? esc_attr( $this->db_options[$option] ) : $default );
306
- }
307
- } else {
308
- $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( __( $this->atts[$options], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
309
- }
310
- }
311
-
312
- /**
313
- * creates the appropriate style attribute string for the text size setting
314
- *
315
- * @param $value mixed pixel size or other that the user has selected
316
- * @return string string for the style attribute
317
- */
318
- public static function processTextSizeStyle( $value )
319
- {
320
- if ( $value == '' ) {
321
- return '';
322
- }
323
- $processed_value = $value == 'inherit' ? '' : 'font-size: ' . $value . 'px;';
324
-
325
- return $processed_value;
326
- }
327
-
328
- /**
329
- * determines what value to use and saves it for the appropriate key in the feed_options array
330
- *
331
- * @param $options mixed the key or array of keys to be set
332
- * @param string $default default value to use if there is no user input
333
- */
334
- public function setTextSizeOptions( $options, $default = '' )
335
- {
336
- if ( is_array( $options ) ) {
337
- foreach ( $options as $option ) {
338
- $this->feed_options[$option] = isset( $this->atts[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$option] ) ) : ( isset( $this->db_options[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$option] ) ) : $default );
339
- }
340
- } else {
341
- $this->feed_options[$options] = isset( $this->atts[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$options] ) ) : ( isset( $this->db_options[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$options] ) ) : $default );
342
- }
343
- }
344
-
345
- /**
346
- * determines what value to use and saves it for the appropriate key in the feed_options array
347
- *
348
- * @param $options mixed the key or array of keys to be set
349
- * @param $property string name of the property to be set
350
- * @param string $default default value to use if there is no user input
351
- */
352
- public function setStandardStyleProperty( $options, $property, $default = '' )
353
- {
354
- if ( is_array( $options ) ) {
355
- foreach ( $options as $option ) {
356
- $this->feed_options[$option] = isset( $this->atts[$option] ) && $this->atts[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$option] ) . ';' : ( isset( $this->db_options[$option] ) && $this->db_options[$option] != '#' && $this->db_options[$option] != '' && $this->db_options[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$option] ) . ';' : $default );
357
- }
358
- } else {
359
- $this->feed_options[$options] = isset( $this->atts[$options] ) && $this->atts[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$options] ) . ';' : ( isset( $this->db_options[$options] ) && $this->db_options[$options] != '#' && $this->db_options[$options] != '' && $this->db_options[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$options] ) . ';' : $default );
360
- }
361
- }
362
-
363
- /**
364
- * determines what value to use and saves it for the appropriate key in the feed_options array
365
- *
366
- * @param $options mixed the key or array of keys to be set
367
- * @param bool|true $default default value to use if there is no user input
368
- */
369
- public function setStandardBoolOptions( $options, $default = true )
370
- {
371
- if ( is_array( $options ) ) {
372
- foreach ( $options as $option ) {
373
- $this->feed_options[$option] = isset( $this->atts[$option] ) ? ( $this->atts[$option] === 'true' ) : ( isset( $this->db_options[$option] ) ? (bool) $this->db_options[$option] : (bool) $default );
374
- }
375
- } else {
376
- $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( $this->atts[$options] ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
377
- }
378
- }
379
-
380
- /**
381
- * sets the width and height of the feed based on user input
382
- */
383
- public function setDimensionOptions()
384
- {
385
- $this->feed_options['width'] = isset( $this->atts['width'] ) ? 'width: '. esc_attr( $this->atts['width'] ) .';' : ( ( isset( $this->db_options['width'] ) && $this->db_options['width'] != '' ) ? 'width: '. esc_attr( $this->db_options['width'] ) . ( isset( $this->db_options['width_unit'] ) ? esc_attr( $this->db_options['width_unit'] ) : '%' ) . ';' : '' );
386
- $this->feed_options['height'] = isset( $this->atts['height'] ) ? 'height: '. esc_attr( $this->atts['height'] ) .';' : ( ( isset( $this->db_options['height'] ) && $this->db_options['height'] != '' ) ? 'height: '. esc_attr( $this->db_options['height'] ) . ( isset( $this->db_options['height_unit'] ) ? esc_attr( $this->db_options['height_unit'] ) : 'px' ) . ';' : '' );
387
- }
388
-
389
- /**
390
- * sets the cache time based on user input
391
- */
392
- public function setCacheTimeOptions()
393
- {
394
- $user_cache = isset( $this->db_options['cache_time'] ) ? ( $this->db_options['cache_time'] * $this->db_options['cache_time_unit'] ) : HOUR_IN_SECONDS;
395
-
396
- if ( $this->feed_options['have_own_tokens'] ) {
397
- $this->feed_options['cache_time'] = max( $user_cache, 60 );
398
- } else {
399
- $this->feed_options['cache_time'] = max( $user_cache, 3600 );
400
- }
401
- }
402
-
403
-
404
- /**
405
- * sets the number of tweets to retrieve
406
- */
407
- public function setTweetsToRetrieve()
408
- {
409
- $min_tweets_to_retrieve = 10;
410
-
411
- if ( $this->num_needed_input < 1 ) {
412
- if ( $this->feed_options['includereplies'] ) {
413
- $this->feed_options['count'] = $this->feed_options['num'];
414
- } else {
415
- if ( $this->feed_options['num'] < 10 ) {
416
- $this->feed_options['count'] = max( round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.6 ), $min_tweets_to_retrieve );
417
- } elseif ( $this->feed_options['num'] < 30 ) {
418
- $this->feed_options['count'] = round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.2 );
419
- } else {
420
- $this->feed_options['count'] = round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] );
421
- }
422
- }
423
- } else {
424
- $this->feed_options['count'] = max( $this->num_needed_input, 50 );
425
- $this->feed_options['num'] = $this->num_needed_input;
426
- }
427
-
428
- }
429
-
430
- /**
431
- * sets the feed type and associated parameter
432
- */
433
- public function setFeedTypeAndTermOptions()
434
- {
435
- $this->feed_options['type'] = '';
436
- $this->feed_options['feed_term'] = '';
437
- $this->feed_options['screenname'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
438
-
439
- if ( isset( $this->atts['home'] ) && $this->atts['home'] == 'true' ) {
440
- $this->feed_options['type'] = 'hometimeline';
441
- }
442
- if ( isset( $this->atts['screenname'] ) ) {
443
- $this->feed_options['type'] = 'usertimeline';
444
- $this->feed_options['feed_term'] = isset( $this->atts['screenname'] ) ? ctf_validate_usertimeline_text( $this->atts['screenname'] ) : ( ( isset( $this->db_options['usertimeline_text'] ) ) ? $this->db_options['usertimeline_text'] : '' );
445
- $this->feed_options['screenname'] = $this->feed_options['feed_term'];
446
- }
447
- if ( isset( $this->atts['search'] ) || isset( $this->atts['hashtag'] ) ) {
448
- $this->feed_options['type'] = 'search';
449
- $this->working_term = isset( $this->atts['hashtag'] ) ? $this->atts['hashtag'] : ( isset( $this->atts['search'] ) ? $this->atts['search'] : '' );
450
- $this->feed_options['feed_term'] = isset( $this->working_term ) ? ctf_validate_search_text( $this->working_term ) . ' -filter:retweets' : ( ( isset( $this->db_options['search_text'] ) ) ? $this->db_options['search_text'] . ' -filter:retweets' : '' );
451
- $this->check_for_duplicates = true;
452
- }
453
-
454
- if ( $this->feed_options['type'] == '' ) {
455
- $this->feed_options['type'] = isset( $this->db_options['type'] ) ? $this->db_options['type'] : 'usertimeline';
456
- switch ( $this->feed_options['type'] ) {
457
- case 'usertimeline':
458
- $this->feed_options['feed_term'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
459
- break;
460
- case 'hometimeline':
461
- $this->feed_options['type'] = 'hometimeline';
462
- break;
463
- case 'search':
464
- $this->feed_options['feed_term'] = isset( $this->db_options['search_text'] ) ? $this->db_options['search_text'] . ' -filter:retweets' : '';
465
- $this->check_for_duplicates = true;
466
- break;
467
- }
468
- }
469
- }
470
-
471
- /**
472
- * sets the visible parts of each tweet for the feed
473
- */
474
- public function setIncludeExcludeOptions()
475
- {
476
- $this->feed_options['tweet_excludes'] = array();
477
- $this->feed_options['tweet_includes'] = isset( $this->atts['include'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['include'] ) ) ) : array();
478
-
479
- if ( empty( $this->feed_options['tweet_includes'][0] ) ) {
480
- $this->feed_options['tweet_excludes'] = isset( $this->atts['exclude'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['exclude'] ) ) ) : array();
481
- }
482
- if ( empty( $this->feed_options['tweet_excludes'][0] ) && empty( $this->feed_options['tweet_includes'][0] ) ) {
483
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_retweeter'] ) && $this->db_options['include_retweeter'] == false ? null : 'retweeter';
484
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_avatar'] ) && $this->db_options['include_avatar'] == false ? null : 'avatar';
485
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_author'] ) && $this->db_options['include_author'] == false ? null : 'author';
486
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_text'] ) && $this->db_options['include_text'] == false ? null : 'text';
487
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_media_placeholder'] ) && $this->db_options['include_media_placeholder'] == false ? null : 'placeholder';
488
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_date'] ) && $this->db_options['include_date'] == false ? null : 'date';
489
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_actions'] ) && $this->db_options['include_actions'] == false ? null : 'actions';
490
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_twitterlink'] ) && $this->db_options['include_twitterlink'] == false ? null : 'twitterlink';
491
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_linkbox'] ) && $this->db_options['include_linkbox'] == false ? null : 'linkbox';
492
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_logo'] ) && $this->db_options['include_logo'] == false ? null : 'logo';
493
- }
494
-
495
- }
496
-
497
- /**
498
- * sets the transient name for the caching system
499
- */
500
- public function setTransientName()
501
- {
502
- $last_id_data = $this->last_id_data;
503
- $num = isset( $this->feed_options['num'] ) ? $this->feed_options['num'] : '';
504
-
505
- switch ( $this->feed_options['type'] ) {
506
- case 'hometimeline' :
507
- $this->transient_name = 'ctf_' . $last_id_data . 'hometimeline'. $num;
508
- break;
509
- case 'usertimeline' :
510
- $screenname = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
511
- $this->transient_name = substr( 'ctf__' . $last_id_data . $screenname . $num, 0, 45 );
512
- break;
513
- case 'search' :
514
- $hashtag = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
515
- $hashtag = str_replace( ' -filter:retweets', '', $hashtag );
516
- $this->transient_name = substr( 'ctf_' . $last_id_data . $hashtag . $num, 0, 45 );
517
- break;
518
- }
519
- }
520
-
521
- public function setCacheTypeOption() {
522
- if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
523
- $this->feed_options['persistentcache'] = true;
524
- } else {
525
- $this->feed_options['persistentcache'] = false;
526
- }
527
- }
528
-
529
- /**
530
- * checks the data available in the cache to make sure it seems to be valid
531
- *
532
- * @return bool|string false if the cache is valid, error otherwise
533
- */
534
- private function validateCache()
535
- {
536
- if ( isset( $this->transient_data[0] ) ) {
537
- return false;
538
- } else {
539
- return 'invalid cache';
540
- }
541
- }
542
-
543
- /**
544
- * will use the cached data in the feed if data seems to be valid and user
545
- * wants to use caching
546
- *
547
- * @return bool|mixed false if none is set, tweet set otherwise
548
- */
549
- public function maybeSetTweetsFromCache()
550
- {
551
- if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
552
- $persistent_cache_tweets = $this->persistentCacheTweets();
553
- if ( is_array( $persistent_cache_tweets ) ) {
554
- $this->transient_data = array_slice( $persistent_cache_tweets, ( $this->persistent_index - $this->feed_options['num'] - 1 ) , $this->persistent_index );
555
- } else {
556
- $this->transient_data = $persistent_cache_tweets;
557
- }
558
- } else {
559
- $this->transient_data = get_transient( $this->transient_name );
560
- if ( ! is_array( $this->transient_data ) ) {
561
- $this->transient_data = json_decode( $this->transient_data, $assoc = true );
562
- }
563
-
564
- if ( $this->feed_options['cache_time'] <= 0 ) {
565
- return $this->tweet_set = false;
566
- }
567
- }
568
- // validate the transient data
569
- if ( $this->transient_data ) {
570
- $this->errors['cache_status'] = $this->validateCache();
571
- if ( $this->errors['cache_status'] === false ) {
572
- return $this->tweet_set = $this->transient_data;
573
- } else {
574
- return $this->tweet_set = false;
575
- }
576
- } else {
577
- $this->errors['cache_status'] = 'none found';
578
- return $this->tweet_set = false;
579
- }
580
- }
581
-
582
- private function persistentCacheTweets()
583
- {
584
- // if cache exists get cached data
585
- $includewords = ! empty( $this->feed_options['includewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['includewords'] ), 0, 10 ) : '';
586
- $excludewords = ! empty( $this->feed_options['excludewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['excludewords'] ), 0, 5 ) : '';
587
- $cache_name = substr( 'ctf_!_' . $this->feed_options['feed_term'] . $includewords . $excludewords, 0, 45 );
588
-
589
- if ( $this->feed_options['type'] === 'search' ) {
590
- $cache_name = str_replace( ' -filter:retweets', '', $cache_name );
591
- }
592
-
593
- $cache_time_limit_reached = get_transient( $cache_name ) ? false : true;
594
-
595
- $existing_cache = get_option( $cache_name, false );
596
- if ( $existing_cache && ! is_array( $existing_cache ) ) {
597
- $existing_cache = json_decode( $existing_cache, $assoc = true );
598
- }
599
-
600
- $this->persistent_index = $this->persistent_index + $this->feed_options['num'];
601
-
602
- $this->feed_options['count'] = 200;
603
-
604
- if ( ! empty( $this->last_id_data ) || ( ! $cache_time_limit_reached && $existing_cache ) ) {
605
- return $existing_cache;
606
- } elseif ( $existing_cache ) {
607
- // use "since-id" to look for more in an api request
608
- $since_id = $existing_cache[0]['id_str'];
609
- $api_obj = $this->getTweetsSinceID( $since_id, 'search', $this->feed_options['feed_term'], $this->feed_options['count'] );
610
- // add any new tweets to the cache
611
- $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
612
-
613
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : array();
614
-
615
- // add a transient to delay another api retrieval
616
- set_transient( $cache_name, true, $this->feed_options['cache_time'] );
617
-
618
- if ( empty( $tweets ) ) {
619
- if ( ! is_array( $existing_cache ) ) {
620
- return false;
621
- } else {
622
- return $existing_cache;
623
- }
624
- } else {
625
- $tweet_set = $this->reduceTweetSetData( $tweets, false );
626
- }
627
- $tweet_set = $this->appendPersistentCacheTweets( $existing_cache, $tweet_set );
628
- $cache_set = json_encode( $tweet_set );
629
-
630
- update_option( $cache_name, $cache_set );
631
-
632
- return $tweet_set;
633
- // else if cached data doesn't exist
634
- } else {
635
- // make a request for last 200 tweets
636
- $api_obj = $this->apiConnectionResponse( 'search', $this->feed_options['feed_term'] );
637
- // cache them in a regular option
638
- $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
639
-
640
- // check for errors/tweets present
641
- if ( isset( $this->tweet_set['errors'][0] ) ) {
642
- if ( empty( $this->api_obj ) ) {
643
- $this->api_obj = new stdClass();
644
- }
645
- $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
646
- $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
647
-
648
- $this->tweet_set = false;
649
- }
650
-
651
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
652
-
653
- if ( empty( $tweets ) ) {
654
- $this->errors['error_message'] = 'No Tweets returned';
655
- $this->tweet_set = false;
656
- } else {
657
- $this->tweet_set = $this->reduceTweetSetData( $tweets, false );
658
- }
659
-
660
- // create a new persistent cache
661
- if ( $this->tweet_set && isset( $this->tweet_set[0] ) ) {
662
- $tweet_set = json_encode( $this->tweet_set );
663
-
664
- update_option( $cache_name, $tweet_set );
665
-
666
- // update list of persistent cache
667
- $cache_list = get_option( 'ctf_cache_list', array() );
668
-
669
- $cache_list[] = $cache_name;
670
-
671
- update_option( 'ctf_cache_list', $cache_list );
672
- }
673
-
674
- return $this->tweet_set;
675
- }
676
-
677
- // add the search parameter to another option that contains a list of all persistent caches available
678
- }
679
-
680
- private function reduceTweetSetData( $tweet_set, $limit = true ) {
681
- if ( $this->check_for_duplicates ) {
682
- $this->tweet_set = $this->removeDuplicates( $tweet_set, $limit );
683
- }
684
-
685
- if ( $this->feed_options['selfreplies'] ) {
686
- $this->tweet_set = $this->filterTweetSet( $tweet_set, $limit );
687
- }
688
-
689
- $this->tweet_set = $tweet_set;
690
- $this->trimTweetData( false );
691
- return $this->tweet_set;
692
- }
693
-
694
- /**
695
- * this takes the current set of tweets and processes them until there are
696
- * enough filtered tweets to create the feed from
697
- */
698
- private function filterTweetSet( $tweet_set, $limit = true )
699
- {
700
- $working_tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
701
- $usable_tweets = 0;
702
- if ( $limit ) {
703
- $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
704
- } else {
705
- $tweets_needed = 200;
706
- }
707
- $i = 0; // index of working_tweet_set
708
- $still_setting_filtered_tweets = true;
709
-
710
- while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
711
- if ( isset ( $working_tweet_set[$i] ) ) { // if there is another tweet available
712
- if ( !$this->feed_options['selfreplies'] && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] ) ) {
713
- unset( $working_tweet_set[$i] );
714
- } elseif ( $this->feed_options['selfreplies']
715
- && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] )
716
- && $working_tweet_set[$i]['in_reply_to_screen_name'] !== $working_tweet_set[$i]['user']['screen_name']) {
717
- unset( $working_tweet_set[$i] );
718
- } else {
719
- $usable_tweets++;
720
- }
721
- } else {
722
- $still_setting_filtered_tweets = false;
723
- }
724
-
725
- // if there are no more tweets needed
726
- if ( $usable_tweets >= $tweets_needed ) {
727
- $still_setting_filtered_tweets = false;
728
- } else {
729
- $i++;
730
- }
731
-
732
- }
733
-
734
- if ( is_array( $working_tweet_set ) ) {
735
- return array_values( $working_tweet_set );
736
- } else {
737
- return false;
738
- }
739
- }
740
-
741
- private function appendPersistentCacheTweets( $existing_cache )
742
- {
743
- if ( is_array( $this->tweet_set ) ) {
744
- $tweet_set = array_merge( $this->tweet_set, $existing_cache );
745
- } else {
746
- $tweet_set = $existing_cache;
747
- }
748
-
749
- $tweet_set = array_slice( $tweet_set, 0, 150 );
750
-
751
- return $tweet_set;
752
- }
753
-
754
-
755
- private function removeDuplicates( $tweet_set, $limit = true )
756
- {
757
- $tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
758
- $usable_tweets = 0;
759
- if ( $limit ) {
760
- $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
761
- } else {
762
- $tweets_needed = 200;
763
- }
764
- $ids_of_tweets_to_remove = array();
765
-
766
- $i = 0; // index of tweet_set
767
- $still_setting_filtered_tweets = true;
768
- while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
769
- if ( isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ) {
770
- unset( $tweet_set[$i] );
771
- } elseif ( isset( $tweet_set[$i] ) ) {
772
- $id = isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ? $tweet_set[$i]['retweeted_status']['id_str'] : $tweet_set[$i]['id_str'];
773
- if ( in_array( $id, $ids_of_tweets_to_remove ) ) {
774
- unset( $tweet_set[$i] );
775
- } else {
776
- $usable_tweets++;
777
- $ids_of_tweets_to_remove[] = $id;
778
- }
779
- } else {
780
- $still_setting_filtered_tweets = false;
781
- }
782
-
783
- // if there are no more tweets needed
784
- if ( $usable_tweets >= $tweets_needed ) {
785
- $still_setting_filtered_tweets = false;
786
- } else {
787
- $i++;
788
- }
789
-
790
- }
791
-
792
- if ( is_array( $tweet_set ) ) {
793
- return array_values( $tweet_set );
794
- } else {
795
- return false;
796
- }
797
- }
798
-
799
- /**
800
- * will attempt to connect to the api to retrieve current tweets
801
- */
802
- public function maybeSetTweetsFromTwitter()
803
- {
804
- $this->setTweetsToRetrieve();
805
- $this->api_obj = $this->apiConnectionResponse( $this->feed_options['type'], $this->feed_options['feed_term'] );
806
- $this->tweet_set = json_decode( $this->api_obj->json , $assoc = true );
807
-
808
- $working_tweet_set = $this->tweet_set;
809
- if ( ! isset( $working_tweet_set['errors'][0] ) ) {
810
- if ( isset( $working_tweet_set[0] ) ) {
811
- $value = array_values( array_slice( $working_tweet_set, -1 ) );
812
- $this->last_id_data = $value[0]['id_str'];
813
- }
814
-
815
- $working_tweet_set = $this->reduceTweetSetData( $working_tweet_set );
816
- if ( $working_tweet_set === false ) {
817
- $working_tweet_set = array();
818
- }
819
- }
820
-
821
- $num_tweets = is_array( $working_tweet_set ) ? count( $working_tweet_set ) : 500;
822
-
823
- if ( ! isset( $working_tweet_set['errors'][0] )
824
- && $num_tweets < $this->feed_options['count'] ) {
825
- // remove the last tweet as it is returned in the next request
826
- array_pop( $working_tweet_set );
827
- $original_count = $this->feed_options['count'];
828
- $this->feed_options['count'] = 200;
829
- $api_obj = $this->apiConnectionResponse( $this->feed_options['type'], $this->feed_options['feed_term'] );
830
- $tweet_set_to_merge = json_decode( $api_obj->json , $assoc = true );
831
-
832
- if ( isset( $tweet_set_to_merge['statuses'] ) ) {
833
- $working_tweet_set = array_merge( $working_tweet_set, $tweet_set_to_merge['statuses'] );
834
- } elseif ( isset( $tweet_set_to_merge[0]['created_at'] ) ) {
835
- $working_tweet_set = array_merge( $working_tweet_set, $tweet_set_to_merge );
836
- }
837
-
838
- $this->feed_options['count'] = $original_count;
839
- }
840
-
841
- $this->tweet_set = $working_tweet_set;
842
-
843
- // check for errors/tweets present
844
- if ( isset( $this->tweet_set['errors'][0] ) ) {
845
- $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
846
- $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
847
- $this->tweet_set = false;
848
- }
849
-
850
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
851
-
852
- if ( empty( $tweets ) ) {
853
- $this->errors['error_message'] = 'No Tweets returned';
854
- $this->tweet_set = false;
855
- }
856
-
857
- if ( $this->check_for_duplicates ) {
858
- $this->tweet_set = $this->removeDuplicates( $this->tweet_set );
859
- }
860
- }
861
-
862
-
863
- /**
864
- * calculates how many tweets short the feed is so more can be retrieved via ajax
865
- *
866
- * @return int number of tweets needed
867
- */
868
- protected function numTweetsNeeded() {
869
- $tweet_count = 0;
870
- if ( isset( $this->tweet_set['statuses'] ) && is_array( $this->tweet_set['statuses'] ) ) {
871
- $tweet_count = count( $this->tweet_set['statuses'] );
872
- } elseif ( isset( $this->tweet_set ) && is_array( $this->tweet_set ) ) {
873
- $tweet_count = count( $this->tweet_set );
874
- }
875
-
876
- return $this->feed_options['num'] - $tweet_count;
877
- }
878
-
879
- /**
880
- * trims the unused data retrieved for more efficient caching
881
- */
882
- protected function trimTweetData( $limit = true )
883
- {
884
- $is_pagination = !empty( $this->last_id_data ) ? 1 : 0;
885
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
886
- if ( $limit ) {
887
- $len = min( $this->feed_options['num'] + $is_pagination, count( $tweets ) );
888
- } else {
889
- $len = count( $tweets );
890
- }
891
- $trimmed_tweets = array();
892
-
893
- // for header
894
- if ( $this->last_id_data == '' && isset( $tweets[0] ) ) { // if this is the first set of tweets
895
- $trimmed_tweets[0]['user']['name']= $tweets[0]['user']['name'];
896
- $trimmed_tweets[0]['user']['description']= $tweets[0]['user']['description'];
897
- $trimmed_tweets[0]['user']['statuses_count']= $tweets[0]['user']['statuses_count'];
898
- $trimmed_tweets[0]['user']['followers_count']= $tweets[0]['user']['followers_count'];
899
- }
900
-
901
- for ( $i = 0; $i < $len; $i++ ) {
902
- $trimmed_tweets[$i]['user']['name'] = $tweets[$i]['user']['name'];
903
- $trimmed_tweets[$i]['user']['screen_name'] = $tweets[$i]['user']['screen_name'];
904
- $trimmed_tweets[$i]['user']['verified'] = $tweets[$i]['user']['verified'];
905
- $trimmed_tweets[$i]['user']['profile_image_url_https'] = $tweets[$i]['user']['profile_image_url_https'];
906
- $trimmed_tweets[$i]['user']['utc_offset']= $tweets[$i]['user']['utc_offset'];
907
- $trimmed_tweets[$i]['text'] = isset( $tweets[$i]['text'] ) ? $tweets[$i]['text'] : $tweets[$i]['full_text'];
908
- $trimmed_tweets[$i]['id_str']= $tweets[$i]['id_str'];
909
- $trimmed_tweets[$i]['created_at']= $tweets[$i]['created_at'];
910
- $trimmed_tweets[$i]['retweet_count']= $tweets[$i]['retweet_count'];
911
- $trimmed_tweets[$i]['favorite_count']= $tweets[$i]['favorite_count'];
912
-
913
- if ( isset( $tweets[$i]['entities']['urls'][0] ) ) {
914
- foreach ( $tweets[$i]['entities']['urls'] as $url ) {
915
- $trimmed_tweets[$i]['entities']['urls'][] = array(
916
- 'url' => $url['url'],
917
- 'expanded_url' => $url['expanded_url'],
918
- 'display_url' => $url['display_url'],
919
-
920
- );
921
- }
922
- }
923
-
924
- if ( isset( $tweets[$i]['retweeted_status'] ) ) {
925
- $trimmed_tweets[$i]['retweeted_status']['user']['name'] = $tweets[$i]['retweeted_status']['user']['name'];
926
- $trimmed_tweets[$i]['retweeted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['user']['screen_name'];
927
- $trimmed_tweets[$i]['retweeted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['user']['verified'];
928
- $trimmed_tweets[$i]['retweeted_status']['user']['profile_image_url_https'] = $tweets[$i]['retweeted_status']['user']['profile_image_url_https'];
929
- $trimmed_tweets[$i]['retweeted_status']['user']['utc_offset']= $tweets[$i]['retweeted_status']['user']['utc_offset'];
930
- $trimmed_tweets[$i]['retweeted_status']['text'] = isset( $tweets[$i]['retweeted_status']['text'] ) ? $tweets[$i]['retweeted_status']['text'] : $tweets[$i]['retweeted_status']['full_text'];
931
- $trimmed_tweets[$i]['retweeted_status']['id_str'] = $tweets[$i]['retweeted_status']['id_str'];
932
- $trimmed_tweets[$i]['retweeted_status']['created_at']= $tweets[$i]['retweeted_status']['created_at'];
933
- $trimmed_tweets[$i]['retweeted_status']['retweet_count']= $tweets[$i]['retweeted_status']['retweet_count'];
934
- $trimmed_tweets[$i]['retweeted_status']['favorite_count']= $tweets[$i]['retweeted_status']['favorite_count'];
935
- if ( isset( $tweets[$i]['retweeted_status']['entities']['urls'][0] ) ) {
936
- foreach ( $tweets[$i]['retweeted_status']['entities']['urls'] as $url ) {
937
- $trimmed_tweets[$i]['retweeted_status']['entities']['urls'][] = array(
938
- 'url' => $url['url'],
939
- 'expanded_url' => $url['expanded_url'],
940
- 'display_url' => $url['display_url'],
941
-
942
- );
943
- }
944
- }
945
- }
946
-
947
- if ( isset( $tweets[$i]['retweeted_status']['quoted_status'] ) ) {
948
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['name'];
949
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'];
950
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['verified'];
951
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['text'] = isset( $tweets[$i]['retweeted_status']['quoted_status']['text'] ) ? $tweets[$i]['retweeted_status']['quoted_status']['text'] : $tweets[$i]['retweeted_status']['quoted_status']['full_text'];
952
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['id_str'] = $tweets[$i]['retweeted_status']['quoted_status']['id_str'];
953
- if ( isset( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][0] ) ) {
954
- foreach ( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'] as $url ) {
955
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][] = array(
956
- 'url' => $url['url'],
957
- 'expanded_url' => $url['expanded_url'],
958
- 'display_url' => $url['display_url'],
959
- );
960
- }
961
- }
962
- }
963
-
964
- if ( isset( $tweets[$i]['quoted_status'] ) ) {
965
- $trimmed_tweets[$i]['quoted_status']['user']['name'] = $tweets[$i]['quoted_status']['user']['name'];
966
- $trimmed_tweets[$i]['quoted_status']['user']['screen_name'] = $tweets[$i]['quoted_status']['user']['screen_name'];
967
- $trimmed_tweets[$i]['quoted_status']['user']['verified'] = $tweets[$i]['quoted_status']['user']['verified'];
968
- $trimmed_tweets[$i]['quoted_status']['text'] = isset( $tweets[$i]['quoted_status']['text'] ) ? $tweets[$i]['quoted_status']['text'] : $tweets[$i]['quoted_status']['full_text'];
969
- $trimmed_tweets[$i]['quoted_status']['id_str'] = $tweets[$i]['quoted_status']['id_str'];
970
- if ( isset( $tweets[$i]['quoted_status']['entities']['urls'][0] ) ) {
971
- foreach ( $tweets[$i]['quoted_status']['entities']['urls'] as $url ) {
972
- $trimmed_tweets[$i]['quoted_status']['entities']['urls'][] = array(
973
- 'url' => $url['url'],
974
- 'expanded_url' => $url['expanded_url'],
975
- 'display_url' => $url['display_url'],
976
- );
977
- }
978
- }
979
- }
980
-
981
- $trimmed_tweets[$i] = $this->filterTrimmedTweets( $trimmed_tweets[$i], $tweets[$i] );
982
- }
983
-
984
- $this->tweet_set = $trimmed_tweets;
985
- }
986
-
987
- protected function removeStringFromText( $string, $text) {
988
- return str_replace( $string, '', $text );
989
- }
990
-
991
- /**
992
- * captures additional data for "Pro" features
993
- *
994
- * @param $trimmed array current set of trimmed tweets
995
- * @param $tweet array raw tweet data from api
996
- * @return array
997
- */
998
- protected function filterTrimmedTweets( $trimmed, $tweet )
999
- {
1000
- if ( isset( $tweet['in_reply_to_screen_name'] ) ) {
1001
- $trimmed['in_reply_to_screen_name'] = $tweet['in_reply_to_screen_name'];
1002
- $trimmed['entities']['user_mentions'][0]['name'] = isset( $tweet['entities']['user_mentions'][0]['name'] ) ? $tweet['entities']['user_mentions'][0]['name'] : '';
1003
- $trimmed['in_reply_to_status_id_str'] = $tweet['in_reply_to_status_id_str'];
1004
- }
1005
-
1006
- if ( isset( $tweet['extended_entities']['media'] ) ) {
1007
- // if there is media, we need to remove the media url from the tweet text
1008
- $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
1009
- if ( isset( $tweet['extended_entities']['media'][0]['url'] ) ) {
1010
- $trimmed['text'] = $this->removeStringFromText( $tweet['extended_entities']['media'][0]['url'], $text );
1011
- }
1012
- $num_media = count( $tweet['extended_entities']['media'] );
1013
- for ( $i = 0; $i < $num_media; $i++ ) {
1014
- $trimmed['extended_entities']['media'][$i]['media_url_https'] = $tweet['extended_entities']['media'][$i]['media_url_https'];
1015
- $trimmed['extended_entities']['media'][$i]['type'] = $tweet['extended_entities']['media'][$i]['type'];
1016
- if ( isset( $tweet['extended_entities']['media'][$i]['sizes'] ) ) {
1017
- $trimmed['extended_entities']['media'][$i]['sizes'] = $tweet['extended_entities']['media'][$i]['sizes'];
1018
- }
1019
- if ( $tweet['extended_entities']['media'][$i]['type'] == 'video' || $tweet['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1020
- foreach ( $tweet['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1021
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1022
- $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1023
- }
1024
- }
1025
- if ( ! isset( $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1026
- $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1027
- }
1028
- }
1029
- }
1030
-
1031
- } elseif ( isset( $tweet['entities']['media'] ) ) {
1032
- // if there is media, we need to remove the media url from the tweet text
1033
- $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
1034
- if ( isset( $tweet['entities']['media'][0]['url'] ) ) {
1035
- $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['media'][0]['url'], $text );
1036
- }
1037
-
1038
- $num_media = count( $tweet['entities']['media'] );
1039
- for ( $i = 0; $i < $num_media; $i++ ) {
1040
- $trimmed['entities']['media'][$i]['media_url_https'] = $tweet['entities']['media'][$i]['media_url_https'];
1041
- $trimmed['entities']['media'][$i]['type'] = $tweet['entities']['media'][$i]['type'];
1042
- if ( isset( $tweet['entities']['media'][$i]['sizes'] ) ) {
1043
- $trimmed['entities']['media'][$i]['sizes'] = $tweet['entities']['media'][$i]['sizes'];
1044
- }
1045
- if ( $tweet['entities']['media'][$i]['type'] == 'video' || $tweet['entities']['media'][$i]['type'] == 'animated_gif' ) {
1046
- foreach ( $tweet['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1047
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1048
- $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1049
- }
1050
- }
1051
- if ( ! isset( $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1052
- $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['entities']['media'][$i]['video_info']['variants'][0]['url'];
1053
- }
1054
- }
1055
- }
1056
-
1057
- }
1058
-
1059
- if ( isset( $tweet['retweeted_status']['extended_entities']['media'] ) ) {
1060
- // if there is media, we need to remove the media url from the tweet text
1061
- $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1062
- if ( isset( $tweet['retweeted_status']['extended_entities']['media'][0]['url'] ) ) {
1063
- $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1064
- }
1065
-
1066
- $num_media = count( $tweet['retweeted_status']['extended_entities']['media'] );
1067
- for ( $i = 0; $i < $num_media; $i++ ) {
1068
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['media_url_https'];
1069
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['type'];
1070
- if ( isset( $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1071
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'];
1072
- }
1073
- if ( $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1074
- foreach ( $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1075
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1076
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1077
- }
1078
- }
1079
- if ( ! isset( $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1080
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1081
- }
1082
- }
1083
- }
1084
-
1085
- } elseif ( isset( $tweet['retweeted_status']['entities']['media'] ) ) {
1086
- // if there is media, we need to remove the media url from the tweet text
1087
- $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1088
- if ( isset( $tweet['retweeted_status']['entities']['media'][0]['url'] ) ) {
1089
- $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['entities']['media'][0]['url'], $retweeted_text );
1090
- }
1091
-
1092
- $num_media = count( $tweet['retweeted_status']['entities']['media'] );
1093
- for( $i = 0; $i < $num_media; $i++ ) {
1094
- $trimmed['retweeted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['entities']['media'][$i]['media_url_https'];
1095
- $trimmed['retweeted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['entities']['media'][$i]['type'];
1096
- if ( isset( $tweet['retweeted_status']['entities']['media'][$i]['sizes'] ) ) {
1097
- $trimmed['retweeted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['entities']['media'][$i]['sizes'];
1098
- }
1099
- if ( $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1100
- foreach ( $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1101
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1102
- $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1103
- }
1104
- }
1105
- if ( ! isset( $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1106
- $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1107
- }
1108
- }
1109
- }
1110
-
1111
- } elseif ( isset( $tweet['quoted_status']['extended_entities']['media'] ) ) {
1112
- // if there is media, we need to remove the media url from the tweet text
1113
- $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1114
- if ( isset( $tweet['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1115
- $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['extended_entities']['media'][0]['url'], $quoted_text );
1116
- }
1117
-
1118
- $num_media = count( $tweet['quoted_status']['extended_entities']['media'] );
1119
- for( $i = 0; $i < $num_media; $i++ ) {
1120
- $trimmed['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1121
- $trimmed['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['quoted_status']['extended_entities']['media'][$i]['type'];
1122
- if ( $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1123
- foreach ( $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1124
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1125
- $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1126
- }
1127
- }
1128
- if ( ! isset( $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1129
- $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1130
- }
1131
- }
1132
- }
1133
-
1134
- } elseif ( isset( $tweet['quoted_status']['entities']['media'] ) ) {
1135
- // if there is media, we need to remove the media url from the tweet text
1136
- $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1137
- if ( isset( $tweet['quoted_status']['entities']['media'][0]['url'] ) ) {
1138
- $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['entities']['media'][0]['url'], $quoted_text );
1139
- }
1140
-
1141
- $num_media = count( $tweet['quoted_status']['entities']['media'] );
1142
- for( $i = 0; $i < $num_media; $i++ ) {
1143
- $trimmed['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['entities']['media'][$i]['media_url_https'];
1144
- $trimmed['quoted_status']['entities']['media'][$i]['type'] = $tweet['quoted_status']['entities']['media'][$i]['type'];
1145
- if ( $tweet['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1146
- foreach ( $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1147
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1148
- $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1149
- }
1150
- }
1151
- if ( ! isset( $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1152
- $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1153
- }
1154
- }
1155
- }
1156
-
1157
- }
1158
-
1159
- if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] ) ) {
1160
- // if there is media, we need to remove the media url from the tweet text
1161
- $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1162
- if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1163
- $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1164
- }
1165
- $num_media = count( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] );
1166
- for ( $i = 0; $i < $num_media; $i++ ) {
1167
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1168
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'];
1169
- if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1170
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'];
1171
- }
1172
- if ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1173
- foreach ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1174
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1175
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1176
- }
1177
- }
1178
- if ( ! isset( $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1179
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1180
- }
1181
- }
1182
- }
1183
- } elseif ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'] ) ) {
1184
- // if there is media, we need to remove the media url from the tweet text
1185
- $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1186
- if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'] ) ) {
1187
- $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'], $retweeted_text );
1188
- }
1189
- $num_media = count( $tweet['retweeted_status']['quoted_status']['entities']['media'] );
1190
- for( $i = 0; $i < $num_media; $i++ ) {
1191
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'];
1192
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'];
1193
- if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] ) ) {
1194
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'];
1195
- }
1196
- if ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1197
- foreach ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1198
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1199
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1200
- }
1201
- }
1202
- if ( ! isset( $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1203
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1204
- }
1205
- }
1206
- }
1207
- }
1208
-
1209
- //remove the url from the text if it links to a quoted tweet that is already linked to
1210
- if ( isset( $tweet['quoted_status'] ) ) {
1211
- $maybe_remove_index = count( $tweet['entities']['urls'] ) - 1;
1212
- if ( isset( $tweet['entities']['urls'][$maybe_remove_index]['url'] ) ) {
1213
- $text = isset( $trimmed['full_text'] ) ? $trimmed['full_text'] : $trimmed['text'];
1214
- $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['urls'][$maybe_remove_index]['url'], $text );
1215
- }
1216
- }
1217
-
1218
-
1219
- // used to generate twitter cards
1220
- if ( isset( $tweet['entities']['urls'][0]['expanded_url'] ) ) {
1221
- $trimmed['entities']['urls'][0]['expanded_url'] = $tweet['entities']['urls'][0]['expanded_url'];
1222
- }
1223
-
1224
- if ( isset( $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'] ) ) {
1225
- $trimmed['retweeted_status']['entities']['urls'][0]['expanded_url'] = $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'];
1226
- }
1227
-
1228
- return $trimmed;
1229
- }
1230
-
1231
- /**
1232
- * will create a transient with the tweet cache if one doesn't exist, the data seems valid, and caching is active
1233
- */
1234
- public function maybeCacheTweets()
1235
- {
1236
- if ( ( ! $this->transient_data || $this->errors['cache_status'] ) && $this->feed_options['cache_time'] > 0 ) {
1237
- $this->trimTweetData();
1238
- $cache = json_encode( $this->tweet_set );
1239
- set_transient( $this->transient_name, $cache, $this->feed_options['cache_time'] );
1240
- }
1241
- }
1242
-
1243
- /**
1244
- * returns a JSON string to be used in the data attribute that contains the shortcode data
1245
- */
1246
- public function getShortCodeJSON()
1247
- {
1248
- $json_data = '{';
1249
- $i = 0;
1250
- $len = is_array( $this->atts ) ? count( $this->atts ) : 0;
1251
-
1252
- if ( ! empty( $this->atts ) ) {
1253
- foreach ( $this->atts as $key => $value) {
1254
- if ( $i == $len - 1 ) {
1255
- $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;';
1256
- } else {
1257
- $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;, ';
1258
- }
1259
- $i++;
1260
- }
1261
- }
1262
-
1263
- $json_data .= '}';
1264
-
1265
- return $json_data;
1266
- }
1267
-
1268
- /**
1269
- * uses the endpoint to determing what get fields need to be set
1270
- *
1271
- * @param $end_point api endpoint needed
1272
- * @param $feed_term term associated with the endpoint, user name or search term
1273
- * @return array the get fields for the request
1274
- */
1275
- protected function setGetFieldsArray( $end_point, $feed_term )
1276
- {
1277
- $get_fields = array();
1278
-
1279
- $get_fields['tweet_mode'] = 'extended';
1280
-
1281
- if ( $end_point === 'usertimeline' ) {
1282
- if ( ! empty ( $feed_term ) ) {
1283
- $get_fields['screen_name'] = $feed_term;
1284
- }
1285
- if ( !$this->feed_options['selfreplies'] ) {
1286
- $get_fields['exclude_replies'] = 'true';
1287
- }
1288
- }
1289
- if ( $end_point === 'hometimeline' ) {
1290
- $get_fields['exclude_replies'] = 'true';
1291
- if ( !$this->feed_options['selfreplies'] ) {
1292
- $get_fields['exclude_replies'] = 'true';
1293
- }
1294
- }
1295
- if ( $end_point === 'search' ) {
1296
- $get_fields['q'] = $feed_term;
1297
- }
1298
-
1299
- return $get_fields;
1300
- }
1301
-
1302
- /**
1303
- * attempts to connect and retrieve tweets from the Twitter api
1304
- *
1305
- * @return mixed|string object containing the response
1306
- */
1307
- public function apiConnectionResponse( $end_point, $feed_term )
1308
- {
1309
- // Only can be set in the options page
1310
- $request_settings = array(
1311
- 'consumer_key' => $this->feed_options['consumer_key'],
1312
- 'consumer_secret' => $this->feed_options['consumer_secret'],
1313
- 'access_token' => $this->feed_options['access_token'],
1314
- 'access_token_secret' => $this->feed_options['access_token_secret'],
1315
- );
1316
-
1317
- // For pagination, an extra post needs to be retrieved since the last post is
1318
- // included in the next set
1319
- $count = $this->feed_options['count'];
1320
-
1321
- $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1322
-
1323
- if ( ! empty( $this->last_id_data ) ) {
1324
- $count++;
1325
- $max_id = $this->last_id_data;
1326
- }
1327
- $get_fields['count'] = $count;
1328
-
1329
- // max_id parameter should only be included for the second set of posts
1330
- if ( isset( $max_id ) ) {
1331
- $get_fields['max_id'] = $max_id;
1332
- }
1333
-
1334
- include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1335
-
1336
- // actual connection
1337
- $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1338
- $twitter_connect->setUrlBase();
1339
- $twitter_connect->setGetFields( $get_fields );
1340
- $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1341
-
1342
- return $twitter_connect->performRequest();
1343
- }
1344
-
1345
- private function getTweetsSinceID( $since_id, $end_point = 'search', $feed_term, $count )
1346
- {
1347
- // Only can be set in the options page
1348
- $request_settings = array(
1349
- 'consumer_key' => $this->feed_options['consumer_key'],
1350
- 'consumer_secret' => $this->feed_options['consumer_secret'],
1351
- 'access_token' => $this->feed_options['access_token'],
1352
- 'access_token_secret' => $this->feed_options['access_token_secret'],
1353
- );
1354
-
1355
- $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1356
-
1357
- $get_fields['since_id'] = $since_id;
1358
-
1359
- $get_fields['count'] = $count;
1360
-
1361
- include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1362
-
1363
- // actual connection
1364
- $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1365
- $twitter_connect->setUrlBase();
1366
- $twitter_connect->setGetFields( $get_fields );
1367
- $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1368
-
1369
- return $twitter_connect->performRequest();
1370
- }
1371
-
1372
- public function feedID() {
1373
- if ( $this->feed_options['persistentcache'] ) {
1374
- $feed_id = substr( 'ctf_!_' . $this->feed_options['feed_term'], 0, 45 );
1375
- $feed_id = str_replace( ' -filter:retweets', '', $feed_id );
1376
- } else {
1377
- $feed_id = $this->transient_name;
1378
- }
1379
-
1380
- return $feed_id;
1381
- }
1382
-
1383
- /**
1384
- * If the feed runs out of tweets to display for some reason,
1385
- * this function creates a graceful failure message
1386
- *
1387
- * @param $feed_options
1388
- * @return string html for "out of tweets" message
1389
- */
1390
- protected function getOutOfTweetsHtml( $feed_options )
1391
- {
1392
- $html = '';
1393
-
1394
- $html .= '<div class="ctf-out-of-tweets">';
1395
- $html .= '<p>' . __( "That's all! No more Tweets to load", 'custom-twitter-feeds' ) . '</p>';
1396
- $html .= '<p>';
1397
- $html .= '<a class="twitter-share-button" href="https://twitter.com/share" target="_blank" rel="noopener noreferrer" data-size="large" data-url="'.get_home_url().'">Share</a>';
1398
- if ( !empty( $feed_options['screenname'] ) ) {
1399
- $html .= '<a class="twitter-follow-button" href="https://twitter.com/' . $feed_options['screenname'] . '" target="_blank" rel="noopener noreferrer" data-show-count="false" data-size="large" data-dnt="true">Follow</a>';
1400
- }
1401
- $html .= '</p>';
1402
- $html .= '</div>';
1403
-
1404
- return $html;
1405
- }
1406
-
1407
- /**
1408
- * creates opening html for the feed
1409
- *
1410
- * @return string opening html that creates the feed
1411
- */
1412
- public function getFeedOpeningHtml()
1413
- {
1414
- $feed_options = $this->feed_options;
1415
- $ctf_data_disablelinks = ($feed_options['disablelinks'] == 'true') ? ' data-ctfdisablelinks="true"' : '';
1416
- $ctf_data_linktextcolor = $feed_options['linktextcolor'] != '' ? ' data-ctflinktextcolor="'.$feed_options['linktextcolor'].'"' : '';
1417
- $ctf_enable_intents = $feed_options['disableintents'] === false && ctf_show( 'actions', $feed_options ) ? ' data-ctfintents="1"' : '';
1418
- $ctf_data_needed = $this->num_tweets_needed;
1419
- $ctf_feed_type = ! empty ( $feed_options['type'] ) ? esc_attr( $feed_options['type'] ) : 'multiple';
1420
- $ctf_feed_classes = 'ctf ctf-type-' . $ctf_feed_type;
1421
- $ctf_feed_classes .= ' ' . $feed_options['class'] . ' ctf-styles';
1422
- $ctf_feed_classes .= $feed_options['width_mobile_no_fixed'] ? ' ctf-width-resp' : '';
1423
- if ( $this->check_for_duplicates ) { $ctf_feed_classes .= ' ctf-no-duplicates'; }
1424
- $ctf_feed_classes = apply_filters( 'ctf_feed_classes', $ctf_feed_classes ); //add_filter( 'ctf_feed_classes', function( $ctf_feed_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1425
- $ctf_feed_html = '';
1426
-
1427
- $flags_att = '';
1428
- $flags = array();
1429
- if ( CTF_GDPR_Integrations::doing_gdpr( $feed_options ) ) {
1430
- $flags[] = 'gdpr';
1431
- }
1432
- if ( ! is_admin()
1433
- && CTF_Feed_Locator::should_do_ajax_locating( $this->feedID(), get_the_ID() ) ) {
1434
- $flags[] = 'locator';
1435
- }
1436
- if ( ! empty( $flags ) ) {
1437
- $flags_att = ' data-ctf-flags="' . implode( ',', $flags ) . '"';
1438
- }
1439
- $post_id_att = ' data-postid="' . esc_attr( get_the_ID() ) . '"';
1440
- $feed_id_att = ' data-feed-id="' . $this->feedID() . '"';
1441
-
1442
- $ctf_feed_html .= '<!-- Custom Twitter Feeds by Smash Balloon -->';
1443
- $ctf_feed_html .= '<div id="ctf" class="' . $ctf_feed_classes . '" style="' . $feed_options['width'] . $feed_options['height'] . $feed_options['bgcolor'] . '" data-ctfshortcode="' . $this->getShortCodeJSON() . '"' .$ctf_data_disablelinks . $ctf_data_linktextcolor . $ctf_enable_intents . $flags_att . $post_id_att . $feed_id_att .' data-ctfneeded="'. $ctf_data_needed .'">';
1444
- $tweet_set = $this->tweet_set;
1445
-
1446
- // dynamically include header
1447
- if ( $feed_options['showheader'] ) {
1448
- $ctf_feed_html .= $this->getFeedHeaderHtml( $tweet_set, $this->feed_options );
1449
- }
1450
-
1451
- $ctf_feed_html .= '<div class="ctf-tweets">';
1452
-
1453
- return $ctf_feed_html;
1454
- }
1455
-
1456
- /**
1457
- * creates opening html for the feed
1458
- *
1459
- * @return string opening html that creates the feed
1460
- */
1461
- public function getFeedClosingHtml()
1462
- {
1463
- $feed_options = $this->feed_options;
1464
- $ctf_feed_html = '';
1465
-
1466
- $ctf_feed_html .= '</div>'; // closing div for ctf-tweets
1467
-
1468
- if ( $feed_options['showbutton'] ) {
1469
- $ctf_feed_html .= '<a href="javascript:void(0);" id="ctf-more" class="ctf-more" style="' . $feed_options['buttoncolor'] . $feed_options['buttontextcolor'] . '"><span>' . $feed_options['buttontext'] . '</span></a>';
1470
- }
1471
-
1472
- if ( $feed_options['creditctf'] ) {
1473
- $ctf_feed_html .= '<div class="ctf-credit-link"><a href="https://smashballoon.com/custom-twitter-feeds" target="_blank" rel="noopener noreferrer">' . ctf_get_fa_el( 'fa-twitter' ) . 'Custom Twitter Feeds Plugin</a></div>';
1474
- }
1475
-
1476
- $ctf_feed_html .= '</div>'; // closing div tag for #ctf
1477
-
1478
- if ( $feed_options['ajax_theme'] ) {
1479
- $ctf_feed_html .= '<script type="text/javascript" src="' . CTF_JS_URL . '"></script>';
1480
- }
1481
-
1482
- return $ctf_feed_html;
1483
- }
1484
-
1485
- /**
1486
- * creates html for header of the feed
1487
- *
1488
- * @param $tweet_set string trimmed tweets to be added to the feed
1489
- * @param $feed_options options for the feed
1490
- * @return string html that creates the header of the feed
1491
- */
1492
- protected function getFeedHeaderHtml( $tweet_set, $feed_options )
1493
- {
1494
- $ctf_header_html = '';
1495
- $ctf_no_bio = ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) ? '' : ' ctf-no-bio';
1496
-
1497
- // temporary workaround for cached http images
1498
- $tweet_set[0]['user']['profile_image_url_https'] = isset( $tweet_set[0]['user']['profile_image_url_https'] ) ? $tweet_set[0]['user']['profile_image_url_https'] : $tweet_set[0]['user']['profile_image_url'];
1499
-
1500
-
1501
- if ( $feed_options['type'] === 'usertimeline' ) {
1502
- $ctf_header_html .= '<div class="ctf-header' . $ctf_no_bio . '" style="' . $feed_options['headerbgcolor'] . '">';
1503
- $ctf_header_html .= '<a href="https://twitter.com/' . $tweet_set[0]['user']['screen_name'] . '" target="_blank" rel="noopener noreferrer" title="@' . $tweet_set[0]['user']['screen_name'] . '" class="ctf-header-link">';
1504
- $ctf_header_html .= '<div class="ctf-header-text">';
1505
- $ctf_header_html .= '<p class="ctf-header-user" style="' . $feed_options['headertextcolor'] . '">';
1506
- $ctf_header_html .= '<span class="ctf-header-name">';
1507
-
1508
- if ( $feed_options['headertext'] != '' ) {
1509
- $ctf_header_html .= esc_html( $feed_options['headertext'] );
1510
- } else {
1511
- $ctf_header_html .= esc_html( $tweet_set[0]['user']['name'] );
1512
- }
1513
-
1514
- $ctf_header_html .= '</span>';
1515
-
1516
- if ( $tweet_set[0]['user']['verified'] == 1 ) {
1517
- $ctf_header_html .= '<span class="ctf-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1518
- }
1519
-
1520
- $ctf_header_html .= '<span class="ctf-header-follow">' . ctf_get_fa_el( 'fa-twitter' ) . __( 'Follow', 'custom-twitter-feeds' ) . '</span>';
1521
- $ctf_header_html .= '</p>';
1522
-
1523
- if ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) {
1524
- $ctf_header_html .= '<p class="ctf-header-bio" style="' . $feed_options['headertextcolor'] . '">' . $tweet_set[0]['user']['description'] . '</p>';
1525
- }
1526
-
1527
- $ctf_header_html .= '</div>';
1528
- $ctf_header_html .= '<div class="ctf-header-img">';
1529
- $ctf_header_html .= '<div class="ctf-header-img-hover">' . ctf_get_fa_el( 'fa-twitter' ) . '</div>';
1530
- if ( CTF_GDPR_Integrations::doing_gdpr( $feed_options ) ) {
1531
- $ctf_header_html .= '<span data-avatar="' . esc_url( $tweet_set[0]['user']['profile_image_url_https'] ) . '" data-alt="' . $tweet_set[0]['user']['name'] . '" style="display: none;">Avatar</span>';
1532
- } else {
1533
- $ctf_header_html .= '<img src="' . $tweet_set[0]['user']['profile_image_url_https'] . '" alt="' . $tweet_set[0]['user']['name'] . '" width="48" height="48">';
1534
- }
1535
- $ctf_header_html .= '</div>';
1536
- $ctf_header_html .= '</a>';
1537
- $ctf_header_html .= '</div>';
1538
- } else {
1539
-
1540
- if ( $feed_options['type'] === 'search' ) {
1541
- $default_header_text = $feed_options['headertext'] != '' ? esc_html($feed_options['headertext']) : $feed_options['feed_term'];
1542
- $url_part = 'hashtag/' . str_replace("#", "", $feed_options['feed_term']);
1543
- } else {
1544
- $default_header_text = 'Twitter';
1545
- $url_part = $feed_options['screenname']; //Need to get screenname here
1546
- }
1547
-
1548
- $default_header_text = str_replace( ' -filter:retweets', '', $default_header_text );
1549
-
1550
- $ctf_header_html .= '<div class="ctf-header ctf-header-type-generic" style="' . $feed_options['headerbgcolor'] . '">';
1551
- $ctf_header_html .= '<a href="https://twitter.com/' . $url_part . '" target="_blank" rel="noopener noreferrer" class="ctf-header-link">';
1552
- $ctf_header_html .= '<div class="ctf-header-text">';
1553
- $ctf_header_html .= '<p class="ctf-header-no-bio" style="' . $feed_options['headertextcolor'] . '">' . $default_header_text . '</p>';
1554
- $ctf_header_html .= '</div>';
1555
- $ctf_header_html .= '<div class="ctf-header-img">';
1556
- $ctf_header_html .= '<div class="ctf-header-generic-icon">';
1557
- $ctf_header_html .= ctf_get_fa_el( 'fa-twitter' );
1558
- $ctf_header_html .= '</div>';
1559
- $ctf_header_html .= '</div>';
1560
- $ctf_header_html .= '</a>';
1561
- $ctf_header_html .= '</div>';
1562
- }
1563
-
1564
- return $ctf_header_html;
1565
- }
1566
-
1567
- /**
1568
- * outputs the html for a set of tweets to be used in the feed
1569
- *
1570
- * @param int $is_pagination 1 or 0, used to differentiate between the first set and subsequent tweet sets
1571
- *
1572
- * @return string $tweet_html
1573
- */
1574
- public function getTweetSetHtml( $is_pagination = 0 )
1575
- {
1576
- $tweet_set = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
1577
- $len = min( $this->feed_options['num'] + $is_pagination, count( $tweet_set ) );
1578
- $i = $is_pagination; // starts at index "1" to offset duplicate tweet
1579
- $feed_options = $this->feed_options;
1580
- $tweet_html = $this->feed_html;
1581
-
1582
- if ( $is_pagination && ( ! isset ( $tweet_set[1]['id_str'] ) ) ) {
1583
- $tweet_html .= $this->getOutOfTweetsHtml( $this->feed_options );
1584
- } else {
1585
- while ( $i < $len ) {
1586
-
1587
- // run a check to accommodate the "search" endpoint as well
1588
- $post = $tweet_set[$i];
1589
-
1590
- // temporary workaround for cached http images
1591
- $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1592
-
1593
- // save the original tweet data in case it's a retweet
1594
- $post_id = $post['id_str'];
1595
- $author = strtolower( $post['user']['screen_name'] );
1596
-
1597
- // creates a string of classes applied to each tweet
1598
- $tweet_classes = 'ctf-item ctf-author-' . $author .' ctf-new';
1599
- if ( !ctf_show( 'avatar', $feed_options ) ) $tweet_classes .= ' ctf-hide-avatar';
1600
- $tweet_classes = apply_filters( 'ctf_tweet_classes', $tweet_classes ); // add_filter( 'ctf_tweet_classes', function( $tweet_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1601
-
1602
- // check for retweet
1603
- $retweet_data_att = '';
1604
- if ( isset( $post['retweeted_status'] ) ) {
1605
- $retweeter = array(
1606
- 'name' => $post['user']['name'],
1607
- 'screen_name' => $post['user']['screen_name']
1608
- );
1609
- $retweet_data_att = ( $this->check_for_duplicates ) ? ' data-ctfretweetid="'.$post['retweeted_status']['id_str'].'"' : '';
1610
- if ( isset( $post['retweeted_status'] ))
1611
- $post = $post['retweeted_status'];
1612
-
1613
- // temporary workaround for cached http images
1614
- $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1615
- $tweet_classes .= ' ctf-retweet';
1616
- } else {
1617
- unset( $retweeter );
1618
- }
1619
-
1620
- // check for quoted
1621
- if ( isset( $post['quoted_status'] ) ) {
1622
- $tweet_classes .= ' ctf-quoted';
1623
- $quoted = $post['quoted_status'];
1624
- $quoted_media_text = '';
1625
- if ( ( isset( $quoted['extended_entities']['media'][0] ) || isset( $quoted['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1626
- $quoted_media = isset( $quoted['extended_entities']['media'] ) ? $quoted['extended_entities']['media'] : $quoted['entities']['media'];
1627
- $quoted_media_count = count( $quoted_media );
1628
- switch ( $quoted_media[0]['type'] ) {
1629
- case 'video':
1630
- case 'animated_gif':
1631
- $quoted_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1632
- break;
1633
- default:
1634
- if ( $quoted_media_count > 1 ) {
1635
- $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap ctf-multi-media-icon">' . $quoted_media_count . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1636
- } else {
1637
- $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap">' . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1638
- }
1639
- break;
1640
- }
1641
- } else {
1642
- unset( $quoted_media );
1643
- }
1644
- } else {
1645
- unset( $quoted );
1646
- unset( $quoted_media_text );
1647
- }
1648
-
1649
- // check for media [0]['type']
1650
- $post_media_text = '';
1651
- $post_media_count = 0;
1652
- if ( ( isset( $post['extended_entities']['media'][0] ) || isset( $post['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1653
- $post_media = isset( $post['extended_entities']['media'] ) ? $post['extended_entities']['media'] : $post['entities']['media'];
1654
- $post_media_count = count( $post_media );
1655
- switch ( $post_media[0]['type'] ) {
1656
- case 'video':
1657
- case 'animated_gif':
1658
- $post_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1659
- break;
1660
- default:
1661
- if ( $post_media_count > 1 ) {
1662
- $post_media_text .= $post_media_count . ctf_get_fa_el( 'fa-picture-o' );
1663
- } else {
1664
- $post_media_text .= ctf_get_fa_el( 'fa-picture-o' );
1665
- }
1666
- break;
1667
- }
1668
- } else {
1669
- unset( $post_media );
1670
- }
1671
-
1672
- // include tweet view
1673
- $tweet_html .= '<div class="'. $tweet_classes . '" id="' . $post_id . '" style="' . $feed_options['tweetbgcolor'] . '"' . $retweet_data_att . '>';
1674
-
1675
- if ( isset( $retweeter ) && ctf_show( 'retweeter', $feed_options ) ) {
1676
- $tweet_html .= '<div class="ctf-context">';
1677
- $tweet_html .= '<a href="https://twitter.com/intent/user?screen_name=' . $retweeter['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-retweet-icon">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">'.__( 'Retweet on Twitter', 'custom-twitter-feeds' ).'</span></a>';
1678
- $tweet_html .= '<a href="https://twitter.com/' . $retweeter['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-retweet-text" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $retweeter['name'] . ' ' . __( $feed_options['retweetedtext'], 'custom-twitter-feeds' ) . '</a>';
1679
- $tweet_html .= '</div>';
1680
- }
1681
-
1682
- if ( ctf_show( 'avatar', $feed_options ) || ctf_show( 'logo', $feed_options ) || ctf_show( 'author', $feed_options ) || ctf_show( 'date', $feed_options ) ) {
1683
-
1684
- $tweet_html .= '<div class="ctf-author-box">';
1685
- $tweet_html .= '<div class="ctf-author-box-link" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1686
- if ( ctf_show( 'avatar', $feed_options ) ) {
1687
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-avatar" target="_blank" rel="noopener noreferrer" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1688
- if ( CTF_GDPR_Integrations::doing_gdpr( $feed_options ) ) {
1689
- $tweet_html .= '<span data-avatar="' . esc_url( $post['user']['profile_image_url_https'] ) . '" data-alt="' . $post['user']['screen_name'] . '">Avatar</span>';
1690
- } else {
1691
- $tweet_html .= '<img src="' . esc_url( $post['user']['profile_image_url_https'] ) . '" alt="' . $post['user']['screen_name'] . '" width="48" height="48">';
1692
- }
1693
- $tweet_html .= '</a>';
1694
- }
1695
-
1696
- if ( ctf_show( 'author', $feed_options ) ) {
1697
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-author-name" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $post['user']['name'] . '</a>';
1698
- if ( $post['user']['verified'] == 1 ) {
1699
- $tweet_html .= '<span class="ctf-verified" >' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1700
- }
1701
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-screenname" target="_blank" rel="noopener noreferrer" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">@' . $post['user']['screen_name'] . '</a>';
1702
- $sep_style_att = ! empty( $feed_options['authortextsize'] ) ? ' style="' . $feed_options['authortextsize'] . '"' : '';
1703
- $tweet_html .= '<span class="ctf-screename-sep"' . $sep_style_att . '>&middot;</span>';
1704
- }
1705
-
1706
- if ( ctf_show( 'date', $feed_options ) ) {
1707
- $tweet_html .= '<div class="ctf-tweet-meta">';
1708
- //https://twitter.com/EnterLaw/status/869452491041243137
1709
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-tweet-date" target="_blank" rel="noopener noreferrer" style="' . $feed_options['datetextsize'] . $feed_options['datetextweight'] . $feed_options['textcolor'] . '">' . ctf_get_formatted_date( $post['created_at'], $feed_options, $post['user']['utc_offset'] ) . '</a>';
1710
- $tweet_html .= '</div>';
1711
- } // show date
1712
- $tweet_html .= '</div>';
1713
- if ( ctf_show( 'logo', $feed_options ) ) {
1714
- $tweet_html .= '<div class="ctf-corner-logo" style="' . $feed_options['logosize'] . $feed_options['logocolor'] . '">';
1715
- $tweet_html .= ctf_get_fa_el( 'fa-twitter' );
1716
- $tweet_html .= '</div>';
1717
- }
1718
- $tweet_html .= '</div>';
1719
- }
1720
-
1721
- if ( ctf_show( 'text', $feed_options ) ) {
1722
- $post_text = apply_filters( 'ctf_tweet_text', $post['text'], $feed_options, $post );
1723
-
1724
- $tweet_html .= '<div class="ctf-tweet-content">';
1725
-
1726
- if ( $feed_options['linktexttotwitter'] ) {
1727
- $tweet_html .= '<a class="ctf-tweet-text-link" href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" rel="noopener noreferrer">';
1728
- $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text ) . $post_media_text .'</p>';
1729
- $tweet_html .= '</a>';
1730
- } else {
1731
- $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text );
1732
-
1733
- if( $post_media_count > 0 ){
1734
- $multi_class = '';
1735
- if ( $post_media_count > 1 ) {
1736
- $multi_class = ' ctf-multi-media-icon';
1737
- }
1738
- if ( $feed_options['disablelinks'] ) {
1739
- $tweet_html .= '<span class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</span>' . '</p>';
1740
- } else {
1741
- $tweet_html .= '</p><a href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" rel="noopener noreferrer" class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</a>';
1742
- }
1743
- }
1744
- } // link text to twitter option is selected
1745
-
1746
- $tweet_html .= '</div>';
1747
- } // show tweet text
1748
-
1749
- if ( ctf_show( 'linkbox', $feed_options ) && isset( $quoted ) ) {
1750
- $tweet_html .= '<a href="https://twitter.com/' . $quoted['user']['screen_name'] . '/status/' . $quoted['id_str'] . '" class="ctf-quoted-tweet" style="' . $feed_options['quotedauthorsize'] . $feed_options['quotedauthorweight'] . $feed_options['textcolor'] . '" target="_blank" rel="noopener noreferrer">';
1751
- $tweet_html .= '<span class="ctf-quoted-author-name">' . $quoted['user']['name'] . '</span>';
1752
-
1753
- if ($quoted['user']['verified'] == 1) {
1754
- $tweet_html .= '<span class="ctf-quoted-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1755
- } // user is verified
1756
- $quoted_text = apply_filters( 'ctf_quoted_tweet_text', $quoted['text'], $feed_options, $quoted );
1757
-
1758
- $tweet_html .= '<span class="ctf-quoted-author-screenname">@' . $quoted['user']['screen_name'] . '</span>';
1759
- $tweet_html .= '<p class="ctf-quoted-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $quoted_text ) . $quoted_media_text . '</p>';
1760
- //$tweet_html .= ;
1761
- $tweet_html .= '</a>';
1762
- }// show link box
1763
-
1764
- $tweet_html .= '<div class="ctf-tweet-actions">';
1765
- if ( ctf_show( 'actions', $feed_options ) ) {
1766
- $tweet_html .= '<a href="https://twitter.com/intent/tweet?in_reply_to=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-reply" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-reply' ) . '<span class="ctf-screenreader">Reply on Twitter ' . $post['id_str'] . '</span></a>';
1767
- $tweet_html .= '<a href="https://twitter.com/intent/retweet?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-retweet" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">Retweet on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-retweet-count">';
1768
- if ( $post['retweet_count'] > 0 ) {
1769
- $tweet_html .= $post['retweet_count'];
1770
- }
1771
- $tweet_html .= '</span></a>';
1772
- $tweet_html .= '<a href="https://twitter.com/intent/like?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-like" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-heart' ) . '<span class="ctf-screenreader">Like on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-favorite-count">';
1773
- if ( $post['favorite_count'] > 0 ) {
1774
- $tweet_html .= $post['favorite_count'];
1775
- }
1776
- $tweet_html .= '</span></a>';
1777
- }
1778
- if ( ctf_show( 'twitterlink', $feed_options ) ) {
1779
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-twitterlink" style="' . $feed_options['textcolor'] . '" target="_blank" rel="noopener noreferrer">' . esc_html( $feed_options['twitterlinktext'] ) . ' <span class="ctf-screenreader">' . $post['id_str'] . '</span></a>';
1780
- } // show twitter link or actions
1781
- $tweet_html .= '</div>';
1782
- $tweet_html .= '</div>';
1783
-
1784
- $i++;
1785
- }
1786
- }
1787
- return $tweet_html;
1788
- }
1789
-
1790
- /**
1791
- * displays a message if there is an error in the feed
1792
- *
1793
- * @return string error html
1794
- */
1795
- public function getErrorHtml()
1796
- {
1797
- $error_html = '';
1798
- $error_html .= '<div id="ctf" class="ctf" data-ctfshortcode="' . $this->getShortCodeJSON() . '">';
1799
- $error_html .= '<div class="ctf-error">';
1800
- $error_html .= '<div class="ctf-error-user">';
1801
-
1802
- $error_html .= '</div>';
1803
-
1804
- if ( current_user_can( 'manage_options' ) ) {
1805
- $error_html .= '<div class="ctf-error-admin">';
1806
-
1807
- if ( ! empty( $this->api_obj->api_error_no ) ) {
1808
-
1809
- $error_html .= '<p>Unable to load Tweets</p>';
1810
- $error_html .= '<a class="twitter-share-button"';
1811
- $error_html .= 'href="https://twitter.com/share"';
1812
- $error_html .= 'data-size="large"';
1813
- $error_html .= 'data-url="' . get_the_permalink() . '"';
1814
- $error_html .= 'data-text="Check out this website">';
1815
- $error_html .= '</a>';
1816
-
1817
- if ( !empty( $this->feed_options['screenname'] ) ) {
1818
- $error_html .= '<a class="twitter-follow-button"';
1819
- $error_html .= 'href="https://twitter.com/' . $this->feed_options['screenname'] . '"';
1820
- $error_html .= 'data-show-count="false"';
1821
- $error_html .= 'data-size="large"';
1822
- $error_html .= 'data-dnt="true">Follow</a>';
1823
- }
1824
-
1825
- $error_html .= '<p><b>This message is only visible to admins:</b><br />';
1826
- $error_html .= 'An error has occurred with your feed.<br />';
1827
- if ( $this->missing_credentials ) {
1828
- $error_html .= 'There is a problem with your access token, access token secret, consumer token, or consumer secret<br />';
1829
- }
1830
- if ( isset( $this->errors['error_message'] ) ) {
1831
- $error_html .= $this->errors['error_message'] . '<br />';
1832
- }
1833
- $error_html .= 'The error response from the Twitter API is the following:<br />';
1834
- $error_html .= '<code>Error number: ' . $this->api_obj->api_error_no . '<br />';
1835
- $error_html .= 'Message: ' . $this->api_obj->api_error_message . '</code>';
1836
- $error_html .= '<a href="https://smashballoon.com/custom-twitter-feeds/docs/errors/?utm_campaign=twitter-free&utm_source=frontend&utm_medium=errormessage" target="_blank" rel="noopener noreferrer">Click here to troubleshoot</a></p>';
1837
-
1838
-
1839
- }
1840
-
1841
- $error_html .= '</div>';
1842
- }
1843
- $error_html .= '</div>'; // end .ctf-error
1844
- $error_html .= '</div>'; // end #ctf
1845
-
1846
- return $error_html;
1847
- }
1848
  }
1
+ <?php
2
+ /**
3
+ * Class CtfFeed
4
+ *
5
+ * Creates the settings for the feed and outputs the html
6
+ */
7
+
8
+ // Don't load directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ die( '-1' );
11
+ }
12
+
13
+ class CtfFeed
14
+ {
15
+ /**
16
+ * @var array
17
+ */
18
+ public $errors = array();
19
+
20
+ /**
21
+ * @var array
22
+ */
23
+ protected $atts;
24
+
25
+ /**
26
+ * @var string
27
+ */
28
+ protected $last_id_data;
29
+
30
+ private $num_needed_input;
31
+
32
+ /**
33
+ * @var mixed|void
34
+ */
35
+ protected $db_options;
36
+
37
+ /**
38
+ * @var array
39
+ */
40
+ public $feed_options = array();
41
+
42
+ /**
43
+ * @var mixed|void
44
+ */
45
+ public $missing_credentials;
46
+
47
+ /**
48
+ * @var string
49
+ */
50
+ public $transient_name;
51
+
52
+ /**
53
+ * @var bool
54
+ */
55
+ protected $transient_data = false;
56
+
57
+ /**
58
+ * @var int
59
+ */
60
+ private $num_tweets_needed;
61
+
62
+ private $check_for_duplicates = false;
63
+
64
+ /**
65
+ * @var array
66
+ */
67
+ public $tweet_set;
68
+
69
+ /**
70
+ * @var object
71
+ */
72
+ public $api_obj;
73
+
74
+ /**
75
+ * @var string
76
+ */
77
+ public $feed_html;
78
+
79
+ private $persistent_index;
80
+
81
+
82
+ /**
83
+ * retrieves and sets options that apply to the feed
84
+ *
85
+ * @param array $atts data from the shortcode
86
+ * @param string $last_id_data the last visible tweet on the feed, empty string if first set
87
+ * @param int $num_needed_input this number represents the number left to retrieve after the first set
88
+ */
89
+ public function __construct( $atts, $last_id_data, $num_needed_input )
90
+ {
91
+ $this->atts = $atts;
92
+ $this->last_id_data = $last_id_data;
93
+ $this->num_needed_input = $num_needed_input;
94
+ $this->db_options = get_option( 'ctf_options', array() );
95
+ }
96
+
97
+ /**
98
+ * creates and returns all of the data needed to generate the output for the feed
99
+ *
100
+ * @param array $atts data from the shortcode
101
+ * @param string $last_id_data the last visible tweet on the feed, empty string if first set
102
+ * @param int $num_needed_input this number represents the number left to retrieve after the first set
103
+ * @return CtfFeed the complete object for the feed
104
+ */
105
+ public static function init( $atts, $last_id_data = '', $num_needed_input = 0, $ids_to_remove = array(), $persistent_index = 1 )
106
+ {
107
+ $feed = new CtfFeed( $atts, $last_id_data, $num_needed_input );
108
+ $feed->setFeedOptions();
109
+
110
+ $feed->setCacheTypeOption();
111
+ if ( $feed->feed_options['persistentcache'] ) {
112
+ $feed->persistent_index = $persistent_index;
113
+ }
114
+
115
+ $feed->setTweetSet();
116
+ return $feed;
117
+ }
118
+
119
+ /**
120
+ * creates all of the feed options with shortcode settings having the highest priority
121
+ */
122
+ protected function setFeedOptions()
123
+ {
124
+ $this->setFeedTypeAndTermOptions();
125
+
126
+ $bool_false = array (
127
+ 'have_own_tokens',
128
+ 'includereplies',
129
+ 'ajax_theme',
130
+ 'width_mobile_no_fixed',
131
+ 'disablelinks',
132
+ 'linktexttotwitter',
133
+ 'creditctf',
134
+ 'selfreplies',
135
+ 'disableintents',
136
+ 'shorturls'
137
+ );
138
+ $this->setStandardBoolOptions( $bool_false, false );
139
+
140
+ $this->setAccessTokenAndSecretOptions();
141
+ $this->setConsumerKeyAndSecretOptions();
142
+
143
+ $db_only = array(
144
+ 'request_method'
145
+ );
146
+ $this->setDatabaseOnlyOptions( $db_only );
147
+
148
+ $this->setStandardTextOptions( 'num', 5 );
149
+
150
+ $standard_text = array(
151
+ 'class',
152
+ 'headertext',
153
+ 'dateformat',
154
+ 'datecustom',
155
+ 'mtime',
156
+ 'htime',
157
+ 'nowtime'
158
+ );
159
+ $this->setStandardTextOptions( $standard_text, '' );
160
+
161
+ $this->setStandardTextOptions( 'retweetedtext', __( 'Retweeted', 'custom-twitter-feeds' ) );
162
+ $this->setStandardTextOptions( 'font_method', 'svg' );
163
+ $this->setStandardTextOptions( 'multiplier', 1.25 );
164
+ $this->setStandardTextOptions( 'twitterlinktext', 'Twitter' );
165
+ $this->setStandardTextOptions( 'gdpr', 'auto' );
166
+
167
+ $this->setStandardTextOptions( 'buttontext', __( 'Load More...', 'custom-twitter-feeds' ) );
168
+ $this->setStandardTextOptions( 'textlength', 280 );
169
+ $text_size = array(
170
+ 'authortextsize',
171
+ 'tweettextsize',
172
+ 'datetextsize',
173
+ 'quotedauthorsize',
174
+ 'iconsize',
175
+ 'logosize'
176
+ );
177
+ $this->setTextSizeOptions( $text_size );
178
+
179
+ $text_weight = array(
180
+ 'authortextweight',
181
+ 'tweettextweight',
182
+ 'datetextweight',
183
+ 'quotedauthorweight'
184
+ );
185
+ $this->setStandardStyleProperty( $text_weight, 'font-weight' );
186
+
187
+ $text_color = array(
188
+ 'headertextcolor',
189
+ 'textcolor',
190
+ 'linktextcolor',
191
+ 'iconcolor',
192
+ 'logocolor',
193
+ 'buttontextcolor'
194
+ );
195
+ $this->setStandardStyleProperty( $text_color, 'color' );
196
+
197
+ $bg_color = array(
198
+ 'bgcolor',
199
+ 'tweetbgcolor',
200
+ 'headerbgcolor',
201
+ 'buttoncolor'
202
+ );
203
+ $this->setStandardStyleProperty( $bg_color, 'background-color' );
204
+
205
+ $bool_true = array(
206
+ 'persistentcache',
207
+ 'showbutton',
208
+ 'showbio',
209
+ 'showheader'
210
+ );
211
+ $this->setStandardBoolOptions( $bool_true, true );
212
+
213
+ $this->setDimensionOptions();
214
+ $this->setCacheTimeOptions();
215
+ $this->setIncludeExcludeOptions();
216
+
217
+ if ( CTF_GDPR_Integrations::doing_gdpr( $this->feed_options ) ) {
218
+ CTF_GDPR_Integrations::init();
219
+ }
220
+ }
221
+
222
+ /**
223
+ * uses the feed options to set the the tweets in the feed by using
224
+ * an existing set in a cache or by retrieving them from Twitter
225
+ */
226
+ protected function setTweetSet()
227
+ {
228
+ $this->setTransientName();
229
+ $success = $this->maybeSetTweetsFromCache();
230
+
231
+ if ( ! $success ) {
232
+ $this->maybeSetTweetsFromTwitter();
233
+ }
234
+
235
+ $this->num_tweets_needed = $this->numTweetsNeeded();
236
+ }
237
+
238
+ /**
239
+ * the access token and secret must be set in order for the feed to work
240
+ * this function processes the user input and sets a flag if none are entered
241
+ */
242
+ private function setAccessTokenAndSecretOptions()
243
+ {
244
+ $this->feed_options['access_token'] = isset( $this->db_options['access_token'] ) && strlen( $this->db_options['access_token'] ) > 30 ? $this->db_options['access_token'] : 'missing';
245
+ $this->feed_options['access_token_secret'] = isset( $this->db_options['access_token_secret'] ) && strlen( $this->db_options['access_token_secret'] ) > 30 ? $this->db_options['access_token_secret'] : 'missing';
246
+
247
+ // verify that access token and secret have been entered
248
+ $this->setMissingCredentials();
249
+ }
250
+
251
+ /**
252
+ * generates the flag if there are missing access tokens
253
+ */
254
+ private function setMissingCredentials() {
255
+ if ( $this->feed_options['access_token'] == 'missing' || $this->feed_options['access_token_secret'] == 'missing' ) {
256
+ $this->missing_credentials = true;
257
+ } else {
258
+ $this->missing_credentials = false;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * processes the consumer key and secret options
264
+ */
265
+ protected function setConsumerKeyAndSecretOptions()
266
+ {
267
+ if ( $this->feed_options['have_own_tokens'] ) {
268
+ $this->feed_options['consumer_key'] = isset( $this->db_options['consumer_key'] ) && strlen( $this->db_options['consumer_key'] ) > 15 ? $this->db_options['consumer_key'] : 'FPYSYWIdyUIQ76Yz5hdYo5r7y';
269
+ $this->feed_options['consumer_secret'] = isset( $this->db_options['consumer_secret'] ) && strlen( $this->db_options['consumer_secret'] ) > 30 ? $this->db_options['consumer_secret'] : 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
270
+ } else {
271
+ $this->feed_options['consumer_key'] ='FPYSYWIdyUIQ76Yz5hdYo5r7y';
272
+ $this->feed_options['consumer_secret'] = 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
273
+ }
274
+ }
275
+
276
+ /**
277
+ * determines what value to use and saves it for the appropriate key in the feed_options array
278
+ *
279
+ * @param $options mixed the key or array of keys to be set
280
+ * @param $options_page string options page this setting is set on
281
+ * @param string $default default value to use if there is no user input
282
+ */
283
+ public function setDatabaseOnlyOptions( $options, $default = '' )
284
+ {
285
+ if ( is_array( $options ) ) {
286
+ foreach ( $options as $option ) {
287
+ $this->feed_options[$option] = isset( $this->db_options[$option] ) && ! empty( $this->db_options[$option] ) ? $this->db_options[$option] : $default;
288
+ }
289
+ } else {
290
+ $this->feed_options[$options] = isset( $this->db_options[$options] ) && ! empty( $this->db_options[$options] ) ? $this->db_options[$options] : $default;
291
+ }
292
+ }
293
+
294
+ /**
295
+ * determines what value to use and saves it for the appropriate key in the feed_options array
296
+ *
297
+ * @param $options mixed the key or array of keys to be set
298
+ * @param $options_page string options page this setting is set on
299
+ * @param string $default default value to use if there is no user input
300
+ */
301
+ public function setStandardTextOptions( $options, $default = '' )
302
+ {
303
+ if ( is_array( $options ) ) {
304
+ foreach ( $options as $option ) {
305
+ $this->feed_options[$option] = isset( $this->atts[$option] ) ? esc_attr( __( $this->atts[$option], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$option] ) ? esc_attr( $this->db_options[$option] ) : $default );
306
+ }
307
+ } else {
308
+ $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( __( $this->atts[$options], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
309
+ }
310
+ }
311
+
312
+ /**
313
+ * creates the appropriate style attribute string for the text size setting
314
+ *
315
+ * @param $value mixed pixel size or other that the user has selected
316
+ * @return string string for the style attribute
317
+ */
318
+ public static function processTextSizeStyle( $value )
319
+ {
320
+ if ( $value == '' ) {
321
+ return '';
322
+ }
323
+ $processed_value = $value == 'inherit' ? '' : 'font-size: ' . $value . 'px;';
324
+
325
+ return $processed_value;
326
+ }
327
+
328
+ /**
329
+ * determines what value to use and saves it for the appropriate key in the feed_options array
330
+ *
331
+ * @param $options mixed the key or array of keys to be set
332
+ * @param string $default default value to use if there is no user input
333
+ */
334
+ public function setTextSizeOptions( $options, $default = '' )
335
+ {
336
+ if ( is_array( $options ) ) {
337
+ foreach ( $options as $option ) {
338
+ $this->feed_options[$option] = isset( $this->atts[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$option] ) ) : ( isset( $this->db_options[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$option] ) ) : $default );
339
+ }
340
+ } else {
341
+ $this->feed_options[$options] = isset( $this->atts[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$options] ) ) : ( isset( $this->db_options[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$options] ) ) : $default );
342
+ }
343
+ }
344
+
345
+ /**
346
+ * determines what value to use and saves it for the appropriate key in the feed_options array
347
+ *
348
+ * @param $options mixed the key or array of keys to be set
349
+ * @param $property string name of the property to be set
350
+ * @param string $default default value to use if there is no user input
351
+ */
352
+ public function setStandardStyleProperty( $options, $property, $default = '' )
353
+ {
354
+ if ( is_array( $options ) ) {
355
+ foreach ( $options as $option ) {
356
+ $this->feed_options[$option] = isset( $this->atts[$option] ) && $this->atts[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$option] ) . ';' : ( isset( $this->db_options[$option] ) && $this->db_options[$option] != '#' && $this->db_options[$option] != '' && $this->db_options[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$option] ) . ';' : $default );
357
+ }
358
+ } else {
359
+ $this->feed_options[$options] = isset( $this->atts[$options] ) && $this->atts[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$options] ) . ';' : ( isset( $this->db_options[$options] ) && $this->db_options[$options] != '#' && $this->db_options[$options] != '' && $this->db_options[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$options] ) . ';' : $default );
360
+ }
361
+ }
362
+
363
+ /**
364
+ * determines what value to use and saves it for the appropriate key in the feed_options array
365
+ *
366
+ * @param $options mixed the key or array of keys to be set
367
+ * @param bool|true $default default value to use if there is no user input
368
+ */
369
+ public function setStandardBoolOptions( $options, $default = true )
370
+ {
371
+ if ( is_array( $options ) ) {
372
+ foreach ( $options as $option ) {
373
+ $this->feed_options[$option] = isset( $this->atts[$option] ) ? ( $this->atts[$option] === 'true' ) : ( isset( $this->db_options[$option] ) ? (bool) $this->db_options[$option] : (bool) $default );
374
+ }
375
+ } else {
376
+ $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( $this->atts[$options] ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
377
+ }
378
+ }
379
+
380
+ /**
381
+ * sets the width and height of the feed based on user input
382
+ */
383
+ public function setDimensionOptions()
384
+ {
385
+ $this->feed_options['width'] = isset( $this->atts['width'] ) ? 'width: '. esc_attr( $this->atts['width'] ) .';' : ( ( isset( $this->db_options['width'] ) && $this->db_options['width'] != '' ) ? 'width: '. esc_attr( $this->db_options['width'] ) . ( isset( $this->db_options['width_unit'] ) ? esc_attr( $this->db_options['width_unit'] ) : '%' ) . ';' : '' );
386
+ $this->feed_options['height'] = isset( $this->atts['height'] ) ? 'height: '. esc_attr( $this->atts['height'] ) .';' : ( ( isset( $this->db_options['height'] ) && $this->db_options['height'] != '' ) ? 'height: '. esc_attr( $this->db_options['height'] ) . ( isset( $this->db_options['height_unit'] ) ? esc_attr( $this->db_options['height_unit'] ) : 'px' ) . ';' : '' );
387
+ }
388
+
389
+ /**
390
+ * sets the cache time based on user input
391
+ */
392
+ public function setCacheTimeOptions()
393
+ {
394
+ $user_cache = isset( $this->db_options['cache_time'] ) ? ( $this->db_options['cache_time'] * $this->db_options['cache_time_unit'] ) : HOUR_IN_SECONDS;
395
+
396
+ if ( $this->feed_options['have_own_tokens'] ) {
397
+ $this->feed_options['cache_time'] = max( $user_cache, 60 );
398
+ } else {
399
+ $this->feed_options['cache_time'] = max( $user_cache, 3600 );
400
+ }
401
+ }
402
+
403
+
404
+ /**
405
+ * sets the number of tweets to retrieve
406
+ */
407
+ public function setTweetsToRetrieve()
408
+ {
409
+ $min_tweets_to_retrieve = 10;
410
+
411
+ if ( $this->num_needed_input < 1 ) {
412
+ if ( $this->feed_options['includereplies'] ) {
413
+ $this->feed_options['count'] = $this->feed_options['num'];
414
+ } else {
415
+ if ( $this->feed_options['num'] < 10 ) {
416
+ $this->feed_options['count'] = max( round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.6 ), $min_tweets_to_retrieve );
417
+ } elseif ( $this->feed_options['num'] < 30 ) {
418
+ $this->feed_options['count'] = round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.2 );
419
+ } else {
420
+ $this->feed_options['count'] = round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] );
421
+ }
422
+ }
423
+ } else {
424
+ $this->feed_options['count'] = max( $this->num_needed_input, 50 );
425
+ $this->feed_options['num'] = $this->num_needed_input;
426
+ }
427
+
428
+ }
429
+
430
+ /**
431
+ * sets the feed type and associated parameter
432
+ */
433
+ public function setFeedTypeAndTermOptions()
434
+ {
435
+ $this->feed_options['type'] = '';
436
+ $this->feed_options['feed_term'] = '';
437
+ $this->feed_options['screenname'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
438
+
439
+ if ( isset( $this->atts['home'] ) && $this->atts['home'] == 'true' ) {
440
+ $this->feed_options['type'] = 'hometimeline';
441
+ }
442
+ if ( isset( $this->atts['screenname'] ) ) {
443
+ $this->feed_options['type'] = 'usertimeline';
444
+ $this->feed_options['feed_term'] = isset( $this->atts['screenname'] ) ? ctf_validate_usertimeline_text( $this->atts['screenname'] ) : ( ( isset( $this->db_options['usertimeline_text'] ) ) ? $this->db_options['usertimeline_text'] : '' );
445
+ $this->feed_options['screenname'] = $this->feed_options['feed_term'];
446
+ }
447
+ if ( isset( $this->atts['search'] ) || isset( $this->atts['hashtag'] ) ) {
448
+ $this->feed_options['type'] = 'search';
449
+ $this->working_term = isset( $this->atts['hashtag'] ) ? $this->atts['hashtag'] : ( isset( $this->atts['search'] ) ? $this->atts['search'] : '' );
450
+ $this->feed_options['feed_term'] = isset( $this->working_term ) ? ctf_validate_search_text( $this->working_term ) . ' -filter:retweets' : ( ( isset( $this->db_options['search_text'] ) ) ? $this->db_options['search_text'] . ' -filter:retweets' : '' );
451
+ $this->check_for_duplicates = true;
452
+ }
453
+
454
+ if ( $this->feed_options['type'] == '' ) {
455
+ $this->feed_options['type'] = isset( $this->db_options['type'] ) ? $this->db_options['type'] : 'usertimeline';
456
+ switch ( $this->feed_options['type'] ) {
457
+ case 'usertimeline':
458
+ $this->feed_options['feed_term'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
459
+ break;
460
+ case 'hometimeline':
461
+ $this->feed_options['type'] = 'hometimeline';
462
+ break;
463
+ case 'search':
464
+ $this->feed_options['feed_term'] = isset( $this->db_options['search_text'] ) ? $this->db_options['search_text'] . ' -filter:retweets' : '';
465
+ $this->check_for_duplicates = true;
466
+ break;
467
+ }
468
+ }
469
+ }
470
+
471
+ /**
472
+ * sets the visible parts of each tweet for the feed
473
+ */
474
+ public function setIncludeExcludeOptions()
475
+ {
476
+ $this->feed_options['tweet_excludes'] = array();
477
+ $this->feed_options['tweet_includes'] = isset( $this->atts['include'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['include'] ) ) ) : array();
478
+
479
+ if ( empty( $this->feed_options['tweet_includes'][0] ) ) {
480
+ $this->feed_options['tweet_excludes'] = isset( $this->atts['exclude'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['exclude'] ) ) ) : array();
481
+ }
482
+ if ( empty( $this->feed_options['tweet_excludes'][0] ) && empty( $this->feed_options['tweet_includes'][0] ) ) {
483
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_retweeter'] ) && $this->db_options['include_retweeter'] == false ? null : 'retweeter';
484
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_avatar'] ) && $this->db_options['include_avatar'] == false ? null : 'avatar';
485
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_author'] ) && $this->db_options['include_author'] == false ? null : 'author';
486
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_text'] ) && $this->db_options['include_text'] == false ? null : 'text';
487
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_media_placeholder'] ) && $this->db_options['include_media_placeholder'] == false ? null : 'placeholder';
488
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_date'] ) && $this->db_options['include_date'] == false ? null : 'date';
489
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_actions'] ) && $this->db_options['include_actions'] == false ? null : 'actions';
490
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_twitterlink'] ) && $this->db_options['include_twitterlink'] == false ? null : 'twitterlink';
491
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_linkbox'] ) && $this->db_options['include_linkbox'] == false ? null : 'linkbox';
492
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_logo'] ) && $this->db_options['include_logo'] == false ? null : 'logo';
493
+ }
494
+
495
+ }
496
+
497
+ /**
498
+ * sets the transient name for the caching system
499
+ */
500
+ public function setTransientName()
501
+ {
502
+ $last_id_data = $this->last_id_data;
503
+ $num = isset( $this->feed_options['num'] ) ? $this->feed_options['num'] : '';
504
+
505
+ switch ( $this->feed_options['type'] ) {
506
+ case 'hometimeline' :
507
+ $this->transient_name = 'ctf_' . $last_id_data . 'hometimeline'. $num;
508
+ break;
509
+ case 'usertimeline' :
510
+ $screenname = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
511
+ $this->transient_name = substr( 'ctf__' . $last_id_data . $screenname . $num, 0, 45 );
512
+ break;
513
+ case 'search' :
514
+ $hashtag = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
515
+ $hashtag = str_replace( ' -filter:retweets', '', $hashtag );
516
+ $this->transient_name = substr( 'ctf_' . $last_id_data . $hashtag . $num, 0, 45 );
517
+ break;
518
+ }
519
+ }
520
+
521
+ public function setCacheTypeOption() {
522
+ if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
523
+ $this->feed_options['persistentcache'] = true;
524
+ } else {
525
+ $this->feed_options['persistentcache'] = false;
526
+ }
527
+ }
528
+
529
+ /**
530
+ * checks the data available in the cache to make sure it seems to be valid
531
+ *
532
+ * @return bool|string false if the cache is valid, error otherwise
533
+ */
534
+ private function validateCache()
535
+ {
536
+ if ( isset( $this->transient_data[0] ) ) {
537
+ return false;
538
+ } else {
539
+ return 'invalid cache';
540
+ }
541
+ }
542
+
543
+ /**
544
+ * will use the cached data in the feed if data seems to be valid and user
545
+ * wants to use caching
546
+ *
547
+ * @return bool|mixed false if none is set, tweet set otherwise
548
+ */
549
+ public function maybeSetTweetsFromCache()
550
+ {
551
+ if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
552
+ $persistent_cache_tweets = $this->persistentCacheTweets();
553
+ if ( is_array( $persistent_cache_tweets ) ) {
554
+ $this->transient_data = array_slice( $persistent_cache_tweets, ( $this->persistent_index - $this->feed_options['num'] - 1 ) , $this->persistent_index );
555
+ } else {
556
+ $this->transient_data = $persistent_cache_tweets;
557
+ }
558
+ } else {
559
+ $this->transient_data = get_transient( $this->transient_name );
560
+ if ( ! is_array( $this->transient_data ) ) {
561
+ $this->transient_data = json_decode( $this->transient_data, $assoc = true );
562
+ }
563
+
564
+ if ( $this->feed_options['cache_time'] <= 0 ) {
565
+ return $this->tweet_set = false;
566
+ }
567
+ }
568
+ // validate the transient data
569
+ if ( $this->transient_data ) {
570
+ $this->errors['cache_status'] = $this->validateCache();
571
+ if ( $this->errors['cache_status'] === false ) {
572
+ return $this->tweet_set = $this->transient_data;
573
+ } else {
574
+ return $this->tweet_set = false;
575
+ }
576
+ } else {
577
+ $this->errors['cache_status'] = 'none found';
578
+ return $this->tweet_set = false;
579
+ }
580
+ }
581
+
582
+ private function persistentCacheTweets()
583
+ {
584
+ // if cache exists get cached data
585
+ $includewords = ! empty( $this->feed_options['includewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['includewords'] ), 0, 10 ) : '';
586
+ $excludewords = ! empty( $this->feed_options['excludewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['excludewords'] ), 0, 5 ) : '';
587
+ $cache_name = substr( 'ctf_!_' . $this->feed_options['feed_term'] . $includewords . $excludewords, 0, 45 );
588
+
589
+ if ( $this->feed_options['type'] === 'search' ) {
590
+ $cache_name = str_replace( ' -filter:retweets', '', $cache_name );
591
+ }
592
+
593
+ $cache_time_limit_reached = get_transient( $cache_name ) ? false : true;
594
+
595
+ $existing_cache = get_option( $cache_name, false );
596
+ if ( $existing_cache && ! is_array( $existing_cache ) ) {
597
+ $existing_cache = json_decode( $existing_cache, $assoc = true );
598
+ }
599
+
600
+ $this->persistent_index = $this->persistent_index + $this->feed_options['num'];
601
+
602
+ $this->feed_options['count'] = 200;
603
+
604
+ if ( ! empty( $this->last_id_data ) || ( ! $cache_time_limit_reached && $existing_cache ) ) {
605
+ return $existing_cache;
606
+ } elseif ( $existing_cache ) {
607
+ // use "since-id" to look for more in an api request
608
+ $since_id = $existing_cache[0]['id_str'];
609
+ $api_obj = $this->getTweetsSinceID( $since_id, 'search', $this->feed_options['feed_term'], $this->feed_options['count'] );
610
+ // add any new tweets to the cache
611
+ $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
612
+
613
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : array();
614
+
615
+ // add a transient to delay another api retrieval
616
+ set_transient( $cache_name, true, $this->feed_options['cache_time'] );
617
+
618
+ if ( empty( $tweets ) ) {
619
+ if ( ! is_array( $existing_cache ) ) {
620
+ return false;
621
+ } else {
622
+ return $existing_cache;
623
+ }
624
+ } else {
625
+ $tweet_set = $this->reduceTweetSetData( $tweets, false );
626
+ }
627
+ $tweet_set = $this->appendPersistentCacheTweets( $existing_cache, $tweet_set );
628
+ $cache_set = json_encode( $tweet_set );
629
+
630
+ update_option( $cache_name, $cache_set );
631
+
632
+ return $tweet_set;
633
+ // else if cached data doesn't exist
634
+ } else {
635
+ // make a request for last 200 tweets
636
+ $api_obj = $this->apiConnectionResponse( 'search', $this->feed_options['feed_term'] );
637
+ // cache them in a regular option
638
+ $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
639
+
640
+ // check for errors/tweets present
641
+ if ( isset( $this->tweet_set['errors'][0] ) ) {
642
+ if ( empty( $this->api_obj ) ) {
643
+ $this->api_obj = new stdClass();
644
+ }
645
+ $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
646
+ $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
647
+
648
+ $this->tweet_set = false;
649
+ }
650
+
651
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
652
+
653
+ if ( empty( $tweets ) ) {
654
+ $this->errors['error_message'] = 'No Tweets returned';
655
+ $this->tweet_set = false;
656
+ } else {
657
+ $this->tweet_set = $this->reduceTweetSetData( $tweets, false );
658
+ }
659
+
660
+ // create a new persistent cache
661
+ if ( $this->tweet_set && isset( $this->tweet_set[0] ) ) {
662
+ $tweet_set = json_encode( $this->tweet_set );
663
+
664
+ update_option( $cache_name, $tweet_set );
665
+
666
+ // update list of persistent cache
667
+ $cache_list = get_option( 'ctf_cache_list', array() );
668
+
669
+ $cache_list[] = $cache_name;
670
+
671
+ update_option( 'ctf_cache_list', $cache_list );
672
+ }
673
+
674
+ return $this->tweet_set;
675
+ }
676
+
677
+ // add the search parameter to another option that contains a list of all persistent caches available
678
+ }
679
+
680
+ private function reduceTweetSetData( $tweet_set, $limit = true ) {
681
+ if ( $this->check_for_duplicates ) {
682
+ $this->tweet_set = $this->removeDuplicates( $tweet_set, $limit );
683
+ }
684
+
685
+ if ( $this->feed_options['selfreplies'] ) {
686
+ $this->tweet_set = $this->filterTweetSet( $tweet_set, $limit );
687
+ }
688
+
689
+ $this->tweet_set = $tweet_set;
690
+ $this->trimTweetData( false );
691
+ return $this->tweet_set;
692
+ }
693
+
694
+ /**
695
+ * this takes the current set of tweets and processes them until there are
696
+ * enough filtered tweets to create the feed from
697
+ */
698
+ private function filterTweetSet( $tweet_set, $limit = true )
699
+ {
700
+ $working_tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
701
+ $usable_tweets = 0;
702
+ if ( $limit ) {
703
+ $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
704
+ } else {
705
+ $tweets_needed = 200;
706
+ }
707
+ $i = 0; // index of working_tweet_set
708
+ $still_setting_filtered_tweets = true;
709
+
710
+ while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
711
+ if ( isset ( $working_tweet_set[$i] ) ) { // if there is another tweet available
712
+ if ( !$this->feed_options['selfreplies'] && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] ) ) {
713
+ unset( $working_tweet_set[$i] );
714
+ } elseif ( $this->feed_options['selfreplies']
715
+ && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] )
716
+ && $working_tweet_set[$i]['in_reply_to_screen_name'] !== $working_tweet_set[$i]['user']['screen_name']) {
717
+ unset( $working_tweet_set[$i] );
718
+ } else {
719
+ $usable_tweets++;
720
+ }
721
+ } else {
722
+ $still_setting_filtered_tweets = false;
723
+ }
724
+
725
+ // if there are no more tweets needed
726
+ if ( $usable_tweets >= $tweets_needed ) {
727
+ $still_setting_filtered_tweets = false;
728
+ } else {
729
+ $i++;
730
+ }
731
+
732
+ }
733
+
734
+ if ( is_array( $working_tweet_set ) ) {
735
+ return array_values( $working_tweet_set );
736
+ } else {
737
+ return false;
738
+ }
739
+ }
740
+
741
+ private function appendPersistentCacheTweets( $existing_cache )
742
+ {
743
+ if ( is_array( $this->tweet_set ) ) {
744
+ $tweet_set = array_merge( $this->tweet_set, $existing_cache );
745
+ } else {
746
+ $tweet_set = $existing_cache;
747
+ }
748
+
749
+ $tweet_set = array_slice( $tweet_set, 0, 150 );
750
+
751
+ return $tweet_set;
752
+ }
753
+
754
+
755
+ private function removeDuplicates( $tweet_set, $limit = true )
756
+ {
757
+ $tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
758
+ $usable_tweets = 0;
759
+ if ( $limit ) {
760
+ $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
761
+ } else {
762
+ $tweets_needed = 200;
763
+ }
764
+ $ids_of_tweets_to_remove = array();
765
+
766
+ $i = 0; // index of tweet_set
767
+ $still_setting_filtered_tweets = true;
768
+ while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
769
+ if ( isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ) {
770
+ unset( $tweet_set[$i] );
771
+ } elseif ( isset( $tweet_set[$i] ) ) {
772
+ $id = isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ? $tweet_set[$i]['retweeted_status']['id_str'] : $tweet_set[$i]['id_str'];
773
+ if ( in_array( $id, $ids_of_tweets_to_remove ) ) {
774
+ unset( $tweet_set[$i] );
775
+ } else {
776
+ $usable_tweets++;
777
+ $ids_of_tweets_to_remove[] = $id;
778
+ }
779
+ } else {
780
+ $still_setting_filtered_tweets = false;
781
+ }
782
+
783
+ // if there are no more tweets needed
784
+ if ( $usable_tweets >= $tweets_needed ) {
785
+ $still_setting_filtered_tweets = false;
786
+ } else {
787
+ $i++;
788
+ }
789
+
790
+ }
791
+
792
+ if ( is_array( $tweet_set ) ) {
793
+ return array_values( $tweet_set );
794
+ } else {
795
+ return false;
796
+ }
797
+ }
798
+
799
+ /**
800
+ * will attempt to connect to the api to retrieve current tweets
801
+ */
802
+ public function maybeSetTweetsFromTwitter()
803
+ {
804
+ $this->setTweetsToRetrieve();
805
+ $this->api_obj = $this->apiConnectionResponse( $this->feed_options['type'], $this->feed_options['feed_term'] );
806
+ $this->tweet_set = json_decode( $this->api_obj->json , $assoc = true );
807
+
808
+ $working_tweet_set = $this->tweet_set;
809
+ if ( ! isset( $working_tweet_set['errors'][0] ) ) {
810
+ if ( isset( $working_tweet_set[0] ) ) {
811
+ $value = array_values( array_slice( $working_tweet_set, -1 ) );
812
+ $this->last_id_data = $value[0]['id_str'];
813
+ }
814
+
815
+ $working_tweet_set = $this->reduceTweetSetData( $working_tweet_set );
816
+ if ( $working_tweet_set === false ) {
817
+ $working_tweet_set = array();
818
+ }
819
+ }
820
+
821
+ $num_tweets = is_array( $working_tweet_set ) ? count( $working_tweet_set ) : 500;
822
+
823
+ if ( ! isset( $working_tweet_set['errors'][0] )
824
+ && $num_tweets < $this->feed_options['count'] ) {
825
+ // remove the last tweet as it is returned in the next request
826
+ array_pop( $working_tweet_set );
827
+ $original_count = $this->feed_options['count'];
828
+ $this->feed_options['count'] = 200;
829
+ $api_obj = $this->apiConnectionResponse( $this->feed_options['type'], $this->feed_options['feed_term'] );
830
+ $tweet_set_to_merge = json_decode( $api_obj->json , $assoc = true );
831
+
832
+ if ( isset( $tweet_set_to_merge['statuses'] ) ) {
833
+ $working_tweet_set = array_merge( $working_tweet_set, $tweet_set_to_merge['statuses'] );
834
+ } elseif ( isset( $tweet_set_to_merge[0]['created_at'] ) ) {
835
+ $working_tweet_set = array_merge( $working_tweet_set, $tweet_set_to_merge );
836
+ }
837
+
838
+ $this->feed_options['count'] = $original_count;
839
+ }
840
+
841
+ $this->tweet_set = $working_tweet_set;
842
+
843
+ // check for errors/tweets present
844
+ if ( isset( $this->tweet_set['errors'][0] ) ) {
845
+ $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
846
+ $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
847
+ $this->tweet_set = false;
848
+ }
849
+
850
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
851
+
852
+ if ( empty( $tweets ) ) {
853
+ $this->errors['error_message'] = 'No Tweets returned';
854
+ $this->tweet_set = false;
855
+ }
856
+
857
+ if ( $this->check_for_duplicates ) {
858
+ $this->tweet_set = $this->removeDuplicates( $this->tweet_set );
859
+ }
860
+ }
861
+
862
+
863
+ /**
864
+ * calculates how many tweets short the feed is so more can be retrieved via ajax
865
+ *
866
+ * @return int number of tweets needed
867
+ */
868
+ protected function numTweetsNeeded() {
869
+ $tweet_count = 0;
870
+ if ( isset( $this->tweet_set['statuses'] ) && is_array( $this->tweet_set['statuses'] ) ) {
871
+ $tweet_count = count( $this->tweet_set['statuses'] );
872
+ } elseif ( isset( $this->tweet_set ) && is_array( $this->tweet_set ) ) {
873
+ $tweet_count = count( $this->tweet_set );
874
+ }
875
+
876
+ return $this->feed_options['num'] - $tweet_count;
877
+ }
878
+
879
+ /**
880
+ * trims the unused data retrieved for more efficient caching
881
+ */
882
+ protected function trimTweetData( $limit = true )
883
+ {
884
+ $is_pagination = !empty( $this->last_id_data ) ? 1 : 0;
885
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
886
+ if ( $limit ) {
887
+ $len = min( $this->feed_options['num'] + $is_pagination, count( $tweets ) );
888
+ } else {
889
+ $len = count( $tweets );
890
+ }
891
+ $trimmed_tweets = array();
892
+
893
+ // for header
894
+ if ( $this->last_id_data == '' && isset( $tweets[0] ) ) { // if this is the first set of tweets
895
+ $trimmed_tweets[0]['user']['name']= $tweets[0]['user']['name'];
896
+ $trimmed_tweets[0]['user']['description']= $tweets[0]['user']['description'];
897
+ $trimmed_tweets[0]['user']['statuses_count']= $tweets[0]['user']['statuses_count'];
898
+ $trimmed_tweets[0]['user']['followers_count']= $tweets[0]['user']['followers_count'];
899
+ }
900
+
901
+ for ( $i = 0; $i < $len; $i++ ) {
902
+ $trimmed_tweets[$i]['user']['name'] = $tweets[$i]['user']['name'];
903
+ $trimmed_tweets[$i]['user']['screen_name'] = $tweets[$i]['user']['screen_name'];
904
+ $trimmed_tweets[$i]['user']['verified'] = $tweets[$i]['user']['verified'];
905
+ $trimmed_tweets[$i]['user']['profile_image_url_https'] = $tweets[$i]['user']['profile_image_url_https'];
906
+ $trimmed_tweets[$i]['user']['utc_offset']= $tweets[$i]['user']['utc_offset'];
907
+ $trimmed_tweets[$i]['text'] = isset( $tweets[$i]['text'] ) ? $tweets[$i]['text'] : $tweets[$i]['full_text'];
908
+ $trimmed_tweets[$i]['id_str']= $tweets[$i]['id_str'];
909
+ $trimmed_tweets[$i]['created_at']= $tweets[$i]['created_at'];
910
+ $trimmed_tweets[$i]['retweet_count']= $tweets[$i]['retweet_count'];
911
+ $trimmed_tweets[$i]['favorite_count']= $tweets[$i]['favorite_count'];
912
+
913
+ if ( isset( $tweets[$i]['entities']['urls'][0] ) ) {
914
+ foreach ( $tweets[$i]['entities']['urls'] as $url ) {
915
+ $trimmed_tweets[$i]['entities']['urls'][] = array(
916
+ 'url' => $url['url'],
917
+ 'expanded_url' => $url['expanded_url'],
918
+ 'display_url' => $url['display_url'],
919
+
920
+ );
921
+ }
922
+ }
923
+
924
+ if ( isset( $tweets[$i]['retweeted_status'] ) ) {
925
+ $trimmed_tweets[$i]['retweeted_status']['user']['name'] = $tweets[$i]['retweeted_status']['user']['name'];
926
+ $trimmed_tweets[$i]['retweeted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['user']['screen_name'];
927
+ $trimmed_tweets[$i]['retweeted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['user']['verified'];
928
+ $trimmed_tweets[$i]['retweeted_status']['user']['profile_image_url_https'] = $tweets[$i]['retweeted_status']['user']['profile_image_url_https'];
929
+ $trimmed_tweets[$i]['retweeted_status']['user']['utc_offset']= $tweets[$i]['retweeted_status']['user']['utc_offset'];
930
+ $trimmed_tweets[$i]['retweeted_status']['text'] = isset( $tweets[$i]['retweeted_status']['text'] ) ? $tweets[$i]['retweeted_status']['text'] : $tweets[$i]['retweeted_status']['full_text'];
931
+ $trimmed_tweets[$i]['retweeted_status']['id_str'] = $tweets[$i]['retweeted_status']['id_str'];
932
+ $trimmed_tweets[$i]['retweeted_status']['created_at']= $tweets[$i]['retweeted_status']['created_at'];
933
+ $trimmed_tweets[$i]['retweeted_status']['retweet_count']= $tweets[$i]['retweeted_status']['retweet_count'];
934
+ $trimmed_tweets[$i]['retweeted_status']['favorite_count']= $tweets[$i]['retweeted_status']['favorite_count'];
935
+ if ( isset( $tweets[$i]['retweeted_status']['entities']['urls'][0] ) ) {
936
+ foreach ( $tweets[$i]['retweeted_status']['entities']['urls'] as $url ) {
937
+ $trimmed_tweets[$i]['retweeted_status']['entities']['urls'][] = array(
938
+ 'url' => $url['url'],
939
+ 'expanded_url' => $url['expanded_url'],
940
+ 'display_url' => $url['display_url'],
941
+
942
+ );
943
+ }
944
+ }
945
+ }
946
+
947
+ if ( isset( $tweets[$i]['retweeted_status']['quoted_status'] ) ) {
948
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['name'];
949
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'];
950
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['verified'];
951
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['text'] = isset( $tweets[$i]['retweeted_status']['quoted_status']['text'] ) ? $tweets[$i]['retweeted_status']['quoted_status']['text'] : $tweets[$i]['retweeted_status']['quoted_status']['full_text'];
952
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['id_str'] = $tweets[$i]['retweeted_status']['quoted_status']['id_str'];
953
+ if ( isset( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][0] ) ) {
954
+ foreach ( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'] as $url ) {
955
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][] = array(
956
+ 'url' => $url['url'],
957
+ 'expanded_url' => $url['expanded_url'],
958
+ 'display_url' => $url['display_url'],
959
+ );
960
+ }
961
+ }
962
+ }
963
+
964
+ if ( isset( $tweets[$i]['quoted_status'] ) ) {
965
+ $trimmed_tweets[$i]['quoted_status']['user']['name'] = $tweets[$i]['quoted_status']['user']['name'];
966
+ $trimmed_tweets[$i]['quoted_status']['user']['screen_name'] = $tweets[$i]['quoted_status']['user']['screen_name'];
967
+ $trimmed_tweets[$i]['quoted_status']['user']['verified'] = $tweets[$i]['quoted_status']['user']['verified'];
968
+ $trimmed_tweets[$i]['quoted_status']['text'] = isset( $tweets[$i]['quoted_status']['text'] ) ? $tweets[$i]['quoted_status']['text'] : $tweets[$i]['quoted_status']['full_text'];
969
+ $trimmed_tweets[$i]['quoted_status']['id_str'] = $tweets[$i]['quoted_status']['id_str'];
970
+ if ( isset( $tweets[$i]['quoted_status']['entities']['urls'][0] ) ) {
971
+ foreach ( $tweets[$i]['quoted_status']['entities']['urls'] as $url ) {
972
+ $trimmed_tweets[$i]['quoted_status']['entities']['urls'][] = array(
973
+ 'url' => $url['url'],
974
+ 'expanded_url' => $url['expanded_url'],
975
+ 'display_url' => $url['display_url'],
976
+ );
977
+ }
978
+ }
979
+ }
980
+
981
+ $trimmed_tweets[$i] = $this->filterTrimmedTweets( $trimmed_tweets[$i], $tweets[$i] );
982
+ }
983
+
984
+ $this->tweet_set = $trimmed_tweets;
985
+ }
986
+
987
+ protected function removeStringFromText( $string, $text) {
988
+ return str_replace( $string, '', $text );
989
+ }
990
+
991
+ /**
992
+ * captures additional data for "Pro" features
993
+ *
994
+ * @param $trimmed array current set of trimmed tweets
995
+ * @param $tweet array raw tweet data from api
996
+ * @return array
997
+ */
998
+ protected function filterTrimmedTweets( $trimmed, $tweet )
999
+ {
1000
+ if ( isset( $tweet['in_reply_to_screen_name'] ) ) {
1001
+ $trimmed['in_reply_to_screen_name'] = $tweet['in_reply_to_screen_name'];
1002
+ $trimmed['entities']['user_mentions'][0]['name'] = isset( $tweet['entities']['user_mentions'][0]['name'] ) ? $tweet['entities']['user_mentions'][0]['name'] : '';
1003
+ $trimmed['in_reply_to_status_id_str'] = $tweet['in_reply_to_status_id_str'];
1004
+ }
1005
+
1006
+ if ( isset( $tweet['extended_entities']['media'] ) ) {
1007
+ // if there is media, we need to remove the media url from the tweet text
1008
+ $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
1009
+ if ( isset( $tweet['extended_entities']['media'][0]['url'] ) ) {
1010
+ $trimmed['text'] = $this->removeStringFromText( $tweet['extended_entities']['media'][0]['url'], $text );
1011
+ }
1012
+ $num_media = count( $tweet['extended_entities']['media'] );
1013
+ for ( $i = 0; $i < $num_media; $i++ ) {
1014
+ $trimmed['extended_entities']['media'][$i]['media_url_https'] = $tweet['extended_entities']['media'][$i]['media_url_https'];
1015
+ $trimmed['extended_entities']['media'][$i]['type'] = $tweet['extended_entities']['media'][$i]['type'];
1016
+ if ( isset( $tweet['extended_entities']['media'][$i]['sizes'] ) ) {
1017
+ $trimmed['extended_entities']['media'][$i]['sizes'] = $tweet['extended_entities']['media'][$i]['sizes'];
1018
+ }
1019
+ if ( $tweet['extended_entities']['media'][$i]['type'] == 'video' || $tweet['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1020
+ foreach ( $tweet['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1021
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1022
+ $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1023
+ }
1024
+ }
1025
+ if ( ! isset( $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1026
+ $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1027
+ }
1028
+ }
1029
+ }
1030
+
1031
+ } elseif ( isset( $tweet['entities']['media'] ) ) {
1032
+ // if there is media, we need to remove the media url from the tweet text
1033
+ $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
1034
+ if ( isset( $tweet['entities']['media'][0]['url'] ) ) {
1035
+ $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['media'][0]['url'], $text );
1036
+ }
1037
+
1038
+ $num_media = count( $tweet['entities']['media'] );
1039
+ for ( $i = 0; $i < $num_media; $i++ ) {
1040
+ $trimmed['entities']['media'][$i]['media_url_https'] = $tweet['entities']['media'][$i]['media_url_https'];
1041
+ $trimmed['entities']['media'][$i]['type'] = $tweet['entities']['media'][$i]['type'];
1042
+ if ( isset( $tweet['entities']['media'][$i]['sizes'] ) ) {
1043
+ $trimmed['entities']['media'][$i]['sizes'] = $tweet['entities']['media'][$i]['sizes'];
1044
+ }
1045
+ if ( $tweet['entities']['media'][$i]['type'] == 'video' || $tweet['entities']['media'][$i]['type'] == 'animated_gif' ) {
1046
+ foreach ( $tweet['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1047
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1048
+ $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1049
+ }
1050
+ }
1051
+ if ( ! isset( $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1052
+ $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['entities']['media'][$i]['video_info']['variants'][0]['url'];
1053
+ }
1054
+ }
1055
+ }
1056
+
1057
+ }
1058
+
1059
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'] ) ) {
1060
+ // if there is media, we need to remove the media url from the tweet text
1061
+ $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1062
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'][0]['url'] ) ) {
1063
+ $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1064
+ }
1065
+
1066
+ $num_media = count( $tweet['retweeted_status']['extended_entities']['media'] );
1067
+ for ( $i = 0; $i < $num_media; $i++ ) {
1068
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['media_url_https'];
1069
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['type'];
1070
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1071
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'];
1072
+ }
1073
+ if ( $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1074
+ foreach ( $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1075
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1076
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1077
+ }
1078
+ }
1079
+ if ( ! isset( $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1080
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1081
+ }
1082
+ }
1083
+ }
1084
+
1085
+ } elseif ( isset( $tweet['retweeted_status']['entities']['media'] ) ) {
1086
+ // if there is media, we need to remove the media url from the tweet text
1087
+ $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1088
+ if ( isset( $tweet['retweeted_status']['entities']['media'][0]['url'] ) ) {
1089
+ $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['entities']['media'][0]['url'], $retweeted_text );
1090
+ }
1091
+
1092
+ $num_media = count( $tweet['retweeted_status']['entities']['media'] );
1093
+ for( $i = 0; $i < $num_media; $i++ ) {
1094
+ $trimmed['retweeted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['entities']['media'][$i]['media_url_https'];
1095
+ $trimmed['retweeted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['entities']['media'][$i]['type'];
1096
+ if ( isset( $tweet['retweeted_status']['entities']['media'][$i]['sizes'] ) ) {
1097
+ $trimmed['retweeted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['entities']['media'][$i]['sizes'];
1098
+ }
1099
+ if ( $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1100
+ foreach ( $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1101
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1102
+ $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1103
+ }
1104
+ }
1105
+ if ( ! isset( $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1106
+ $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ } elseif ( isset( $tweet['quoted_status']['extended_entities']['media'] ) ) {
1112
+ // if there is media, we need to remove the media url from the tweet text
1113
+ $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1114
+ if ( isset( $tweet['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1115
+ $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['extended_entities']['media'][0]['url'], $quoted_text );
1116
+ }
1117
+
1118
+ $num_media = count( $tweet['quoted_status']['extended_entities']['media'] );
1119
+ for( $i = 0; $i < $num_media; $i++ ) {
1120
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1121
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['quoted_status']['extended_entities']['media'][$i]['type'];
1122
+ if ( $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1123
+ foreach ( $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1124
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1125
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1126
+ }
1127
+ }
1128
+ if ( ! isset( $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1129
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1130
+ }
1131
+ }
1132
+ }
1133
+
1134
+ } elseif ( isset( $tweet['quoted_status']['entities']['media'] ) ) {
1135
+ // if there is media, we need to remove the media url from the tweet text
1136
+ $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1137
+ if ( isset( $tweet['quoted_status']['entities']['media'][0]['url'] ) ) {
1138
+ $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['entities']['media'][0]['url'], $quoted_text );
1139
+ }
1140
+
1141
+ $num_media = count( $tweet['quoted_status']['entities']['media'] );
1142
+ for( $i = 0; $i < $num_media; $i++ ) {
1143
+ $trimmed['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['entities']['media'][$i]['media_url_https'];
1144
+ $trimmed['quoted_status']['entities']['media'][$i]['type'] = $tweet['quoted_status']['entities']['media'][$i]['type'];
1145
+ if ( $tweet['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1146
+ foreach ( $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1147
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1148
+ $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1149
+ }
1150
+ }
1151
+ if ( ! isset( $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1152
+ $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1153
+ }
1154
+ }
1155
+ }
1156
+
1157
+ }
1158
+
1159
+ if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] ) ) {
1160
+ // if there is media, we need to remove the media url from the tweet text
1161
+ $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1162
+ if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1163
+ $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1164
+ }
1165
+ $num_media = count( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] );
1166
+ for ( $i = 0; $i < $num_media; $i++ ) {
1167
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1168
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'];
1169
+ if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1170
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'];
1171
+ }
1172
+ if ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1173
+ foreach ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1174
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1175
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1176
+ }
1177
+ }
1178
+ if ( ! isset( $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1179
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1180
+ }
1181
+ }
1182
+ }
1183
+ } elseif ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'] ) ) {
1184
+ // if there is media, we need to remove the media url from the tweet text
1185
+ $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1186
+ if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'] ) ) {
1187
+ $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'], $retweeted_text );
1188
+ }
1189
+ $num_media = count( $tweet['retweeted_status']['quoted_status']['entities']['media'] );
1190
+ for( $i = 0; $i < $num_media; $i++ ) {
1191
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'];
1192
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'];
1193
+ if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] ) ) {
1194
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'];
1195
+ }
1196
+ if ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1197
+ foreach ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1198
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1199
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1200
+ }
1201
+ }
1202
+ if ( ! isset( $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1203
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1204
+ }
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ //remove the url from the text if it links to a quoted tweet that is already linked to
1210
+ if ( isset( $tweet['quoted_status'] ) ) {
1211
+ $maybe_remove_index = count( $tweet['entities']['urls'] ) - 1;
1212
+ if ( isset( $tweet['entities']['urls'][$maybe_remove_index]['url'] ) ) {
1213
+ $text = isset( $trimmed['full_text'] ) ? $trimmed['full_text'] : $trimmed['text'];
1214
+ $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['urls'][$maybe_remove_index]['url'], $text );
1215
+ }
1216
+ }
1217
+
1218
+
1219
+ // used to generate twitter cards
1220
+ if ( isset( $tweet['entities']['urls'][0]['expanded_url'] ) ) {
1221
+ $trimmed['entities']['urls'][0]['expanded_url'] = $tweet['entities']['urls'][0]['expanded_url'];
1222
+ }
1223
+
1224
+ if ( isset( $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'] ) ) {
1225
+ $trimmed['retweeted_status']['entities']['urls'][0]['expanded_url'] = $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'];
1226
+ }
1227
+
1228
+ return $trimmed;
1229
+ }
1230
+
1231
+ /**
1232
+ * will create a transient with the tweet cache if one doesn't exist, the data seems valid, and caching is active
1233
+ */
1234
+ public function maybeCacheTweets()
1235
+ {
1236
+ if ( ( ! $this->transient_data || $this->errors['cache_status'] ) && $this->feed_options['cache_time'] > 0 ) {
1237
+ $this->trimTweetData();
1238
+ $cache = json_encode( $this->tweet_set );
1239
+ set_transient( $this->transient_name, $cache, $this->feed_options['cache_time'] );
1240
+ }
1241
+ }
1242
+
1243
+ /**
1244
+ * returns a JSON string to be used in the data attribute that contains the shortcode data
1245
+ */
1246
+ public function getShortCodeJSON()
1247
+ {
1248
+ $json_data = '{';
1249
+ $i = 0;
1250
+ $len = is_array( $this->atts ) ? count( $this->atts ) : 0;
1251
+
1252
+ if ( ! empty( $this->atts ) ) {
1253
+ foreach ( $this->atts as $key => $value) {
1254
+ if ( $i == $len - 1 ) {
1255
+ $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;';
1256
+ } else {
1257
+ $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;, ';
1258
+ }
1259
+ $i++;
1260
+ }
1261
+ }
1262
+
1263
+ $json_data .= '}';
1264
+
1265
+ return $json_data;
1266
+ }
1267
+
1268
+ /**
1269
+ * uses the endpoint to determing what get fields need to be set
1270
+ *
1271
+ * @param $end_point api endpoint needed
1272
+ * @param $feed_term term associated with the endpoint, user name or search term
1273
+ * @return array the get fields for the request
1274
+ */
1275
+ protected function setGetFieldsArray( $end_point, $feed_term )
1276
+ {
1277
+ $get_fields = array();
1278
+
1279
+ $get_fields['tweet_mode'] = 'extended';
1280
+
1281
+ if ( $end_point === 'usertimeline' ) {
1282
+ if ( ! empty ( $feed_term ) ) {
1283
+ $get_fields['screen_name'] = $feed_term;
1284
+ }
1285
+ if ( !$this->feed_options['selfreplies'] ) {
1286
+ $get_fields['exclude_replies'] = 'true';
1287
+ }
1288
+ }
1289
+ if ( $end_point === 'hometimeline' ) {
1290
+ $get_fields['exclude_replies'] = 'true';
1291
+ if ( !$this->feed_options['selfreplies'] ) {
1292
+ $get_fields['exclude_replies'] = 'true';
1293
+ }
1294
+ }
1295
+ if ( $end_point === 'search' ) {
1296
+ $get_fields['q'] = $feed_term;
1297
+ }
1298
+
1299
+ return $get_fields;
1300
+ }
1301
+
1302
+ /**
1303
+ * attempts to connect and retrieve tweets from the Twitter api
1304
+ *
1305
+ * @return mixed|string object containing the response
1306
+ */
1307
+ public function apiConnectionResponse( $end_point, $feed_term )
1308
+ {
1309
+ // Only can be set in the options page
1310
+ $request_settings = array(
1311
+ 'consumer_key' => $this->feed_options['consumer_key'],
1312
+ 'consumer_secret' => $this->feed_options['consumer_secret'],
1313
+ 'access_token' => $this->feed_options['access_token'],
1314
+ 'access_token_secret' => $this->feed_options['access_token_secret'],
1315
+ );
1316
+
1317
+ // For pagination, an extra post needs to be retrieved since the last post is
1318
+ // included in the next set
1319
+ $count = $this->feed_options['count'];
1320
+
1321
+ $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1322
+
1323
+ if ( ! empty( $this->last_id_data ) ) {
1324
+ $count++;
1325
+ $max_id = $this->last_id_data;
1326
+ }
1327
+ $get_fields['count'] = $count;
1328
+
1329
+ // max_id parameter should only be included for the second set of posts
1330
+ if ( isset( $max_id ) ) {
1331
+ $get_fields['max_id'] = $max_id;
1332
+ }
1333
+
1334
+ include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1335
+
1336
+ // actual connection
1337
+ $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1338
+ $twitter_connect->setUrlBase();
1339
+ $twitter_connect->setGetFields( $get_fields );
1340
+ $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1341
+
1342
+ return $twitter_connect->performRequest();
1343
+ }
1344
+
1345
+ private function getTweetsSinceID( $since_id, $end_point = 'search', $feed_term, $count )
1346
+ {
1347
+ // Only can be set in the options page
1348
+ $request_settings = array(
1349
+ 'consumer_key' => $this->feed_options['consumer_key'],
1350
+ 'consumer_secret' => $this->feed_options['consumer_secret'],
1351
+ 'access_token' => $this->feed_options['access_token'],
1352
+ 'access_token_secret' => $this->feed_options['access_token_secret'],
1353
+ );
1354
+
1355
+ $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1356
+
1357
+ $get_fields['since_id'] = $since_id;
1358
+
1359
+ $get_fields['count'] = $count;
1360
+
1361
+ include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1362
+
1363
+ // actual connection
1364
+ $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1365
+ $twitter_connect->setUrlBase();
1366
+ $twitter_connect->setGetFields( $get_fields );
1367
+ $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1368
+
1369
+ return $twitter_connect->performRequest();
1370
+ }
1371
+
1372
+ public function feedID() {
1373
+ if ( $this->feed_options['persistentcache'] ) {
1374
+ $feed_id = substr( 'ctf_!_' . $this->feed_options['feed_term'], 0, 45 );
1375
+ $feed_id = str_replace( ' -filter:retweets', '', $feed_id );
1376
+ } else {
1377
+ $feed_id = $this->transient_name;
1378
+ }
1379
+
1380
+ return $feed_id;
1381
+ }
1382
+
1383
+ /**
1384
+ * If the feed runs out of tweets to display for some reason,
1385
+ * this function creates a graceful failure message
1386
+ *
1387
+ * @param $feed_options
1388
+ * @return string html for "out of tweets" message
1389
+ */
1390
+ protected function getOutOfTweetsHtml( $feed_options )
1391
+ {
1392
+ $html = '';
1393
+
1394
+ $html .= '<div class="ctf-out-of-tweets">';
1395
+ $html .= '<p>' . __( "That's all! No more Tweets to load", 'custom-twitter-feeds' ) . '</p>';
1396
+ $html .= '<p>';
1397
+ $html .= '<a class="twitter-share-button" href="https://twitter.com/share" target="_blank" rel="noopener noreferrer" data-size="large" data-url="'.get_home_url().'">Share</a>';
1398
+ if ( !empty( $feed_options['screenname'] ) ) {
1399
+ $html .= '<a class="twitter-follow-button" href="https://twitter.com/' . $feed_options['screenname'] . '" target="_blank" rel="noopener noreferrer" data-show-count="false" data-size="large" data-dnt="true">Follow</a>';
1400
+ }
1401
+ $html .= '</p>';
1402
+ $html .= '</div>';
1403
+
1404
+ return $html;
1405
+ }
1406
+
1407
+ /**
1408
+ * creates opening html for the feed
1409
+ *
1410
+ * @return string opening html that creates the feed
1411
+ */
1412
+ public function getFeedOpeningHtml()
1413
+ {
1414
+ $feed_options = $this->feed_options;
1415
+ $ctf_data_disablelinks = ($feed_options['disablelinks'] == 'true') ? ' data-ctfdisablelinks="true"' : '';
1416
+ $ctf_data_linktextcolor = $feed_options['linktextcolor'] != '' ? ' data-ctflinktextcolor="'.$feed_options['linktextcolor'].'"' : '';
1417
+ $ctf_enable_intents = $feed_options['disableintents'] === false && ctf_show( 'actions', $feed_options ) ? ' data-ctfintents="1"' : '';
1418
+ $ctf_data_needed = $this->num_tweets_needed;
1419
+ $ctf_feed_type = ! empty ( $feed_options['type'] ) ? esc_attr( $feed_options['type'] ) : 'multiple';
1420
+ $ctf_feed_classes = 'ctf ctf-type-' . $ctf_feed_type;
1421
+ $ctf_feed_classes .= ' ' . $feed_options['class'] . ' ctf-styles';
1422
+ $ctf_feed_classes .= $feed_options['width_mobile_no_fixed'] ? ' ctf-width-resp' : '';
1423
+ if ( $this->check_for_duplicates ) { $ctf_feed_classes .= ' ctf-no-duplicates'; }
1424
+ $ctf_feed_classes = apply_filters( 'ctf_feed_classes', $ctf_feed_classes ); //add_filter( 'ctf_feed_classes', function( $ctf_feed_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1425
+ $ctf_feed_html = '';
1426
+
1427
+ $flags_att = '';
1428
+ $flags = array();
1429
+ if ( CTF_GDPR_Integrations::doing_gdpr( $feed_options ) ) {
1430
+ $flags[] = 'gdpr';
1431
+ }
1432
+ if ( ! is_admin()
1433
+ && CTF_Feed_Locator::should_do_ajax_locating( $this->feedID(), get_the_ID() ) ) {
1434
+ $flags[] = 'locator';
1435
+ }
1436
+ if ( ! empty( $flags ) ) {
1437
+ $flags_att = ' data-ctf-flags="' . implode( ',', $flags ) . '"';
1438
+ }
1439
+ $post_id_att = ' data-postid="' . esc_attr( get_the_ID() ) . '"';
1440
+ $feed_id_att = ' data-feed-id="' . $this->feedID() . '"';
1441
+
1442
+ $ctf_feed_html .= '<!-- Custom Twitter Feeds by Smash Balloon -->';
1443
+ $ctf_feed_html .= '<div id="ctf" class="' . $ctf_feed_classes . '" style="' . $feed_options['width'] . $feed_options['height'] . $feed_options['bgcolor'] . '" data-ctfshortcode="' . $this->getShortCodeJSON() . '"' .$ctf_data_disablelinks . $ctf_data_linktextcolor . $ctf_enable_intents . $flags_att . $post_id_att . $feed_id_att .' data-ctfneeded="'. $ctf_data_needed .'">';
1444
+ $tweet_set = $this->tweet_set;
1445
+
1446
+ // dynamically include header
1447
+ if ( $feed_options['showheader'] ) {
1448
+ $ctf_feed_html .= $this->getFeedHeaderHtml( $tweet_set, $this->feed_options );
1449
+ }
1450
+
1451
+ $ctf_feed_html .= '<div class="ctf-tweets">';
1452
+
1453
+ return $ctf_feed_html;
1454
+ }
1455
+
1456
+ /**
1457
+ * creates opening html for the feed
1458
+ *
1459
+ * @return string opening html that creates the feed
1460
+ */
1461
+ public function getFeedClosingHtml()
1462
+ {
1463
+ $feed_options = $this->feed_options;
1464
+ $ctf_feed_html = '';
1465
+
1466
+ $ctf_feed_html .= '</div>'; // closing div for ctf-tweets
1467
+
1468
+ if ( $feed_options['showbutton'] ) {
1469
+ $ctf_feed_html .= '<a href="javascript:void(0);" id="ctf-more" class="ctf-more" style="' . $feed_options['buttoncolor'] . $feed_options['buttontextcolor'] . '"><span>' . $feed_options['buttontext'] . '</span></a>';
1470
+ }
1471
+
1472
+ if ( $feed_options['creditctf'] ) {
1473
+ $ctf_feed_html .= '<div class="ctf-credit-link"><a href="https://smashballoon.com/custom-twitter-feeds" target="_blank" rel="noopener noreferrer">' . ctf_get_fa_el( 'fa-twitter' ) . 'Custom Twitter Feeds Plugin</a></div>';
1474
+ }
1475
+
1476
+ $ctf_feed_html .= '</div>'; // closing div tag for #ctf
1477
+
1478
+ if ( $feed_options['ajax_theme'] ) {
1479
+ $ctf_feed_html .= '<script type="text/javascript" src="' . CTF_JS_URL . '"></script>';
1480
+ }
1481
+
1482
+ return $ctf_feed_html;
1483
+ }
1484
+
1485
+ /**
1486
+ * creates html for header of the feed
1487
+ *
1488
+ * @param $tweet_set string trimmed tweets to be added to the feed
1489
+ * @param $feed_options options for the feed
1490
+ * @return string html that creates the header of the feed
1491
+ */
1492
+ protected function getFeedHeaderHtml( $tweet_set, $feed_options )
1493
+ {
1494
+ $ctf_header_html = '';
1495
+ $ctf_no_bio = ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) ? '' : ' ctf-no-bio';
1496
+
1497
+ // temporary workaround for cached http images
1498
+ $tweet_set[0]['user']['profile_image_url_https'] = isset( $tweet_set[0]['user']['profile_image_url_https'] ) ? $tweet_set[0]['user']['profile_image_url_https'] : $tweet_set[0]['user']['profile_image_url'];
1499
+
1500
+
1501
+ if ( $feed_options['type'] === 'usertimeline' ) {
1502
+ $ctf_header_html .= '<div class="ctf-header' . $ctf_no_bio . '" style="' . $feed_options['headerbgcolor'] . '">';
1503
+ $ctf_header_html .= '<a href="https://twitter.com/' . $tweet_set[0]['user']['screen_name'] . '" target="_blank" rel="noopener noreferrer" title="@' . $tweet_set[0]['user']['screen_name'] . '" class="ctf-header-link">';
1504
+ $ctf_header_html .= '<div class="ctf-header-text">';
1505
+ $ctf_header_html .= '<p class="ctf-header-user" style="' . $feed_options['headertextcolor'] . '">';
1506
+ $ctf_header_html .= '<span class="ctf-header-name">';
1507
+
1508
+ if ( $feed_options['headertext'] != '' ) {
1509
+ $ctf_header_html .= esc_html( $feed_options['headertext'] );
1510
+ } else {
1511
+ $ctf_header_html .= esc_html( $tweet_set[0]['user']['name'] );
1512
+ }
1513
+
1514
+ $ctf_header_html .= '</span>';
1515
+
1516
+ if ( $tweet_set[0]['user']['verified'] == 1 ) {
1517
+ $ctf_header_html .= '<span class="ctf-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1518
+ }
1519
+
1520
+ $ctf_header_html .= '<span class="ctf-header-follow">' . ctf_get_fa_el( 'fa-twitter' ) . __( 'Follow', 'custom-twitter-feeds' ) . '</span>';
1521
+ $ctf_header_html .= '</p>';
1522
+
1523
+ if ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) {
1524
+ $ctf_header_html .= '<p class="ctf-header-bio" style="' . $feed_options['headertextcolor'] . '">' . $tweet_set[0]['user']['description'] . '</p>';
1525
+ }
1526
+
1527
+ $ctf_header_html .= '</div>';
1528
+ $ctf_header_html .= '<div class="ctf-header-img">';
1529
+ $ctf_header_html .= '<div class="ctf-header-img-hover">' . ctf_get_fa_el( 'fa-twitter' ) . '</div>';
1530
+ if ( CTF_GDPR_Integrations::doing_gdpr( $feed_options ) ) {
1531
+ $ctf_header_html .= '<span data-avatar="' . esc_url( $tweet_set[0]['user']['profile_image_url_https'] ) . '" data-alt="' . $tweet_set[0]['user']['name'] . '" style="display: none;">Avatar</span>';
1532
+ } else {
1533
+ $ctf_header_html .= '<img src="' . $tweet_set[0]['user']['profile_image_url_https'] . '" alt="' . $tweet_set[0]['user']['name'] . '" width="48" height="48">';
1534
+ }
1535
+ $ctf_header_html .= '</div>';
1536
+ $ctf_header_html .= '</a>';
1537
+ $ctf_header_html .= '</div>';
1538
+ } else {
1539
+
1540
+ if ( $feed_options['type'] === 'search' ) {
1541
+ $default_header_text = $feed_options['headertext'] != '' ? esc_html($feed_options['headertext']) : $feed_options['feed_term'];
1542
+ $url_part = 'hashtag/' . str_replace("#", "", $feed_options['feed_term']);
1543
+ } else {
1544
+ $default_header_text = 'Twitter';
1545
+ $url_part = $feed_options['screenname']; //Need to get screenname here
1546
+ }
1547
+
1548
+ $default_header_text = str_replace( ' -filter:retweets', '', $default_header_text );
1549
+
1550
+ $ctf_header_html .= '<div class="ctf-header ctf-header-type-generic" style="' . $feed_options['headerbgcolor'] . '">';
1551
+ $ctf_header_html .= '<a href="https://twitter.com/' . $url_part . '" target="_blank" rel="noopener noreferrer" class="ctf-header-link">';
1552
+ $ctf_header_html .= '<div class="ctf-header-text">';
1553
+ $ctf_header_html .= '<p class="ctf-header-no-bio" style="' . $feed_options['headertextcolor'] . '">' . $default_header_text . '</p>';
1554
+ $ctf_header_html .= '</div>';
1555
+ $ctf_header_html .= '<div class="ctf-header-img">';
1556
+ $ctf_header_html .= '<div class="ctf-header-generic-icon">';
1557
+ $ctf_header_html .= ctf_get_fa_el( 'fa-twitter' );
1558
+ $ctf_header_html .= '</div>';
1559
+ $ctf_header_html .= '</div>';
1560
+ $ctf_header_html .= '</a>';
1561
+ $ctf_header_html .= '</div>';
1562
+ }
1563
+
1564
+ return $ctf_header_html;
1565
+ }
1566
+
1567
+ /**
1568
+ * outputs the html for a set of tweets to be used in the feed
1569
+ *
1570
+ * @param int $is_pagination 1 or 0, used to differentiate between the first set and subsequent tweet sets
1571
+ *
1572
+ * @return string $tweet_html
1573
+ */
1574
+ public function getTweetSetHtml( $is_pagination = 0 )
1575
+ {
1576
+ $tweet_set = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
1577
+ $len = min( $this->feed_options['num'] + $is_pagination, count( $tweet_set ) );
1578
+ $i = $is_pagination; // starts at index "1" to offset duplicate tweet
1579
+ $feed_options = $this->feed_options;
1580
+ $tweet_html = $this->feed_html;
1581
+
1582
+ if ( $is_pagination && ( ! isset ( $tweet_set[1]['id_str'] ) ) ) {
1583
+ $tweet_html .= $this->getOutOfTweetsHtml( $this->feed_options );
1584
+ } else {
1585
+ while ( $i < $len ) {
1586
+
1587
+ // run a check to accommodate the "search" endpoint as well
1588
+ $post = $tweet_set[$i];
1589
+
1590
+ // temporary workaround for cached http images
1591
+ $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1592
+
1593
+ // save the original tweet data in case it's a retweet
1594
+ $post_id = $post['id_str'];
1595
+ $author = strtolower( $post['user']['screen_name'] );
1596
+
1597
+ // creates a string of classes applied to each tweet
1598
+ $tweet_classes = 'ctf-item ctf-author-' . $author .' ctf-new';
1599
+ if ( !ctf_show( 'avatar', $feed_options ) ) $tweet_classes .= ' ctf-hide-avatar';
1600
+ $tweet_classes = apply_filters( 'ctf_tweet_classes', $tweet_classes ); // add_filter( 'ctf_tweet_classes', function( $tweet_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1601
+
1602
+ // check for retweet
1603
+ $retweet_data_att = '';
1604
+ if ( isset( $post['retweeted_status'] ) ) {
1605
+ $retweeter = array(
1606
+ 'name' => $post['user']['name'],
1607
+ 'screen_name' => $post['user']['screen_name']
1608
+ );
1609
+ $retweet_data_att = ( $this->check_for_duplicates ) ? ' data-ctfretweetid="'.$post['retweeted_status']['id_str'].'"' : '';
1610
+ if ( isset( $post['retweeted_status'] ))
1611
+ $post = $post['retweeted_status'];
1612
+
1613
+ // temporary workaround for cached http images
1614
+ $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1615
+ $tweet_classes .= ' ctf-retweet';
1616
+ } else {
1617
+ unset( $retweeter );
1618
+ }
1619
+
1620
+ // check for quoted
1621
+ if ( isset( $post['quoted_status'] ) ) {
1622
+ $tweet_classes .= ' ctf-quoted';
1623
+ $quoted = $post['quoted_status'];
1624
+ $quoted_media_text = '';
1625
+ if ( ( isset( $quoted['extended_entities']['media'][0] ) || isset( $quoted['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1626
+ $quoted_media = isset( $quoted['extended_entities']['media'] ) ? $quoted['extended_entities']['media'] : $quoted['entities']['media'];
1627
+ $quoted_media_count = count( $quoted_media );
1628
+ switch ( $quoted_media[0]['type'] ) {
1629
+ case 'video':
1630
+ case 'animated_gif':
1631
+ $quoted_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1632
+ break;
1633
+ default:
1634
+ if ( $quoted_media_count > 1 ) {
1635
+ $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap ctf-multi-media-icon">' . $quoted_media_count . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1636
+ } else {
1637
+ $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap">' . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1638
+ }
1639
+ break;
1640
+ }
1641
+ } else {
1642
+ unset( $quoted_media );
1643
+ }
1644
+ } else {
1645
+ unset( $quoted );
1646
+ unset( $quoted_media_text );
1647
+ }
1648
+
1649
+ // check for media [0]['type']
1650
+ $post_media_text = '';
1651
+ $post_media_count = 0;
1652
+ if ( ( isset( $post['extended_entities']['media'][0] ) || isset( $post['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1653
+ $post_media = isset( $post['extended_entities']['media'] ) ? $post['extended_entities']['media'] : $post['entities']['media'];
1654
+ $post_media_count = count( $post_media );
1655
+ switch ( $post_media[0]['type'] ) {
1656
+ case 'video':
1657
+ case 'animated_gif':
1658
+ $post_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1659
+ break;
1660
+ default:
1661
+ if ( $post_media_count > 1 ) {
1662
+ $post_media_text .= $post_media_count . ctf_get_fa_el( 'fa-picture-o' );
1663
+ } else {
1664
+ $post_media_text .= ctf_get_fa_el( 'fa-picture-o' );
1665
+ }
1666
+ break;
1667
+ }
1668
+ } else {
1669
+ unset( $post_media );
1670
+ }
1671
+
1672
+ // include tweet view
1673
+ $tweet_html .= '<div class="'. $tweet_classes . '" id="' . $post_id . '" style="' . $feed_options['tweetbgcolor'] . '"' . $retweet_data_att . '>';
1674
+
1675
+ if ( isset( $retweeter ) && ctf_show( 'retweeter', $feed_options ) ) {
1676
+ $tweet_html .= '<div class="ctf-context">';
1677
+ $tweet_html .= '<a href="https://twitter.com/intent/user?screen_name=' . $retweeter['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-retweet-icon">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">'.__( 'Retweet on Twitter', 'custom-twitter-feeds' ).'</span></a>';
1678
+ $tweet_html .= '<a href="https://twitter.com/' . $retweeter['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-retweet-text" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $retweeter['name'] . ' ' . __( $feed_options['retweetedtext'], 'custom-twitter-feeds' ) . '</a>';
1679
+ $tweet_html .= '</div>';
1680
+ }
1681
+
1682
+ if ( ctf_show( 'avatar', $feed_options ) || ctf_show( 'logo', $feed_options ) || ctf_show( 'author', $feed_options ) || ctf_show( 'date', $feed_options ) ) {
1683
+
1684
+ $tweet_html .= '<div class="ctf-author-box">';
1685
+ $tweet_html .= '<div class="ctf-author-box-link" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1686
+ if ( ctf_show( 'avatar', $feed_options ) ) {
1687
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-avatar" target="_blank" rel="noopener noreferrer" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1688
+ if ( CTF_GDPR_Integrations::doing_gdpr( $feed_options ) ) {
1689
+ $tweet_html .= '<span data-avatar="' . esc_url( $post['user']['profile_image_url_https'] ) . '" data-alt="' . $post['user']['screen_name'] . '">Avatar</span>';
1690
+ } else {
1691
+ $tweet_html .= '<img src="' . esc_url( $post['user']['profile_image_url_https'] ) . '" alt="' . $post['user']['screen_name'] . '" width="48" height="48">';
1692
+ }
1693
+ $tweet_html .= '</a>';
1694
+ }
1695
+
1696
+ if ( ctf_show( 'author', $feed_options ) ) {
1697
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-author-name" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $post['user']['name'] . '</a>';
1698
+ if ( $post['user']['verified'] == 1 ) {
1699
+ $tweet_html .= '<span class="ctf-verified" >' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1700
+ }
1701
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-screenname" target="_blank" rel="noopener noreferrer" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">@' . $post['user']['screen_name'] . '</a>';
1702
+ $sep_style_att = ! empty( $feed_options['authortextsize'] ) ? ' style="' . $feed_options['authortextsize'] . '"' : '';
1703
+ $tweet_html .= '<span class="ctf-screename-sep"' . $sep_style_att . '>&middot;</span>';
1704
+ }
1705
+
1706
+ if ( ctf_show( 'date', $feed_options ) ) {
1707
+ $tweet_html .= '<div class="ctf-tweet-meta">';
1708
+ //https://twitter.com/EnterLaw/status/869452491041243137
1709
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-tweet-date" target="_blank" rel="noopener noreferrer" style="' . $feed_options['datetextsize'] . $feed_options['datetextweight'] . $feed_options['textcolor'] . '">' . ctf_get_formatted_date( $post['created_at'], $feed_options, $post['user']['utc_offset'] ) . '</a>';
1710
+ $tweet_html .= '</div>';
1711
+ } // show date
1712
+ $tweet_html .= '</div>';
1713
+ if ( ctf_show( 'logo', $feed_options ) ) {
1714
+ $tweet_html .= '<div class="ctf-corner-logo" style="' . $feed_options['logosize'] . $feed_options['logocolor'] . '">';
1715
+ $tweet_html .= ctf_get_fa_el( 'fa-twitter' );
1716
+ $tweet_html .= '</div>';
1717
+ }
1718
+ $tweet_html .= '</div>';
1719
+ }
1720
+
1721
+ if ( ctf_show( 'text', $feed_options ) ) {
1722
+ $post_text = apply_filters( 'ctf_tweet_text', $post['text'], $feed_options, $post );
1723
+
1724
+ $tweet_html .= '<div class="ctf-tweet-content">';
1725
+
1726
+ if ( $feed_options['linktexttotwitter'] ) {
1727
+ $tweet_html .= '<a class="ctf-tweet-text-link" href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" rel="noopener noreferrer">';
1728
+ $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text ) . $post_media_text .'</p>';
1729
+ $tweet_html .= '</a>';
1730
+ } else {
1731
+ $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text );
1732
+
1733
+ if( $post_media_count > 0 ){
1734
+ $multi_class = '';
1735
+ if ( $post_media_count > 1 ) {
1736
+ $multi_class = ' ctf-multi-media-icon';
1737
+ }
1738
+ if ( $feed_options['disablelinks'] ) {
1739
+ $tweet_html .= '<span class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</span>' . '</p>';
1740
+ } else {
1741
+ $tweet_html .= '</p><a href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" rel="noopener noreferrer" class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</a>';
1742
+ }
1743
+ }
1744
+ } // link text to twitter option is selected
1745
+
1746
+ $tweet_html .= '</div>';
1747
+ } // show tweet text
1748
+
1749
+ if ( ctf_show( 'linkbox', $feed_options ) && isset( $quoted ) ) {
1750
+ $tweet_html .= '<a href="https://twitter.com/' . $quoted['user']['screen_name'] . '/status/' . $quoted['id_str'] . '" class="ctf-quoted-tweet" style="' . $feed_options['quotedauthorsize'] . $feed_options['quotedauthorweight'] . $feed_options['textcolor'] . '" target="_blank" rel="noopener noreferrer">';
1751
+ $tweet_html .= '<span class="ctf-quoted-author-name">' . $quoted['user']['name'] . '</span>';
1752
+
1753
+ if ($quoted['user']['verified'] == 1) {
1754
+ $tweet_html .= '<span class="ctf-quoted-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1755
+ } // user is verified
1756
+ $quoted_text = apply_filters( 'ctf_quoted_tweet_text', $quoted['text'], $feed_options, $quoted );
1757
+
1758
+ $tweet_html .= '<span class="ctf-quoted-author-screenname">@' . $quoted['user']['screen_name'] . '</span>';
1759
+ $tweet_html .= '<p class="ctf-quoted-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $quoted_text ) . $quoted_media_text . '</p>';
1760
+ //$tweet_html .= ;
1761
+ $tweet_html .= '</a>';
1762
+ }// show link box
1763
+
1764
+ $tweet_html .= '<div class="ctf-tweet-actions">';
1765
+ if ( ctf_show( 'actions', $feed_options ) ) {
1766
+ $tweet_html .= '<a href="https://twitter.com/intent/tweet?in_reply_to=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-reply" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-reply' ) . '<span class="ctf-screenreader">Reply on Twitter ' . $post['id_str'] . '</span></a>';
1767
+ $tweet_html .= '<a href="https://twitter.com/intent/retweet?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-retweet" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">Retweet on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-retweet-count">';
1768
+ if ( $post['retweet_count'] > 0 ) {
1769
+ $tweet_html .= $post['retweet_count'];
1770
+ }
1771
+ $tweet_html .= '</span></a>';
1772
+ $tweet_html .= '<a href="https://twitter.com/intent/like?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-like" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-heart' ) . '<span class="ctf-screenreader">Like on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-favorite-count">';
1773
+ if ( $post['favorite_count'] > 0 ) {
1774
+ $tweet_html .= $post['favorite_count'];
1775
+ }
1776
+ $tweet_html .= '</span></a>';
1777
+ }
1778
+ if ( ctf_show( 'twitterlink', $feed_options ) ) {
1779
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-twitterlink" style="' . $feed_options['textcolor'] . '" target="_blank" rel="noopener noreferrer">' . esc_html( $feed_options['twitterlinktext'] ) . ' <span class="ctf-screenreader">' . $post['id_str'] . '</span></a>';
1780
+ } // show twitter link or actions
1781
+ $tweet_html .= '</div>';
1782
+ $tweet_html .= '</div>';
1783
+
1784
+ $i++;
1785
+ }
1786
+ }
1787
+ return $tweet_html;
1788
+ }
1789
+
1790
+ /**
1791
+ * displays a message if there is an error in the feed
1792
+ *
1793
+ * @return string error html
1794
+ */
1795
+ public function getErrorHtml()
1796
+ {
1797
+ $error_html = '';
1798
+ $error_html .= '<div id="ctf" class="ctf" data-ctfshortcode="' . $this->getShortCodeJSON() . '">';
1799
+ $error_html .= '<div class="ctf-error">';
1800
+ $error_html .= '<div class="ctf-error-user">';
1801
+
1802
+ $error_html .= '</div>';
1803
+
1804
+ if ( current_user_can( 'manage_options' ) ) {
1805
+ $error_html .= '<div class="ctf-error-admin">';
1806
+
1807
+ if ( ! empty( $this->api_obj->api_error_no ) ) {
1808
+
1809
+ $error_html .= '<p>Unable to load Tweets</p>';
1810
+ $error_html .= '<a class="twitter-share-button"';
1811
+ $error_html .= 'href="https://twitter.com/share"';
1812
+ $error_html .= 'data-size="large"';
1813
+ $error_html .= 'data-url="' . get_the_permalink() . '"';
1814
+ $error_html .= 'data-text="Check out this website">';
1815
+ $error_html .= '</a>';
1816
+
1817
+ if ( !empty( $this->feed_options['screenname'] ) ) {
1818
+ $error_html .= '<a class="twitter-follow-button"';
1819
+ $error_html .= 'href="https://twitter.com/' . $this->feed_options['screenname'] . '"';
1820
+ $error_html .= 'data-show-count="false"';
1821
+ $error_html .= 'data-size="large"';
1822
+ $error_html .= 'data-dnt="true">Follow</a>';
1823
+ }
1824
+
1825
+ $error_html .= '<p><b>This message is only visible to admins:</b><br />';
1826
+ $error_html .= 'An error has occurred with your feed.<br />';
1827
+ if ( $this->missing_credentials ) {
1828
+ $error_html .= 'There is a problem with your access token, access token secret, consumer token, or consumer secret<br />';
1829
+ }
1830
+ if ( isset( $this->errors['error_message'] ) ) {
1831
+ $error_html .= $this->errors['error_message'] . '<br />';
1832
+ }
1833
+ $error_html .= 'The error response from the Twitter API is the following:<br />';
1834
+ $error_html .= '<code>Error number: ' . $this->api_obj->api_error_no . '<br />';
1835
+ $error_html .= 'Message: ' . $this->api_obj->api_error_message . '</code>';
1836
+ $error_html .= '<a href="https://smashballoon.com/custom-twitter-feeds/docs/errors/?utm_campaign=twitter-free&utm_source=frontend&utm_medium=errormessage" target="_blank" rel="noopener noreferrer">Click here to troubleshoot</a></p>';
1837
+
1838
+
1839
+ }
1840
+
1841
+ $error_html .= '</div>';
1842
+ }
1843
+ $error_html .= '</div>'; // end .ctf-error
1844
+ $error_html .= '</div>'; // end #ctf
1845
+
1846
+ return $error_html;
1847
+ }
1848
  }
inc/CtfOauthConnect.php CHANGED
@@ -1,295 +1,295 @@
1
- <?php
2
- /**
3
- * Class OauthConnect
4
- *
5
- * Simple, lightweight class to make a connection to the Twitter API
6
- * Supports home timeline, user timeline, and search endpoints
7
- */
8
-
9
- // Don't load directly
10
- if ( ! defined( 'ABSPATH' ) ) {
11
- die( '-1' );
12
- }
13
-
14
- class CtfOauthConnect
15
- {
16
- /**
17
- * @var string
18
- */
19
- protected $base_url;
20
-
21
- /**
22
- * @var string
23
- */
24
- private $get_fields;
25
-
26
- /**
27
- * @var string
28
- */
29
- private $request_method;
30
-
31
- /**
32
- * @var array
33
- */
34
- private $oauth;
35
-
36
- /**
37
- * @var string
38
- */
39
- private $header;
40
-
41
- /**
42
- * @var bool
43
- */
44
- public $api_error_no = false;
45
-
46
- /**
47
- * @var bool
48
- */
49
- public $api_error_message = false;
50
-
51
- /**
52
- * @var string
53
- */
54
- public $json;
55
-
56
- /**
57
- * @param array $request_settings all necessary tokens for OAuth connection
58
- * @param $feed_type string type of Twitter feed
59
- */
60
- public function __construct( array $request_settings, $feed_type )
61
- {
62
- $this->consumer_key = $request_settings['consumer_key'];
63
- $this->consumer_secret = $request_settings['consumer_secret'];
64
- $this->access_token = $request_settings['access_token'];
65
- $this->access_token_secret = $request_settings['access_token_secret'];
66
- $this->feed_type = $feed_type;
67
- }
68
-
69
- /**
70
- * Sets the complete url for our API endpoint. GET fields will be added later
71
- */
72
- public function setUrlBase()
73
- {
74
- switch ( $this->feed_type ) {
75
- case "hometimeline":
76
- $this->base_url = 'https://api.twitter.com/1.1/statuses/home_timeline.json';
77
- break;
78
- case "search":
79
- $this->base_url = 'https://api.twitter.com/1.1/search/tweets.json';
80
- break;
81
- default:
82
- $this->base_url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
83
- }
84
- }
85
-
86
- /**
87
- * Encodes an array of GET field data into html characters for including in a URL
88
- *
89
- * @param array $get_fields array of GET fields that are compatible with the Twitter API
90
- */
91
- public function setGetFields( array $get_fields )
92
- {
93
- $url_string = '?';
94
- $length = count( $get_fields );
95
- $j = 1;
96
- foreach ( $get_fields as $key => $value ) {
97
- $url_string .= rawurlencode( $key ) . '=' . rawurlencode( $value );
98
- if ( $j != $length ) {
99
- $url_string .= '&';
100
- }
101
- $j++;
102
- }
103
-
104
- $this->get_fields = $url_string;
105
- }
106
-
107
- /**
108
- * Users can manually set the request method if there is an uncatchable error in
109
- * the other methods
110
- *
111
- * @param string $request_method
112
- */
113
- public function setRequestMethod( $request_method = 'auto' )
114
- {
115
- $this->request_method = $request_method;
116
- }
117
-
118
- /**
119
- * Uses the OAuth data to build the base string needed to create the
120
- * OAuth signature to be used in the header of the request
121
- *
122
- * @param $oauth array oauth data without the signature
123
- * @return string the base string for needed to construct the oauth signature
124
- */
125
- private function buildBaseString( $oauth )
126
- {
127
- $base_string = array();
128
- ksort( $oauth );
129
-
130
- // start forming the header string by creating a numeric index array with
131
- // each part of the header string it's own element in the array
132
- foreach ( $oauth as $key => $value ) {
133
- $base_string[] = rawurlencode( $key ) . '=' . rawurlencode( $value );
134
- }
135
-
136
- // convert the array of values into a single encoded string and return
137
- return 'GET&' . rawurlencode( $this->base_url ) . '&' . rawurlencode( implode( '&', $base_string ) );
138
- }
139
-
140
- /**
141
- * Builds the OAuth data array that is used to authenticate the connection
142
- * to the Twitter API
143
- */
144
- public function buildOauth()
145
- {
146
- $oauth = array(
147
- 'oauth_consumer_key' => $this->consumer_key,
148
- 'oauth_nonce' => time(),
149
- 'oauth_signature_method' => 'HMAC-SHA1',
150
- 'oauth_token' => $this->access_token,
151
- 'oauth_timestamp' => time(),
152
- 'oauth_version' => '1.0'
153
- );
154
-
155
- $getfields = str_replace( '?', '', explode( '&', $this->get_fields ) );
156
-
157
- // add the get fields to the oauth associative array to be
158
- // formed into the header string eventually
159
- foreach ( $getfields as $getfield ) {
160
- $split = explode( '=', $getfield );
161
-
162
- if ( isset( $split[1] ) ) {
163
- $oauth[$split[0]] = urldecode( $split[1] );
164
- }
165
- }
166
-
167
- // the OAuth signature for Twitter is a hashed, encoded version of the base url, 4 different keys
168
- $base_string = $this->buildBaseString( $oauth );
169
- $composite_key = rawurlencode( $this->consumer_secret ) . '&' . rawurlencode( $this->access_token_secret );
170
- $oauth_signature = base64_encode( hash_hmac( 'sha1', $base_string, $composite_key, true ) );
171
- $oauth['oauth_signature'] = $oauth_signature;
172
-
173
- $this->oauth = $oauth;
174
- }
175
-
176
- /**
177
- * Since the OAuth data is passed in a url, special characters need to be encoded
178
- */
179
- private function encodeHeader()
180
- {
181
- $header = 'Authorization: OAuth ';
182
- $values = array();
183
-
184
- // each element of the header needs to have it's special characters encoded for
185
- // passing through a url
186
- foreach ( $this->oauth as $key => $value ) {
187
- if ( in_array( $key, array( 'oauth_consumer_key', 'oauth_nonce', 'oauth_signature',
188
- 'oauth_signature_method', 'oauth_timestamp', 'oauth_token', 'oauth_version' ) ) ){
189
- $values[] = "$key=\"" . rawurlencode( $value ) . "\"";
190
- }
191
- }
192
-
193
- $header .= implode( ', ', $values );
194
- $this->header = $header;
195
- }
196
-
197
- /**
198
- * Attempts to connect to the Twitter api using curl
199
- *
200
- * @param $url string the complete api endpoint url
201
- * @return mixed json string retrieved in the request
202
- */
203
- private function curlRequest( $url )
204
- {
205
- $br = curl_init( $url );
206
-
207
- curl_setopt( $br, CURLOPT_HTTPHEADER, array( $this->header ) ); // must pass in array
208
- curl_setopt( $br, CURLOPT_URL, $url );
209
- curl_setopt( $br, CURLOPT_RETURNTRANSFER, true );
210
- curl_setopt( $br, CURLOPT_TIMEOUT, 10 );
211
- curl_setopt( $br, CURLOPT_SSL_VERIFYPEER, false ); // must be false to connect without signed certificate
212
- curl_setopt( $br, CURLOPT_ENCODING, '' );
213
-
214
- $json = curl_exec( $br );
215
-
216
- if ( curl_errno( $br ) ){
217
- $this->api_error_no = curl_errno( $br );
218
- $this->api_error_message = curl_error( $br );
219
- }
220
-
221
- curl_close( $br );
222
-
223
- return $json;
224
- }
225
-
226
- /**
227
- * Attempts to connect to the Twitter api using file get contents
228
- *
229
- * @param $url string the complete api endpoint url
230
- * @return mixed json string retrieved in the request
231
- */
232
- public function fileGetContentsRequest( $url )
233
- {
234
- $opts = array(
235
- 'http' => array(
236
- 'method' => 'GET',
237
- 'header' => $this->header
238
- )
239
- );
240
-
241
- $context = stream_context_create( $opts );
242
-
243
- return file_get_contents( $url, false, $context );
244
- }
245
-
246
- /**
247
- * Attempts to connect to the Twitter api using WP_HTTP class
248
- *
249
- * @param $url string the complete api endpoint url
250
- * @return mixed json string retrieved in the request
251
- */
252
- private function wpHttpRequest( $url )
253
- {
254
- $args = array(
255
- 'headers' => $this->header,
256
- 'timeout' => 60,
257
- 'sslverify' => false
258
- );
259
- $result = wp_remote_get( $url, $args );
260
-
261
- if ( ! is_wp_error( $result ) ) {
262
- return $result['body']; // just need the body to keep everything simple
263
- } else {
264
- return '{}';
265
- }
266
-
267
- }
268
-
269
- /**
270
- * Uses the data created and gathered up to this point to make the actual connection
271
- * to the Twitter API. It first tests whether or not a curl connection is possible,
272
- * followed by file_get_contents connection, then defaults to the WordPress WP_HTTP object
273
- *
274
- * @return mixed|string raw json data retrieved from the API request
275
- */
276
- public function performRequest()
277
- {
278
- $url = $this->base_url . $this->get_fields;
279
- $this->buildOauth();
280
- $this->encodeHeader();
281
-
282
- switch ( $this->request_method ) {
283
- case 'curl':
284
- $this->json = $this->curlRequest( $url );
285
- break;
286
- case 'file_get_contents':
287
- $this->json = $this->fileGetContentsRequest( $url );
288
- break;
289
- default:
290
- $this->json = $this->wpHttpRequest( $url );
291
- }
292
-
293
- return $this;
294
- }
295
  }
1
+ <?php
2
+ /**
3
+ * Class OauthConnect
4
+ *
5
+ * Simple, lightweight class to make a connection to the Twitter API
6
+ * Supports home timeline, user timeline, and search endpoints
7
+ */
8
+
9
+ // Don't load directly
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ die( '-1' );
12
+ }
13
+
14
+ class CtfOauthConnect
15
+ {
16
+ /**
17
+ * @var string
18
+ */
19
+ protected $base_url;
20
+
21
+ /**
22
+ * @var string
23
+ */
24
+ private $get_fields;
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ private $request_method;
30
+
31
+ /**
32
+ * @var array
33
+ */
34
+ private $oauth;
35
+
36
+ /**
37
+ * @var string
38
+ */
39
+ private $header;
40
+
41
+ /**
42
+ * @var bool
43
+ */
44
+ public $api_error_no = false;
45
+
46
+ /**
47
+ * @var bool
48
+ */
49
+ public $api_error_message = false;
50
+
51
+ /**
52
+ * @var string
53
+ */
54
+ public $json;
55
+
56
+ /**
57
+ * @param array $request_settings all necessary tokens for OAuth connection
58
+ * @param $feed_type string type of Twitter feed
59
+ */
60
+ public function __construct( array $request_settings, $feed_type )
61
+ {
62
+ $this->consumer_key = $request_settings['consumer_key'];
63
+ $this->consumer_secret = $request_settings['consumer_secret'];
64
+ $this->access_token = $request_settings['access_token'];
65
+ $this->access_token_secret = $request_settings['access_token_secret'];
66
+ $this->feed_type = $feed_type;
67
+ }
68
+
69
+ /**
70
+ * Sets the complete url for our API endpoint. GET fields will be added later
71
+ */
72
+ public function setUrlBase()
73
+ {
74
+ switch ( $this->feed_type ) {
75
+ case "hometimeline":
76
+ $this->base_url = 'https://api.twitter.com/1.1/statuses/home_timeline.json';
77
+ break;
78
+ case "search":
79
+ $this->base_url = 'https://api.twitter.com/1.1/search/tweets.json';
80
+ break;
81
+ default:
82
+ $this->base_url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Encodes an array of GET field data into html characters for including in a URL
88
+ *
89
+ * @param array $get_fields array of GET fields that are compatible with the Twitter API
90
+ */
91
+ public function setGetFields( array $get_fields )
92
+ {
93
+ $url_string = '?';
94
+ $length = count( $get_fields );
95
+ $j = 1;
96
+ foreach ( $get_fields as $key => $value ) {
97
+ $url_string .= rawurlencode( $key ) . '=' . rawurlencode( $value );
98
+ if ( $j != $length ) {
99
+ $url_string .= '&';
100
+ }
101
+ $j++;
102
+ }
103
+
104
+ $this->get_fields = $url_string;
105
+ }
106
+
107
+ /**
108
+ * Users can manually set the request method if there is an uncatchable error in
109
+ * the other methods
110
+ *
111
+ * @param string $request_method
112
+ */
113
+ public function setRequestMethod( $request_method = 'auto' )
114
+ {
115
+ $this->request_method = $request_method;
116
+ }
117
+
118
+ /**
119
+ * Uses the OAuth data to build the base string needed to create the
120
+ * OAuth signature to be used in the header of the request
121
+ *
122
+ * @param $oauth array oauth data without the signature
123
+ * @return string the base string for needed to construct the oauth signature
124
+ */
125
+ private function buildBaseString( $oauth )
126
+ {
127
+ $base_string = array();
128
+ ksort( $oauth );
129
+
130
+ // start forming the header string by creating a numeric index array with
131
+ // each part of the header string it's own element in the array
132
+ foreach ( $oauth as $key => $value ) {
133
+ $base_string[] = rawurlencode( $key ) . '=' . rawurlencode( $value );
134
+ }
135
+
136
+ // convert the array of values into a single encoded string and return
137
+ return 'GET&' . rawurlencode( $this->base_url ) . '&' . rawurlencode( implode( '&', $base_string ) );
138
+ }
139
+
140
+ /**
141
+ * Builds the OAuth data array that is used to authenticate the connection
142
+ * to the Twitter API
143
+ */
144
+ public function buildOauth()
145
+ {
146
+ $oauth = array(
147
+ 'oauth_consumer_key' => $this->consumer_key,
148
+ 'oauth_nonce' => time(),
149
+ 'oauth_signature_method' => 'HMAC-SHA1',
150
+ 'oauth_token' => $this->access_token,
151
+ 'oauth_timestamp' => time(),
152
+ 'oauth_version' => '1.0'
153
+ );
154
+
155
+ $getfields = str_replace( '?', '', explode( '&', $this->get_fields ) );
156
+
157
+ // add the get fields to the oauth associative array to be
158
+ // formed into the header string eventually
159
+ foreach ( $getfields as $getfield ) {
160
+ $split = explode( '=', $getfield );
161
+
162
+ if ( isset( $split[1] ) ) {
163
+ $oauth[$split[0]] = urldecode( $split[1] );
164
+ }
165
+ }
166
+
167
+ // the OAuth signature for Twitter is a hashed, encoded version of the base url, 4 different keys
168
+ $base_string = $this->buildBaseString( $oauth );
169
+ $composite_key = rawurlencode( $this->consumer_secret ) . '&' . rawurlencode( $this->access_token_secret );
170
+ $oauth_signature = base64_encode( hash_hmac( 'sha1', $base_string, $composite_key, true ) );
171
+ $oauth['oauth_signature'] = $oauth_signature;
172
+
173
+ $this->oauth = $oauth;
174
+ }
175
+
176
+ /**
177
+ * Since the OAuth data is passed in a url, special characters need to be encoded
178
+ */
179
+ private function encodeHeader()
180
+ {
181
+ $header = 'Authorization: OAuth ';
182
+ $values = array();
183
+
184
+ // each element of the header needs to have it's special characters encoded for
185
+ // passing through a url
186
+ foreach ( $this->oauth as $key => $value ) {
187
+ if ( in_array( $key, array( 'oauth_consumer_key', 'oauth_nonce', 'oauth_signature',
188
+ 'oauth_signature_method', 'oauth_timestamp', 'oauth_token', 'oauth_version' ) ) ){
189
+ $values[] = "$key=\"" . rawurlencode( $value ) . "\"";
190
+ }
191
+ }
192
+
193
+ $header .= implode( ', ', $values );
194
+ $this->header = $header;
195
+ }
196
+
197
+ /**
198
+ * Attempts to connect to the Twitter api using curl
199
+ *
200
+ * @param $url string the complete api endpoint url
201
+ * @return mixed json string retrieved in the request
202
+ */
203
+ private function curlRequest( $url )
204
+ {
205
+ $br = curl_init( $url );
206
+
207
+ curl_setopt( $br, CURLOPT_HTTPHEADER, array( $this->header ) ); // must pass in array
208
+ curl_setopt( $br, CURLOPT_URL, $url );
209
+ curl_setopt( $br, CURLOPT_RETURNTRANSFER, true );
210
+ curl_setopt( $br, CURLOPT_TIMEOUT, 10 );
211
+ curl_setopt( $br, CURLOPT_SSL_VERIFYPEER, false ); // must be false to connect without signed certificate
212
+ curl_setopt( $br, CURLOPT_ENCODING, '' );
213
+
214
+ $json = curl_exec( $br );
215
+
216
+ if ( curl_errno( $br ) ){
217
+ $this->api_error_no = curl_errno( $br );
218
+ $this->api_error_message = curl_error( $br );
219
+ }
220
+
221
+ curl_close( $br );
222
+
223
+ return $json;
224
+ }
225
+
226
+ /**
227
+ * Attempts to connect to the Twitter api using file get contents
228
+ *
229
+ * @param $url string the complete api endpoint url
230
+ * @return mixed json string retrieved in the request
231
+ */
232
+ public function fileGetContentsRequest( $url )
233
+ {
234
+ $opts = array(
235
+ 'http' => array(
236
+ 'method' => 'GET',
237
+ 'header' => $this->header
238
+ )
239
+ );
240
+
241
+ $context = stream_context_create( $opts );
242
+
243
+ return file_get_contents( $url, false, $context );
244
+ }
245
+
246
+ /**
247
+ * Attempts to connect to the Twitter api using WP_HTTP class
248
+ *
249
+ * @param $url string the complete api endpoint url
250
+ * @return mixed json string retrieved in the request
251
+ */
252
+ private function wpHttpRequest( $url )
253
+ {
254
+ $args = array(
255
+ 'headers' => $this->header,
256
+ 'timeout' => 60,
257
+ 'sslverify' => false
258
+ );
259
+ $result = wp_remote_get( $url, $args );
260
+
261
+ if ( ! is_wp_error( $result ) ) {
262
+ return $result['body']; // just need the body to keep everything simple
263
+ } else {
264
+ return '{}';
265
+ }
266
+
267
+ }
268
+
269
+ /**
270
+ * Uses the data created and gathered up to this point to make the actual connection
271
+ * to the Twitter API. It first tests whether or not a curl connection is possible,
272
+ * followed by file_get_contents connection, then defaults to the WordPress WP_HTTP object
273
+ *
274
+ * @return mixed|string raw json data retrieved from the API request
275
+ */
276
+ public function performRequest()
277
+ {
278
+ $url = $this->base_url . $this->get_fields;
279
+ $this->buildOauth();
280
+ $this->encodeHeader();
281
+
282
+ switch ( $this->request_method ) {
283
+ case 'curl':
284
+ $this->json = $this->curlRequest( $url );
285
+ break;
286
+ case 'file_get_contents':
287
+ $this->json = $this->fileGetContentsRequest( $url );
288
+ break;
289
+ default:
290
+ $this->json = $this->wpHttpRequest( $url );
291
+ }
292
+
293
+ return $this;
294
+ }
295
  }
inc/admin-hooks.php CHANGED
@@ -1,407 +1,407 @@
1
- <?php
2
- add_filter( 'ctf_admin_search_label', 'ctf_return_string_hashtag' );
3
- function ctf_return_string_hashtag( $val ) {
4
- return 'Hashtag:';
5
- }
6
-
7
- add_filter( 'ctf_admin_search_whatis', 'ctf_return_string_instructions' );
8
- function ctf_return_string_instructions( $val ) {
9
- return 'Select this option and enter any single hashtag for a hashtag feed. Only tweets made within the last 7 days are available initially. Once a tweet has been retrieved the plugin will keep it in a persistent cache indefinitely';
10
- }
11
-
12
- add_filter( 'ctf_admin_validate_search_text', 'ctf_validate_search_text', 10, 1 );
13
- function ctf_validate_search_text( $val ) {
14
- preg_match( "/^[\p{L}0-9_]+|^#+[\p{L}0-9_]+/u", trim( $val ), $hashtags );
15
-
16
- $hashtags = preg_replace( "/#{2,}/", '', $hashtags );
17
-
18
- $new_val = ! empty( $hashtags ) ? $new_val = $hashtags[0] : '';
19
-
20
- if ( substr( $new_val, 0, 1 ) != '#' && $new_val != '' ) {
21
- $new_val = '#' . $new_val;
22
- }
23
-
24
- return $new_val;
25
- }
26
-
27
- add_filter( 'ctf_admin_validate_usertimeline_text', 'ctf_validate_usertimeline_text', 10, 1 );
28
- function ctf_validate_usertimeline_text( $val ) {
29
- preg_match( "/^[\p{L}0-9_]{1,16}/u" , str_replace( '@', '', trim( $val ) ), $screenname );
30
-
31
- $new_val = isset( $screenname[0] ) ? $screenname[0] : '';
32
-
33
- return $new_val;
34
- }
35
-
36
- add_filter( 'ctf_admin_validate_include_replies', 'ctf_validate_include_replies', 10, 1 );
37
- function ctf_validate_include_replies( $val ) {
38
- return false;
39
- }
40
-
41
- add_filter( 'ctf_admin_set_include_replies', 'ctf_set_include_replies', 10, 1 );
42
- function ctf_set_include_replies( $new_input ) {
43
- return false;
44
- }
45
-
46
- add_filter( 'ctf_admin_feed_type_list', 'ctf_return_feed_types' );
47
- function ctf_return_feed_types( $val ) {
48
- return array( 'hometimelineinclude_replies', 'usertimelineinclude_replies' );
49
- }
50
-
51
- add_action( 'ctf_admin_upgrade_note', 'ctf_update_note' );
52
- function ctf_update_note() {
53
- ?>
54
- <span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=proonly" target="_blank">Available in Pro version</a></span>
55
- <?php
56
- }
57
-
58
- add_action( 'ctf_admin_feed_settings_radio_extra', 'ctf_usertimeline_error_message' );
59
- function ctf_usertimeline_error_message( $args )
60
- { //sbi_notice sbi_user_id_error
61
- if ( $args['name'] == 'usertimeline') : ?>
62
- <div class="ctf_error_notice ctf_usertimeline_error">
63
- <?php _e( "<p>Please use a single screenname or Twitter handle of numbers and letters. If you would like to use more than one screen name for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multiuser' target='_blank'>Pro version</a>.</p>" ); ?>
64
- </div>
65
- <?php endif;
66
- }
67
-
68
- add_action( 'ctf_admin_feed_settings_search_extra', 'ctf_hashtag_error_message' );
69
- function ctf_hashtag_error_message() {
70
- ?>
71
- <div class="ctf_error_notice ctf_search_error">
72
- <?php _e( "<p>Please use a single hashtag of numbers and letters. If you would like to use more than one hashtag or use search terms for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multisearch' target='_blank'>Pro version</a>.</p>" ); ?>
73
- </div>
74
- <?php
75
- }
76
-
77
- add_filter( 'ctf_admin_customize_quick_links', 'ctf_return_customize_quick_links' );
78
- function ctf_return_customize_quick_links() {
79
- return array(
80
- 0 => array( 'general', 'General' ),
81
- 1 => array( 'showhide', 'Show/Hide' ),
82
- 2 => array( 'misc', 'Misc' ),
83
- 3 => array( 'advanced', 'Advanced' )
84
- );
85
- }
86
-
87
- add_filter( 'ctf_admin_style_quick_links', 'ctf_return_style_quick_links' );
88
- function ctf_return_style_quick_links() {
89
- return array(
90
- 0 => array( 'general', 'General' ),
91
- 1 => array( 'header', 'Header' ),
92
- 2 => array( 'date', 'Date' ),
93
- 3 => array( 'author', 'Author' ),
94
- 4 => array( 'text', 'Tweet Text' ),
95
- 5 => array( 'links', 'Links' ),
96
- 6 => array( 'quoted', 'Retweet Boxes' ),
97
- 7 => array( 'actions', 'Tweet Actions' ),
98
- 8 => array( 'load', 'Load More' )
99
- );
100
- }
101
-
102
- /*
103
- * Pro Options ----------------------------------------
104
- */
105
-
106
- add_action( 'ctf_admin_endpoints', 'ctf_add_mentionstimeline_options', 10, 1 );
107
- function ctf_add_mentionstimeline_options( $admin ) {
108
- $admin->create_settings_field( array(
109
- 'name' => 'search_pro',
110
- 'title' => '<label></label>', // label for the input field
111
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
112
- 'page' => 'ctf_options_feed_settings', // matches the section name
113
- 'section' => 'ctf_options_feed_settings', // matches the section name
114
- 'option' => 'ctf_options', // matches the options name
115
- 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
116
- 'whatis' => 'You can create search feeds which contain a large variety of different terms and operators, such as a combination of #hashtags, @mentions, words, or "phrases"', // what is this? text
117
- 'label' => "Search",
118
- 'has_input' => false,
119
- 'has_replies' => false
120
- ));
121
- $admin->create_settings_field( array(
122
- 'name' => 'mentionstimeline',
123
- 'title' => '<label></label>', // label for the input field
124
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
125
- 'page' => 'ctf_options_feed_settings', // matches the section name
126
- 'section' => 'ctf_options_feed_settings', // matches the section name
127
- 'option' => 'ctf_options', // matches the options name
128
- 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
129
- 'whatis' => 'Select this option to display tweets that @mention your twitter handle', // what is this? text
130
- 'label' => "Mentions",
131
- 'has_input' => false,
132
- 'has_replies' => false
133
- ));
134
- $admin->create_settings_field( array(
135
- 'name' => 'lists',
136
- 'title' => '<label></label>', // label for the input field
137
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
138
- 'page' => 'ctf_options_feed_settings', // matches the section name
139
- 'section' => 'ctf_options_feed_settings', // matches the section name
140
- 'option' => 'ctf_options', // matches the options name
141
- 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
142
- 'whatis' => 'Enter the list ID of the list(s) you want to display. Use this FAQ to create a list on Twitter. Use the helper to find IDs', // what is this? text
143
- 'label' => "Lists",
144
- 'has_input' => false,
145
- 'has_replies' => false
146
- ));
147
- }
148
-
149
- add_filter( 'ctf_admin_show_hide_list', 'ctf_show_hide_list', 10, 1 );
150
- function ctf_show_hide_list( $show_hide_list ) {
151
- $show_hide_list[] = array( 'include_replied_to', 'In reply to text' );
152
- $show_hide_list[] = array( 'include_media', 'Media (images, videos, gifs)' );
153
- $show_hide_list[] = array( 'include_twittercards', 'Twitter Cards' );
154
- return $show_hide_list;
155
- }
156
-
157
- function ctf_pro_autoscroll_section() {
158
- ?>
159
- <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=autoscroll" target="_blank">Upgrade to Pro to enable Autoscroll loading</a></p>
160
- <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
161
-
162
- <div class="ctf-pro-options">
163
- <table class="form-table"><tbody><tr><th scope="row"><label for="ctf_autoscroll" title="Click for shortcode option">Set Load More on Scroll as Default</label><code class="ctf_shortcode">autoscroll
164
- Eg: autoscroll=true</code></th><td> <input name="ctf_options[autoscroll]" id="ctf_autoscroll" type="checkbox" disabled>
165
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
166
- <p class="ctf-tooltip ctf-more-info">This will make every Twitter feed load more Tweets as the user gets to the bottom of the feed.</p>
167
- </td></tr><tr class="default-text"><th scope="row"><label for="ctf_autoscrolldistance">Auto Scroll Trigger Distance</label><code class="ctf_shortcode">autoscrolldistance
168
- Eg: autoscrolldistance=2</code></th><td> <input name="ctf_options[autoscrolldistance]" id="ctf_autoscrolldistance" class="default-text" type="text" value="200" disabled>
169
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
170
- <p class="ctf-tooltip ctf-more-info">This is the distance in pixels from the bottom of the page the user must scroll to to trigger the loading of more tweets.</p>
171
- </td></tr></tbody></table>
172
- </div>
173
- <div style="height: 18px;"></div>
174
- <?php
175
- }
176
-
177
- function ctf_pro_moderation_section() {
178
- ?>
179
- <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=moderation" target="_blank">Upgrade to Pro to enable Tweet moderation</a></p>
180
- <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
181
-
182
- <div class="ctf-pro-options">
183
- <table class="form-table"><tbody><tr class="large-text"><th scope="row"><label for="ctf_includewords" title="Click for shortcode option">Show Tweets containing these words or hashtags</label><code class="ctf_shortcode">includewords
184
- Eg: includewords="#puppy,#cute"</code></th><td> <input name="ctf_options[includewords]" id="ctf_includewords" class="large-text" type="text" value="" disabled>
185
- <span>"includewords" separate words by comma</span>
186
- </td></tr><tr class="large-text"><th scope="row"><label for="ctf_excludewords">Remove Tweets containing these words or hashtags</label><code class="ctf_shortcode">excludewords
187
- Eg: excludewords="#ugly,#bad"</code></th><td> <input name="ctf_options[excludewords]" id="ctf_excludewords" class="large-text" type="text" value="" disabled>
188
- <span>"excludewords" separate words by comma</span>
189
- </td></tr><tr><th scope="row"></th><td> <p>Show Tweets that contain
190
- <select name="ctf_options[includeanyall]" id="ctf_includeanyall" disabled>
191
- <option value="any" selected="selected">any</option>
192
- <option value="all">all</option>
193
- </select>
194
- of the "includewords"
195
- <select name="ctf_options[filterandor]" id="ctf_filterandor" disabled>
196
- <option value="and" selected="selected">and</option>
197
- <option value="or">or</option>
198
- </select>
199
- do not contain
200
- <select name="ctf_options[excludeanyall]" id="ctf_excludeanyall" disabled>
201
- <option value="any" selected="selected">any</option>
202
- <option value="all">all</option>
203
- </select>
204
- of the "excludewords"
205
- </p>
206
- </td></tr><tr><th scope="row"><label for="ctf_remove_by_id">Hide Specific Tweets</label></th><td> <textarea name="ctf_options[remove_by_id]" id="ctf_remove_by_id" style="width: 70%;" rows="3" disabled></textarea>
207
- <p>separate IDs by comma <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
208
- <span class="ctf-tooltip ctf-more-info">These are the specific ID numbers associated with a tweet. You can find the ID of a Tweet by viewing the Tweet on Twitter and copy/pasting the ID number from the end of the URL.</span>
209
- </p> </td></tr></tbody></table>
210
- </div>
211
- <div style="height: 18px;"></div>
212
- <?php
213
- }
214
-
215
- add_action( 'ctf_admin_style_option', 'ctf_add_masonry_autoscroll_options', 5, 1 );
216
- function ctf_add_masonry_autoscroll_options( $admin ) {
217
- // custom in reply to text
218
- $admin->create_settings_field( array(
219
- 'name' => 'inreplytotext',
220
- 'title' => '<label for="ctf_inreplytotext">Translation for "In reply to"</label><code class="ctf_shortcode">inreplytotext
221
- Eg: inreplytotext="Als Antwort an"</code>', // label for the input field
222
- 'callback' => 'default_text', // name of the function that outputs the html
223
- 'page' => 'ctf_options_text', // matches the section name
224
- 'section' => 'ctf_options_text', // matches the section name
225
- 'option' => 'ctf_options', // matches the options name
226
- 'class' => 'default-text ctf_pro', // class for the wrapper and input field
227
- 'whatis' => 'This will replace the default text displayed for "In reply to"',
228
- 'default' => 'In reply to'// "what is this?" text
229
- ) );
230
-
231
- add_settings_section(
232
- 'ctf_options_autoscroll', // matches the section name
233
- '<span class="ctf_pro_header">Autoscroll Loading</span>',
234
- 'ctf_pro_autoscroll_section', // callback function to explain the section
235
- 'ctf_options_autoscroll' // matches the section name
236
- );
237
-
238
- add_settings_section(
239
- 'ctf_options_filter', // matches the section name
240
- '<span class="ctf_pro_header">Moderation</span>',
241
- 'ctf_pro_moderation_section', // callback function to explain the section
242
- 'ctf_options_filter' // matches the section name
243
- );
244
- }
245
-
246
- add_action( 'ctf_admin_customize_option', 'ctf_add_customize_general_options', 20, 1 );
247
- function ctf_add_customize_general_options( $admin ) {
248
-
249
- // Disable the lightbox
250
- $admin->create_settings_field( array(
251
- 'name' => 'disablelightbox',
252
- 'title' => '<label for="ctf_disablelightbox">Disable the lightbox</label><code class="ctf_shortcode">disablelightbox
253
- Eg: disablelightbox=true</code>', // label for the input field
254
- 'callback' => 'default_checkbox', // name of the function that outputs the html
255
- 'page' => 'ctf_options_general', // matches the section name
256
- 'section' => 'ctf_options_general', // matches the section name
257
- 'option' => 'ctf_options', // matches the options name
258
- 'class' => 'default-text ctf_pro', // class for the wrapper and input field
259
- 'whatis' => 'Disable the popup lightbox for media in the feed'
260
- ) );
261
- }
262
-
263
-
264
- add_action( 'ctf_admin_customize_option', 'ctf_add_filter_options', 10, 1 );
265
- function ctf_add_filter_options( $admin ) {
266
-
267
- add_settings_field(
268
- 'clear_tc_cache_button',
269
- '<label for="ctf_clear_tc_cache_button">Clear Twitter Card Cache</label>',
270
- 'ctf_clear_tc_cache_button',
271
- 'ctf_options_advanced',
272
- 'ctf_options_advanced',
273
- array( 'class' => 'ctf_pro')
274
- );
275
- }
276
-
277
- function ctf_remove_by_id( $args ) {
278
- $options = get_option( $args['option'] );
279
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
280
- ?>
281
- <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="3"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
282
- <?php if ( isset( $args['extra'] ) ) : ?><p><?php _e( $args['extra'], 'custom-twitter-feeds' ); ?>
283
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
284
- <span class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</span>
285
- </p> <?php endif; ?>
286
- <?php
287
- }
288
-
289
- function ctf_clear_tc_cache_button() {
290
- ?>
291
- <input id="ctf-clear-tc-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cards' ); ?>" />
292
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
293
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your links that have Twitter Cards', 'custom-twitter-feeds' ); ?>.</p>
294
- <?php
295
- }
296
-
297
- function ctf_filter_operator( $args ) {
298
- $options = get_option( $args['option'] );
299
- $include_any_all = ( isset( $options['includeanyall'] ) ) ? esc_attr( $options['includeanyall'] ) : 'any';
300
- $filter_and_or = ( isset( $options['filterandor'] ) ) ? esc_attr( $options['filterandor'] ) : 'and';
301
- $exclude_any_all = ( isset( $options['excludeanyall'] ) ) ? esc_attr( $options['excludeanyall'] ) : 'any';
302
-
303
- ?>
304
- <p>Show Tweets that contain
305
- <select name="<?php echo $args['option'].'[includeanyall]'; ?>" id="ctf_includeanyall">
306
- <option value="any" <?php if ( $include_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
307
- <option value="all" <?php if ( $include_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
308
- </select>
309
- of the "includewords"
310
- <select name="<?php echo $args['option'].'[filterandor]'; ?>" id="ctf_filterandor">
311
- <option value="and" <?php if ( $filter_and_or == "and" ) echo 'selected="selected"'; ?> ><?php _e('and'); ?></option>
312
- <option value="or" <?php if ( $filter_and_or == "or" ) echo 'selected="selected"'; ?> ><?php _e('or'); ?></option>
313
- </select>
314
- do not contain
315
- <select name="<?php echo $args['option'].'[excludeanyall]'; ?>" id="ctf_excludeanyall">
316
- <option value="any" <?php if ( $exclude_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
317
- <option value="all" <?php if ( $exclude_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
318
- </select>
319
- of the "excludewords"
320
- </p>
321
- <?php if ( isset( $args['whatis'] ) ) : ?>
322
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
323
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
324
- <?php endif; ?>
325
- <?php
326
- }
327
-
328
- add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_masonry_autoload_section_to_customize' );
329
- function ctf_add_masonry_autoload_section_to_customize() {
330
- ?>
331
- <a id="autoscroll"></a>
332
- <?php do_settings_sections( 'ctf_options_autoscroll' ); ?>
333
- <!-- <p class="submit"><input class="button-primary" type="submit" name="save" value="<?php esc_attr_e( 'Save Changes' ); ?>" /></p> -->
334
- <hr>
335
- <?php
336
- }
337
-
338
- add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_filter_section_to_customize' );
339
- function ctf_add_filter_section_to_customize() {
340
- echo '<a id="moderation"></a>';
341
- do_settings_sections( 'ctf_options_filter' ); // matches the section name
342
- echo '<hr>';
343
- }
344
-
345
- function ctf_lite_dismiss() {
346
- $nonce = isset( $_POST['ctf_nonce'] ) ? sanitize_text_field( $_POST['ctf_nonce'] ) : '';
347
-
348
- if ( ! wp_verify_nonce( $nonce, 'ctf-smash-balloon' ) ) {
349
- die ( 'You did not do this the right way!' );
350
- }
351
-
352
- set_transient( 'twitter_feed_dismiss_lite', 'dismiss', 1 * WEEK_IN_SECONDS );
353
-
354
- die();
355
- }
356
- add_action( 'wp_ajax_ctf_lite_dismiss', 'ctf_lite_dismiss' );
357
-
358
- function ctf_admin_hide_unrelated_notices() {
359
-
360
- // Bail if we're not on a ctf screen or page.
361
- if ( ! isset( $_GET['page'] )
362
- || ($_GET['page'] !== 'custom-twitter-feeds' && $_GET['page'] !== 'ctf-sw') ) {
363
- return;
364
- }
365
-
366
- // Extra banned classes and callbacks from third-party plugins.
367
- $blacklist = array(
368
- 'classes' => array(),
369
- 'callbacks' => array(
370
- 'ctfdb_admin_notice', // 'Database for ctf' plugin.
371
- ),
372
- );
373
-
374
- global $wp_filter;
375
-
376
- foreach ( array( 'user_admin_notices', 'admin_notices', 'all_admin_notices' ) as $notices_type ) {
377
- if ( empty( $wp_filter[ $notices_type ]->callbacks ) || ! is_array( $wp_filter[ $notices_type ]->callbacks ) ) {
378
- continue;
379
- }
380
- foreach ( $wp_filter[ $notices_type ]->callbacks as $priority => $hooks ) {
381
- foreach ( $hooks as $name => $arr ) {
382
- if ( is_object( $arr['function'] ) && $arr['function'] instanceof Closure ) {
383
- unset( $wp_filter[ $notices_type ]->callbacks[ $priority ][ $name ] );
384
- continue;
385
- }
386
- $class = ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) ? strtolower( get_class( $arr['function'][0] ) ) : '';
387
- if (
388
- ! empty( $class ) &&
389
- strpos( $class, 'ctf' ) !== false &&
390
- ! in_array( $class, $blacklist['classes'], true )
391
- ) {
392
- continue;
393
- }
394
- if (
395
- ! empty( $name ) && (
396
- strpos( $name, 'ctf' ) === false ||
397
- in_array( $class, $blacklist['classes'], true ) ||
398
- in_array( $name, $blacklist['callbacks'], true )
399
- )
400
- ) {
401
- unset( $wp_filter[ $notices_type ]->callbacks[ $priority ][ $name ] );
402
- }
403
- }
404
- }
405
- }
406
- }
407
  add_action( 'admin_print_scripts', 'ctf_admin_hide_unrelated_notices' );
1
+ <?php
2
+ add_filter( 'ctf_admin_search_label', 'ctf_return_string_hashtag' );
3
+ function ctf_return_string_hashtag( $val ) {
4
+ return 'Hashtag:';
5
+ }
6
+
7
+ add_filter( 'ctf_admin_search_whatis', 'ctf_return_string_instructions' );
8
+ function ctf_return_string_instructions( $val ) {
9
+ return 'Select this option and enter any single hashtag for a hashtag feed. Only tweets made within the last 7 days are available initially. Once a tweet has been retrieved the plugin will keep it in a persistent cache indefinitely';
10
+ }
11
+
12
+ add_filter( 'ctf_admin_validate_search_text', 'ctf_validate_search_text', 10, 1 );
13
+ function ctf_validate_search_text( $val ) {
14
+ preg_match( "/^[\p{L}0-9_]+|^#+[\p{L}0-9_]+/u", trim( $val ), $hashtags );
15
+
16
+ $hashtags = preg_replace( "/#{2,}/", '', $hashtags );
17
+
18
+ $new_val = ! empty( $hashtags ) ? $new_val = $hashtags[0] : '';
19
+
20
+ if ( substr( $new_val, 0, 1 ) != '#' && $new_val != '' ) {
21
+ $new_val = '#' . $new_val;
22
+ }
23
+
24
+ return $new_val;
25
+ }
26
+
27
+ add_filter( 'ctf_admin_validate_usertimeline_text', 'ctf_validate_usertimeline_text', 10, 1 );
28
+ function ctf_validate_usertimeline_text( $val ) {
29
+ preg_match( "/^[\p{L}0-9_]{1,16}/u" , str_replace( '@', '', trim( $val ) ), $screenname );
30
+
31
+ $new_val = isset( $screenname[0] ) ? $screenname[0] : '';
32
+
33
+ return $new_val;
34
+ }
35
+
36
+ add_filter( 'ctf_admin_validate_include_replies', 'ctf_validate_include_replies', 10, 1 );
37
+ function ctf_validate_include_replies( $val ) {
38
+ return false;
39
+ }
40
+
41
+ add_filter( 'ctf_admin_set_include_replies', 'ctf_set_include_replies', 10, 1 );
42
+ function ctf_set_include_replies( $new_input ) {
43
+ return false;
44
+ }
45
+
46
+ add_filter( 'ctf_admin_feed_type_list', 'ctf_return_feed_types' );
47
+ function ctf_return_feed_types( $val ) {
48
+ return array( 'hometimelineinclude_replies', 'usertimelineinclude_replies' );
49
+ }
50
+
51
+ add_action( 'ctf_admin_upgrade_note', 'ctf_update_note' );
52
+ function ctf_update_note() {
53
+ ?>
54
+ <span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=proonly" target="_blank">Available in Pro version</a></span>
55
+ <?php
56
+ }
57
+
58
+ add_action( 'ctf_admin_feed_settings_radio_extra', 'ctf_usertimeline_error_message' );
59
+ function ctf_usertimeline_error_message( $args )
60
+ { //sbi_notice sbi_user_id_error
61
+ if ( $args['name'] == 'usertimeline') : ?>
62
+ <div class="ctf_error_notice ctf_usertimeline_error">
63
+ <?php _e( "<p>Please use a single screenname or Twitter handle of numbers and letters. If you would like to use more than one screen name for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multiuser' target='_blank'>Pro version</a>.</p>" ); ?>
64
+ </div>
65
+ <?php endif;
66
+ }
67
+
68
+ add_action( 'ctf_admin_feed_settings_search_extra', 'ctf_hashtag_error_message' );
69
+ function ctf_hashtag_error_message() {
70
+ ?>
71
+ <div class="ctf_error_notice ctf_search_error">
72
+ <?php _e( "<p>Please use a single hashtag of numbers and letters. If you would like to use more than one hashtag or use search terms for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multisearch' target='_blank'>Pro version</a>.</p>" ); ?>
73
+ </div>
74
+ <?php
75
+ }
76
+
77
+ add_filter( 'ctf_admin_customize_quick_links', 'ctf_return_customize_quick_links' );
78
+ function ctf_return_customize_quick_links() {
79
+ return array(
80
+ 0 => array( 'general', 'General' ),
81
+ 1 => array( 'showhide', 'Show/Hide' ),
82
+ 2 => array( 'misc', 'Misc' ),
83
+ 3 => array( 'advanced', 'Advanced' )
84
+ );
85
+ }
86
+
87
+ add_filter( 'ctf_admin_style_quick_links', 'ctf_return_style_quick_links' );
88
+ function ctf_return_style_quick_links() {
89
+ return array(
90
+ 0 => array( 'general', 'General' ),
91
+ 1 => array( 'header', 'Header' ),
92
+ 2 => array( 'date', 'Date' ),
93
+ 3 => array( 'author', 'Author' ),
94
+ 4 => array( 'text', 'Tweet Text' ),
95
+ 5 => array( 'links', 'Links' ),
96
+ 6 => array( 'quoted', 'Retweet Boxes' ),
97
+ 7 => array( 'actions', 'Tweet Actions' ),
98
+ 8 => array( 'load', 'Load More' )
99
+ );
100
+ }
101
+
102
+ /*
103
+ * Pro Options ----------------------------------------
104
+ */
105
+
106
+ add_action( 'ctf_admin_endpoints', 'ctf_add_mentionstimeline_options', 10, 1 );
107
+ function ctf_add_mentionstimeline_options( $admin ) {
108
+ $admin->create_settings_field( array(
109
+ 'name' => 'search_pro',
110
+ 'title' => '<label></label>', // label for the input field
111
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
112
+ 'page' => 'ctf_options_feed_settings', // matches the section name
113
+ 'section' => 'ctf_options_feed_settings', // matches the section name
114
+ 'option' => 'ctf_options', // matches the options name
115
+ 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
116
+ 'whatis' => 'You can create search feeds which contain a large variety of different terms and operators, such as a combination of #hashtags, @mentions, words, or "phrases"', // what is this? text
117
+ 'label' => "Search",
118
+ 'has_input' => false,
119
+ 'has_replies' => false
120
+ ));
121
+ $admin->create_settings_field( array(
122
+ 'name' => 'mentionstimeline',
123
+ 'title' => '<label></label>', // label for the input field
124
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
125
+ 'page' => 'ctf_options_feed_settings', // matches the section name
126
+ 'section' => 'ctf_options_feed_settings', // matches the section name
127
+ 'option' => 'ctf_options', // matches the options name
128
+ 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
129
+ 'whatis' => 'Select this option to display tweets that @mention your twitter handle', // what is this? text
130
+ 'label' => "Mentions",
131
+ 'has_input' => false,
132
+ 'has_replies' => false
133
+ ));
134
+ $admin->create_settings_field( array(
135
+ 'name' => 'lists',
136
+ 'title' => '<label></label>', // label for the input field
137
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
138
+ 'page' => 'ctf_options_feed_settings', // matches the section name
139
+ 'section' => 'ctf_options_feed_settings', // matches the section name
140
+ 'option' => 'ctf_options', // matches the options name
141
+ 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
142
+ 'whatis' => 'Enter the list ID of the list(s) you want to display. Use this FAQ to create a list on Twitter. Use the helper to find IDs', // what is this? text
143
+ 'label' => "Lists",
144
+ 'has_input' => false,
145
+ 'has_replies' => false
146
+ ));
147
+ }
148
+
149
+ add_filter( 'ctf_admin_show_hide_list', 'ctf_show_hide_list', 10, 1 );
150
+ function ctf_show_hide_list( $show_hide_list ) {
151
+ $show_hide_list[] = array( 'include_replied_to', 'In reply to text' );
152
+ $show_hide_list[] = array( 'include_media', 'Media (images, videos, gifs)' );
153
+ $show_hide_list[] = array( 'include_twittercards', 'Twitter Cards' );
154
+ return $show_hide_list;
155
+ }
156
+
157
+ function ctf_pro_autoscroll_section() {
158
+ ?>
159
+ <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=autoscroll" target="_blank">Upgrade to Pro to enable Autoscroll loading</a></p>
160
+ <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
161
+
162
+ <div class="ctf-pro-options">
163
+ <table class="form-table"><tbody><tr><th scope="row"><label for="ctf_autoscroll" title="Click for shortcode option">Set Load More on Scroll as Default</label><code class="ctf_shortcode">autoscroll
164
+ Eg: autoscroll=true</code></th><td> <input name="ctf_options[autoscroll]" id="ctf_autoscroll" type="checkbox" disabled>
165
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
166
+ <p class="ctf-tooltip ctf-more-info">This will make every Twitter feed load more Tweets as the user gets to the bottom of the feed.</p>
167
+ </td></tr><tr class="default-text"><th scope="row"><label for="ctf_autoscrolldistance">Auto Scroll Trigger Distance</label><code class="ctf_shortcode">autoscrolldistance
168
+ Eg: autoscrolldistance=2</code></th><td> <input name="ctf_options[autoscrolldistance]" id="ctf_autoscrolldistance" class="default-text" type="text" value="200" disabled>
169
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
170
+ <p class="ctf-tooltip ctf-more-info">This is the distance in pixels from the bottom of the page the user must scroll to to trigger the loading of more tweets.</p>
171
+ </td></tr></tbody></table>
172
+ </div>
173
+ <div style="height: 18px;"></div>
174
+ <?php
175
+ }
176
+
177
+ function ctf_pro_moderation_section() {
178
+ ?>
179
+ <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=moderation" target="_blank">Upgrade to Pro to enable Tweet moderation</a></p>
180
+ <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
181
+
182
+ <div class="ctf-pro-options">
183
+ <table class="form-table"><tbody><tr class="large-text"><th scope="row"><label for="ctf_includewords" title="Click for shortcode option">Show Tweets containing these words or hashtags</label><code class="ctf_shortcode">includewords
184
+ Eg: includewords="#puppy,#cute"</code></th><td> <input name="ctf_options[includewords]" id="ctf_includewords" class="large-text" type="text" value="" disabled>
185
+ <span>"includewords" separate words by comma</span>
186
+ </td></tr><tr class="large-text"><th scope="row"><label for="ctf_excludewords">Remove Tweets containing these words or hashtags</label><code class="ctf_shortcode">excludewords
187
+ Eg: excludewords="#ugly,#bad"</code></th><td> <input name="ctf_options[excludewords]" id="ctf_excludewords" class="large-text" type="text" value="" disabled>
188
+ <span>"excludewords" separate words by comma</span>
189
+ </td></tr><tr><th scope="row"></th><td> <p>Show Tweets that contain
190
+ <select name="ctf_options[includeanyall]" id="ctf_includeanyall" disabled>
191
+ <option value="any" selected="selected">any</option>
192
+ <option value="all">all</option>
193
+ </select>
194
+ of the "includewords"
195
+ <select name="ctf_options[filterandor]" id="ctf_filterandor" disabled>
196
+ <option value="and" selected="selected">and</option>
197
+ <option value="or">or</option>
198
+ </select>
199
+ do not contain
200
+ <select name="ctf_options[excludeanyall]" id="ctf_excludeanyall" disabled>
201
+ <option value="any" selected="selected">any</option>
202
+ <option value="all">all</option>
203
+ </select>
204
+ of the "excludewords"
205
+ </p>
206
+ </td></tr><tr><th scope="row"><label for="ctf_remove_by_id">Hide Specific Tweets</label></th><td> <textarea name="ctf_options[remove_by_id]" id="ctf_remove_by_id" style="width: 70%;" rows="3" disabled></textarea>
207
+ <p>separate IDs by comma <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
208
+ <span class="ctf-tooltip ctf-more-info">These are the specific ID numbers associated with a tweet. You can find the ID of a Tweet by viewing the Tweet on Twitter and copy/pasting the ID number from the end of the URL.</span>
209
+ </p> </td></tr></tbody></table>
210
+ </div>
211
+ <div style="height: 18px;"></div>
212
+ <?php
213
+ }
214
+
215
+ add_action( 'ctf_admin_style_option', 'ctf_add_masonry_autoscroll_options', 5, 1 );
216
+ function ctf_add_masonry_autoscroll_options( $admin ) {
217
+ // custom in reply to text
218
+ $admin->create_settings_field( array(
219
+ 'name' => 'inreplytotext',
220
+ 'title' => '<label for="ctf_inreplytotext">Translation for "In reply to"</label><code class="ctf_shortcode">inreplytotext
221
+ Eg: inreplytotext="Als Antwort an"</code>', // label for the input field
222
+ 'callback' => 'default_text', // name of the function that outputs the html
223
+ 'page' => 'ctf_options_text', // matches the section name
224
+ 'section' => 'ctf_options_text', // matches the section name
225
+ 'option' => 'ctf_options', // matches the options name
226
+ 'class' => 'default-text ctf_pro', // class for the wrapper and input field
227
+ 'whatis' => 'This will replace the default text displayed for "In reply to"',
228
+ 'default' => 'In reply to'// "what is this?" text
229
+ ) );
230
+
231
+ add_settings_section(
232
+ 'ctf_options_autoscroll', // matches the section name
233
+ '<span class="ctf_pro_header">Autoscroll Loading</span>',
234
+ 'ctf_pro_autoscroll_section', // callback function to explain the section
235
+ 'ctf_options_autoscroll' // matches the section name
236
+ );
237
+
238
+ add_settings_section(
239
+ 'ctf_options_filter', // matches the section name
240
+ '<span class="ctf_pro_header">Moderation</span>',
241
+ 'ctf_pro_moderation_section', // callback function to explain the section
242
+ 'ctf_options_filter' // matches the section name
243
+ );
244
+ }
245
+
246
+ add_action( 'ctf_admin_customize_option', 'ctf_add_customize_general_options', 20, 1 );
247
+ function ctf_add_customize_general_options( $admin ) {
248
+
249
+ // Disable the lightbox
250
+ $admin->create_settings_field( array(
251
+ 'name' => 'disablelightbox',
252
+ 'title' => '<label for="ctf_disablelightbox">Disable the lightbox</label><code class="ctf_shortcode">disablelightbox
253
+ Eg: disablelightbox=true</code>', // label for the input field
254
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
255
+ 'page' => 'ctf_options_general', // matches the section name
256
+ 'section' => 'ctf_options_general', // matches the section name
257
+ 'option' => 'ctf_options', // matches the options name
258
+ 'class' => 'default-text ctf_pro', // class for the wrapper and input field
259
+ 'whatis' => 'Disable the popup lightbox for media in the feed'
260
+ ) );
261
+ }
262
+
263
+
264
+ add_action( 'ctf_admin_customize_option', 'ctf_add_filter_options', 10, 1 );
265
+ function ctf_add_filter_options( $admin ) {
266
+
267
+ add_settings_field(
268
+ 'clear_tc_cache_button',
269
+ '<label for="ctf_clear_tc_cache_button">Clear Twitter Card Cache</label>',
270
+ 'ctf_clear_tc_cache_button',
271
+ 'ctf_options_advanced',
272
+ 'ctf_options_advanced',
273
+ array( 'class' => 'ctf_pro')
274
+ );
275
+ }
276
+
277
+ function ctf_remove_by_id( $args ) {
278
+ $options = get_option( $args['option'] );
279
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
280
+ ?>
281
+ <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="3"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
282
+ <?php if ( isset( $args['extra'] ) ) : ?><p><?php _e( $args['extra'], 'custom-twitter-feeds' ); ?>
283
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
284
+ <span class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</span>
285
+ </p> <?php endif; ?>
286
+ <?php
287
+ }
288
+
289
+ function ctf_clear_tc_cache_button() {
290
+ ?>
291
+ <input id="ctf-clear-tc-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cards' ); ?>" />
292
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
293
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your links that have Twitter Cards', 'custom-twitter-feeds' ); ?>.</p>
294
+ <?php
295
+ }
296
+
297
+ function ctf_filter_operator( $args ) {
298
+ $options = get_option( $args['option'] );
299
+ $include_any_all = ( isset( $options['includeanyall'] ) ) ? esc_attr( $options['includeanyall'] ) : 'any';
300
+ $filter_and_or = ( isset( $options['filterandor'] ) ) ? esc_attr( $options['filterandor'] ) : 'and';
301
+ $exclude_any_all = ( isset( $options['excludeanyall'] ) ) ? esc_attr( $options['excludeanyall'] ) : 'any';
302
+
303
+ ?>
304
+ <p>Show Tweets that contain
305
+ <select name="<?php echo $args['option'].'[includeanyall]'; ?>" id="ctf_includeanyall">
306
+ <option value="any" <?php if ( $include_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
307
+ <option value="all" <?php if ( $include_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
308
+ </select>
309
+ of the "includewords"
310
+ <select name="<?php echo $args['option'].'[filterandor]'; ?>" id="ctf_filterandor">
311
+ <option value="and" <?php if ( $filter_and_or == "and" ) echo 'selected="selected"'; ?> ><?php _e('and'); ?></option>
312
+ <option value="or" <?php if ( $filter_and_or == "or" ) echo 'selected="selected"'; ?> ><?php _e('or'); ?></option>
313
+ </select>
314
+ do not contain
315
+ <select name="<?php echo $args['option'].'[excludeanyall]'; ?>" id="ctf_excludeanyall">
316
+ <option value="any" <?php if ( $exclude_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
317
+ <option value="all" <?php if ( $exclude_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
318
+ </select>
319
+ of the "excludewords"
320
+ </p>
321
+ <?php if ( isset( $args['whatis'] ) ) : ?>
322
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
323
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
324
+ <?php endif; ?>
325
+ <?php
326
+ }
327
+
328
+ add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_masonry_autoload_section_to_customize' );
329
+ function ctf_add_masonry_autoload_section_to_customize() {
330
+ ?>
331
+ <a id="autoscroll"></a>
332
+ <?php do_settings_sections( 'ctf_options_autoscroll' ); ?>
333
+ <!-- <p class="submit"><input class="button-primary" type="submit" name="save" value="<?php esc_attr_e( 'Save Changes' ); ?>" /></p> -->
334
+ <hr>
335
+ <?php
336
+ }
337
+
338
+ add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_filter_section_to_customize' );
339
+ function ctf_add_filter_section_to_customize() {
340
+ echo '<a id="moderation"></a>';
341
+ do_settings_sections( 'ctf_options_filter' ); // matches the section name
342
+ echo '<hr>';
343
+ }
344
+
345
+ function ctf_lite_dismiss() {
346
+ $nonce = isset( $_POST['ctf_nonce'] ) ? sanitize_text_field( $_POST['ctf_nonce'] ) : '';
347
+
348
+ if ( ! wp_verify_nonce( $nonce, 'ctf-smash-balloon' ) ) {
349
+ die ( 'You did not do this the right way!' );
350
+ }
351
+
352
+ set_transient( 'twitter_feed_dismiss_lite', 'dismiss', 1 * WEEK_IN_SECONDS );
353
+
354
+ die();
355
+ }
356
+ add_action( 'wp_ajax_ctf_lite_dismiss', 'ctf_lite_dismiss' );
357
+
358
+ function ctf_admin_hide_unrelated_notices() {
359
+
360
+ // Bail if we're not on a ctf screen or page.
361
+ if ( ! isset( $_GET['page'] )
362
+ || ($_GET['page'] !== 'custom-twitter-feeds' && $_GET['page'] !== 'ctf-sw') ) {
363
+ return;
364
+ }
365
+
366
+ // Extra banned classes and callbacks from third-party plugins.
367
+ $blacklist = array(
368
+ 'classes' => array(),
369
+ 'callbacks' => array(
370
+ 'ctfdb_admin_notice', // 'Database for ctf' plugin.
371
+ ),
372
+ );
373
+
374
+ global $wp_filter;
375
+
376
+ foreach ( array( 'user_admin_notices', 'admin_notices', 'all_admin_notices' ) as $notices_type ) {
377
+ if ( empty( $wp_filter[ $notices_type ]->callbacks ) || ! is_array( $wp_filter[ $notices_type ]->callbacks ) ) {
378
+ continue;
379
+ }
380
+ foreach ( $wp_filter[ $notices_type ]->callbacks as $priority => $hooks ) {
381
+ foreach ( $hooks as $name => $arr ) {
382
+ if ( is_object( $arr['function'] ) && $arr['function'] instanceof Closure ) {
383
+ unset( $wp_filter[ $notices_type ]->callbacks[ $priority ][ $name ] );
384
+ continue;
385
+ }
386
+ $class = ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) ? strtolower( get_class( $arr['function'][0] ) ) : '';
387
+ if (
388
+ ! empty( $class ) &&
389
+ strpos( $class, 'ctf' ) !== false &&
390
+ ! in_array( $class, $blacklist['classes'], true )
391
+ ) {
392
+ continue;
393
+ }
394
+ if (
395
+ ! empty( $name ) && (
396
+ strpos( $name, 'ctf' ) === false ||
397
+ in_array( $class, $blacklist['classes'], true ) ||
398
+ in_array( $name, $blacklist['callbacks'], true )
399
+ )
400
+ ) {
401
+ unset( $wp_filter[ $notices_type ]->callbacks[ $priority ][ $name ] );
402
+ }
403
+ }
404
+ }
405
+ }
406
+ }
407
  add_action( 'admin_print_scripts', 'ctf_admin_hide_unrelated_notices' );
inc/admin/PluginSilentUpgrader.php CHANGED
@@ -1,576 +1,576 @@
1
- <?php
2
-
3
- namespace CTF\Helpers;
4
-
5
- use WP_Error;
6
- use WP_Upgrader;
7
- use WP_Filesystem_Base;
8
-
9
- /** \WP_Upgrader class */
10
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
11
-
12
- /** \Plugin_Upgrader class */
13
- require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
14
-
15
- /**
16
- * In WP 5.3 a PHP 5.6 splat operator (...$args) was added to \WP_Upgrader_Skin::feedback().
17
- * We need to remove all calls to *Skin::feedback() method, as we can't override it in own Skins
18
- * without breaking support for PHP 5.3-5.5.
19
- *
20
- * @internal Please do not use this class outside of core WPForms development. May be removed at any time.
21
- *
22
- * @since 1.5.6.1
23
- */
24
- class PluginSilentUpgrader extends \Plugin_Upgrader {
25
-
26
- /**
27
- * Run an upgrade/installation.
28
- *
29
- * Attempts to download the package (if it is not a local file), unpack it, and
30
- * install it in the destination folder.
31
- *
32
- * @since 1.5.6.1
33
- *
34
- * @param array $options {
35
- * Array or string of arguments for upgrading/installing a package.
36
- *
37
- * @type string $package The full path or URI of the package to install.
38
- * Default empty.
39
- * @type string $destination The full path to the destination folder.
40
- * Default empty.
41
- * @type bool $clear_destination Whether to delete any files already in the
42
- * destination folder. Default false.
43
- * @type bool $clear_working Whether to delete the files form the working
44
- * directory after copying to the destination.
45
- * Default false.
46
- * @type bool $abort_if_destination_exists Whether to abort the installation if the destination
47
- * folder already exists. When true, `$clear_destination`
48
- * should be false. Default true.
49
- * @type bool $is_multi Whether this run is one of multiple upgrade/installation
50
- * actions being performed in bulk. When true, the skin
51
- * WP_Upgrader::header() and WP_Upgrader::footer()
52
- * aren't called. Default false.
53
- * @type array $hook_extra Extra arguments to pass to the filter hooks called by
54
- * WP_Upgrader::run().
55
- * }
56
- * @return array|false|WP_error The result from self::install_package() on success, otherwise a WP_Error,
57
- * or false if unable to connect to the filesystem.
58
- */
59
- public function run( $options ) {
60
-
61
- $defaults = array(
62
- 'package' => '', // Please always pass this.
63
- 'destination' => '', // And this
64
- 'clear_destination' => false,
65
- 'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please
66
- 'clear_working' => true,
67
- 'is_multi' => false,
68
- 'hook_extra' => array(), // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
69
- );
70
-
71
- $options = wp_parse_args( $options, $defaults );
72
-
73
- /**
74
- * Filters the package options before running an update.
75
- *
76
- * See also {@see 'upgrader_process_complete'}.
77
- *
78
- * @since 4.3.0
79
- *
80
- * @param array $options {
81
- * Options used by the upgrader.
82
- *
83
- * @type string $package Package for update.
84
- * @type string $destination Update location.
85
- * @type bool $clear_destination Clear the destination resource.
86
- * @type bool $clear_working Clear the working resource.
87
- * @type bool $abort_if_destination_exists Abort if the Destination directory exists.
88
- * @type bool $is_multi Whether the upgrader is running multiple times.
89
- * @type array $hook_extra {
90
- * Extra hook arguments.
91
- *
92
- * @type string $action Type of action. Default 'update'.
93
- * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
94
- * @type bool $bulk Whether the update process is a bulk update. Default true.
95
- * @type string $plugin Path to the plugin file relative to the plugins directory.
96
- * @type string $theme The stylesheet or template name of the theme.
97
- * @type string $language_update_type The language pack update type. Accepts 'plugin', 'theme',
98
- * or 'core'.
99
- * @type object $language_update The language pack update offer.
100
- * }
101
- * }
102
- */
103
- $options = apply_filters( 'upgrader_package_options', $options );
104
-
105
- if ( ! $options['is_multi'] ) { // call $this->header separately if running multiple times
106
- $this->skin->header();
107
- }
108
-
109
- // Connect to the Filesystem first.
110
- $res = $this->fs_connect( array( WP_CONTENT_DIR, $options['destination'] ) );
111
- // Mainly for non-connected filesystem.
112
- if ( ! $res ) {
113
- if ( ! $options['is_multi'] ) {
114
- $this->skin->footer();
115
- }
116
- return false;
117
- }
118
-
119
- $this->skin->before();
120
-
121
- if ( is_wp_error( $res ) ) {
122
- $this->skin->error( $res );
123
- $this->skin->after();
124
- if ( ! $options['is_multi'] ) {
125
- $this->skin->footer();
126
- }
127
- return $res;
128
- }
129
-
130
- /*
131
- * Download the package (Note, This just returns the filename
132
- * of the file if the package is a local file)
133
- */
134
- $download = $this->download_package( $options['package'], true );
135
-
136
- // Allow for signature soft-fail.
137
- // WARNING: This may be removed in the future.
138
- if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
139
-
140
- // Don't output the 'no signature could be found' failure message for now.
141
- if ( 'signature_verification_no_signature' != $download->get_error_code() || WP_DEBUG ) {
142
- // Outout the failure error as a normal feedback, and not as an error:
143
- //$this->skin->feedback( $download->get_error_message() );
144
-
145
- // Report this failure back to WordPress.org for debugging purposes.
146
- wp_version_check(
147
- array(
148
- 'signature_failure_code' => $download->get_error_code(),
149
- 'signature_failure_data' => $download->get_error_data(),
150
- )
151
- );
152
- }
153
-
154
- // Pretend this error didn't happen.
155
- $download = $download->get_error_data( 'softfail-filename' );
156
- }
157
-
158
- if ( is_wp_error( $download ) ) {
159
- $this->skin->error( $download );
160
- $this->skin->after();
161
- if ( ! $options['is_multi'] ) {
162
- $this->skin->footer();
163
- }
164
- return $download;
165
- }
166
-
167
- $delete_package = ( $download != $options['package'] ); // Do not delete a "local" file
168
-
169
- // Unzips the file into a temporary directory.
170
- $working_dir = $this->unpack_package( $download, $delete_package );
171
- if ( is_wp_error( $working_dir ) ) {
172
- $this->skin->error( $working_dir );
173
- $this->skin->after();
174
- if ( ! $options['is_multi'] ) {
175
- $this->skin->footer();
176
- }
177
- return $working_dir;
178
- }
179
-
180
- // With the given options, this installs it to the destination directory.
181
- $result = $this->install_package(
182
- array(
183
- 'source' => $working_dir,
184
- 'destination' => $options['destination'],
185
- 'clear_destination' => $options['clear_destination'],
186
- 'abort_if_destination_exists' => $options['abort_if_destination_exists'],
187
- 'clear_working' => $options['clear_working'],
188
- 'hook_extra' => $options['hook_extra'],
189
- )
190
- );
191
-
192
- $this->skin->set_result( $result );
193
- if ( is_wp_error( $result ) ) {
194
- $this->skin->error( $result );
195
- //$this->skin->feedback( 'process_failed' );
196
- } else {
197
- // Installation succeeded.
198
- //$this->skin->feedback( 'process_success' );
199
- }
200
-
201
- $this->skin->after();
202
-
203
- if ( ! $options['is_multi'] ) {
204
-
205
- /**
206
- * Fires when the upgrader process is complete.
207
- *
208
- * See also {@see 'upgrader_package_options'}.
209
- *
210
- * @since 3.6.0
211
- * @since 3.7.0 Added to WP_Upgrader::run().
212
- * @since 4.6.0 `$translations` was added as a possible argument to `$hook_extra`.
213
- *
214
- * @param WP_Upgrader $this WP_Upgrader instance. In other contexts, $this, might be a
215
- * Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance.
216
- * @param array $hook_extra {
217
- * Array of bulk item update data.
218
- *
219
- * @type string $action Type of action. Default 'update'.
220
- * @type string $type Type of update process. Accepts 'plugin', 'theme', 'translation', or 'core'.
221
- * @type bool $bulk Whether the update process is a bulk update. Default true.
222
- * @type array $plugins Array of the basename paths of the plugins' main files.
223
- * @type array $themes The theme slugs.
224
- * @type array $translations {
225
- * Array of translations update data.
226
- *
227
- * @type string $language The locale the translation is for.
228
- * @type string $type Type of translation. Accepts 'plugin', 'theme', or 'core'.
229
- * @type string $slug Text domain the translation is for. The slug of a theme/plugin or
230
- * 'default' for core translations.
231
- * @type string $version The version of a theme, plugin, or core.
232
- * }
233
- * }
234
- */
235
- do_action( 'upgrader_process_complete', $this, $options['hook_extra'] );
236
-
237
- $this->skin->footer();
238
- }
239
-
240
- return $result;
241
- }
242
-
243
- /**
244
- * Toggle maintenance mode for the site.
245
- *
246
- * Create/delete the maintenance file to enable/disable maintenance mode.
247
- *
248
- * @since 2.8.0
249
- *
250
- * @global WP_Filesystem_Base $wp_filesystem Subclass
251
- *
252
- * @param bool $enable True to enable maintenance mode, false to disable.
253
- */
254
- public function maintenance_mode( $enable = false ) {
255
- global $wp_filesystem;
256
- $file = $wp_filesystem->abspath() . '.maintenance';
257
- if ( $enable ) {
258
- //$this->skin->feedback( 'maintenance_start' );
259
- // Create maintenance file to signal that we are upgrading
260
- $maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
261
- $wp_filesystem->delete( $file );
262
- $wp_filesystem->put_contents( $file, $maintenance_string, FS_CHMOD_FILE );
263
- } elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
264
- //$this->skin->feedback( 'maintenance_end' );
265
- $wp_filesystem->delete( $file );
266
- }
267
- }
268
-
269
- /**
270
- * Download a package.
271
- *
272
- * @since 2.8.0
273
- *
274
- * @param string $package The URI of the package. If this is the full path to an
275
- * existing local file, it will be returned untouched.
276
- * @param bool $check_signatures Whether to validate file signatures. Default false.
277
- * @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
278
- */
279
- public function download_package( $package, $check_signatures = false, $hook_extra = array() ) {
280
-
281
- /**
282
- * Filters whether to return the package.
283
- *
284
- * @since 3.7.0
285
- *
286
- * @param bool $reply Whether to bail without returning the package.
287
- * Default false.
288
- * @param string $package The package file name.
289
- * @param WP_Upgrader $this The WP_Upgrader instance.
290
- */
291
- $reply = apply_filters( 'upgrader_pre_download', false, $package, $this );
292
- if ( false !== $reply ) {
293
- return $reply;
294
- }
295
-
296
- if ( ! preg_match( '!^(http|https|ftp)://!i', $package ) && file_exists( $package ) ) { //Local file or remote?
297
- return $package; //must be a local file..
298
- }
299
-
300
- if ( empty( $package ) ) {
301
- return new WP_Error( 'no_package', $this->strings['no_package'] );
302
- }
303
-
304
- //$this->skin->feedback( 'downloading_package', $package );
305
-
306
- $download_file = download_url( $package, 300, $check_signatures );
307
-
308
- if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
309
- return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
310
- }
311
-
312
- return $download_file;
313
- }
314
-
315
- /**
316
- * Unpack a compressed package file.
317
- *
318
- * @since 2.8.0
319
- *
320
- * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
321
- *
322
- * @param string $package Full path to the package file.
323
- * @param bool $delete_package Optional. Whether to delete the package file after attempting
324
- * to unpack it. Default true.
325
- * @return string|WP_Error The path to the unpacked contents, or a WP_Error on failure.
326
- */
327
- public function unpack_package( $package, $delete_package = true ) {
328
- global $wp_filesystem;
329
-
330
- //$this->skin->feedback( 'unpack_package' );
331
-
332
- $upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
333
-
334
- //Clean up contents of upgrade directory beforehand.
335
- $upgrade_files = $wp_filesystem->dirlist( $upgrade_folder );
336
- if ( ! empty( $upgrade_files ) ) {
337
- foreach ( $upgrade_files as $file ) {
338
- $wp_filesystem->delete( $upgrade_folder . $file['name'], true );
339
- }
340
- }
341
-
342
- // We need a working directory - Strip off any .tmp or .zip suffixes
343
- $working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
344
-
345
- // Clean up working directory
346
- if ( $wp_filesystem->is_dir( $working_dir ) ) {
347
- $wp_filesystem->delete( $working_dir, true );
348
- }
349
-
350
- // Unzip package to working directory
351
- $result = unzip_file( $package, $working_dir );
352
-
353
- // Once extracted, delete the package if required.
354
- if ( $delete_package ) {
355
- unlink( $package );
356
- }
357
-
358
- if ( is_wp_error( $result ) ) {
359
- $wp_filesystem->delete( $working_dir, true );
360
- if ( 'incompatible_archive' == $result->get_error_code() ) {
361
- return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );
362
- }
363
- return $result;
364
- }
365
-
366
- return $working_dir;
367
- }
368
-
369
- /**
370
- * Install a package.
371
- *
372
- * Copies the contents of a package form a source directory, and installs them in
373
- * a destination directory. Optionally removes the source. It can also optionally
374
- * clear out the destination folder if it already exists.
375
- *
376
- * @since 2.8.0
377
- *
378
- * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
379
- * @global array $wp_theme_directories
380
- *
381
- * @param array|string $args {
382
- * Optional. Array or string of arguments for installing a package. Default empty array.
383
- *
384
- * @type string $source Required path to the package source. Default empty.
385
- * @type string $destination Required path to a folder to install the package in.
386
- * Default empty.
387
- * @type bool $clear_destination Whether to delete any files already in the destination
388
- * folder. Default false.
389
- * @type bool $clear_working Whether to delete the files form the working directory
390
- * after copying to the destination. Default false.
391
- * @type bool $abort_if_destination_exists Whether to abort the installation if
392
- * the destination folder already exists. Default true.
393
- * @type array $hook_extra Extra arguments to pass to the filter hooks called by
394
- * WP_Upgrader::install_package(). Default empty array.
395
- * }
396
- *
397
- * @return array|WP_Error The result (also stored in `WP_Upgrader::$result`), or a WP_Error on failure.
398
- */
399
- public function install_package( $args = array() ) {
400
- global $wp_filesystem, $wp_theme_directories;
401
-
402
- $defaults = array(
403
- 'source' => '', // Please always pass this
404
- 'destination' => '', // and this
405
- 'clear_destination' => false,
406
- 'clear_working' => false,
407
- 'abort_if_destination_exists' => true,
408
- 'hook_extra' => array(),
409
- );
410
-
411
- $args = wp_parse_args( $args, $defaults );
412
-
413
- // These were previously extract()'d.
414
- $source = $args['source'];
415
- $destination = $args['destination'];
416
- $clear_destination = $args['clear_destination'];
417
-
418
- set_time_limit( 300 );
419
-
420
- if ( empty( $source ) || empty( $destination ) ) {
421
- return new WP_Error( 'bad_request', $this->strings['bad_request'] );
422
- }
423
- //$this->skin->feedback( 'installing_package' );
424
-
425
- /**
426
- * Filters the install response before the installation has started.
427
- *
428
- * Returning a truthy value, or one that could be evaluated as a WP_Error
429
- * will effectively short-circuit the installation, returning that value
430
- * instead.
431
- *
432
- * @since 2.8.0
433
- *
434
- * @param bool|WP_Error $response Response.
435
- * @param array $hook_extra Extra arguments passed to hooked filters.
436
- */
437
- $res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
438
-
439
- if ( is_wp_error( $res ) ) {
440
- return $res;
441
- }
442
-
443
- //Retain the Original source and destinations
444
- $remote_source = $args['source'];
445
- $local_destination = $destination;
446
-
447
- $source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
448
- $remote_destination = $wp_filesystem->find_folder( $local_destination );
449
-
450
- //Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
451
- if ( 1 == count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { //Only one folder? Then we want its contents.
452
- $source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
453
- } elseif ( count( $source_files ) == 0 ) {
454
- return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] ); // There are no files?
455
- } else { // It's only a single file, the upgrader will use the folder name of this file as the destination folder. Folder name is based on zip filename.
456
- $source = trailingslashit( $args['source'] );
457
- }
458
-
459
- /**
460
- * Filters the source file location for the upgrade package.
461
- *
462
- * @since 2.8.0
463
- * @since 4.4.0 The $hook_extra parameter became available.
464
- *
465
- * @param string $source File source location.
466
- * @param string $remote_source Remote file source location.
467
- * @param WP_Upgrader $this WP_Upgrader instance.
468
- * @param array $hook_extra Extra arguments passed to hooked filters.
469
- */
470
- $source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this, $args['hook_extra'] );
471
-
472
- if ( is_wp_error( $source ) ) {
473
- return $source;
474
- }
475
-
476
- // Has the source location changed? If so, we need a new source_files list.
477
- if ( $source !== $remote_source ) {
478
- $source_files = array_keys( $wp_filesystem->dirlist( $source ) );
479
- }
480
-
481
- /*
482
- * Protection against deleting files in any important base directories.
483
- * Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
484
- * destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
485
- * to copy the directory into the directory, whilst they pass the source
486
- * as the actual files to copy.
487
- */
488
- $protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
489
-
490
- if ( is_array( $wp_theme_directories ) ) {
491
- $protected_directories = array_merge( $protected_directories, $wp_theme_directories );
492
- }
493
-
494
- if ( in_array( $destination, $protected_directories ) ) {
495
- $remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
496
- $destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
497
- }
498
-
499
- if ( $clear_destination ) {
500
- // We're going to clear the destination if there's something there.
501
- //$this->skin->feedback( 'remove_old' );
502
-
503
- $removed = $this->clear_destination( $remote_destination );
504
-
505
- /**
506
- * Filters whether the upgrader cleared the destination.
507
- *
508
- * @since 2.8.0
509
- *
510
- * @param mixed $removed Whether the destination was cleared. true on success, WP_Error on failure
511
- * @param string $local_destination The local package destination.
512
- * @param string $remote_destination The remote package destination.
513
- * @param array $hook_extra Extra arguments passed to hooked filters.
514
- */
515
- $removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
516
-
517
- if ( is_wp_error( $removed ) ) {
518
- return $removed;
519
- }
520
- } elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
521
- //If we're not clearing the destination folder and something exists there already, Bail.
522
- //But first check to see if there are actually any files in the folder.
523
- $_files = $wp_filesystem->dirlist( $remote_destination );
524
- if ( ! empty( $_files ) ) {
525
- $wp_filesystem->delete( $remote_source, true ); //Clear out the source files.
526
- return new WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
527
- }
528
- }
529
-
530
- //Create destination if needed
531
- if ( ! $wp_filesystem->exists( $remote_destination ) ) {
532
- if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
533
- return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
534
- }
535
- }
536
- // Copy new version of item into place.
537
- $result = copy_dir( $source, $remote_destination );
538
- if ( is_wp_error( $result ) ) {
539
- if ( $args['clear_working'] ) {
540
- $wp_filesystem->delete( $remote_source, true );
541
- }
542
- return $result;
543
- }
544
-
545
- //Clear the Working folder?
546
- if ( $args['clear_working'] ) {
547
- $wp_filesystem->delete( $remote_source, true );
548
- }
549
-
550
- $destination_name = basename( str_replace( $local_destination, '', $destination ) );
551
- if ( '.' == $destination_name ) {
552
- $destination_name = '';
553
- }
554
-
555
- $this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
556
-
557
- /**
558
- * Filters the installation response after the installation has finished.
559
- *
560
- * @since 2.8.0
561
- *
562
- * @param bool $response Installation response.
563
- * @param array $hook_extra Extra arguments passed to hooked filters.
564
- * @param array $result Installation result data.
565
- */
566
- $res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
567
-
568
- if ( is_wp_error( $res ) ) {
569
- $this->result = $res;
570
- return $res;
571
- }
572
-
573
- //Bombard the calling function will all the info which we've just used.
574
- return $this->result;
575
- }
576
- }
1
+ <?php
2
+
3
+ namespace CTF\Helpers;
4
+
5
+ use WP_Error;
6
+ use WP_Upgrader;
7
+ use WP_Filesystem_Base;
8
+
9
+ /** \WP_Upgrader class */
10
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
11
+
12
+ /** \Plugin_Upgrader class */
13
+ require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
14
+
15
+ /**
16
+ * In WP 5.3 a PHP 5.6 splat operator (...$args) was added to \WP_Upgrader_Skin::feedback().
17
+ * We need to remove all calls to *Skin::feedback() method, as we can't override it in own Skins
18
+ * without breaking support for PHP 5.3-5.5.
19
+ *
20
+ * @internal Please do not use this class outside of core WPForms development. May be removed at any time.
21
+ *
22
+ * @since 1.5.6.1
23
+ */
24
+ class PluginSilentUpgrader extends \Plugin_Upgrader {
25
+
26
+ /**
27
+ * Run an upgrade/installation.
28
+ *
29
+ * Attempts to download the package (if it is not a local file), unpack it, and
30
+ * install it in the destination folder.
31
+ *
32
+ * @since 1.5.6.1
33
+ *
34
+ * @param array $options {
35
+ * Array or string of arguments for upgrading/installing a package.
36
+ *
37
+ * @type string $package The full path or URI of the package to install.
38
+ * Default empty.
39
+ * @type string $destination The full path to the destination folder.
40
+ * Default empty.
41
+ * @type bool $clear_destination Whether to delete any files already in the
42
+ * destination folder. Default false.
43
+ * @type bool $clear_working Whether to delete the files form the working
44
+ * directory after copying to the destination.
45
+ * Default false.
46
+ * @type bool $abort_if_destination_exists Whether to abort the installation if the destination
47
+ * folder already exists. When true, `$clear_destination`
48
+ * should be false. Default true.
49
+ * @type bool $is_multi Whether this run is one of multiple upgrade/installation
50
+ * actions being performed in bulk. When true, the skin
51
+ * WP_Upgrader::header() and WP_Upgrader::footer()
52
+ * aren't called. Default false.
53
+ * @type array $hook_extra Extra arguments to pass to the filter hooks called by
54
+ * WP_Upgrader::run().
55
+ * }
56
+ * @return array|false|WP_error The result from self::install_package() on success, otherwise a WP_Error,
57
+ * or false if unable to connect to the filesystem.
58
+ */
59
+ public function run( $options ) {
60
+
61
+ $defaults = array(
62
+ 'package' => '', // Please always pass this.
63
+ 'destination' => '', // And this
64
+ 'clear_destination' => false,
65
+ 'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please
66
+ 'clear_working' => true,
67
+ 'is_multi' => false,
68
+ 'hook_extra' => array(), // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
69
+ );
70
+
71
+ $options = wp_parse_args( $options, $defaults );
72
+
73
+ /**
74
+ * Filters the package options before running an update.
75
+ *
76
+ * See also {@see 'upgrader_process_complete'}.
77
+ *
78
+ * @since 4.3.0
79
+ *
80
+ * @param array $options {
81
+ * Options used by the upgrader.
82
+ *
83
+ * @type string $package Package for update.
84
+ * @type string $destination Update location.
85
+ * @type bool $clear_destination Clear the destination resource.
86
+ * @type bool $clear_working Clear the working resource.
87
+ * @type bool $abort_if_destination_exists Abort if the Destination directory exists.
88
+ * @type bool $is_multi Whether the upgrader is running multiple times.
89
+ * @type array $hook_extra {
90
+ * Extra hook arguments.
91
+ *
92
+ * @type string $action Type of action. Default 'update'.
93
+ * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
94
+ * @type bool $bulk Whether the update process is a bulk update. Default true.
95
+ * @type string $plugin Path to the plugin file relative to the plugins directory.
96
+ * @type string $theme The stylesheet or template name of the theme.
97
+ * @type string $language_update_type The language pack update type. Accepts 'plugin', 'theme',
98
+ * or 'core'.
99
+ * @type object $language_update The language pack update offer.
100
+ * }
101
+ * }
102
+ */
103
+ $options = apply_filters( 'upgrader_package_options', $options );
104
+
105
+ if ( ! $options['is_multi'] ) { // call $this->header separately if running multiple times
106
+ $this->skin->header();
107
+ }
108
+
109
+ // Connect to the Filesystem first.
110
+ $res = $this->fs_connect( array( WP_CONTENT_DIR, $options['destination'] ) );
111
+ // Mainly for non-connected filesystem.
112
+ if ( ! $res ) {
113
+ if ( ! $options['is_multi'] ) {
114
+ $this->skin->footer();
115
+ }
116
+ return false;
117
+ }
118
+
119
+ $this->skin->before();
120
+
121
+ if ( is_wp_error( $res ) ) {
122
+ $this->skin->error( $res );
123
+ $this->skin->after();
124
+ if ( ! $options['is_multi'] ) {
125
+ $this->skin->footer();
126
+ }
127
+ return $res;
128
+ }
129
+
130
+ /*
131
+ * Download the package (Note, This just returns the filename
132
+ * of the file if the package is a local file)
133
+ */
134
+ $download = $this->download_package( $options['package'], true );
135
+
136
+ // Allow for signature soft-fail.
137
+ // WARNING: This may be removed in the future.
138
+ if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
139
+
140
+ // Don't output the 'no signature could be found' failure message for now.
141
+ if ( 'signature_verification_no_signature' != $download->get_error_code() || WP_DEBUG ) {
142
+ // Outout the failure error as a normal feedback, and not as an error:
143
+ //$this->skin->feedback( $download->get_error_message() );
144
+
145
+ // Report this failure back to WordPress.org for debugging purposes.
146
+ wp_version_check(
147
+ array(
148
+ 'signature_failure_code' => $download->get_error_code(),
149
+ 'signature_failure_data' => $download->get_error_data(),
150
+ )
151
+ );
152
+ }
153
+
154
+ // Pretend this error didn't happen.
155
+ $download = $download->get_error_data( 'softfail-filename' );
156
+ }
157
+
158
+ if ( is_wp_error( $download ) ) {
159
+ $this->skin->error( $download );
160
+ $this->skin->after();
161
+ if ( ! $options['is_multi'] ) {
162
+ $this->skin->footer();
163
+ }
164
+ return $download;
165
+ }
166
+
167
+ $delete_package = ( $download != $options['package'] ); // Do not delete a "local" file
168
+
169
+ // Unzips the file into a temporary directory.
170
+ $working_dir = $this->unpack_package( $download, $delete_package );
171
+ if ( is_wp_error( $working_dir ) ) {
172
+ $this->skin->error( $working_dir );
173
+ $this->skin->after();
174
+ if ( ! $options['is_multi'] ) {
175
+ $this->skin->footer();
176
+ }
177
+ return $working_dir;
178
+ }
179
+
180
+ // With the given options, this installs it to the destination directory.
181
+ $result = $this->install_package(
182
+ array(
183
+ 'source' => $working_dir,
184
+ 'destination' => $options['destination'],
185
+ 'clear_destination' => $options['clear_destination'],
186
+ 'abort_if_destination_exists' => $options['abort_if_destination_exists'],
187
+ 'clear_working' => $options['clear_working'],
188
+ 'hook_extra' => $options['hook_extra'],
189
+ )
190
+ );
191
+
192
+ $this->skin->set_result( $result );
193
+ if ( is_wp_error( $result ) ) {
194
+ $this->skin->error( $result );
195
+ //$this->skin->feedback( 'process_failed' );
196
+ } else {
197
+ // Installation succeeded.
198
+ //$this->skin->feedback( 'process_success' );
199
+ }
200
+
201
+ $this->skin->after();
202
+
203
+ if ( ! $options['is_multi'] ) {
204
+
205
+ /**
206
+ * Fires when the upgrader process is complete.
207
+ *
208
+ * See also {@see 'upgrader_package_options'}.
209
+ *
210
+ * @since 3.6.0
211
+ * @since 3.7.0 Added to WP_Upgrader::run().
212
+ * @since 4.6.0 `$translations` was added as a possible argument to `$hook_extra`.
213
+ *
214
+ * @param WP_Upgrader $this WP_Upgrader instance. In other contexts, $this, might be a
215
+ * Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance.
216
+ * @param array $hook_extra {
217
+ * Array of bulk item update data.
218
+ *
219
+ * @type string $action Type of action. Default 'update'.
220
+ * @type string $type Type of update process. Accepts 'plugin', 'theme', 'translation', or 'core'.
221
+ * @type bool $bulk Whether the update process is a bulk update. Default true.
222
+ * @type array $plugins Array of the basename paths of the plugins' main files.
223
+ * @type array $themes The theme slugs.
224
+ * @type array $translations {
225
+ * Array of translations update data.
226
+ *
227
+ * @type string $language The locale the translation is for.
228
+ * @type string $type Type of translation. Accepts 'plugin', 'theme', or 'core'.
229
+ * @type string $slug Text domain the translation is for. The slug of a theme/plugin or
230
+ * 'default' for core translations.
231
+ * @type string $version The version of a theme, plugin, or core.
232
+ * }
233
+ * }
234
+ */
235
+ do_action( 'upgrader_process_complete', $this, $options['hook_extra'] );
236
+
237
+ $this->skin->footer();
238
+ }
239
+
240
+ return $result;
241
+ }
242
+
243
+ /**
244
+ * Toggle maintenance mode for the site.
245
+ *
246
+ * Create/delete the maintenance file to enable/disable maintenance mode.
247
+ *
248
+ * @since 2.8.0
249
+ *
250
+ * @global WP_Filesystem_Base $wp_filesystem Subclass
251
+ *
252
+ * @param bool $enable True to enable maintenance mode, false to disable.
253
+ */
254
+ public function maintenance_mode( $enable = false ) {
255
+ global $wp_filesystem;
256
+ $file = $wp_filesystem->abspath() . '.maintenance';
257
+ if ( $enable ) {
258
+ //$this->skin->feedback( 'maintenance_start' );
259
+ // Create maintenance file to signal that we are upgrading
260
+ $maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
261
+ $wp_filesystem->delete( $file );
262
+ $wp_filesystem->put_contents( $file, $maintenance_string, FS_CHMOD_FILE );
263
+ } elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
264
+ //$this->skin->feedback( 'maintenance_end' );
265
+ $wp_filesystem->delete( $file );
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Download a package.
271
+ *
272
+ * @since 2.8.0
273
+ *
274
+ * @param string $package The URI of the package. If this is the full path to an
275
+ * existing local file, it will be returned untouched.
276
+ * @param bool $check_signatures Whether to validate file signatures. Default false.
277
+ * @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
278
+ */
279
+ public function download_package( $package, $check_signatures = false, $hook_extra = array() ) {
280
+
281
+ /**
282
+ * Filters whether to return the package.
283
+ *
284
+ * @since 3.7.0
285
+ *
286
+ * @param bool $reply Whether to bail without returning the package.
287
+ * Default false.
288
+ * @param string $package The package file name.
289
+ * @param WP_Upgrader $this The WP_Upgrader instance.
290
+ */
291
+ $reply = apply_filters( 'upgrader_pre_download', false, $package, $this );
292
+ if ( false !== $reply ) {
293
+ return $reply;
294
+ }
295
+
296
+ if ( ! preg_match( '!^(http|https|ftp)://!i', $package ) && file_exists( $package ) ) { //Local file or remote?
297
+ return $package; //must be a local file..
298
+ }
299
+
300
+ if ( empty( $package ) ) {
301
+ return new WP_Error( 'no_package', $this->strings['no_package'] );
302
+ }
303
+
304
+ //$this->skin->feedback( 'downloading_package', $package );
305
+
306
+ $download_file = download_url( $package, 300, $check_signatures );
307
+
308
+ if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
309
+ return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
310
+ }
311
+
312
+ return $download_file;
313
+ }
314
+
315
+ /**
316
+ * Unpack a compressed package file.
317
+ *
318
+ * @since 2.8.0
319
+ *
320
+ * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
321
+ *
322
+ * @param string $package Full path to the package file.
323
+ * @param bool $delete_package Optional. Whether to delete the package file after attempting
324
+ * to unpack it. Default true.
325
+ * @return string|WP_Error The path to the unpacked contents, or a WP_Error on failure.
326
+ */
327
+ public function unpack_package( $package, $delete_package = true ) {
328
+ global $wp_filesystem;
329
+
330
+ //$this->skin->feedback( 'unpack_package' );
331
+
332
+ $upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
333
+
334
+ //Clean up contents of upgrade directory beforehand.
335
+ $upgrade_files = $wp_filesystem->dirlist( $upgrade_folder );
336
+ if ( ! empty( $upgrade_files ) ) {
337
+ foreach ( $upgrade_files as $file ) {
338
+ $wp_filesystem->delete( $upgrade_folder . $file['name'], true );
339
+ }
340
+ }
341
+
342
+ // We need a working directory - Strip off any .tmp or .zip suffixes
343
+ $working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
344
+
345
+ // Clean up working directory
346
+ if ( $wp_filesystem->is_dir( $working_dir ) ) {
347
+ $wp_filesystem->delete( $working_dir, true );
348
+ }
349
+
350
+ // Unzip package to working directory
351
+ $result = unzip_file( $package, $working_dir );
352
+
353
+ // Once extracted, delete the package if required.
354
+ if ( $delete_package ) {
355
+ unlink( $package );
356
+ }
357
+
358
+ if ( is_wp_error( $result ) ) {
359
+ $wp_filesystem->delete( $working_dir, true );
360
+ if ( 'incompatible_archive' == $result->get_error_code() ) {
361
+ return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );
362
+ }
363
+ return $result;
364
+ }
365
+
366
+ return $working_dir;
367
+ }
368
+
369
+ /**
370
+ * Install a package.
371
+ *
372
+ * Copies the contents of a package form a source directory, and installs them in
373
+ * a destination directory. Optionally removes the source. It can also optionally
374
+ * clear out the destination folder if it already exists.
375
+ *
376
+ * @since 2.8.0
377
+ *
378
+ * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
379
+ * @global array $wp_theme_directories
380
+ *
381
+ * @param array|string $args {
382
+ * Optional. Array or string of arguments for installing a package. Default empty array.
383
+ *
384
+ * @type string $source Required path to the package source. Default empty.
385
+ * @type string $destination Required path to a folder to install the package in.
386
+ * Default empty.
387
+ * @type bool $clear_destination Whether to delete any files already in the destination
388
+ * folder. Default false.
389
+ * @type bool $clear_working Whether to delete the files form the working directory
390
+ * after copying to the destination. Default false.
391
+ * @type bool $abort_if_destination_exists Whether to abort the installation if
392
+ * the destination folder already exists. Default true.
393
+ * @type array $hook_extra Extra arguments to pass to the filter hooks called by
394
+ * WP_Upgrader::install_package(). Default empty array.
395
+ * }
396
+ *
397
+ * @return array|WP_Error The result (also stored in `WP_Upgrader::$result`), or a WP_Error on failure.
398
+ */
399
+ public function install_package( $args = array() ) {
400
+ global $wp_filesystem, $wp_theme_directories;
401
+
402
+ $defaults = array(
403
+ 'source' => '', // Please always pass this
404
+ 'destination' => '', // and this
405
+ 'clear_destination' => false,
406
+ 'clear_working' => false,
407
+ 'abort_if_destination_exists' => true,
408
+ 'hook_extra' => array(),
409
+ );
410
+
411
+ $args = wp_parse_args( $args, $defaults );
412
+
413
+ // These were previously extract()'d.
414
+ $source = $args['source'];
415
+ $destination = $args['destination'];
416
+ $clear_destination = $args['clear_destination'];
417
+
418
+ set_time_limit( 300 );
419
+
420
+ if ( empty( $source ) || empty( $destination ) ) {
421
+ return new WP_Error( 'bad_request', $this->strings['bad_request'] );
422
+ }
423
+ //$this->skin->feedback( 'installing_package' );
424
+
425
+ /**
426
+ * Filters the install response before the installation has started.
427
+ *
428
+ * Returning a truthy value, or one that could be evaluated as a WP_Error
429
+ * will effectively short-circuit the installation, returning that value
430
+ * instead.
431
+ *
432
+ * @since 2.8.0
433
+ *
434
+ * @param bool|WP_Error $response Response.
435
+ * @param array $hook_extra Extra arguments passed to hooked filters.
436
+ */
437
+ $res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
438
+
439
+ if ( is_wp_error( $res ) ) {
440
+ return $res;
441
+ }
442
+
443
+ //Retain the Original source and destinations
444
+ $remote_source = $args['source'];
445
+ $local_destination = $destination;
446
+
447
+ $source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
448
+ $remote_destination = $wp_filesystem->find_folder( $local_destination );
449
+
450
+ //Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
451
+ if ( 1 == count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { //Only one folder? Then we want its contents.
452
+ $source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
453
+ } elseif ( count( $source_files ) == 0 ) {
454
+ return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] ); // There are no files?
455
+ } else { // It's only a single file, the upgrader will use the folder name of this file as the destination folder. Folder name is based on zip filename.
456
+ $source = trailingslashit( $args['source'] );
457
+ }
458
+
459
+ /**
460
+ * Filters the source file location for the upgrade package.
461
+ *
462
+ * @since 2.8.0
463
+ * @since 4.4.0 The $hook_extra parameter became available.
464
+ *
465
+ * @param string $source File source location.
466
+ * @param string $remote_source Remote file source location.
467
+ * @param WP_Upgrader $this WP_Upgrader instance.
468
+ * @param array $hook_extra Extra arguments passed to hooked filters.
469
+ */
470
+ $source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this, $args['hook_extra'] );
471
+
472
+ if ( is_wp_error( $source ) ) {
473
+ return $source;
474
+ }
475
+
476
+ // Has the source location changed? If so, we need a new source_files list.
477
+ if ( $source !== $remote_source ) {
478
+ $source_files = array_keys( $wp_filesystem->dirlist( $source ) );
479
+ }
480
+
481
+ /*
482
+ * Protection against deleting files in any important base directories.
483
+ * Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
484
+ * destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
485
+ * to copy the directory into the directory, whilst they pass the source
486
+ * as the actual files to copy.
487
+ */
488
+ $protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
489
+
490
+ if ( is_array( $wp_theme_directories ) ) {
491
+ $protected_directories = array_merge( $protected_directories, $wp_theme_directories );
492
+ }
493
+
494
+ if ( in_array( $destination, $protected_directories ) ) {
495
+ $remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
496
+ $destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
497
+ }
498
+
499
+ if ( $clear_destination ) {
500
+ // We're going to clear the destination if there's something there.
501
+ //$this->skin->feedback( 'remove_old' );
502
+
503
+ $removed = $this->clear_destination( $remote_destination );
504
+
505
+ /**
506
+ * Filters whether the upgrader cleared the destination.
507
+ *
508
+ * @since 2.8.0
509
+ *
510
+ * @param mixed $removed Whether the destination was cleared. true on success, WP_Error on failure
511
+ * @param string $local_destination The local package destination.
512
+ * @param string $remote_destination The remote package destination.
513
+ * @param array $hook_extra Extra arguments passed to hooked filters.
514
+ */
515
+ $removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
516
+
517
+ if ( is_wp_error( $removed ) ) {
518
+ return $removed;
519
+ }
520
+ } elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
521
+ //If we're not clearing the destination folder and something exists there already, Bail.
522
+ //But first check to see if there are actually any files in the folder.
523
+ $_files = $wp_filesystem->dirlist( $remote_destination );
524
+ if ( ! empty( $_files ) ) {
525
+ $wp_filesystem->delete( $remote_source, true ); //Clear out the source files.
526
+ return new WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
527
+ }
528
+ }
529
+
530
+ //Create destination if needed
531
+ if ( ! $wp_filesystem->exists( $remote_destination ) ) {
532
+ if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
533
+ return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
534
+ }
535
+ }
536
+ // Copy new version of item into place.
537
+ $result = copy_dir( $source, $remote_destination );
538
+ if ( is_wp_error( $result ) ) {
539
+ if ( $args['clear_working'] ) {
540
+ $wp_filesystem->delete( $remote_source, true );
541
+ }
542
+ return $result;
543
+ }
544
+
545
+ //Clear the Working folder?
546
+ if ( $args['clear_working'] ) {
547
+ $wp_filesystem->delete( $remote_source, true );
548
+ }
549
+
550
+ $destination_name = basename( str_replace( $local_destination, '', $destination ) );
551
+ if ( '.' == $destination_name ) {
552
+ $destination_name = '';
553
+ }
554
+
555
+ $this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
556
+
557
+ /**
558
+ * Filters the installation response after the installation has finished.
559
+ *
560
+ * @since 2.8.0
561
+ *
562
+ * @param bool $response Installation response.
563
+ * @param array $hook_extra Extra arguments passed to hooked filters.
564
+ * @param array $result Installation result data.
565
+ */
566
+ $res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
567
+
568
+ if ( is_wp_error( $res ) ) {
569
+ $this->result = $res;
570
+ return $res;
571
+ }
572
+
573
+ //Bombard the calling function will all the info which we've just used.
574
+ return $this->result;
575
+ }
576
+ }
inc/admin/PluginSilentUpgraderSkin.php CHANGED
@@ -1,57 +1,57 @@
1
- <?php
2
-
3
- namespace CTF\Helpers;
4
-
5
- /** \WP_Upgrader_Skin class */
6
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php';
7
-
8
- /**
9
- * Class PluginSilentUpgraderSkin.
10
- *
11
- * @internal Please do not use this class outside of core WPForms development. May be removed at any time.
12
- *
13
- * @since 1.5.6.1
14
- */
15
- class PluginSilentUpgraderSkin extends \WP_Upgrader_Skin {
16
-
17
- /**
18
- * Empty out the header of its HTML content and only check to see if it has
19
- * been performed or not.
20
- *
21
- * @since 1.5.6.1
22
- */
23
- public function header() {
24
- }
25
-
26
- /**
27
- * Empty out the footer of its HTML contents.
28
- *
29
- * @since 1.5.6.1
30
- */
31
- public function footer() {
32
- }
33
-
34
- /**
35
- * Instead of outputting HTML for errors, just return them.
36
- * Ajax request will just ignore it.
37
- *
38
- * @since 1.5.6.1
39
- *
40
- * @param array $errors Array of errors with the install process.
41
- *
42
- * @return array
43
- */
44
- public function error( $errors ) {
45
- return $errors;
46
- }
47
-
48
- /**
49
- * Empty out JavaScript output that calls function to decrement the update counts.
50
- *
51
- * @since 1.5.6.1
52
- *
53
- * @param string $type Type of update count to decrement.
54
- */
55
- public function decrement_update_count( $type ) {
56
- }
57
- }
1
+ <?php
2
+
3
+ namespace CTF\Helpers;
4
+
5
+ /** \WP_Upgrader_Skin class */
6
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php';
7
+
8
+ /**
9
+ * Class PluginSilentUpgraderSkin.
10
+ *
11
+ * @internal Please do not use this class outside of core WPForms development. May be removed at any time.
12
+ *
13
+ * @since 1.5.6.1
14
+ */
15
+ class PluginSilentUpgraderSkin extends \WP_Upgrader_Skin {
16
+
17
+ /**
18
+ * Empty out the header of its HTML content and only check to see if it has
19
+ * been performed or not.
20
+ *
21
+ * @since 1.5.6.1
22
+ */
23
+ public function header() {
24
+ }
25
+
26
+ /**
27
+ * Empty out the footer of its HTML contents.
28
+ *
29
+ * @since 1.5.6.1
30
+ */
31
+ public function footer() {
32
+ }
33
+
34
+ /**
35
+ * Instead of outputting HTML for errors, just return them.
36
+ * Ajax request will just ignore it.
37
+ *
38
+ * @since 1.5.6.1
39
+ *
40
+ * @param array $errors Array of errors with the install process.
41
+ *
42
+ * @return array
43
+ */
44
+ public function error( $errors ) {
45
+ return $errors;
46
+ }
47
+
48
+ /**
49
+ * Empty out JavaScript output that calls function to decrement the update counts.
50
+ *
51
+ * @since 1.5.6.1
52
+ *
53
+ * @param string $type Type of update count to decrement.
54
+ */
55
+ public function decrement_update_count( $type ) {
56
+ }
57
+ }
inc/admin/addon-functions.php CHANGED
@@ -1,180 +1,180 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
-
4
- /**
5
- * Deactivate addon.
6
- *
7
- * @since 1.0.0
8
- */
9
- function ctf_deactivate_addon() {
10
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgrader.php';
11
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgraderSkin.php';
12
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-install-skin.php';
13
- // Run a security check.
14
- check_ajax_referer( 'ctf-admin', 'nonce' );
15
-
16
- // Check for permissions.
17
- if ( ! current_user_can( 'manage_options' ) ) {
18
- wp_send_json_error();
19
- }
20
-
21
- $type = 'addon';
22
- if ( ! empty( $_POST['type'] ) ) {
23
- $type = sanitize_key( $_POST['type'] );
24
- }
25
-
26
- if ( isset( $_POST['plugin'] ) ) {
27
- deactivate_plugins( $_POST['plugin'] );
28
-
29
- if ( 'plugin' === $type ) {
30
- wp_send_json_success( esc_html__( 'Plugin deactivated.', 'custom-twitter-feeds' ) );
31
- } else {
32
- wp_send_json_success( esc_html__( 'Addon deactivated.', 'custom-twitter-feeds' ) );
33
- }
34
- }
35
-
36
- wp_send_json_error( esc_html__( 'Could not deactivate the addon. Please deactivate from the Plugins page.', 'custom-twitter-feeds' ) );
37
- }
38
- add_action( 'wp_ajax_ctf_deactivate_addon', 'ctf_deactivate_addon' );
39
-
40
- /**
41
- * Activate addon.
42
- *
43
- * @since 1.0.0
44
- */
45
- function ctf_activate_addon() {
46
-
47
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgrader.php';
48
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgraderSkin.php';
49
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-install-skin.php';
50
- // Run a security check.
51
- check_ajax_referer( 'ctf-admin', 'nonce' );
52
- // Check for permissions.
53
- if ( ! current_user_can( 'manage_options' ) ) {
54
- wp_send_json_error();
55
- }
56
-
57
- if ( isset( $_POST['plugin'] ) ) {
58
-
59
- $type = 'addon';
60
- if ( ! empty( $_POST['type'] ) ) {
61
- $type = sanitize_key( $_POST['type'] );
62
- }
63
-
64
- $activate = activate_plugins( $_POST['plugin'] );
65
-
66
- if ( ! is_wp_error( $activate ) ) {
67
- if ( 'plugin' === $type ) {
68
- wp_send_json_success( esc_html__( 'Plugin activated.', 'custom-twitter-feeds' ) );
69
- } else {
70
- wp_send_json_success( esc_html__( 'Addon activated.', 'custom-twitter-feeds' ) );
71
- }
72
- }
73
- }
74
-
75
- wp_send_json_error( esc_html__( 'Could not activate addon. Please activate from the Plugins page.', 'custom-twitter-feeds' ) );
76
- }
77
- add_action( 'wp_ajax_ctf_activate_addon', 'ctf_activate_addon' );
78
-
79
- /**
80
- * Install addon.
81
- *
82
- * @since 1.0.0
83
- */
84
- function ctf_install_addon() {
85
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgrader.php';
86
- require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgraderSkin.php';
87
- // Run a security check.
88
- check_ajax_referer( 'ctf-admin', 'nonce' );
89
-
90
- // Check for permissions.
91
- if ( ! current_user_can( 'manage_options' ) ) {
92
- wp_send_json_error();
93
- }
94
-
95
- $error = esc_html__( 'Could not install addon. Please download from wpforms.com and install manually.', 'custom-twitter-feeds' );
96
-
97
- if ( empty( $_POST['plugin'] ) ) {
98
- wp_send_json_error( $error );
99
- }
100
-
101
- // Set the current screen to avoid undefined notices.
102
- set_current_screen( 'custom-twitter-feeds' );
103
-
104
- // Prepare variables.
105
- $url = esc_url_raw(
106
- add_query_arg(
107
- array(
108
- 'page' => 'custom-twitter-feeds',
109
- ),
110
- admin_url( 'admin.php' )
111
- )
112
- );
113
-
114
- $creds = request_filesystem_credentials( $url, '', false, false, null );
115
-
116
- // Check for file system permissions.
117
- if ( false === $creds ) {
118
- wp_send_json_error( $error );
119
- }
120
-
121
- if ( ! WP_Filesystem( $creds ) ) {
122
- wp_send_json_error( $error );
123
- }
124
-
125
- /*
126
- * We do not need any extra credentials if we have gotten this far, so let's install the plugin.
127
- */
128
-
129
- require_once CTF_PLUGIN_DIR . 'inc/admin/class-install-skin.php';
130
-
131
- // Do not allow WordPress to search/download translations, as this will break JS output.
132
- remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
133
-
134
- // Create the plugin upgrader with our custom skin.
135
- $installer = new CTF\Helpers\PluginSilentUpgrader( new CTF_Install_Skin() );
136
-
137
- // Error check.
138
- if ( ! method_exists( $installer, 'install' ) || empty( $_POST['plugin'] ) ) {
139
- wp_send_json_error( $error );
140
- }
141
-
142
- $installer->install( $_POST['plugin'] ); // phpcs:ignore
143
-
144
- // Flush the cache and return the newly installed plugin basename.
145
- wp_cache_flush();
146
-
147
- $plugin_basename = $installer->plugin_info();
148
-
149
- if ( $plugin_basename ) {
150
-
151
- $type = 'addon';
152
- if ( ! empty( $_POST['type'] ) ) {
153
- $type = sanitize_key( $_POST['type'] );
154
- }
155
-
156
- // Activate the plugin silently.
157
- $activated = activate_plugin( $plugin_basename );
158
-
159
- if ( ! is_wp_error( $activated ) ) {
160
- wp_send_json_success(
161
- array(
162
- 'msg' => 'plugin' === $type ? esc_html__( 'Plugin installed & activated.', 'custom-twitter-feeds' ) : esc_html__( 'Addon installed & activated.', 'custom-twitter-feeds' ),
163
- 'is_activated' => true,
164
- 'basename' => $plugin_basename,
165
- )
166
- );
167
- } else {
168
- wp_send_json_success(
169
- array(
170
- 'msg' => 'plugin' === $type ? esc_html__( 'Plugin installed.', 'custom-twitter-feeds' ) : esc_html__( 'Addon installed.', 'custom-twitter-feeds' ),
171
- 'is_activated' => false,
172
- 'basename' => $plugin_basename,
173
- )
174
- );
175
- }
176
- }
177
-
178
- wp_send_json_error( $error );
179
- }
180
  add_action( 'wp_ajax_ctf_install_addon', 'ctf_install_addon' );
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+
4
+ /**
5
+ * Deactivate addon.
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+ function ctf_deactivate_addon() {
10
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgrader.php';
11
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgraderSkin.php';
12
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-install-skin.php';
13
+ // Run a security check.
14
+ check_ajax_referer( 'ctf-admin', 'nonce' );
15
+
16
+ // Check for permissions.
17
+ if ( ! current_user_can( 'manage_options' ) ) {
18
+ wp_send_json_error();
19
+ }
20
+
21
+ $type = 'addon';
22
+ if ( ! empty( $_POST['type'] ) ) {
23
+ $type = sanitize_key( $_POST['type'] );
24
+ }
25
+
26
+ if ( isset( $_POST['plugin'] ) ) {
27
+ deactivate_plugins( $_POST['plugin'] );
28
+
29
+ if ( 'plugin' === $type ) {
30
+ wp_send_json_success( esc_html__( 'Plugin deactivated.', 'custom-twitter-feeds' ) );
31
+ } else {
32
+ wp_send_json_success( esc_html__( 'Addon deactivated.', 'custom-twitter-feeds' ) );
33
+ }
34
+ }
35
+
36
+ wp_send_json_error( esc_html__( 'Could not deactivate the addon. Please deactivate from the Plugins page.', 'custom-twitter-feeds' ) );
37
+ }
38
+ add_action( 'wp_ajax_ctf_deactivate_addon', 'ctf_deactivate_addon' );
39
+
40
+ /**
41
+ * Activate addon.
42
+ *
43
+ * @since 1.0.0
44
+ */
45
+ function ctf_activate_addon() {
46
+
47
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgrader.php';
48
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgraderSkin.php';
49
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-install-skin.php';
50
+ // Run a security check.
51
+ check_ajax_referer( 'ctf-admin', 'nonce' );
52
+ // Check for permissions.
53
+ if ( ! current_user_can( 'manage_options' ) ) {
54
+ wp_send_json_error();
55
+ }
56
+
57
+ if ( isset( $_POST['plugin'] ) ) {
58
+
59
+ $type = 'addon';
60
+ if ( ! empty( $_POST['type'] ) ) {
61
+ $type = sanitize_key( $_POST['type'] );
62
+ }
63
+
64
+ $activate = activate_plugins( $_POST['plugin'] );
65
+
66
+ if ( ! is_wp_error( $activate ) ) {
67
+ if ( 'plugin' === $type ) {
68
+ wp_send_json_success( esc_html__( 'Plugin activated.', 'custom-twitter-feeds' ) );
69
+ } else {
70
+ wp_send_json_success( esc_html__( 'Addon activated.', 'custom-twitter-feeds' ) );
71
+ }
72
+ }
73
+ }
74
+
75
+ wp_send_json_error( esc_html__( 'Could not activate addon. Please activate from the Plugins page.', 'custom-twitter-feeds' ) );
76
+ }
77
+ add_action( 'wp_ajax_ctf_activate_addon', 'ctf_activate_addon' );
78
+
79
+ /**
80
+ * Install addon.
81
+ *
82
+ * @since 1.0.0
83
+ */
84
+ function ctf_install_addon() {
85
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgrader.php';
86
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/PluginSilentUpgraderSkin.php';
87
+ // Run a security check.
88
+ check_ajax_referer( 'ctf-admin', 'nonce' );
89
+
90
+ // Check for permissions.
91
+ if ( ! current_user_can( 'manage_options' ) ) {
92
+ wp_send_json_error();
93
+ }
94
+
95
+ $error = esc_html__( 'Could not install addon. Please download from wpforms.com and install manually.', 'custom-twitter-feeds' );
96
+
97
+ if ( empty( $_POST['plugin'] ) ) {
98
+ wp_send_json_error( $error );
99
+ }
100
+
101
+ // Set the current screen to avoid undefined notices.
102
+ set_current_screen( 'custom-twitter-feeds' );
103
+
104
+ // Prepare variables.
105
+ $url = esc_url_raw(
106
+ add_query_arg(
107
+ array(
108
+ 'page' => 'custom-twitter-feeds',
109
+ ),
110
+ admin_url( 'admin.php' )
111
+ )
112
+ );
113
+
114
+ $creds = request_filesystem_credentials( $url, '', false, false, null );
115
+
116
+ // Check for file system permissions.
117
+ if ( false === $creds ) {
118
+ wp_send_json_error( $error );
119
+ }
120
+
121
+ if ( ! WP_Filesystem( $creds ) ) {
122
+ wp_send_json_error( $error );
123
+ }
124
+
125
+ /*
126
+ * We do not need any extra credentials if we have gotten this far, so let's install the plugin.
127
+ */
128
+
129
+ require_once CTF_PLUGIN_DIR . 'inc/admin/class-install-skin.php';
130
+
131
+ // Do not allow WordPress to search/download translations, as this will break JS output.
132
+ remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
133
+
134
+ // Create the plugin upgrader with our custom skin.
135
+ $installer = new CTF\Helpers\PluginSilentUpgrader( new CTF_Install_Skin() );
136
+
137
+ // Error check.
138
+ if ( ! method_exists( $installer, 'install' ) || empty( $_POST['plugin'] ) ) {
139
+ wp_send_json_error( $error );
140
+ }
141
+
142
+ $installer->install( $_POST['plugin'] ); // phpcs:ignore
143
+
144
+ // Flush the cache and return the newly installed plugin basename.
145
+ wp_cache_flush();
146
+
147
+ $plugin_basename = $installer->plugin_info();
148
+
149
+ if ( $plugin_basename ) {
150
+
151
+ $type = 'addon';
152
+ if ( ! empty( $_POST['type'] ) ) {
153
+ $type = sanitize_key( $_POST['type'] );
154
+ }
155
+
156
+ // Activate the plugin silently.
157
+ $activated = activate_plugin( $plugin_basename );
158
+
159
+ if ( ! is_wp_error( $activated ) ) {
160
+ wp_send_json_success(
161
+ array(
162
+ 'msg' => 'plugin' === $type ? esc_html__( 'Plugin installed & activated.', 'custom-twitter-feeds' ) : esc_html__( 'Addon installed & activated.', 'custom-twitter-feeds' ),
163
+ 'is_activated' => true,
164
+ 'basename' => $plugin_basename,
165
+ )
166
+ );
167
+ } else {
168
+ wp_send_json_success(
169
+ array(
170
+ 'msg' => 'plugin' === $type ? esc_html__( 'Plugin installed.', 'custom-twitter-feeds' ) : esc_html__( 'Addon installed.', 'custom-twitter-feeds' ),
171
+ 'is_activated' => false,
172
+ 'basename' => $plugin_basename,
173
+ )
174
+ );
175
+ }
176
+ }
177
+
178
+ wp_send_json_error( $error );
179
+ }
180
  add_action( 'wp_ajax_ctf_install_addon', 'ctf_install_addon' );
inc/admin/class-ctf-new-user.php CHANGED
@@ -1,396 +1,397 @@
1
- <?php
2
- /**
3
- * CTF_New_User.
4
- *
5
- * @since 2.18
6
- */
7
-
8
- // Exit if accessed directly
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class CTF_New_User extends CTF_Notifications {
14
-
15
- /**
16
- * Source of notifications content.
17
- *
18
- * @since 2.18
19
- *
20
- * @var string
21
- */
22
- const SOURCE_URL = 'http://plugin.smashballoon.com/newuser.json';
23
-
24
- /**
25
- * @var string
26
- */
27
- const OPTION_NAME = 'ctf_newuser_notifications';
28
-
29
- /**
30
- * Register hooks.
31
- *
32
- * @since 2.18
33
- */
34
- public function hooks() {
35
- add_action( 'admin_notices', array( $this, 'output' ), 8 );
36
-
37
- add_action( 'admin_init', array( $this, 'dismiss' ) );
38
- }
39
-
40
- public function option_name() {
41
- return self::OPTION_NAME;
42
- }
43
-
44
- public function source_url() {
45
- return self::SOURCE_URL;
46
- }
47
-
48
- /**
49
- * Verify notification data before it is saved.
50
- *
51
- * @param array $notifications Array of notifications items to verify.
52
- *
53
- * @return array
54
- *
55
- * @since 2.18
56
- */
57
- public function verify( $notifications ) {
58
- $data = array();
59
-
60
- if ( ! is_array( $notifications ) || empty( $notifications ) ) {
61
- return $data;
62
- }
63
-
64
- $option = $this->get_option();
65
-
66
- foreach ( $notifications as $key => $notification ) {
67
-
68
- // The message should never be empty, if they are, ignore.
69
- if ( empty( $notification['content'] ) ) {
70
- continue;
71
- }
72
-
73
- // Ignore if notification has already been dismissed.
74
- if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
75
- continue;
76
- }
77
-
78
- $data[ $key ] = $notification;
79
- }
80
-
81
- return $data;
82
- }
83
-
84
- /**
85
- * Verify saved notification data for active notifications.
86
- *
87
- * @since 2.18
88
- *
89
- * @param array $notifications Array of notifications items to verify.
90
- *
91
- * @return array
92
- */
93
- public function verify_active( $notifications ) {
94
- if ( ! is_array( $notifications ) || empty( $notifications ) ) {
95
- return array();
96
- }
97
-
98
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
99
- $current_time = ctf_get_current_time();
100
-
101
- // rating notice logic
102
- $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
103
- $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
104
- $should_show_rating_notice = ($ctf_rating_notice_waiting !== 'waiting' && $ctf_rating_notice_option !== 'dismissed');
105
-
106
- // new user discount logic
107
- $in_new_user_month_range = true;
108
- $should_show_new_user_discount = false;
109
- $has_been_one_month_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + ((int)$notifications['review']['wait'] * DAY_IN_SECONDS)) < $current_time + 1: true;
110
-
111
- if ( isset( $ctf_statuses_option['first_install'] ) && $ctf_statuses_option['first_install'] === 'from_update' ) {
112
- global $current_user;
113
- $user_id = $current_user->ID;
114
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
115
- $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
116
- if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
117
- $should_show_new_user_discount = true;
118
- }
119
- } elseif ( $in_new_user_month_range && $has_been_one_month_since_rating_dismissal && $ctf_rating_notice_waiting !== 'waiting' ) {
120
- global $current_user;
121
- $user_id = $current_user->ID;
122
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
123
- $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
124
-
125
- if ( $ignore_new_user_sale_notice_meta !== 'always'
126
- && isset( $ctf_statuses_option['first_install'] )
127
- && $current_time > (int)$ctf_statuses_option['first_install'] + ((int)$notifications['discount']['wait'] * DAY_IN_SECONDS) ) {
128
- $should_show_new_user_discount = true;
129
- }
130
- }
131
-
132
- if ( isset( $notifications['review'] ) && $should_show_rating_notice ) {
133
- return array( $notifications['review'] );
134
- } elseif ( isset( $notifications['discount'] ) && $should_show_new_user_discount ) {
135
- return array( $notifications['discount'] );
136
- }
137
-
138
- return array();
139
- }
140
-
141
- /**
142
- * Get notification data.
143
- *
144
- * @since 2.18
145
- *
146
- * @return array
147
- */
148
- public function get() {
149
- if ( ! $this->has_access() ) {
150
- return array();
151
- }
152
-
153
- $option = $this->get_option();
154
-
155
- // Only update if does not exist.
156
- if ( empty( $option['update'] ) ) {
157
- $this->update();
158
- }
159
-
160
- $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
161
- $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
162
-
163
- return array_merge( $events, $feed );
164
- }
165
-
166
- /**
167
- * Add a manual notification event.
168
- *
169
- * @since 2.18
170
- *
171
- * @param array $notification Notification data.
172
- */
173
- public function add( $notification ) {
174
- if ( empty( $notification['id'] ) ) {
175
- return;
176
- }
177
-
178
- $option = $this->get_option();
179
-
180
- if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
181
- return;
182
- }
183
-
184
- foreach ( $option['events'] as $item ) {
185
- if ( $item['id'] === $notification['id'] ) {
186
- return;
187
- }
188
- }
189
-
190
- $notification = $this->verify( array( $notification ) );
191
-
192
- update_option(
193
- $this->option_name(),
194
- array(
195
- 'update' => $option['update'],
196
- 'feed' => $option['feed'],
197
- 'events' => array_merge( $notification, $option['events'] ),
198
- 'dismissed' => $option['dismissed'],
199
- )
200
- );
201
- }
202
-
203
- /**
204
- * Update notification data from feed.
205
- *
206
- * @since 2.18
207
- */
208
- public function update() {
209
- $feed = $this->fetch_feed();
210
- $option = $this->get_option();
211
-
212
- update_option(
213
- $this->option_name(),
214
- array(
215
- 'update' => time(),
216
- 'feed' => $feed,
217
- 'events' => $option['events'],
218
- 'dismissed' => $option['dismissed'],
219
- )
220
- );
221
- }
222
-
223
- /**
224
- * Do not enqueue anything extra.
225
- *
226
- * @since 2.18
227
- */
228
- public function enqueues() {
229
-
230
- }
231
-
232
- /**
233
- * Output notifications on Form Overview admin area.
234
- *
235
- * @since 2.18
236
- */
237
- public function output() {
238
- // If the Instagram Feed plugin is active, notices only shown on CTF Settings pages
239
- if ( function_exists( 'sb_instagram_activate' )
240
- && ! function_exists( 'sb_instagram_feed_pro_init' ) ) {
241
- return;
242
- }
243
-
244
- if ( function_exists( 'cff_check_for_db_updates' ) ) {
245
- return;
246
- }
247
-
248
- $notifications = $this->get();
249
-
250
- if ( empty( $notifications ) ) {
251
- return;
252
- }
253
-
254
- // new user notices included in regular settings page notifications so this
255
- // checks to see if user is one of those pages
256
- if ( ! empty( $_GET['page'] )
257
- && strpos( $_GET['page'], 'custom-twitter-feeds' ) !== false ) {
258
- return;
259
- }
260
-
261
- $content_allowed_tags = array(
262
- 'em' => array(),
263
- 'strong' => array(),
264
- 'span' => array(
265
- 'style' => array(),
266
- ),
267
- 'a' => array(
268
- 'href' => array(),
269
- 'target' => array(),
270
- 'rel' => array(),
271
- ),
272
- );
273
- $image_overlay = '';
274
-
275
- foreach ( $notifications as $notification ) {
276
- $type = sanitize_text_field( $notification['id'] );
277
- $img_src = CTF_PLUGIN_URL . 'img/' . sanitize_text_field( $notification['image'] );
278
- $content = '';
279
- if ( ! empty( $notification['content'] ) ) {
280
- $content = wp_kses( $this->replace_merge_fields( $notification['content'], $notification ), $content_allowed_tags );
281
- }
282
- $buttons = array();
283
- if ( ! empty( $notification['btns'] ) && is_array( $notification['btns'] ) ) {
284
- foreach ( $notification['btns'] as $btn_type => $btn ) {
285
- if ( ! is_array( $btn['url'] ) ) {
286
- $buttons[ $btn_type ]['url'] = $this->replace_merge_fields( $btn['url'], $notification );
287
- } elseif ( is_array( $btn['url'] ) ) {
288
- $buttons[ $btn_type ]['url'] = add_query_arg( $btn['url'] );
289
- }
290
-
291
- $buttons[ $btn_type ]['attr'] = '';
292
- if ( ! empty( $btn['attr'] ) ) {
293
- $buttons[ $btn_type ]['attr'] = ' target="_blank" rel="noopener noreferrer"';
294
- }
295
-
296
- $buttons[ $btn_type ]['class'] = '';
297
- if ( ! empty( $btn['class'] ) ) {
298
- $buttons[ $btn_type ]['class'] = ' ' . $btn['class'];
299
- }
300
-
301
- $buttons[ $btn_type ]['text'] = '';
302
- if ( ! empty( $btn['text'] ) ) {
303
- $buttons[ $btn_type ]['text'] = wp_kses( $btn['text'], $content_allowed_tags );
304
- }
305
- }
306
- }
307
- if ( isset( $notification['image_overlay'] ) ) {
308
- $image_overlay = '<div class="img-overlay">'. esc_html( $notification['image_overlay'] ).'</div>';
309
- }
310
- }
311
- ?>
312
-
313
- <div class="ctf_notice ctf_<?php echo esc_attr( $type ); ?>_notice">
314
- <div class="ctf_thumb">
315
- <img src="<?php echo esc_url( $img_src ); ?>" alt="notice">
316
- <?php echo $image_overlay; ?>
317
- </div>
318
- <div class="ctf-notice-text">
319
- <p style="padding-top: 4px;"><?php echo $content; ?></p>
320
- <p class="links">
321
- <?php foreach ( $buttons as $button ) : ?>
322
- <a class="<?php echo esc_attr( $button['class'] ); ?>" href="<?php echo esc_attr( $button['url'] ); ?>"<?php echo $button['attr']; ?>><?php echo $button['text']; ?></a>
323
- <?php endforeach; ?>
324
- </p>
325
- </div>
326
- <a class="ctf_notice_close" href="<?php echo add_query_arg( array( 'ctf_dismiss' => $type ) ); ?>"><i class="fa fa-close"></i></a>
327
- </div>
328
- <?php
329
- }
330
-
331
- /**
332
- * Hide messages permanently or some can be dismissed temporarily
333
- *
334
- * @since 2.18
335
- */
336
- public function dismiss() {
337
- global $current_user;
338
- $user_id = $current_user->ID;
339
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
340
-
341
- if ( isset( $_GET['ctf_ignore_rating_notice_nag'] ) ) {
342
- if ( (int)$_GET['ctf_ignore_rating_notice_nag'] === 1 ) {
343
- update_option( 'ctf_rating_notice', 'dismissed', false );
344
- $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
345
- update_option( 'ctf_statuses', $ctf_statuses_option, false );
346
-
347
- } elseif ( $_GET['ctf_ignore_rating_notice_nag'] === 'later' ) {
348
- set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
349
- update_option( 'ctf_rating_notice', 'pending', false );
350
- }
351
- }
352
-
353
- if ( isset( $_GET['ctf_ignore_new_user_sale_notice'] ) ) {
354
- $response = sanitize_text_field( $_GET['ctf_ignore_new_user_sale_notice'] );
355
- if ( $response === 'always' ) {
356
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
357
-
358
- $current_month_number = (int)date('n', ctf_get_current_time() );
359
- $not_early_in_the_year = ($current_month_number > 5);
360
-
361
- if ( $not_early_in_the_year ) {
362
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
363
- }
364
-
365
- }
366
- }
367
-
368
- if ( isset( $_GET['ctf_ignore_bfcm_sale_notice'] ) ) {
369
- $response = sanitize_text_field( $_GET['ctf_ignore_bfcm_sale_notice'] );
370
- if ( $response === 'always' ) {
371
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', 'always' );
372
- } elseif ( $response === date( 'Y', ctf_get_current_time() ) ) {
373
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
374
- }
375
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
376
- }
377
-
378
- if ( isset( $_GET['ctf_dismiss'] ) ) {
379
- if ( $_GET['ctf_dismiss'] === 'review' ) {
380
- update_option( 'ctf_rating_notice', 'dismissed', false );
381
- $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
382
- update_option( 'ctf_statuses', $ctf_statuses_option, false );
383
- } elseif ( $_GET['ctf_dismiss'] === 'discount' ) {
384
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
385
-
386
- $current_month_number = (int)date('n', ctf_get_current_time() );
387
- $not_early_in_the_year = ($current_month_number > 5);
388
-
389
- if ( $not_early_in_the_year ) {
390
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
391
- }
392
- }
393
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
394
- }
395
- }
396
- }
 
1
+ <?php
2
+ /**
3
+ * CTF_New_User.
4
+ *
5
+ * @since 2.18
6
+ */
7
+
8
+ // Exit if accessed directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class CTF_New_User extends CTF_Notifications {
14
+
15
+ /**
16
+ * Source of notifications content.
17
+ *
18
+ * @since 2.18
19
+ *
20
+ * @var string
21
+ */
22
+ const SOURCE_URL = 'http://plugin.smashballoon.com/newuser.json';
23
+
24
+ /**
25
+ * @var string
26
+ */
27
+ const OPTION_NAME = 'ctf_newuser_notifications';
28
+
29
+ /**
30
+ * Register hooks.
31
+ *
32
+ * @since 2.18
33
+ */
34
+ public function hooks() {
35
+ add_action( 'admin_notices', array( $this, 'output' ), 8 );
36
+
37
+ add_action( 'admin_init', array( $this, 'dismiss' ) );
38
+ }
39
+
40
+ public function option_name() {
41
+ return self::OPTION_NAME;
42
+ }
43
+
44
+ public function source_url() {
45
+ return self::SOURCE_URL;
46
+ }
47
+
48
+ /**
49
+ * Verify notification data before it is saved.
50
+ *
51
+ * @param array $notifications Array of notifications items to verify.
52
+ *
53
+ * @return array
54
+ *
55
+ * @since 2.18
56
+ */
57
+ public function verify( $notifications ) {
58
+ $data = array();
59
+
60
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
61
+ return $data;
62
+ }
63
+
64
+ $option = $this->get_option();
65
+
66
+ foreach ( $notifications as $key => $notification ) {
67
+
68
+ // The message should never be empty, if they are, ignore.
69
+ if ( empty( $notification['content'] ) ) {
70
+ continue;
71
+ }
72
+
73
+ // Ignore if notification has already been dismissed.
74
+ if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
75
+ continue;
76
+ }
77
+
78
+ $data[ $key ] = $notification;
79
+ }
80
+
81
+ return $data;
82
+ }
83
+
84
+ /**
85
+ * Verify saved notification data for active notifications.
86
+ *
87
+ * @since 2.18
88
+ *
89
+ * @param array $notifications Array of notifications items to verify.
90
+ *
91
+ * @return array
92
+ */
93
+ public function verify_active( $notifications ) {
94
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
95
+ return array();
96
+ }
97
+
98
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
99
+ $current_time = ctf_get_current_time();
100
+
101
+ // rating notice logic
102
+ $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
103
+ $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
104
+ $should_show_rating_notice = ($ctf_rating_notice_waiting !== 'waiting' && $ctf_rating_notice_option !== 'dismissed');
105
+
106
+ // new user discount logic
107
+ $in_new_user_month_range = true;
108
+ $should_show_new_user_discount = false;
109
+ $has_been_one_month_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + ((int)$notifications['review']['wait'] * DAY_IN_SECONDS)) < $current_time + 1: true;
110
+
111
+ if ( isset( $ctf_statuses_option['first_install'] ) && $ctf_statuses_option['first_install'] === 'from_update' ) {
112
+ global $current_user;
113
+ $user_id = $current_user->ID;
114
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
115
+ $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
116
+ if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
117
+ $should_show_new_user_discount = true;
118
+ }
119
+ } elseif ( $in_new_user_month_range && $has_been_one_month_since_rating_dismissal && $ctf_rating_notice_waiting !== 'waiting' ) {
120
+ global $current_user;
121
+ $user_id = $current_user->ID;
122
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
123
+ $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
124
+
125
+ if ( $ignore_new_user_sale_notice_meta !== 'always'
126
+ && isset( $ctf_statuses_option['first_install'] )
127
+ && $current_time > (int)$ctf_statuses_option['first_install'] + ((int)$notifications['discount']['wait'] * DAY_IN_SECONDS) ) {
128
+ $should_show_new_user_discount = true;
129
+ }
130
+ }
131
+
132
+ if ( isset( $notifications['review'] ) && $should_show_rating_notice ) {
133
+ return array( $notifications['review'] );
134
+ } elseif ( isset( $notifications['discount'] ) && $should_show_new_user_discount ) {
135
+ return array( $notifications['discount'] );
136
+ }
137
+
138
+ return array();
139
+ }
140
+
141
+ /**
142
+ * Get notification data.
143
+ *
144
+ * @since 2.18
145
+ *
146
+ * @return array
147
+ */
148
+ public function get() {
149
+ if ( ! $this->has_access() ) {
150
+ return array();
151
+ }
152
+
153
+ $option = $this->get_option();
154
+
155
+ // Only update if does not exist.
156
+ if ( empty( $option['update'] ) ) {
157
+ $this->update();
158
+ }
159
+
160
+ $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
161
+ $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
162
+
163
+ return array_merge( $events, $feed );
164
+ }
165
+
166
+ /**
167
+ * Add a manual notification event.
168
+ *
169
+ * @since 2.18
170
+ *
171
+ * @param array $notification Notification data.
172
+ */
173
+ public function add( $notification ) {
174
+ if ( empty( $notification['id'] ) ) {
175
+ return;
176
+ }
177
+
178
+ $option = $this->get_option();
179
+
180
+ if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
181
+ return;
182
+ }
183
+
184
+ foreach ( $option['events'] as $item ) {
185
+ if ( $item['id'] === $notification['id'] ) {
186
+ return;
187
+ }
188
+ }
189
+
190
+ $notification = $this->verify( array( $notification ) );
191
+
192
+ update_option(
193
+ $this->option_name(),
194
+ array(
195
+ 'update' => $option['update'],
196
+ 'feed' => $option['feed'],
197
+ 'events' => array_merge( $notification, $option['events'] ),
198
+ 'dismissed' => $option['dismissed'],
199
+ )
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Update notification data from feed.
205
+ *
206
+ * @since 2.18
207
+ */
208
+ public function update() {
209
+ $feed = $this->fetch_feed();
210
+ $option = $this->get_option();
211
+
212
+ update_option(
213
+ $this->option_name(),
214
+ array(
215
+ 'update' => time(),
216
+ 'feed' => $feed,
217
+ 'events' => $option['events'],
218
+ 'dismissed' => $option['dismissed'],
219
+ )
220
+ );
221
+ }
222
+
223
+ /**
224
+ * Do not enqueue anything extra.
225
+ *
226
+ * @since 2.18
227
+ */
228
+ public function enqueues() {
229
+
230
+ }
231
+
232
+ /**
233
+ * Output notifications on Form Overview admin area.
234
+ *
235
+ * @since 2.18
236
+ */
237
+ public function output() {
238
+ // If the Instagram Feed plugin is active, notices only shown on CTF Settings pages
239
+ if ( function_exists( 'sb_instagram_activate' )
240
+ && ! function_exists( 'sb_instagram_feed_pro_init' ) ) {
241
+ return;
242
+ }
243
+
244
+ if ( function_exists( 'cff_check_for_db_updates' ) ) {
245
+ return;
246
+ }
247
+
248
+ $notifications = $this->get();
249
+
250
+ if ( empty( $notifications ) ) {
251
+ return;
252
+ }
253
+
254
+ // new user notices included in regular settings page notifications so this
255
+ // checks to see if user is one of those pages
256
+ if ( ! empty( $_GET['page'] )
257
+ && strpos( $_GET['page'], 'custom-twitter-feeds' ) !== false ) {
258
+ return;
259
+ }
260
+
261
+ $content_allowed_tags = array(
262
+ 'em' => array(),
263
+ 'strong' => array(),
264
+ 'span' => array(
265
+ 'style' => array(),
266
+ ),
267
+ 'a' => array(
268
+ 'href' => array(),
269
+ 'target' => array(),
270
+ 'rel' => array(),
271
+ ),
272
+ );
273
+ $image_overlay = '';
274
+
275
+ foreach ( $notifications as $notification ) {
276
+ $type = sanitize_text_field( $notification['id'] );
277
+ $close_href = add_query_arg( array( 'ctf_dismiss' => $type ) );
278
+ $img_src = CTF_PLUGIN_URL . 'img/' . sanitize_text_field( $notification['image'] );
279
+ $content = '';
280
+ if ( ! empty( $notification['content'] ) ) {
281
+ $content = wp_kses( $this->replace_merge_fields( $notification['content'], $notification ), $content_allowed_tags );
282
+ }
283
+ $buttons = array();
284
+ if ( ! empty( $notification['btns'] ) && is_array( $notification['btns'] ) ) {
285
+ foreach ( $notification['btns'] as $btn_type => $btn ) {
286
+ if ( ! is_array( $btn['url'] ) ) {
287
+ $buttons[ $btn_type ]['url'] = $this->replace_merge_fields( $btn['url'], $notification );
288
+ } elseif ( is_array( $btn['url'] ) ) {
289
+ $buttons[ $btn_type ]['url'] = add_query_arg( $btn['url'] );
290
+ }
291
+
292
+ $buttons[ $btn_type ]['attr'] = '';
293
+ if ( ! empty( $btn['attr'] ) ) {
294
+ $buttons[ $btn_type ]['attr'] = ' target="_blank" rel="noopener noreferrer"';
295
+ }
296
+
297
+ $buttons[ $btn_type ]['class'] = '';
298
+ if ( ! empty( $btn['class'] ) ) {
299
+ $buttons[ $btn_type ]['class'] = ' ' . $btn['class'];
300
+ }
301
+
302
+ $buttons[ $btn_type ]['text'] = '';
303
+ if ( ! empty( $btn['text'] ) ) {
304
+ $buttons[ $btn_type ]['text'] = wp_kses( $btn['text'], $content_allowed_tags );
305
+ }
306
+ }
307
+ }
308
+ if ( isset( $notification['image_overlay'] ) ) {
309
+ $image_overlay = '<div class="img-overlay">'. esc_html( $notification['image_overlay'] ).'</div>';
310
+ }
311
+ }
312
+ ?>
313
+
314
+ <div class="ctf_notice ctf_<?php echo esc_attr( $type ); ?>_notice">
315
+ <div class="ctf_thumb">
316
+ <img src="<?php echo esc_url( $img_src ); ?>" alt="notice">
317
+ <?php echo $image_overlay; ?>
318
+ </div>
319
+ <div class="ctf-notice-text">
320
+ <p style="padding-top: 4px;"><?php echo $content; ?></p>
321
+ <p class="links">
322
+ <?php foreach ( $buttons as $button ) : ?>
323
+ <a class="<?php echo esc_attr( $button['class'] ); ?>" href="<?php echo esc_attr( $button['url'] ); ?>"<?php echo $button['attr']; ?>><?php echo $button['text']; ?></a>
324
+ <?php endforeach; ?>
325
+ </p>
326
+ </div>
327
+ <a class="ctf_notice_close" href="<?php echo esc_attr( $close_href ); ?>"><i class="fa fa-close"></i></a>
328
+ </div>
329
+ <?php
330
+ }
331
+
332
+ /**
333
+ * Hide messages permanently or some can be dismissed temporarily
334
+ *
335
+ * @since 2.18
336
+ */
337
+ public function dismiss() {
338
+ global $current_user;
339
+ $user_id = $current_user->ID;
340
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
341
+
342
+ if ( isset( $_GET['ctf_ignore_rating_notice_nag'] ) ) {
343
+ if ( (int)$_GET['ctf_ignore_rating_notice_nag'] === 1 ) {
344
+ update_option( 'ctf_rating_notice', 'dismissed', false );
345
+ $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
346
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
347
+
348
+ } elseif ( $_GET['ctf_ignore_rating_notice_nag'] === 'later' ) {
349
+ set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
350
+ update_option( 'ctf_rating_notice', 'pending', false );
351
+ }
352
+ }
353
+
354
+ if ( isset( $_GET['ctf_ignore_new_user_sale_notice'] ) ) {
355
+ $response = sanitize_text_field( $_GET['ctf_ignore_new_user_sale_notice'] );
356
+ if ( $response === 'always' ) {
357
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
358
+
359
+ $current_month_number = (int)date('n', ctf_get_current_time() );
360
+ $not_early_in_the_year = ($current_month_number > 5);
361
+
362
+ if ( $not_early_in_the_year ) {
363
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
364
+ }
365
+
366
+ }
367
+ }
368
+
369
+ if ( isset( $_GET['ctf_ignore_bfcm_sale_notice'] ) ) {
370
+ $response = sanitize_text_field( $_GET['ctf_ignore_bfcm_sale_notice'] );
371
+ if ( $response === 'always' ) {
372
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', 'always' );
373
+ } elseif ( $response === date( 'Y', ctf_get_current_time() ) ) {
374
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
375
+ }
376
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
377
+ }
378
+
379
+ if ( isset( $_GET['ctf_dismiss'] ) ) {
380
+ if ( $_GET['ctf_dismiss'] === 'review' ) {
381
+ update_option( 'ctf_rating_notice', 'dismissed', false );
382
+ $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
383
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
384
+ } elseif ( $_GET['ctf_dismiss'] === 'discount' ) {
385
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
386
+
387
+ $current_month_number = (int)date('n', ctf_get_current_time() );
388
+ $not_early_in_the_year = ($current_month_number > 5);
389
+
390
+ if ( $not_early_in_the_year ) {
391
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
392
+ }
393
+ }
394
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
395
+ }
396
+ }
397
+ }
inc/admin/class-ctf-notifications.php CHANGED
@@ -1,581 +1,581 @@
1
- <?php
2
- /**
3
- * CTF_Notifications.
4
- *
5
- * @since 1.7/1.11
6
- */
7
-
8
- // Exit if accessed directly
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class CTF_Notifications {
14
-
15
- /**
16
- * Source of notifications content.
17
- *
18
- * @var string
19
- */
20
- const SOURCE_URL = 'http://plugin.smashballoon.com/notifications.json';
21
-
22
- /**
23
- * @var string
24
- */
25
- const OPTION_NAME = 'ctf_notifications';
26
-
27
- /**
28
- * JSON data contains notices for all plugins. This is used
29
- * to select messages only meant for this plugin
30
- *
31
- * @var string
32
- */
33
- const PLUGIN = 'twitter';
34
-
35
- /**
36
- * Option value.
37
- *
38
- * @since 1.7/1.11
39
- *
40
- * @var bool|array
41
- */
42
- public $option = false;
43
-
44
- /**
45
- * Initialize class.
46
- *
47
- * @since 1.7/1.11
48
- */
49
- public function init() {
50
- $this->hooks();
51
- }
52
-
53
- /**
54
- * Use this function to get the option name to allow
55
- * inheritance for the New_User class
56
- *
57
- * @return string
58
- */
59
- public function option_name() {
60
- return self::OPTION_NAME;
61
- }
62
-
63
- /**
64
- * Use this function to get the source URL to allow
65
- * inheritance for the New_User class
66
- *
67
- * @return string
68
- */
69
- public function source_url() {
70
- return self::SOURCE_URL;
71
- }
72
-
73
- /**
74
- * Register hooks.
75
- *
76
- * @since 1.7/1.11
77
- */
78
- public function hooks() {
79
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueues' ) );
80
-
81
- add_action( 'ctf_admin_overview_before_title', array( $this, 'output' ) );
82
-
83
- // on cron. Once a week?
84
- add_action( 'ctf_notification_update', array( $this, 'update' ) );
85
-
86
- add_action( 'wp_ajax_ctf_dashboard_notification_dismiss', array( $this, 'dismiss' ) );
87
- }
88
-
89
-
90
- /**
91
- * Check if user has access and is enabled.
92
- *
93
- * @since 1.7/1.11
94
- *
95
- * @return bool
96
- */
97
- public function has_access() {
98
- $access = false;
99
-
100
- if ( current_user_can( 'manage_options' ) ) {
101
- $access = true;
102
- }
103
-
104
- return apply_filters( 'ctf_admin_notifications_has_access', $access );
105
- }
106
-
107
- /**
108
- * Get option value.
109
- *
110
- * @since 1.7/1.11
111
- *
112
- * @param bool $cache Reference property cache if available.
113
- *
114
- * @return array
115
- */
116
- public function get_option( $cache = true ) {
117
- if ( $this->option && $cache ) {
118
- return $this->option;
119
- }
120
-
121
- $option = get_option( $this->option_name(), array() );
122
-
123
- $this->option = array(
124
- 'update' => ! empty( $option['update'] ) ? $option['update'] : 0,
125
- 'events' => ! empty( $option['events'] ) ? $option['events'] : array(),
126
- 'feed' => ! empty( $option['feed'] ) ? $option['feed'] : array(),
127
- 'dismissed' => ! empty( $option['dismissed'] ) ? $option['dismissed'] : array(),
128
- );
129
-
130
- return $this->option;
131
- }
132
-
133
- /**
134
- * Fetch notifications from feed.
135
- *
136
- * @since 1.7/1.11
137
- *
138
- * @return array
139
- */
140
- public function fetch_feed() {
141
- $res = wp_remote_get( $this->source_url() );
142
-
143
- if ( is_wp_error( $res ) ) {
144
- return array();
145
- }
146
-
147
- $body = wp_remote_retrieve_body( $res );
148
-
149
- if ( empty( $body ) ) {
150
- return array();
151
- }
152
-
153
- $body = str_replace( array( 'sbi_', 'sbi-' ), array( 'ctf_', 'ctf-' ), $body );
154
-
155
- return $this->verify( json_decode( $body, true ) );
156
- }
157
-
158
- /**
159
- * Verify notification data before it is saved.
160
- *
161
- * @since 1.7/1.11
162
- *
163
- * @param array $notifications Array of notifications items to verify.
164
- *
165
- * @return array
166
- */
167
- public function verify( $notifications ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
168
- $data = array();
169
-
170
- if ( ! is_array( $notifications ) || empty( $notifications ) ) {
171
- return $data;
172
- }
173
-
174
- $option = $this->get_option();
175
-
176
- foreach ( $notifications as $notification ) {
177
-
178
- // The message and license should never be empty, if they are, ignore.
179
- if ( empty( $notification['content'] ) || empty( $notification['type'] ) ) {
180
- continue;
181
- }
182
-
183
- // Ignore if license type does not match.
184
- $license = ctf_is_pro_version() ? 'pro' : 'free';
185
-
186
- if ( ! in_array( $license, $notification['type'], true ) ) {
187
- continue;
188
- }
189
-
190
- // Ignore if expired.
191
- if ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) {
192
- continue;
193
- }
194
-
195
- // Ignore if notification has already been dismissed.
196
- if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
197
- continue;
198
- }
199
-
200
- // TODO: Ignore if notification existed before installing CTF.
201
- // Prevents bombarding the user with notifications after activation.
202
- $activated = false;
203
- if ( ! empty( $activated )
204
- && ! empty( $notification['start'] )
205
- && $activated > strtotime( $notification['start'] ) ) {
206
- continue;
207
- }
208
-
209
- $data[] = $notification;
210
- }
211
-
212
- return $data;
213
- }
214
-
215
- /**
216
- * Verify saved notification data for active notifications.
217
- *
218
- * @since 1.7/1.11
219
- *
220
- * @param array $notifications Array of notifications items to verify.
221
- *
222
- * @return array
223
- */
224
- public function verify_active( $notifications ) {
225
- if ( ! is_array( $notifications ) || empty( $notifications ) ) {
226
- return array();
227
- }
228
-
229
- // Remove notfications that are not active.
230
- foreach ( $notifications as $key => $notification ) {
231
- if ( ( ! empty( $notification['start'] ) && ctf_get_current_time() < strtotime( $notification['start'] ) )
232
- || ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) ) {
233
- unset( $notifications[ $key ] );
234
- }
235
- }
236
-
237
- return $notifications;
238
- }
239
-
240
- /**
241
- * Get notification data.
242
- *
243
- * @since 1.7/1.11
244
- *
245
- * @return array
246
- */
247
- public function get() {
248
- if ( ! $this->has_access() ) {
249
- return array();
250
- }
251
-
252
- $option = $this->get_option();
253
-
254
- // Update notifications using async task.
255
- if ( empty( $option['update'] ) || ctf_get_current_time() > $option['update'] + DAY_IN_SECONDS ) {
256
- $this->update();
257
- }
258
-
259
- $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
260
- $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
261
-
262
- // If there is a new user notification, add it to the beginning of the notification list
263
- $ctf_newuser = new CTF_New_User();
264
- $newuser_notifications = $ctf_newuser->get();
265
-
266
- if ( ! empty( $newuser_notifications ) ) {
267
- $events = array_merge( $newuser_notifications, $events );
268
- }
269
-
270
- return array_merge( $events, $feed );
271
- }
272
-
273
- /**
274
- * Get notification count.
275
- *
276
- * @since 1.7/1.11
277
- *
278
- * @return int
279
- */
280
- public function get_count() {
281
- return count( $this->get() );
282
- }
283
-
284
- /**
285
- * Add a manual notification event.
286
- *
287
- * @since 1.7/1.11
288
- *
289
- * @param array $notification Notification data.
290
- */
291
- public function add( $notification ) {
292
- if ( empty( $notification['id'] ) ) {
293
- return;
294
- }
295
-
296
- $option = $this->get_option();
297
-
298
- if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
299
- return;
300
- }
301
-
302
- foreach ( $option['events'] as $item ) {
303
- if ( $item['id'] === $notification['id'] ) {
304
- return;
305
- }
306
- }
307
-
308
- $notification = $this->verify( array( $notification ) );
309
-
310
- update_option(
311
- 'ctf_notifications',
312
- array(
313
- 'update' => $option['update'],
314
- 'feed' => $option['feed'],
315
- 'events' => array_merge( $notification, $option['events'] ),
316
- 'dismissed' => $option['dismissed'],
317
- )
318
- );
319
- }
320
-
321
- /**
322
- * Update notification data from feed.
323
- *
324
- * @since 1.7/1.11
325
- */
326
- public function update() {
327
- $feed = $this->fetch_feed();
328
- $option = $this->get_option();
329
-
330
- update_option(
331
- 'ctf_notifications',
332
- array(
333
- 'update' => ctf_get_current_time(),
334
- 'feed' => $feed,
335
- 'events' => $option['events'],
336
- 'dismissed' => $option['dismissed'],
337
- )
338
- );
339
- }
340
-
341
- /**
342
- * Admin area Form Overview enqueues.
343
- *
344
- * @since 1.7/1.11
345
- */
346
- public function enqueues() {
347
- if ( ! $this->has_access() ) {
348
- return;
349
- }
350
-
351
- $notifications = $this->get();
352
-
353
- if ( empty( $notifications ) ) {
354
- return;
355
- }
356
-
357
- $min = '';
358
-
359
- wp_enqueue_style(
360
- 'ctf-admin-notifications',
361
- CTF_PLUGIN_URL . "css/admin-notifications{$min}.css",
362
- array(),
363
- CTF_VERSION
364
- );
365
-
366
- wp_enqueue_script(
367
- 'ctf-admin-notifications',
368
- CTF_PLUGIN_URL . "js/admin-notifications{$min}.js",
369
- array( 'jquery' ),
370
- CTF_VERSION,
371
- true
372
- );
373
-
374
- wp_localize_script( 'ctf-admin-notifications', 'ctf_admin', array(
375
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
376
- 'nonce' => wp_create_nonce( 'ctf-admin' )
377
- )
378
- );
379
- }
380
-
381
- /**
382
- * Fields from the remote source contain placeholders to allow
383
- * some messages to be used for multiple plugins.
384
- *
385
- * @param $content string
386
- * @param $notification array
387
- *
388
- * @return string
389
- *
390
- * @since 1.7/1.11
391
- */
392
- public function replace_merge_fields( $content, $notification ) {
393
- $merge_fields = array(
394
- '{plugin}' => 'Custom Twitter Feeds',
395
- '{amount}' => isset( $notification['amount'] ) ? $notification['amount'] : '',
396
- '{platform}' => 'Twitter',
397
- '{lowerplatform}' => 'twitter',
398
- '{review-url}' => 'https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/',
399
- '{slug}' => 'custom-twitter-feed',
400
- '{campaign}' => 'twitter-free'
401
- );
402
-
403
- if ( ctf_is_pro_version() ) {
404
- $merge_fields['{campaign}'] = 'twitter-pro';
405
- $merge_fields['{plugin}'] = 'Custom Twitter Feeds Pro';
406
- }
407
-
408
- foreach ( $merge_fields as $find => $replace ) {
409
- $content = str_replace( $find, $replace, $content );
410
- }
411
-
412
- return $content;
413
- }
414
-
415
- /**
416
- * Output notifications on Custom Twitter Feed admin area.
417
- *
418
- * @since 1.7/1.11
419
- */
420
- public function output() {
421
- $notifications = $this->get();
422
-
423
- if ( empty( $notifications ) ) {
424
- return;
425
- }
426
-
427
- $notifications_html = '';
428
- $current_class = ' current';
429
- $content_allowed_tags = array(
430
- 'em' => array(),
431
- 'strong' => array(),
432
- 'span' => array(
433
- 'style' => array(),
434
- ),
435
- 'a' => array(
436
- 'href' => array(),
437
- 'target' => array(),
438
- 'rel' => array(),
439
- ),
440
- );
441
-
442
- foreach ( $notifications as $notification ) {
443
-
444
- // Buttons HTML.
445
- $buttons_html = '';
446
- if ( ! empty( $notification['btns'] ) && is_array( $notification['btns'] ) ) {
447
- foreach ( $notification['btns'] as $btn_type => $btn ) {
448
- if ( is_array( $btn['url'] ) ) {
449
- $btn['url'] = add_query_arg( $btn['url'] );
450
- }
451
- if ( ! empty( $btn['attr'] ) ) {
452
- $btn['target'] = '_blank';
453
- }
454
- $buttons_html .= sprintf(
455
- '<a href="%1$s" class="button button-%2$s"%3$s>%4$s</a>',
456
- ! empty( $btn['url'] ) ? esc_url( $this->replace_merge_fields( $btn['url'], $notification ) ) : '',
457
- $btn_type === 'primary' ? 'primary' : 'secondary',
458
- ! empty( $btn['target'] ) && $btn['target'] === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : '',
459
- ! empty( $btn['text'] ) ? sanitize_text_field( $btn['text'] ) : ''
460
- );
461
- }
462
- $buttons_html = ! empty( $buttons_html ) ? '<div class="buttons">' . $buttons_html . '</div>' : '';
463
- }
464
-
465
- if ( empty( $notification['image'] ) ) {
466
- $image_html = '<div class="bell">';
467
-
468
- $image_html .= '<svg xmlns="http://www.w3.org/2000/svg" width="42" height="48" viewBox="0 0 42 48"><defs><style>.a{fill:#777;}.b{fill:#ca4a1f;}</style></defs><path class="a" d="M23-79a6.005,6.005,0,0,1-6-6h10.06a12.066,12.066,0,0,0,1.791,1.308,6.021,6.021,0,0,1-2.077,3.352A6.008,6.008,0,0,1,23-79Zm1.605-9H5.009a2.955,2.955,0,0,1-2.173-.923A3.088,3.088,0,0,1,2-91a2.919,2.919,0,0,1,.807-2.036c.111-.12.229-.243.351-.371a14.936,14.936,0,0,0,3.126-4.409A23.283,23.283,0,0,0,8.007-107.5a14.846,14.846,0,0,1,.906-5.145,14.5,14.5,0,0,1,2.509-4.324A15.279,15.279,0,0,1,20-122.046V-124a3,3,0,0,1,3-3,3,3,0,0,1,3,3v1.954a15.28,15.28,0,0,1,8.58,5.078,14.5,14.5,0,0,1,2.509,4.324,14.846,14.846,0,0,1,.906,5.145c0,.645.016,1.281.047,1.888A12.036,12.036,0,0,0,35-106a11.921,11.921,0,0,0-8.485,3.515A11.923,11.923,0,0,0,23-94a12,12,0,0,0,1.6,6Z" transform="translate(-2 127)"/><circle class="b" cx="9" cy="9" r="9" transform="translate(24 24)"/></svg>';
469
- $image_html .= '</div>';
470
- } else {
471
- if ( $notification['image'] === 'balloon'
472
- || $notification['id'] === 'review'
473
- || $notification['id'] === 'discount') {
474
- $image_html = '<div class="bell">';
475
-
476
- $image_html .= '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1438 1878" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2">';
477
- $image_html .= ' <path d="M671.51004 492.9884C539.9423 433.8663 402.90125 345.5722 274.97656 304.47286c45.45163 108.39592 83.81332 223.88017 123.51 338.03105C319.308 702.00293 226.8217 748.19258 138.46278 798.51607c75.1914 74.32371 181.67968 117.34651 266.52444 182.01607-67.96124 83.86195-201.48527 171.01801-234.02107 247.01998 140.6922-17.6268 304.63688-46.21031 435.53794-52.00418 28.76427 144.58328 43.5987 303.09763 84.50756 435.53713 60.92033-175.26574 116.0014-356.37317 188.51594-520.0451 111.90644 46.2857 248.29012 102.72607 357.52902 130.01188-76.64636-107.5347-146.59346-221.76948-214.5166-338.02903 100.51162-72.83876 202.1718-144.52451 299.02538-221.02092-136.89514-12.61229-278.73428-20.28827-422.53618-25.99865-22.85288-148.33212-16.84826-325.51604-52.005-461.53983-53.19327 111.4882-115.96694 213.39155-175.51418 318.52497m65.00513 1228.60735c-18.0795 77.37586 41.4876 109.11326 32.50298 156.01215-58.8141-20.268-103.0576-30.67962-182.01567-19.50203 2.47018-60.37036 56.76662-68.90959 45.50432-143.0108C-208.90184 1619.4318-210.59186 99.02478 626.00572 5.44992c1046.0409-117.00405 1078.86445 1689.2596 110.50945 1716.14582" fill="#e34f0e"/>';
478
- $image_html .= ' <path d="M847.02422 174.46342c35.15674 136.02379 29.15212 313.20771 52.0046 461.53578 143.8023 5.71443 285.63982 13.38636 422.53658 26.0027-96.85317 76.4964-198.51497 148.18216-299.02579 221.0189 67.92355 116.26239 137.87024 230.49432 214.51864 338.03024-109.24093-27.28662-245.62461-83.72577-357.53106-130.01269-72.51454 163.67274-127.5956 344.78017-188.51553 520.0459-40.90926-132.4395-55.74329-290.95384-84.50796-435.53712-130.90066 5.79549-294.84493 34.37738-435.53754 52.00418 32.5358-76.00075 166.05902-163.156 234.02026-247.02038-84.84516-64.67037-191.33222-107.69074-266.52363-182.01486 88.35892-50.32349 180.8436-96.51314 260.02295-156.0162-39.69708-114.14683-78.05674-229.63108-123.50878-338.027C402.89923 345.5722 539.9423 433.86629 671.51004 492.98839c59.54684-105.13342 122.3209-207.03677 175.51418-318.52497" fill="#fff"/>';
479
- $image_html .= '</svg>';
480
- } else {
481
- $image_html = '<div class="thumb">';
482
- $img_src = SBY_PLUGIN_URL . 'img/' . sanitize_text_field( $notification['image'] );
483
- $image_html .= '<img src="'.esc_url( $img_src ).'" alt="notice">';
484
-
485
- if ( isset( $notification['image_overlay'] ) ) {
486
- $image_html .= '<div class="img-overlay">'. esc_html( str_replace( '%', '%%', $notification['image_overlay'] ) ).'</div>';
487
- }
488
- }
489
- $image_html .= '</div>';
490
-
491
- }
492
-
493
- // Notification HTML.
494
- $notifications_html .= sprintf(
495
- '<div class="message%5$s" data-message-id="%4$s">' . $image_html . '
496
- <h3 class="title">%1$s</h3>
497
- <p class="content">%2$s</p>
498
- %3$s
499
- </div>',
500
- ! empty( $notification['title'] ) ? $this->replace_merge_fields( sanitize_text_field( $notification['title'] ), $notification ) : '',
501
- ! empty( $notification['content'] ) ? wp_kses( $this->replace_merge_fields( $notification['content'], $notification ), $content_allowed_tags ) : '',
502
- $buttons_html,
503
- ! empty( $notification['id'] ) ? esc_attr( sanitize_text_field( $notification['id'] ) ) : 0,
504
- $current_class
505
- );
506
-
507
- // Only first notification is current.
508
- $current_class = '';
509
- }
510
- ?>
511
-
512
- <div id="ctf-notifications">
513
- <a class="dismiss" title="<?php echo esc_attr__( 'Dismiss this message', 'custom-twitter-feeds' ); ?>"><i class="fa fa-times-circle" aria-hidden="true"></i></a>
514
-
515
- <div class="navigation">
516
- <a class="prev disabled" title="<?php echo esc_attr__( 'Previous message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-left" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-left fa-w-10"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z" class=""></path></svg></a>
517
- <a class="next disabled" title="<?php echo esc_attr__( 'Next message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-right fa-w-10"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z" class=""></path></svg></a>
518
- </div>
519
-
520
- <div class="messages">
521
- <?php echo $notifications_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
522
- </div>
523
- </div>
524
- <?php
525
- }
526
-
527
- /**
528
- * Dismiss notification via AJAX. If it's a new user message, also dismiss it
529
- * on all admin pages.
530
- *
531
- * @since 1.7/1.11
532
- */
533
- public function dismiss() {
534
- // Run a security check.
535
- check_ajax_referer( 'ctf-admin', 'nonce' );
536
-
537
- // Check for access and required param.
538
- if ( ! $this->has_access() || empty( $_POST['id'] ) ) {
539
- wp_send_json_error();
540
- }
541
-
542
- $id = sanitize_text_field( wp_unslash( $_POST['id'] ) );
543
-
544
- if ( $id === 'review' ) {
545
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
546
-
547
- update_option( 'ctf_rating_notice', 'dismissed', false );
548
- $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
549
- update_option( 'ctf_statuses', $ctf_statuses_option, false );
550
- } elseif ( $id === 'discount' ) {
551
- update_user_meta( get_current_user_id(), 'ctf_ignore_new_user_sale_notice', 'always' );
552
-
553
- $current_month_number = (int)date('n', ctf_get_current_time() );
554
- $not_early_in_the_year = ($current_month_number > 5);
555
-
556
- if ( $not_early_in_the_year ) {
557
- update_user_meta( get_current_user_id(), 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
558
- }
559
- }
560
-
561
- $option = $this->get_option();
562
- $type = is_numeric( $id ) ? 'feed' : 'events';
563
-
564
- $option['dismissed'][] = $id;
565
- $option['dismissed'] = array_unique( $option['dismissed'] );
566
-
567
- // Remove notification.
568
- if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) {
569
- foreach ( $option[ $type ] as $key => $notification ) {
570
- if ( $notification['id'] == $id ) { // phpcs:ignore WordPress.PHP.StrictComparisons
571
- unset( $option[ $type ][ $key ] );
572
- break;
573
- }
574
- }
575
- }
576
-
577
- update_option( 'ctf_notifications', $option );
578
-
579
- wp_send_json_success();
580
- }
581
- }
1
+ <?php
2
+ /**
3
+ * CTF_Notifications.
4
+ *
5
+ * @since 1.7/1.11
6
+ */
7
+
8
+ // Exit if accessed directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class CTF_Notifications {
14
+
15
+ /**
16
+ * Source of notifications content.
17
+ *
18
+ * @var string
19
+ */
20
+ const SOURCE_URL = 'http://plugin.smashballoon.com/notifications.json';
21
+
22
+ /**
23
+ * @var string
24
+ */
25
+ const OPTION_NAME = 'ctf_notifications';
26
+
27
+ /**
28
+ * JSON data contains notices for all plugins. This is used
29
+ * to select messages only meant for this plugin
30
+ *
31
+ * @var string
32
+ */
33
+ const PLUGIN = 'twitter';
34
+
35
+ /**
36
+ * Option value.
37
+ *
38
+ * @since 1.7/1.11
39
+ *
40
+ * @var bool|array
41
+ */
42
+ public $option = false;
43
+
44
+ /**
45
+ * Initialize class.
46
+ *
47
+ * @since 1.7/1.11
48
+ */
49
+ public function init() {
50
+ $this->hooks();
51
+ }
52
+
53
+ /**
54
+ * Use this function to get the option name to allow
55
+ * inheritance for the New_User class
56
+ *
57
+ * @return string
58
+ */
59
+ public function option_name() {
60
+ return self::OPTION_NAME;
61
+ }
62
+
63
+ /**
64
+ * Use this function to get the source URL to allow
65
+ * inheritance for the New_User class
66
+ *
67
+ * @return string
68
+ */
69
+ public function source_url() {
70
+ return self::SOURCE_URL;
71
+ }
72
+
73
+ /**
74
+ * Register hooks.
75
+ *
76
+ * @since 1.7/1.11
77
+ */
78
+ public function hooks() {
79
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueues' ) );
80
+
81
+ add_action( 'ctf_admin_overview_before_title', array( $this, 'output' ) );
82
+
83
+ // on cron. Once a week?
84
+ add_action( 'ctf_notification_update', array( $this, 'update' ) );
85
+
86
+ add_action( 'wp_ajax_ctf_dashboard_notification_dismiss', array( $this, 'dismiss' ) );
87
+ }
88
+
89
+
90
+ /**
91
+ * Check if user has access and is enabled.
92
+ *
93
+ * @since 1.7/1.11
94
+ *
95
+ * @return bool
96
+ */
97
+ public function has_access() {
98
+ $access = false;
99
+
100
+ if ( current_user_can( 'manage_options' ) ) {
101
+ $access = true;
102
+ }
103
+
104
+ return apply_filters( 'ctf_admin_notifications_has_access', $access );
105
+ }
106
+
107
+ /**
108
+ * Get option value.
109
+ *
110
+ * @since 1.7/1.11
111
+ *
112
+ * @param bool $cache Reference property cache if available.
113
+ *
114
+ * @return array
115
+ */
116
+ public function get_option( $cache = true ) {
117
+ if ( $this->option && $cache ) {
118
+ return $this->option;
119
+ }
120
+
121
+ $option = get_option( $this->option_name(), array() );
122
+
123
+ $this->option = array(
124
+ 'update' => ! empty( $option['update'] ) ? $option['update'] : 0,
125
+ 'events' => ! empty( $option['events'] ) ? $option['events'] : array(),
126
+ 'feed' => ! empty( $option['feed'] ) ? $option['feed'] : array(),
127
+ 'dismissed' => ! empty( $option['dismissed'] ) ? $option['dismissed'] : array(),
128
+ );
129
+
130
+ return $this->option;
131
+ }
132
+
133
+ /**
134
+ * Fetch notifications from feed.
135
+ *
136
+ * @since 1.7/1.11
137
+ *
138
+ * @return array
139
+ */
140
+ public function fetch_feed() {
141
+ $res = wp_remote_get( $this->source_url() );
142
+
143
+ if ( is_wp_error( $res ) ) {
144
+ return array();
145
+ }
146
+
147
+ $body = wp_remote_retrieve_body( $res );
148
+
149
+ if ( empty( $body ) ) {
150
+ return array();
151
+ }
152
+
153
+ $body = str_replace( array( 'sbi_', 'sbi-' ), array( 'ctf_', 'ctf-' ), $body );
154
+
155
+ return $this->verify( json_decode( $body, true ) );
156
+ }
157
+
158
+ /**
159
+ * Verify notification data before it is saved.
160
+ *
161
+ * @since 1.7/1.11
162
+ *
163
+ * @param array $notifications Array of notifications items to verify.
164
+ *
165
+ * @return array
166
+ */
167
+ public function verify( $notifications ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
168
+ $data = array();
169
+
170
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
171
+ return $data;
172
+ }
173
+
174
+ $option = $this->get_option();
175
+
176
+ foreach ( $notifications as $notification ) {
177
+
178
+ // The message and license should never be empty, if they are, ignore.
179
+ if ( empty( $notification['content'] ) || empty( $notification['type'] ) ) {
180
+ continue;
181
+ }
182
+
183
+ // Ignore if license type does not match.
184
+ $license = ctf_is_pro_version() ? 'pro' : 'free';
185
+
186
+ if ( ! in_array( $license, $notification['type'], true ) ) {
187
+ continue;
188
+ }
189
+
190
+ // Ignore if expired.
191
+ if ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) {
192
+ continue;
193
+ }
194
+
195
+ // Ignore if notification has already been dismissed.
196
+ if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
197
+ continue;
198
+ }
199
+
200
+ // TODO: Ignore if notification existed before installing CTF.
201
+ // Prevents bombarding the user with notifications after activation.
202
+ $activated = false;
203
+ if ( ! empty( $activated )
204
+ && ! empty( $notification['start'] )
205
+ && $activated > strtotime( $notification['start'] ) ) {
206
+ continue;
207
+ }
208
+
209
+ $data[] = $notification;
210
+ }
211
+
212
+ return $data;
213
+ }
214
+
215
+ /**
216
+ * Verify saved notification data for active notifications.
217
+ *
218
+ * @since 1.7/1.11
219
+ *
220
+ * @param array $notifications Array of notifications items to verify.
221
+ *
222
+ * @return array
223
+ */
224
+ public function verify_active( $notifications ) {
225
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
226
+ return array();
227
+ }
228
+
229
+ // Remove notfications that are not active.
230
+ foreach ( $notifications as $key => $notification ) {
231
+ if ( ( ! empty( $notification['start'] ) && ctf_get_current_time() < strtotime( $notification['start'] ) )
232
+ || ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) ) {
233
+ unset( $notifications[ $key ] );
234
+ }
235
+ }
236
+
237
+ return $notifications;
238
+ }
239
+
240
+ /**
241
+ * Get notification data.
242
+ *
243
+ * @since 1.7/1.11
244
+ *
245
+ * @return array
246
+ */
247
+ public function get() {
248
+ if ( ! $this->has_access() ) {
249
+ return array();
250
+ }
251
+
252
+ $option = $this->get_option();
253
+
254
+ // Update notifications using async task.
255
+ if ( empty( $option['update'] ) || ctf_get_current_time() > $option['update'] + DAY_IN_SECONDS ) {
256
+ $this->update();
257
+ }
258
+
259
+ $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
260
+ $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
261
+
262
+ // If there is a new user notification, add it to the beginning of the notification list
263
+ $ctf_newuser = new CTF_New_User();
264
+ $newuser_notifications = $ctf_newuser->get();
265
+
266
+ if ( ! empty( $newuser_notifications ) ) {
267
+ $events = array_merge( $newuser_notifications, $events );
268
+ }
269
+
270
+ return array_merge( $events, $feed );
271
+ }
272
+
273
+ /**
274
+ * Get notification count.
275
+ *
276
+ * @since 1.7/1.11
277
+ *
278
+ * @return int
279
+ */
280
+ public function get_count() {
281
+ return count( $this->get() );
282
+ }
283
+
284
+ /**
285
+ * Add a manual notification event.
286
+ *
287
+ * @since 1.7/1.11
288
+ *
289
+ * @param array $notification Notification data.
290
+ */
291
+ public function add( $notification ) {
292
+ if ( empty( $notification['id'] ) ) {
293
+ return;
294
+ }
295
+
296
+ $option = $this->get_option();
297
+
298
+ if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
299
+ return;
300
+ }
301
+
302
+ foreach ( $option['events'] as $item ) {
303
+ if ( $item['id'] === $notification['id'] ) {
304
+ return;
305
+ }
306
+ }
307
+
308
+ $notification = $this->verify( array( $notification ) );
309
+
310
+ update_option(
311
+ 'ctf_notifications',
312
+ array(
313
+ 'update' => $option['update'],
314
+ 'feed' => $option['feed'],
315
+ 'events' => array_merge( $notification, $option['events'] ),
316
+ 'dismissed' => $option['dismissed'],
317
+ )
318
+ );
319
+ }
320
+
321
+ /**
322
+ * Update notification data from feed.
323
+ *
324
+ * @since 1.7/1.11
325
+ */
326
+ public function update() {
327
+ $feed = $this->fetch_feed();
328
+ $option = $this->get_option();
329
+
330
+ update_option(
331
+ 'ctf_notifications',
332
+ array(
333
+ 'update' => ctf_get_current_time(),
334
+ 'feed' => $feed,
335
+ 'events' => $option['events'],
336
+ 'dismissed' => $option['dismissed'],
337
+ )
338
+ );
339
+ }
340
+
341
+ /**
342
+ * Admin area Form Overview enqueues.
343
+ *
344
+ * @since 1.7/1.11
345
+ */
346
+ public function enqueues() {
347
+ if ( ! $this->has_access() ) {
348
+ return;
349
+ }
350
+
351
+ $notifications = $this->get();
352
+
353
+ if ( empty( $notifications ) ) {
354
+ return;
355
+ }
356
+
357
+ $min = '';
358
+
359
+ wp_enqueue_style(
360
+ 'ctf-admin-notifications',
361
+ CTF_PLUGIN_URL . "css/admin-notifications{$min}.css",
362
+ array(),
363
+ CTF_VERSION
364
+ );
365
+
366
+ wp_enqueue_script(
367
+ 'ctf-admin-notifications',
368
+ CTF_PLUGIN_URL . "js/admin-notifications{$min}.js",
369
+ array( 'jquery' ),
370
+ CTF_VERSION,
371
+ true
372
+ );
373
+
374
+ wp_localize_script( 'ctf-admin-notifications', 'ctf_admin', array(
375
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
376
+ 'nonce' => wp_create_nonce( 'ctf-admin' )
377
+ )
378
+ );
379
+ }
380
+
381
+ /**
382
+ * Fields from the remote source contain placeholders to allow
383
+ * some messages to be used for multiple plugins.
384
+ *
385
+ * @param $content string
386
+ * @param $notification array
387
+ *
388
+ * @return string
389
+ *
390
+ * @since 1.7/1.11
391
+ */
392
+ public function replace_merge_fields( $content, $notification ) {
393
+ $merge_fields = array(
394
+ '{plugin}' => 'Custom Twitter Feeds',
395
+ '{amount}' => isset( $notification['amount'] ) ? $notification['amount'] : '',
396
+ '{platform}' => 'Twitter',
397
+ '{lowerplatform}' => 'twitter',
398
+ '{review-url}' => 'https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/',
399
+ '{slug}' => 'custom-twitter-feed',
400
+ '{campaign}' => 'twitter-free'
401
+ );
402
+
403
+ if ( ctf_is_pro_version() ) {
404
+ $merge_fields['{campaign}'] = 'twitter-pro';
405
+ $merge_fields['{plugin}'] = 'Custom Twitter Feeds Pro';
406
+ }
407
+
408
+ foreach ( $merge_fields as $find => $replace ) {
409
+ $content = str_replace( $find, $replace, $content );
410
+ }
411
+
412
+ return $content;
413
+ }
414
+
415
+ /**
416
+ * Output notifications on Custom Twitter Feed admin area.
417
+ *
418
+ * @since 1.7/1.11
419
+ */
420
+ public function output() {
421
+ $notifications = $this->get();
422
+
423
+ if ( empty( $notifications ) ) {
424
+ return;
425
+ }
426
+
427
+ $notifications_html = '';
428
+ $current_class = ' current';
429
+ $content_allowed_tags = array(
430
+ 'em' => array(),
431
+ 'strong' => array(),
432
+ 'span' => array(
433
+ 'style' => array(),
434
+ ),
435
+ 'a' => array(
436
+ 'href' => array(),
437
+ 'target' => array(),
438
+ 'rel' => array(),
439
+ ),
440
+ );
441
+
442
+ foreach ( $notifications as $notification ) {
443
+
444
+ // Buttons HTML.
445
+ $buttons_html = '';
446
+ if ( ! empty( $notification['btns'] ) && is_array( $notification['btns'] ) ) {
447
+ foreach ( $notification['btns'] as $btn_type => $btn ) {
448
+ if ( is_array( $btn['url'] ) ) {
449
+ $btn['url'] = add_query_arg( $btn['url'] );
450
+ }
451
+ if ( ! empty( $btn['attr'] ) ) {
452
+ $btn['target'] = '_blank';
453
+ }
454
+ $buttons_html .= sprintf(
455
+ '<a href="%1$s" class="button button-%2$s"%3$s>%4$s</a>',
456
+ ! empty( $btn['url'] ) ? esc_url( $this->replace_merge_fields( $btn['url'], $notification ) ) : '',
457
+ $btn_type === 'primary' ? 'primary' : 'secondary',
458
+ ! empty( $btn['target'] ) && $btn['target'] === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : '',
459
+ ! empty( $btn['text'] ) ? sanitize_text_field( $btn['text'] ) : ''
460
+ );
461
+ }
462
+ $buttons_html = ! empty( $buttons_html ) ? '<div class="buttons">' . $buttons_html . '</div>' : '';
463
+ }
464
+
465
+ if ( empty( $notification['image'] ) ) {
466
+ $image_html = '<div class="bell">';
467
+
468
+ $image_html .= '<svg xmlns="http://www.w3.org/2000/svg" width="42" height="48" viewBox="0 0 42 48"><defs><style>.a{fill:#777;}.b{fill:#ca4a1f;}</style></defs><path class="a" d="M23-79a6.005,6.005,0,0,1-6-6h10.06a12.066,12.066,0,0,0,1.791,1.308,6.021,6.021,0,0,1-2.077,3.352A6.008,6.008,0,0,1,23-79Zm1.605-9H5.009a2.955,2.955,0,0,1-2.173-.923A3.088,3.088,0,0,1,2-91a2.919,2.919,0,0,1,.807-2.036c.111-.12.229-.243.351-.371a14.936,14.936,0,0,0,3.126-4.409A23.283,23.283,0,0,0,8.007-107.5a14.846,14.846,0,0,1,.906-5.145,14.5,14.5,0,0,1,2.509-4.324A15.279,15.279,0,0,1,20-122.046V-124a3,3,0,0,1,3-3,3,3,0,0,1,3,3v1.954a15.28,15.28,0,0,1,8.58,5.078,14.5,14.5,0,0,1,2.509,4.324,14.846,14.846,0,0,1,.906,5.145c0,.645.016,1.281.047,1.888A12.036,12.036,0,0,0,35-106a11.921,11.921,0,0,0-8.485,3.515A11.923,11.923,0,0,0,23-94a12,12,0,0,0,1.6,6Z" transform="translate(-2 127)"/><circle class="b" cx="9" cy="9" r="9" transform="translate(24 24)"/></svg>';
469
+ $image_html .= '</div>';
470
+ } else {
471
+ if ( $notification['image'] === 'balloon'
472
+ || $notification['id'] === 'review'
473
+ || $notification['id'] === 'discount') {
474
+ $image_html = '<div class="bell">';
475
+
476
+ $image_html .= '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1438 1878" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2">';
477
+ $image_html .= ' <path d="M671.51004 492.9884C539.9423 433.8663 402.90125 345.5722 274.97656 304.47286c45.45163 108.39592 83.81332 223.88017 123.51 338.03105C319.308 702.00293 226.8217 748.19258 138.46278 798.51607c75.1914 74.32371 181.67968 117.34651 266.52444 182.01607-67.96124 83.86195-201.48527 171.01801-234.02107 247.01998 140.6922-17.6268 304.63688-46.21031 435.53794-52.00418 28.76427 144.58328 43.5987 303.09763 84.50756 435.53713 60.92033-175.26574 116.0014-356.37317 188.51594-520.0451 111.90644 46.2857 248.29012 102.72607 357.52902 130.01188-76.64636-107.5347-146.59346-221.76948-214.5166-338.02903 100.51162-72.83876 202.1718-144.52451 299.02538-221.02092-136.89514-12.61229-278.73428-20.28827-422.53618-25.99865-22.85288-148.33212-16.84826-325.51604-52.005-461.53983-53.19327 111.4882-115.96694 213.39155-175.51418 318.52497m65.00513 1228.60735c-18.0795 77.37586 41.4876 109.11326 32.50298 156.01215-58.8141-20.268-103.0576-30.67962-182.01567-19.50203 2.47018-60.37036 56.76662-68.90959 45.50432-143.0108C-208.90184 1619.4318-210.59186 99.02478 626.00572 5.44992c1046.0409-117.00405 1078.86445 1689.2596 110.50945 1716.14582" fill="#e34f0e"/>';
478
+ $image_html .= ' <path d="M847.02422 174.46342c35.15674 136.02379 29.15212 313.20771 52.0046 461.53578 143.8023 5.71443 285.63982 13.38636 422.53658 26.0027-96.85317 76.4964-198.51497 148.18216-299.02579 221.0189 67.92355 116.26239 137.87024 230.49432 214.51864 338.03024-109.24093-27.28662-245.62461-83.72577-357.53106-130.01269-72.51454 163.67274-127.5956 344.78017-188.51553 520.0459-40.90926-132.4395-55.74329-290.95384-84.50796-435.53712-130.90066 5.79549-294.84493 34.37738-435.53754 52.00418 32.5358-76.00075 166.05902-163.156 234.02026-247.02038-84.84516-64.67037-191.33222-107.69074-266.52363-182.01486 88.35892-50.32349 180.8436-96.51314 260.02295-156.0162-39.69708-114.14683-78.05674-229.63108-123.50878-338.027C402.89923 345.5722 539.9423 433.86629 671.51004 492.98839c59.54684-105.13342 122.3209-207.03677 175.51418-318.52497" fill="#fff"/>';
479
+ $image_html .= '</svg>';
480
+ } else {
481
+ $image_html = '<div class="thumb">';
482
+ $img_src = SBY_PLUGIN_URL . 'img/' . sanitize_text_field( $notification['image'] );
483
+ $image_html .= '<img src="'.esc_url( $img_src ).'" alt="notice">';
484
+
485
+ if ( isset( $notification['image_overlay'] ) ) {
486
+ $image_html .= '<div class="img-overlay">'. esc_html( str_replace( '%', '%%', $notification['image_overlay'] ) ).'</div>';
487
+ }
488
+ }
489
+ $image_html .= '</div>';
490
+
491
+ }
492
+
493
+ // Notification HTML.
494
+ $notifications_html .= sprintf(
495
+ '<div class="message%5$s" data-message-id="%4$s">' . $image_html . '
496
+ <h3 class="title">%1$s</h3>
497
+ <p class="content">%2$s</p>
498
+ %3$s
499
+ </div>',
500
+ ! empty( $notification['title'] ) ? $this->replace_merge_fields( sanitize_text_field( $notification['title'] ), $notification ) : '',
501
+ ! empty( $notification['content'] ) ? wp_kses( $this->replace_merge_fields( $notification['content'], $notification ), $content_allowed_tags ) : '',
502
+ $buttons_html,
503
+ ! empty( $notification['id'] ) ? esc_attr( sanitize_text_field( $notification['id'] ) ) : 0,
504
+ $current_class
505
+ );
506
+
507
+ // Only first notification is current.
508
+ $current_class = '';
509
+ }
510
+ ?>
511
+
512
+ <div id="ctf-notifications">
513
+ <a class="dismiss" title="<?php echo esc_attr__( 'Dismiss this message', 'custom-twitter-feeds' ); ?>"><i class="fa fa-times-circle" aria-hidden="true"></i></a>
514
+
515
+ <div class="navigation">
516
+ <a class="prev disabled" title="<?php echo esc_attr__( 'Previous message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-left" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-left fa-w-10"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z" class=""></path></svg></a>
517
+ <a class="next disabled" title="<?php echo esc_attr__( 'Next message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-right fa-w-10"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z" class=""></path></svg></a>
518
+ </div>
519
+
520
+ <div class="messages">
521
+ <?php echo $notifications_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
522
+ </div>
523
+ </div>
524
+ <?php
525
+ }
526
+
527
+ /**
528
+ * Dismiss notification via AJAX. If it's a new user message, also dismiss it
529
+ * on all admin pages.
530
+ *
531
+ * @since 1.7/1.11
532
+ */
533
+ public function dismiss() {
534
+ // Run a security check.
535
+ check_ajax_referer( 'ctf-admin', 'nonce' );
536
+
537
+ // Check for access and required param.
538
+ if ( ! $this->has_access() || empty( $_POST['id'] ) ) {
539
+ wp_send_json_error();
540
+ }
541
+
542
+ $id = sanitize_text_field( wp_unslash( $_POST['id'] ) );
543
+
544
+ if ( $id === 'review' ) {
545
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
546
+
547
+ update_option( 'ctf_rating_notice', 'dismissed', false );
548
+ $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
549
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
550
+ } elseif ( $id === 'discount' ) {
551
+ update_user_meta( get_current_user_id(), 'ctf_ignore_new_user_sale_notice', 'always' );
552
+
553
+ $current_month_number = (int)date('n', ctf_get_current_time() );
554
+ $not_early_in_the_year = ($current_month_number > 5);
555
+
556
+ if ( $not_early_in_the_year ) {
557
+ update_user_meta( get_current_user_id(), 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
558
+ }
559
+ }
560
+
561
+ $option = $this->get_option();
562
+ $type = is_numeric( $id ) ? 'feed' : 'events';
563
+
564
+ $option['dismissed'][] = $id;
565
+ $option['dismissed'] = array_unique( $option['dismissed'] );
566
+
567
+ // Remove notification.
568
+ if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) {
569
+ foreach ( $option[ $type ] as $key => $notification ) {
570
+ if ( $notification['id'] == $id ) { // phpcs:ignore WordPress.PHP.StrictComparisons
571
+ unset( $option[ $type ][ $key ] );
572
+ break;
573
+ }
574
+ }
575
+ }
576
+
577
+ update_option( 'ctf_notifications', $option );
578
+
579
+ wp_send_json_success();
580
+ }
581
+ }
inc/admin/class-install-skin.php CHANGED
@@ -1,25 +1,25 @@
1
- <?php
2
- use CTF\Helpers\PluginSilentUpgraderSkin;
3
- /**
4
- * Skin for on-the-fly addon installations.
5
- *
6
- * @since 1.0.0
7
- * @since 1.5.6.1 Extend PluginSilentUpgraderSkin and clean up the class.
8
- */
9
- class CTF_Install_Skin extends PluginSilentUpgraderSkin {
10
-
11
- /**
12
- * Instead of outputting HTML for errors, json_encode the errors and send them
13
- * back to the Ajax script for processing.
14
- *
15
- * @since 1.0.0
16
- *
17
- * @param array $errors Array of errors with the install process.
18
- */
19
- public function error( $errors ) {
20
-
21
- if ( ! empty( $errors ) ) {
22
- wp_send_json_error( $errors );
23
- }
24
- }
25
- }
1
+ <?php
2
+ use CTF\Helpers\PluginSilentUpgraderSkin;
3
+ /**
4
+ * Skin for on-the-fly addon installations.
5
+ *
6
+ * @since 1.0.0
7
+ * @since 1.5.6.1 Extend PluginSilentUpgraderSkin and clean up the class.
8
+ */
9
+ class CTF_Install_Skin extends PluginSilentUpgraderSkin {
10
+
11
+ /**
12
+ * Instead of outputting HTML for errors, json_encode the errors and send them
13
+ * back to the Ajax script for processing.
14
+ *
15
+ * @since 1.0.0
16
+ *
17
+ * @param array $errors Array of errors with the install process.
18
+ */
19
+ public function error( $errors ) {
20
+
21
+ if ( ! empty( $errors ) ) {
22
+ wp_send_json_error( $errors );
23
+ }
24
+ }
25
+ }
inc/blocks/class-ctf-blocks.php CHANGED
@@ -1,150 +1,150 @@
1
- <?php
2
- /**
3
- * Instagram Feed block with live preview.
4
- *
5
- * @since 1.7.1
6
- */
7
- class CTF_Blocks {
8
-
9
- /**
10
- * Indicates if current integration is allowed to load.
11
- *
12
- * @since 1.8
13
- *
14
- * @return bool
15
- */
16
- public function allow_load() {
17
- return function_exists( 'register_block_type' );
18
- }
19
-
20
- /**
21
- * Loads an integration.
22
- *
23
- * @since 1.7.1
24
- */
25
- public function load() {
26
- $this->hooks();
27
- }
28
-
29
- /**
30
- * Integration hooks.
31
- *
32
- * @since 1.7.1
33
- */
34
- protected function hooks() {
35
- add_action( 'init', array( $this, 'register_block' ) );
36
- add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
37
- }
38
-
39
- /**
40
- * Register Instagram Feed Gutenberg block on the backend.
41
- *
42
- * @since 1.7.1
43
- */
44
- public function register_block() {
45
-
46
- wp_register_style(
47
- 'ctf-blocks-styles',
48
- trailingslashit( CTF_PLUGIN_URL ) . 'css/ctf-blocks.css',
49
- array( 'wp-edit-blocks' ),
50
- CTF_VERSION
51
- );
52
-
53
- $attributes = array(
54
- 'shortcodeSettings' => array(
55
- 'type' => 'string',
56
- ),
57
- 'noNewChanges' => array(
58
- 'type' => 'boolean',
59
- ),
60
- 'executed' => array(
61
- 'type' => 'boolean',
62
- )
63
- );
64
-
65
- register_block_type(
66
- 'ctf/ctf-feed-block',
67
- array(
68
- 'attributes' => $attributes,
69
- 'render_callback' => array( $this, 'get_feed_html' ),
70
- )
71
- );
72
- }
73
-
74
- /**
75
- * Load Instagram Feed Gutenberg block scripts.
76
- *
77
- * @since 1.7.1
78
- */
79
- public function enqueue_block_editor_assets() {
80
- ctf_scripts_and_styles( true );
81
-
82
- wp_enqueue_style( 'ctf-blocks-styles' );
83
- wp_enqueue_script(
84
- 'ctf-feed-block',
85
- trailingslashit( CTF_PLUGIN_URL ) . 'js/ctf-blocks.js',
86
- array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
87
- CTF_VERSION,
88
- true
89
- );
90
-
91
- $shortcodeSettings = '';
92
-
93
- $i18n = array(
94
- 'addSettings' => esc_html__( 'Add Settings', 'custom-twitter-feeds' ),
95
- 'shortcodeSettings' => esc_html__( 'Shortcode Settings', 'custom-twitter-feeds' ),
96
- 'example' => esc_html__( 'Example', 'custom-twitter-feeds' ),
97
- 'preview' => esc_html__( 'Apply Changes', 'custom-twitter-feeds' ),
98
-
99
- );
100
-
101
- wp_localize_script(
102
- 'ctf-feed-block',
103
- 'ctf_block_editor',
104
- array(
105
- 'wpnonce' => wp_create_nonce( 'ctf-blocks' ),
106
- 'canShowFeed' => true,
107
- 'configureLink' => get_admin_url() . '?page=custom-twitter-feeds',
108
- 'shortcodeSettings' => $shortcodeSettings,
109
- 'i18n' => $i18n,
110
- )
111
- );
112
- }
113
-
114
- /**
115
- * Get form HTML to display in a Instagram Feed Gutenberg block.
116
- *
117
- * @param array $attr Attributes passed by Instagram Feed Gutenberg block.
118
- *
119
- * @since 1.7.1
120
- *
121
- * @return string
122
- */
123
- public function get_feed_html( $attr ) {
124
-
125
- $return = '';
126
-
127
- $shortcode_settings = isset( $attr['shortcodeSettings'] ) ? $attr['shortcodeSettings'] : '';
128
-
129
- $shortcode_settings = str_replace(array( '[custom-twitter-feeds', ']' ), '', $shortcode_settings );
130
-
131
- $return .= do_shortcode( '[custom-twitter-feeds '.$shortcode_settings.']' );
132
-
133
- return $return;
134
-
135
- }
136
-
137
- /**
138
- * Checking if is Gutenberg REST API call.
139
- *
140
- * @since 1.7.1
141
- *
142
- * @return bool True if is Gutenberg REST API call.
143
- */
144
- public static function is_gb_editor() {
145
-
146
- // TODO: Find a better way to check if is GB editor API call.
147
- return defined( 'REST_REQUEST' ) && REST_REQUEST && ! empty( $_REQUEST['context'] ) && 'edit' === $_REQUEST['context']; // phpcs:ignore
148
- }
149
-
150
- }
1
+ <?php
2
+ /**
3
+ * Instagram Feed block with live preview.
4
+ *
5
+ * @since 1.7.1
6
+ */
7
+ class CTF_Blocks {
8
+
9
+ /**
10
+ * Indicates if current integration is allowed to load.
11
+ *
12
+ * @since 1.8
13
+ *
14
+ * @return bool
15
+ */
16
+ public function allow_load() {
17
+ return function_exists( 'register_block_type' );
18
+ }
19
+
20
+ /**
21
+ * Loads an integration.
22
+ *
23
+ * @since 1.7.1
24
+ */
25
+ public function load() {
26
+ $this->hooks();
27
+ }
28
+
29
+ /**
30
+ * Integration hooks.
31
+ *
32
+ * @since 1.7.1
33
+ */
34
+ protected function hooks() {
35
+ add_action( 'init', array( $this, 'register_block' ) );
36
+ add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
37
+ }
38
+
39
+ /**
40
+ * Register Instagram Feed Gutenberg block on the backend.
41
+ *
42
+ * @since 1.7.1
43
+ */
44
+ public function register_block() {
45
+
46
+ wp_register_style(
47
+ 'ctf-blocks-styles',
48
+ trailingslashit( CTF_PLUGIN_URL ) . 'css/ctf-blocks.css',
49
+ array( 'wp-edit-blocks' ),
50
+ CTF_VERSION
51
+ );
52
+
53
+ $attributes = array(
54
+ 'shortcodeSettings' => array(
55
+ 'type' => 'string',
56
+ ),
57
+ 'noNewChanges' => array(
58
+ 'type' => 'boolean',
59
+ ),
60
+ 'executed' => array(
61
+ 'type' => 'boolean',
62
+ )
63
+ );
64
+
65
+ register_block_type(
66
+ 'ctf/ctf-feed-block',
67
+ array(
68
+ 'attributes' => $attributes,
69
+ 'render_callback' => array( $this, 'get_feed_html' ),
70
+ )
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Load Instagram Feed Gutenberg block scripts.
76
+ *
77
+ * @since 1.7.1
78
+ */
79
+ public function enqueue_block_editor_assets() {
80
+ ctf_scripts_and_styles( true );
81
+
82
+ wp_enqueue_style( 'ctf-blocks-styles' );
83
+ wp_enqueue_script(
84
+ 'ctf-feed-block',
85
+ trailingslashit( CTF_PLUGIN_URL ) . 'js/ctf-blocks.js',
86
+ array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
87
+ CTF_VERSION,
88
+ true
89
+ );
90
+
91
+ $shortcodeSettings = '';
92
+
93
+ $i18n = array(
94
+ 'addSettings' => esc_html__( 'Add Settings', 'custom-twitter-feeds' ),
95
+ 'shortcodeSettings' => esc_html__( 'Shortcode Settings', 'custom-twitter-feeds' ),
96
+ 'example' => esc_html__( 'Example', 'custom-twitter-feeds' ),
97
+ 'preview' => esc_html__( 'Apply Changes', 'custom-twitter-feeds' ),
98
+
99
+ );
100
+
101
+ wp_localize_script(
102
+ 'ctf-feed-block',
103
+ 'ctf_block_editor',
104
+ array(
105
+ 'wpnonce' => wp_create_nonce( 'ctf-blocks' ),
106
+ 'canShowFeed' => true,
107
+ 'configureLink' => get_admin_url() . '?page=custom-twitter-feeds',
108
+ 'shortcodeSettings' => $shortcodeSettings,
109
+ 'i18n' => $i18n,
110
+ )
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Get form HTML to display in a Instagram Feed Gutenberg block.
116
+ *
117
+ * @param array $attr Attributes passed by Instagram Feed Gutenberg block.
118
+ *
119
+ * @since 1.7.1
120
+ *
121
+ * @return string
122
+ */
123
+ public function get_feed_html( $attr ) {
124
+
125
+ $return = '';
126
+
127
+ $shortcode_settings = isset( $attr['shortcodeSettings'] ) ? $attr['shortcodeSettings'] : '';
128
+
129
+ $shortcode_settings = str_replace(array( '[custom-twitter-feeds', ']' ), '', $shortcode_settings );
130
+
131
+ $return .= do_shortcode( '[custom-twitter-feeds '.$shortcode_settings.']' );
132
+
133
+ return $return;
134
+
135
+ }
136
+
137
+ /**
138
+ * Checking if is Gutenberg REST API call.
139
+ *
140
+ * @since 1.7.1
141
+ *
142
+ * @return bool True if is Gutenberg REST API call.
143
+ */
144
+ public static function is_gb_editor() {
145
+
146
+ // TODO: Find a better way to check if is GB editor API call.
147
+ return defined( 'REST_REQUEST' ) && REST_REQUEST && ! empty( $_REQUEST['context'] ) && 'edit' === $_REQUEST['context']; // phpcs:ignore
148
+ }
149
+
150
+ }
inc/class-ctf-feed-locator.php CHANGED
@@ -1,418 +1,418 @@
1
- <?php
2
- /**
3
- * Class CTF_Feed_Locator
4
- *
5
- * Locates feeds on the site and logs information about them in the database.
6
- *
7
- * @since 1.14
8
- */
9
-
10
- if ( ! defined( 'ABSPATH' ) ) {
11
- die( '-1' );
12
- }
13
-
14
- class CTF_Feed_Locator
15
- {
16
- private $feed_details;
17
-
18
- private $expiration_time;
19
-
20
- private $matching_entries;
21
-
22
- public function __construct( $feed_details ) {
23
- /**
24
- * Example of how $feed_details is structured
25
- *
26
- * $feed_details = array(
27
- * 'feed_id' => $transient_name,
28
- * 'atts' => $atts,
29
- * 'location' => array(
30
- * 'post_id' => get_the_ID(),
31
- * 'html' => 'unknown'
32
- * )
33
- * );
34
- */
35
- $this->feed_details = $feed_details;
36
-
37
- $this->matching_entries = array();
38
-
39
- $this->expiration_time = time() - 2 * WEEK_IN_SECONDS;
40
- }
41
-
42
- /**
43
- * Returns records that match the post ID and feed ID
44
- * of the feed being located
45
- *
46
- * @return array
47
- *
48
- * @since 1.14
49
- */
50
- public function retrieve_matching_entries() {
51
- global $wpdb;
52
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
53
-
54
- $results = $wpdb->get_results( $wpdb->prepare("
55
- SELECT *
56
- FROM $feed_locator_table_name
57
- WHERE post_id = %d
58
- AND feed_id = %s", $this->feed_details['location']['post_id'], $this->feed_details['feed_id'] ),ARRAY_A );
59
-
60
- return $results;
61
- }
62
-
63
- /**
64
- * Add feed being located to the database
65
- *
66
- * @since 1.14
67
- */
68
- public function insert_entry() {
69
- global $wpdb;
70
-
71
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
72
-
73
- $affected = $wpdb->query( $wpdb->prepare( "INSERT INTO $feed_locator_table_name
74
- (feed_id,
75
- post_id,
76
- html_location,
77
- shortcode_atts,
78
- last_update)
79
- VALUES (
80
- %s,
81
- %d,
82
- %s,
83
- %s,
84
- %s);",
85
- $this->feed_details['feed_id'],
86
- $this->feed_details['location']['post_id'],
87
- $this->feed_details['location']['html'],
88
- ctf_json_encode( $this->feed_details['atts'] ),
89
- date( 'Y-m-d H:i:s' ) ) );
90
- }
91
-
92
- /**
93
- * Update a record based on the existing "id" column. Location can change
94
- * from "unknown" to one of footer, content, header, or sidebar.
95
- *
96
- * @param $id
97
- * @param $location
98
- *
99
- * @since 1.14
100
- */
101
- public function update_entry( $id, $location ) {
102
- global $wpdb;
103
-
104
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
105
-
106
- $query = $wpdb->query( $wpdb->prepare( "
107
- UPDATE $feed_locator_table_name
108
- SET last_update = %s, html_location = %s
109
- WHERE id = %d;", date( 'Y-m-d H:i:s' ), $location, $id ) );
110
- }
111
-
112
- /**
113
- * Processes a feed being located based on whether or not the record
114
- * exists as well as whether or not an unknown location needs to be
115
- * updated.
116
- *
117
- * @since 1.14
118
- */
119
- public function add_or_update_entry() {
120
- if ( empty( $this->feed_details['feed_id'] ) ) {
121
- return;
122
- }
123
-
124
- $this->matching_entries = $this->retrieve_matching_entries();
125
-
126
- if ( empty( $this->matching_entries ) ) {
127
- $this->insert_entry();
128
- } else {
129
- $matching_indices = array();
130
- $matched_location = false;
131
- $non_unknown_match = false;
132
- $unknown_match = false;
133
-
134
- foreach ( $this->matching_entries as $index => $matching_entry ) {
135
- $details_atts = is_array( $this->feed_details['atts'] ) ? $this->feed_details['atts'] : array();
136
- $matching_atts = json_decode( $matching_entry['shortcode_atts'], true );
137
- if ( ! is_array( $matching_atts ) ) {
138
- $matching_atts = array();
139
- }
140
- $atts_diff = array_diff( $matching_atts, $details_atts ); // determines if the shortcode settings match the shortcode settings of an existing feed
141
- if ( empty( $atts_diff ) ) {
142
- $matching_indices[] = $matching_entry['id'];
143
- if ( $matching_entry['html_location'] === $this->feed_details['location']['html'] ) {
144
- $matched_location = $index;
145
- $this->update_entry( $matching_entry['id'], $matching_entry['html_location'] );
146
- }
147
- if ( $matching_entry['html_location'] !== 'unknown' ) {
148
- $non_unknown_match = $index;
149
- } else {
150
- $unknown_match = $index;
151
- }
152
- }
153
- }
154
-
155
- if ( false === $matched_location ) {
156
- // if there is no matched location, there is only one feed on the page, and the feed being checked has an unknown location, update the known location
157
- if ( count( $matching_indices ) === 1
158
- && $this->feed_details['location']['html'] === 'unknown'
159
- && false !== $non_unknown_match ) {
160
- $this->update_entry( $this->matching_entries[ $non_unknown_match ]['id'], $this->matching_entries[ $non_unknown_match ]['html_location'] );
161
- } else {
162
- if ( $this->feed_details['location']['html'] !== 'unknown'
163
- && false !== $unknown_match ) {
164
- $this->update_entry( $this->matching_entries[ $unknown_match ]['id'], $this->feed_details['location']['html'] );
165
- } else {
166
- $this->insert_entry();
167
- }
168
-
169
- }
170
- }
171
-
172
- }
173
- }
174
-
175
- /**
176
- * Old feeds are only detected once a day to keep load on the server low.
177
- *
178
- * @return bool
179
- *
180
- * @since 1.14
181
- */
182
- public static function should_clear_old_locations() {
183
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
184
- $last_old_feed_check = isset( $ctf_statuses_option['feed_locator']['last_check'] ) ? $ctf_statuses_option['feed_locator']['last_check'] : 0;
185
-
186
- return $last_old_feed_check < time() - DAY_IN_SECONDS;
187
- }
188
-
189
- /**
190
- * Old feeds are removed if they haven't been updated in two weeks.
191
- *
192
- * @since 1.14
193
- */
194
- public static function delete_old_locations() {
195
- global $wpdb;
196
-
197
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
198
- $two_weeks_ago = date( 'Y-m-d H:i:s', time() - 2 * WEEK_IN_SECONDS );
199
-
200
- $affected = $wpdb->query( $wpdb->prepare(
201
- "DELETE FROM $feed_locator_table_name WHERE last_update < %s;", $two_weeks_ago ) );
202
-
203
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
204
- $ctf_statuses_option['feed_locator']['last_check'] = time();
205
- if ( ! isset( $ctf_statuses_option['feed_locator']['initialized'] ) ) {
206
- $ctf_statuses_option['feed_locator']['initialized'] = time();
207
- }
208
-
209
- update_option( 'ctf_statuses', $ctf_statuses_option, true );
210
- }
211
-
212
- /**
213
- * Feeds are located with the page load randomly (5% or 1/30 loads)
214
- * to decrease load on the server.
215
- *
216
- * If the locating just started (within 5 minutes) it is run more often
217
- * to collect feed locations quickly.
218
- *
219
- * @return bool
220
- *
221
- * @since 1.14
222
- */
223
- public static function should_do_locating() {
224
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
225
- if ( isset( $ctf_statuses_option['feed_locator']['initialized'] )
226
- && $ctf_statuses_option['feed_locator']['initialized'] < (time() - 300) ) {
227
- $should_do_locating = rand( 1, 10 ) === 10;
228
- } else {
229
- $should_do_locating = rand( 1, 30 ) === 30;
230
- }
231
- $should_do_locating = apply_filters( 'ctf_should_do_locating', $should_do_locating );
232
-
233
- return $should_do_locating;
234
- }
235
-
236
- /**
237
- * Simliar to the should_do_locating method but will add an additional
238
- * database query to see if there is a feed with an unknown location that
239
- * matches the details of the feed in question.
240
- *
241
- * @param $feed_id
242
- * @param $post_id
243
- *
244
- * @return bool
245
- *
246
- * @since 1.14
247
- */
248
- public static function should_do_ajax_locating( $feed_id, $post_id ) {
249
- $should_do_locating = rand( 1, 50 ) === 50;
250
-
251
- $should_do_locating = apply_filters( 'ctf_should_do_ajax_locating', $should_do_locating );
252
-
253
- return $should_do_locating;
254
- }
255
-
256
- /**
257
- * Feeds are located with the page load randomly (1/30 loads)
258
- * to decrease load on the server.
259
- *
260
- * If the locating just started (within 5 minutes) it is run more often
261
- * to collect feed locations quickly.
262
- *
263
- * @param $feed_id
264
- * @param $post_id
265
- *
266
- * @return bool
267
- *
268
- * @since 1.14
269
- */
270
- public static function entries_need_locating( $feed_id, $post_id ) {
271
- global $wpdb;
272
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
273
-
274
- $one_day_ago = date( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS );
275
-
276
- $results = $wpdb->get_results( $wpdb->prepare("
277
- SELECT id
278
- FROM $feed_locator_table_name
279
- WHERE html_location = 'unknown'
280
- AND last_update < %s
281
- AND feed_id = %s
282
- AND post_id = %d
283
- LIMIT 1;", $one_day_ago, $feed_id, $post_id ),ARRAY_A );
284
-
285
- return isset( $results[0] );
286
- }
287
-
288
- /**
289
- * A custom table stores locations
290
- *
291
- * @since 1.14
292
- */
293
- public static function create_table() {
294
- global $wpdb;
295
-
296
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
297
-
298
- if ( $wpdb->get_var( "show tables like '$feed_locator_table_name'" ) != $feed_locator_table_name ) {
299
- $sql = "CREATE TABLE " . $feed_locator_table_name . " (
300
- id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
301
- feed_id VARCHAR(50) DEFAULT '' NOT NULL,
302
- post_id BIGINT(20) UNSIGNED NOT NULL,
303
- html_location VARCHAR(50) DEFAULT 'unknown' NOT NULL,
304
- shortcode_atts LONGTEXT NOT NULL,
305
- last_update DATETIME
306
- );";
307
- $wpdb->query( $sql );
308
- }
309
- $error = $wpdb->last_error;
310
- $query = $wpdb->last_query;
311
- $had_error = false;
312
- if ( $wpdb->get_var( "show tables like '$feed_locator_table_name'" ) != $feed_locator_table_name ) {
313
- $had_error = true;
314
- }
315
-
316
- if ( ! $had_error ) {
317
- $wpdb->query( "ALTER TABLE $feed_locator_table_name ADD INDEX feed_id (feed_id)" );
318
- $wpdb->query( "ALTER TABLE $feed_locator_table_name ADD INDEX post_id (post_id)" );
319
- }
320
- }
321
-
322
- /**
323
- * Counts the number of unique feeds in the database.
324
- *
325
- * @return int
326
- *
327
- * @since 1.14
328
- */
329
- public static function count_unique() {
330
- global $wpdb;
331
-
332
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
333
- $results_content = $wpdb->get_results( "
334
- SELECT COUNT(*) AS num_entries
335
- FROM $feed_locator_table_name
336
- WHERE html_location = 'content'
337
- ", ARRAY_A );
338
-
339
-
340
- $results_other = $wpdb->get_results( "
341
- SELECT COUNT(*) AS num_entries
342
- FROM $feed_locator_table_name
343
- WHERE html_location != 'content'
344
- AND html_location != 'unknown'
345
- GROUP BY feed_id
346
- ", ARRAY_A );
347
-
348
- $total = 0;
349
- if ( isset( $results_content[0]['num_entries'] ) ) {
350
- $total += (int)$results_content[0]['num_entries'];
351
- }
352
- if ( isset( $results_other[0]['num_entries'] ) ) {
353
- $total += (int)$results_other[0]['num_entries'];
354
- }
355
-
356
- return $total;
357
- }
358
-
359
- /**
360
- * Creates a summary of the located feeds in an array
361
- *
362
- * @return array
363
- *
364
- * @since 1.14
365
- */
366
- public static function summary() {
367
- global $wpdb;
368
-
369
- $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
370
-
371
- $locations = array(
372
- array(
373
- 'label' => __( 'Content', 'custom-twitter-feeds' ),
374
- 'html_locations' => array( 'content', 'unknown' )
375
- ),
376
- array(
377
- 'label' => __( 'Header', 'custom-twitter-feeds' ),
378
- 'html_locations' => array( 'header' ),
379
- 'group_by' => 'feed_id'
380
- ),
381
- array(
382
- 'label' => __( 'Sidebar', 'custom-twitter-feeds' ),
383
- 'html_locations' => array( 'sidebar' ),
384
- 'group_by' => 'feed_id'
385
- ),
386
- array(
387
- 'label' => __( 'Footer', 'custom-twitter-feeds' ),
388
- 'html_locations' => array( 'footer' ),
389
- 'group_by' => 'feed_id'
390
- )
391
- );
392
-
393
- $one_result_found = false;
394
-
395
- foreach ( $locations as $key => $location ) {
396
- $in = implode( "', '", $location['html_locations'] );
397
- $group_by = isset( $location['group_by'] ) ? "GROUP BY " . $location['group_by'] : "";
398
- $results = $wpdb->get_results("
399
- SELECT *
400
- FROM $feed_locator_table_name
401
- WHERE html_location IN ('$in')
402
- $group_by
403
- ORDER BY last_update ASC",ARRAY_A );
404
-
405
- if ( isset( $results[0] ) ) {
406
- $one_result_found = true;
407
- }
408
-
409
- $locations[ $key ]['results'] = $results;
410
- }
411
-
412
- if ( ! $one_result_found ) {
413
- return array();
414
- }
415
-
416
- return $locations;
417
- }
418
  }
1
+ <?php
2
+ /**
3
+ * Class CTF_Feed_Locator
4
+ *
5
+ * Locates feeds on the site and logs information about them in the database.
6
+ *
7
+ * @since 1.14
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ die( '-1' );
12
+ }
13
+
14
+ class CTF_Feed_Locator
15
+ {
16
+ private $feed_details;
17
+
18
+ private $expiration_time;
19
+
20
+ private $matching_entries;
21
+
22
+ public function __construct( $feed_details ) {
23
+ /**
24
+ * Example of how $feed_details is structured
25
+ *
26
+ * $feed_details = array(
27
+ * 'feed_id' => $transient_name,
28
+ * 'atts' => $atts,
29
+ * 'location' => array(
30
+ * 'post_id' => get_the_ID(),
31
+ * 'html' => 'unknown'
32
+ * )
33
+ * );
34
+ */
35
+ $this->feed_details = $feed_details;
36
+
37
+ $this->matching_entries = array();
38
+
39
+ $this->expiration_time = time() - 2 * WEEK_IN_SECONDS;
40
+ }
41
+
42
+ /**
43
+ * Returns records that match the post ID and feed ID
44
+ * of the feed being located
45
+ *
46
+ * @return array
47
+ *
48
+ * @since 1.14
49
+ */
50
+ public function retrieve_matching_entries() {
51
+ global $wpdb;
52
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
53
+
54
+ $results = $wpdb->get_results( $wpdb->prepare("
55
+ SELECT *
56
+ FROM $feed_locator_table_name
57
+ WHERE post_id = %d
58
+ AND feed_id = %s", $this->feed_details['location']['post_id'], $this->feed_details['feed_id'] ),ARRAY_A );
59
+
60
+ return $results;
61
+ }
62
+
63
+ /**
64
+ * Add feed being located to the database
65
+ *
66
+ * @since 1.14
67
+ */
68
+ public function insert_entry() {
69
+ global $wpdb;
70
+
71
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
72
+
73
+ $affected = $wpdb->query( $wpdb->prepare( "INSERT INTO $feed_locator_table_name
74
+ (feed_id,
75
+ post_id,
76
+ html_location,
77
+ shortcode_atts,
78
+ last_update)
79
+ VALUES (
80
+ %s,
81
+ %d,
82
+ %s,
83
+ %s,
84
+ %s);",
85
+ $this->feed_details['feed_id'],
86
+ $this->feed_details['location']['post_id'],
87
+ $this->feed_details['location']['html'],
88
+ ctf_json_encode( $this->feed_details['atts'] ),
89
+ date( 'Y-m-d H:i:s' ) ) );
90
+ }
91
+
92
+ /**
93
+ * Update a record based on the existing "id" column. Location can change
94
+ * from "unknown" to one of footer, content, header, or sidebar.
95
+ *
96
+ * @param $id
97
+ * @param $location
98
+ *
99
+ * @since 1.14
100
+ */
101
+ public function update_entry( $id, $location ) {
102
+ global $wpdb;
103
+
104
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
105
+
106
+ $query = $wpdb->query( $wpdb->prepare( "
107
+ UPDATE $feed_locator_table_name
108
+ SET last_update = %s, html_location = %s
109
+ WHERE id = %d;", date( 'Y-m-d H:i:s' ), $location, $id ) );
110
+ }
111
+
112
+ /**
113
+ * Processes a feed being located based on whether or not the record
114
+ * exists as well as whether or not an unknown location needs to be
115
+ * updated.
116
+ *
117
+ * @since 1.14
118
+ */
119
+ public function add_or_update_entry() {
120
+ if ( empty( $this->feed_details['feed_id'] ) ) {
121
+ return;
122
+ }
123
+
124
+ $this->matching_entries = $this->retrieve_matching_entries();
125
+
126
+ if ( empty( $this->matching_entries ) ) {
127
+ $this->insert_entry();
128
+ } else {
129
+ $matching_indices = array();
130
+ $matched_location = false;
131
+ $non_unknown_match = false;
132
+ $unknown_match = false;
133
+
134
+ foreach ( $this->matching_entries as $index => $matching_entry ) {
135
+ $details_atts = is_array( $this->feed_details['atts'] ) ? $this->feed_details['atts'] : array();
136
+ $matching_atts = json_decode( $matching_entry['shortcode_atts'], true );
137
+ if ( ! is_array( $matching_atts ) ) {
138
+ $matching_atts = array();
139
+ }
140
+ $atts_diff = array_diff( $matching_atts, $details_atts ); // determines if the shortcode settings match the shortcode settings of an existing feed
141
+ if ( empty( $atts_diff ) ) {
142
+ $matching_indices[] = $matching_entry['id'];
143
+ if ( $matching_entry['html_location'] === $this->feed_details['location']['html'] ) {
144
+ $matched_location = $index;
145
+ $this->update_entry( $matching_entry['id'], $matching_entry['html_location'] );
146
+ }
147
+ if ( $matching_entry['html_location'] !== 'unknown' ) {
148
+ $non_unknown_match = $index;
149
+ } else {
150
+ $unknown_match = $index;
151
+ }
152
+ }
153
+ }
154
+
155
+ if ( false === $matched_location ) {
156
+ // if there is no matched location, there is only one feed on the page, and the feed being checked has an unknown location, update the known location
157
+ if ( count( $matching_indices ) === 1
158
+ && $this->feed_details['location']['html'] === 'unknown'
159
+ && false !== $non_unknown_match ) {
160
+ $this->update_entry( $this->matching_entries[ $non_unknown_match ]['id'], $this->matching_entries[ $non_unknown_match ]['html_location'] );
161
+ } else {
162
+ if ( $this->feed_details['location']['html'] !== 'unknown'
163
+ && false !== $unknown_match ) {
164
+ $this->update_entry( $this->matching_entries[ $unknown_match ]['id'], $this->feed_details['location']['html'] );
165
+ } else {
166
+ $this->insert_entry();
167
+ }
168
+
169
+ }
170
+ }
171
+
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Old feeds are only detected once a day to keep load on the server low.
177
+ *
178
+ * @return bool
179
+ *
180
+ * @since 1.14
181
+ */
182
+ public static function should_clear_old_locations() {
183
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
184
+ $last_old_feed_check = isset( $ctf_statuses_option['feed_locator']['last_check'] ) ? $ctf_statuses_option['feed_locator']['last_check'] : 0;
185
+
186
+ return $last_old_feed_check < time() - DAY_IN_SECONDS;
187
+ }
188
+
189
+ /**
190
+ * Old feeds are removed if they haven't been updated in two weeks.
191
+ *
192
+ * @since 1.14
193
+ */
194
+ public static function delete_old_locations() {
195
+ global $wpdb;
196
+
197
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
198
+ $two_weeks_ago = date( 'Y-m-d H:i:s', time() - 2 * WEEK_IN_SECONDS );
199
+
200
+ $affected = $wpdb->query( $wpdb->prepare(
201
+ "DELETE FROM $feed_locator_table_name WHERE last_update < %s;", $two_weeks_ago ) );
202
+
203
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
204
+ $ctf_statuses_option['feed_locator']['last_check'] = time();
205
+ if ( ! isset( $ctf_statuses_option['feed_locator']['initialized'] ) ) {
206
+ $ctf_statuses_option['feed_locator']['initialized'] = time();
207
+ }
208
+
209
+ update_option( 'ctf_statuses', $ctf_statuses_option, true );
210
+ }
211
+
212
+ /**
213
+ * Feeds are located with the page load randomly (5% or 1/30 loads)
214
+ * to decrease load on the server.
215
+ *
216
+ * If the locating just started (within 5 minutes) it is run more often
217
+ * to collect feed locations quickly.
218
+ *
219
+ * @return bool
220
+ *
221
+ * @since 1.14
222
+ */
223
+ public static function should_do_locating() {
224
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
225
+ if ( isset( $ctf_statuses_option['feed_locator']['initialized'] )
226
+ && $ctf_statuses_option['feed_locator']['initialized'] < (time() - 300) ) {
227
+ $should_do_locating = rand( 1, 10 ) === 10;
228
+ } else {
229
+ $should_do_locating = rand( 1, 30 ) === 30;
230
+ }
231
+ $should_do_locating = apply_filters( 'ctf_should_do_locating', $should_do_locating );
232
+
233
+ return $should_do_locating;
234
+ }
235
+
236
+ /**
237
+ * Simliar to the should_do_locating method but will add an additional
238
+ * database query to see if there is a feed with an unknown location that
239
+ * matches the details of the feed in question.
240
+ *
241
+ * @param $feed_id
242
+ * @param $post_id
243
+ *
244
+ * @return bool
245
+ *
246
+ * @since 1.14
247
+ */
248
+ public static function should_do_ajax_locating( $feed_id, $post_id ) {
249
+ $should_do_locating = rand( 1, 50 ) === 50;
250
+
251
+ $should_do_locating = apply_filters( 'ctf_should_do_ajax_locating', $should_do_locating );
252
+
253
+ return $should_do_locating;
254
+ }
255
+
256
+ /**
257
+ * Feeds are located with the page load randomly (1/30 loads)
258
+ * to decrease load on the server.
259
+ *
260
+ * If the locating just started (within 5 minutes) it is run more often
261
+ * to collect feed locations quickly.
262
+ *
263
+ * @param $feed_id
264
+ * @param $post_id
265
+ *
266
+ * @return bool
267
+ *
268
+ * @since 1.14
269
+ */
270
+ public static function entries_need_locating( $feed_id, $post_id ) {
271
+ global $wpdb;
272
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
273
+
274
+ $one_day_ago = date( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS );
275
+
276
+ $results = $wpdb->get_results( $wpdb->prepare("
277
+ SELECT id
278
+ FROM $feed_locator_table_name
279
+ WHERE html_location = 'unknown'
280
+ AND last_update < %s
281
+ AND feed_id = %s
282
+ AND post_id = %d
283
+ LIMIT 1;", $one_day_ago, $feed_id, $post_id ),ARRAY_A );
284
+
285
+ return isset( $results[0] );
286
+ }
287
+
288
+ /**
289
+ * A custom table stores locations
290
+ *
291
+ * @since 1.14
292
+ */
293
+ public static function create_table() {
294
+ global $wpdb;
295
+
296
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
297
+
298
+ if ( $wpdb->get_var( "show tables like '$feed_locator_table_name'" ) != $feed_locator_table_name ) {
299
+ $sql = "CREATE TABLE " . $feed_locator_table_name . " (
300
+ id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
301
+ feed_id VARCHAR(50) DEFAULT '' NOT NULL,
302
+ post_id BIGINT(20) UNSIGNED NOT NULL,
303
+ html_location VARCHAR(50) DEFAULT 'unknown' NOT NULL,
304
+ shortcode_atts LONGTEXT NOT NULL,
305
+ last_update DATETIME
306
+ );";
307
+ $wpdb->query( $sql );
308
+ }
309
+ $error = $wpdb->last_error;
310
+ $query = $wpdb->last_query;
311
+ $had_error = false;
312
+ if ( $wpdb->get_var( "show tables like '$feed_locator_table_name'" ) != $feed_locator_table_name ) {
313
+ $had_error = true;
314
+ }
315
+
316
+ if ( ! $had_error ) {
317
+ $wpdb->query( "ALTER TABLE $feed_locator_table_name ADD INDEX feed_id (feed_id)" );
318
+ $wpdb->query( "ALTER TABLE $feed_locator_table_name ADD INDEX post_id (post_id)" );
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Counts the number of unique feeds in the database.
324
+ *
325
+ * @return int
326
+ *
327
+ * @since 1.14
328
+ */
329
+ public static function count_unique() {
330
+ global $wpdb;
331
+
332
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
333
+ $results_content = $wpdb->get_results( "
334
+ SELECT COUNT(*) AS num_entries
335
+ FROM $feed_locator_table_name
336
+ WHERE html_location = 'content'
337
+ ", ARRAY_A );
338
+
339
+
340
+ $results_other = $wpdb->get_results( "
341
+ SELECT COUNT(*) AS num_entries
342
+ FROM $feed_locator_table_name
343
+ WHERE html_location != 'content'
344
+ AND html_location != 'unknown'
345
+ GROUP BY feed_id
346
+ ", ARRAY_A );
347
+
348
+ $total = 0;
349
+ if ( isset( $results_content[0]['num_entries'] ) ) {
350
+ $total += (int)$results_content[0]['num_entries'];
351
+ }
352
+ if ( isset( $results_other[0]['num_entries'] ) ) {
353
+ $total += (int)$results_other[0]['num_entries'];
354
+ }
355
+
356
+ return $total;
357
+ }
358
+
359
+ /**
360
+ * Creates a summary of the located feeds in an array
361
+ *
362
+ * @return array
363
+ *
364
+ * @since 1.14
365
+ */
366
+ public static function summary() {
367
+ global $wpdb;
368
+
369
+ $feed_locator_table_name = esc_sql( $wpdb->prefix . CTF_FEED_LOCATOR );
370
+
371
+ $locations = array(
372
+ array(
373
+ 'label' => __( 'Content', 'custom-twitter-feeds' ),
374
+ 'html_locations' => array( 'content', 'unknown' )
375
+ ),
376
+ array(
377
+ 'label' => __( 'Header', 'custom-twitter-feeds' ),
378
+ 'html_locations' => array( 'header' ),
379
+ 'group_by' => 'feed_id'
380
+ ),
381
+ array(
382
+ 'label' => __( 'Sidebar', 'custom-twitter-feeds' ),
383
+ 'html_locations' => array( 'sidebar' ),
384
+ 'group_by' => 'feed_id'
385
+ ),
386
+ array(
387
+ 'label' => __( 'Footer', 'custom-twitter-feeds' ),
388
+ 'html_locations' => array( 'footer' ),
389
+ 'group_by' => 'feed_id'
390
+ )
391
+ );
392
+
393
+ $one_result_found = false;
394
+
395
+ foreach ( $locations as $key => $location ) {
396
+ $in = implode( "', '", $location['html_locations'] );
397
+ $group_by = isset( $location['group_by'] ) ? "GROUP BY " . $location['group_by'] : "";
398
+ $results = $wpdb->get_results("
399
+ SELECT *
400
+ FROM $feed_locator_table_name
401
+ WHERE html_location IN ('$in')
402
+ $group_by
403
+ ORDER BY last_update ASC",ARRAY_A );
404
+
405
+ if ( isset( $results[0] ) ) {
406
+ $one_result_found = true;
407
+ }
408
+
409
+ $locations[ $key ]['results'] = $results;
410
+ }
411
+
412
+ if ( ! $one_result_found ) {
413
+ return array();
414
+ }
415
+
416
+ return $locations;
417
+ }
418
  }
inc/class-ctf-gdpr-integrations.php CHANGED
@@ -1,122 +1,122 @@
1
- <?php
2
- /**
3
- * Class CTF_GDPR_Integrations
4
- *
5
- * Adds GDPR related workarounds for third-party plugins:
6
- * https://wordpress.org/plugins/cookie-law-info/
7
- *
8
- * @since 1.7/1.12
9
- */
10
-
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- die( '-1' );
13
- }
14
-
15
- class CTF_GDPR_Integrations {
16
-
17
- /**
18
- * Undoing of Cookie Notice's Twitter Feed related code
19
- * needs to be done late.
20
- *
21
- * @since 1.7/1.12
22
- */
23
- public static function init() {
24
- add_filter( 'wt_cli_third_party_scripts', array( 'CTF_GDPR_Integrations', 'undo_script_blocking' ), 11 );
25
- add_filter( 'cmplz_known_script_tags', array( 'CTF_GDPR_Integrations', 'undo_script_blocking' ), 11 );
26
- }
27
-
28
- /**
29
- * Prevents changes made to how JavaScript file is added to
30
- * pages.
31
- *
32
- * @param array $return
33
- *
34
- * @return array
35
- *
36
- * @since 1.7/1.12
37
- */
38
- public static function undo_script_blocking( $return ) {
39
- $settings = ctf_get_database_settings();
40
- if ( ! CTF_GDPR_Integrations::doing_gdpr( $settings ) ) {
41
- return $return;
42
- }
43
- remove_filter( 'wt_cli_third_party_scripts', 'wt_cli_twitter_feed_script' );
44
- remove_filter( 'cmplz_known_script_tags', 'cmplz_twitter_feed_script' );
45
-
46
- return $return;
47
- }
48
-
49
- /**
50
- * Whether or not consent plugins that Twitter Feed
51
- * is compatible with are active.
52
- *
53
- * @return bool|string
54
- *
55
- * @since 1.7/1.12
56
- */
57
- public static function gdpr_plugins_active() {
58
- if ( class_exists( 'Cookie_Notice' ) ) {
59
- return 'Cookie Notice by dFactory';
60
- }
61
- if ( function_exists( 'run_cookie_law_info' ) ) {
62
- return 'GDPR Cookie Consent by WebToffee';
63
- }
64
- if ( class_exists( 'Cookiebot_WP' ) ) {
65
- return 'Cookiebot by Cybot A/S';
66
- }
67
- if ( class_exists( 'COMPLIANZ' ) ) {
68
- return 'Complianz by Really Simple Plugins';
69
- }
70
- if ( function_exists('BorlabsCookieHelper') ) {
71
- return 'Borlabs Cookie by Borlabs';
72
- }
73
-
74
- return false;
75
- }
76
-
77
- /**
78
- * GDPR features can be added automatically, forced enabled,
79
- * or forced disabled.
80
- *
81
- * @param $settings
82
- *
83
- * @return bool
84
- *
85
- * @since 1.7/1.12
86
- */
87
- public static function doing_gdpr( $settings ) {
88
- $gdpr = isset( $settings['gdpr'] ) ? $settings['gdpr'] : 'auto';
89
- if ( $gdpr === 'no' ) {
90
- return false;
91
- }
92
- if ( $gdpr === 'yes' ) {
93
- return true;
94
- }
95
- return (CTF_GDPR_Integrations::gdpr_plugins_active() !== false);
96
- }
97
-
98
- /**
99
- * No tests needed in free version
100
- *
101
- * @param bool $retest
102
- *
103
- * @return bool
104
- *
105
- * @since 1.7/1.12
106
- */
107
- public static function gdpr_tests_successful( $retest = false ) {
108
- return true;
109
- }
110
-
111
- /**
112
- * No tests needed in free version
113
- *
114
- * @return array
115
- *
116
- * @since 1.7/1.12
117
- */
118
- public static function gdpr_tests_error_message() {
119
- return array();
120
- }
121
-
122
  }
1
+ <?php
2
+ /**
3
+ * Class CTF_GDPR_Integrations
4
+ *
5
+ * Adds GDPR related workarounds for third-party plugins:
6
+ * https://wordpress.org/plugins/cookie-law-info/
7
+ *
8
+ * @since 1.7/1.12
9
+ */
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ die( '-1' );
13
+ }
14
+
15
+ class CTF_GDPR_Integrations {
16
+
17
+ /**
18
+ * Undoing of Cookie Notice's Twitter Feed related code
19
+ * needs to be done late.
20
+ *
21
+ * @since 1.7/1.12
22
+ */
23
+ public static function init() {
24
+ add_filter( 'wt_cli_third_party_scripts', array( 'CTF_GDPR_Integrations', 'undo_script_blocking' ), 11 );
25
+ add_filter( 'cmplz_known_script_tags', array( 'CTF_GDPR_Integrations', 'undo_script_blocking' ), 11 );
26
+ }
27
+
28
+ /**
29
+ * Prevents changes made to how JavaScript file is added to
30
+ * pages.
31
+ *
32
+ * @param array $return
33
+ *
34
+ * @return array
35
+ *
36
+ * @since 1.7/1.12
37
+ */
38
+ public static function undo_script_blocking( $return ) {
39
+ $settings = ctf_get_database_settings();
40
+ if ( ! CTF_GDPR_Integrations::doing_gdpr( $settings ) ) {
41
+ return $return;
42
+ }
43
+ remove_filter( 'wt_cli_third_party_scripts', 'wt_cli_twitter_feed_script' );
44
+ remove_filter( 'cmplz_known_script_tags', 'cmplz_twitter_feed_script' );
45
+
46
+ return $return;
47
+ }
48
+
49
+ /**
50
+ * Whether or not consent plugins that Twitter Feed
51
+ * is compatible with are active.
52
+ *
53
+ * @return bool|string
54
+ *
55
+ * @since 1.7/1.12
56
+ */
57
+ public static function gdpr_plugins_active() {
58
+ if ( class_exists( 'Cookie_Notice' ) ) {
59
+ return 'Cookie Notice by dFactory';
60
+ }
61
+ if ( function_exists( 'run_cookie_law_info' ) ) {
62
+ return 'GDPR Cookie Consent by WebToffee';
63
+ }
64
+ if ( class_exists( 'Cookiebot_WP' ) ) {
65
+ return 'Cookiebot by Cybot A/S';
66
+ }
67
+ if ( class_exists( 'COMPLIANZ' ) ) {
68
+ return 'Complianz by Really Simple Plugins';
69
+ }
70
+ if ( function_exists('BorlabsCookieHelper') ) {
71
+ return 'Borlabs Cookie by Borlabs';
72
+ }
73
+
74
+ return false;
75
+ }
76
+
77
+ /**
78
+ * GDPR features can be added automatically, forced enabled,
79
+ * or forced disabled.
80
+ *
81
+ * @param $settings
82
+ *
83
+ * @return bool
84
+ *
85
+ * @since 1.7/1.12
86
+ */
87
+ public static function doing_gdpr( $settings ) {
88
+ $gdpr = isset( $settings['gdpr'] ) ? $settings['gdpr'] : 'auto';
89
+ if ( $gdpr === 'no' ) {
90
+ return false;
91
+ }
92
+ if ( $gdpr === 'yes' ) {
93
+ return true;
94
+ }
95
+ return (CTF_GDPR_Integrations::gdpr_plugins_active() !== false);
96
+ }
97
+
98
+ /**
99
+ * No tests needed in free version
100
+ *
101
+ * @param bool $retest
102
+ *
103
+ * @return bool
104
+ *
105
+ * @since 1.7/1.12
106
+ */
107
+ public static function gdpr_tests_successful( $retest = false ) {
108
+ return true;
109
+ }
110
+
111
+ /**
112
+ * No tests needed in free version
113
+ *
114
+ * @return array
115
+ *
116
+ * @since 1.7/1.12
117
+ */
118
+ public static function gdpr_tests_error_message() {
119
+ return array();
120
+ }
121
+
122
  }
inc/class-ctf-tracking.php CHANGED
@@ -1,431 +1,431 @@
1
- <?php
2
- /**
3
- * Tracking functions for reporting plugin usage to the Smash Balloon site for users that have opted in
4
- *
5
- * @copyright Copyright (c) 2018, Chris Christoff
6
- * @since
7
- */
8
-
9
- // Exit if accessed directly
10
- if ( ! defined( 'ABSPATH' ) ) {
11
- exit;
12
- }
13
-
14
- /**
15
- * Usage tracking
16
- *
17
- * @access public
18
- * @since 5.6
19
- * @return void
20
- */
21
- class CTF_Tracking {
22
-
23
- public function __construct() {
24
- add_action( 'init', array( $this, 'schedule_send' ) );
25
- add_filter( 'cron_schedules', array( $this, 'add_schedules' ) );
26
- add_action( 'ctf_usage_tracking_cron', array( $this, 'send_checkin' ) );
27
- add_action( 'admin_init', array( $this, 'save_setting' ) );
28
- }
29
-
30
- private function normalize_and_format( $key, $value ) {
31
- $normal_bools = array(
32
- 'ajax_theme',
33
- 'have_own_tokens',
34
- 'use_own_consumer',
35
- 'preserve_settings',
36
- 'usertimeline_includereplies',
37
- 'hometimeline_includereplies',
38
- 'mentionstimeline_includereplies',
39
- 'usertimeline_includeretweets',
40
- 'hometimeline_includeretweets',
41
- 'mentionstimeline_includeretweets',
42
- 'includereplies',
43
- 'includeretweets',
44
- 'include_retweeter',
45
- 'include_avatar',
46
- 'include_author',
47
- 'include_text',
48
- 'include_date',
49
- 'include_actions',
50
- 'include_twitterlink',
51
- 'include_linkbox',
52
- 'include_logo',
53
- 'creditctf',
54
- 'showbutton',
55
- 'showheader',
56
- 'persistentcache',
57
- 'selfreplies',
58
- 'autores',
59
- 'disableintents',
60
- 'disableawesome',
61
- 'shorturls',
62
- 'curlcards',
63
- 'sslonly',
64
- 'disablelightbox',
65
- 'include_media',
66
- 'include_twittercards',
67
- 'include_replied_to',
68
- 'masonry',
69
- 'carousel',
70
- 'carouselpag',
71
- 'autoscroll',
72
- 'showbio',
73
- 'disablelinks',
74
- 'linktexttotwitter',
75
- 'include_media_placeholder'
76
- );
77
- $custom_text_settings = array(
78
- 'retweetedtext',
79
- 'twitterlinktext',
80
- 'buttontext',
81
- 'usertimeline_text',
82
- 'hashtag_text',
83
- 'search_text',
84
- 'lists_id',
85
- 'lists_owner',
86
- 'inreplytotext',
87
- 'custom_css',
88
- 'custom_js'
89
- );
90
- $comma_separate_counts_settings = array(
91
- 'includewords',
92
- 'excludewords',
93
- 'remove_by_id'
94
- );
95
- $defaults = array(
96
- 'ajax_theme' => '0',
97
- 'have_own_tokens' => '',
98
- 'use_own_consumer' => '',
99
- 'preserve_settings' => '',
100
- 'usertimeline_includereplies' => '',
101
- 'hometimeline_includereplies' => '',
102
- 'mentionstimeline_includereplies' => '',
103
- 'usertimeline_includeretweets' => '',
104
- 'hometimeline_includeretweets' => '1',
105
- 'mentionstimeline_includeretweets' => '',
106
- 'tab' => 'configure',
107
- 'consumer_key' => '',
108
- 'consumer_secret' => '',
109
- 'access_token' => '',
110
- 'access_token_secret' => '',
111
- 'type' => 'usertimeline',
112
- 'usertimeline_text' => '',
113
- 'hashtag_text' => '',
114
- 'search_text' => '',
115
- 'lists_id' => '',
116
- 'lists_owner' => '',
117
- 'num' => '5',
118
- 'cache_time' => '1',
119
- 'cache_time_unit' => '3600',
120
- 'includereplies' => '',
121
- 'includeretweets' => '',
122
- 'width_mobile_no_fixed' => '0',
123
- 'include_retweeter' => '1',
124
- 'include_avatar' => '1',
125
- 'include_author' => '1',
126
- 'include_text' => '1',
127
- 'include_date' => '1',
128
- 'include_actions' => '1',
129
- 'include_twitterlink' => '1',
130
- 'include_linkbox' => '1',
131
- 'include_logo' => '1',
132
- 'creditctf' => '0',
133
- 'showbutton' => '1',
134
- 'showheader' => '1',
135
- 'persistentcache' => '1',
136
- 'selfreplies' => '1',
137
- 'autores' => '1',
138
- 'disableintents' => '0',
139
- 'disableawesome' => '0',
140
- 'shorturls' => '0',
141
- 'curlcards' => '1',
142
- 'sslonly' => '0',
143
- 'disablelightbox' => '0',
144
- 'include_media' => '1',
145
- 'include_twittercards' => '1',
146
- 'include_replied_to' => '1',
147
- 'masonry' => '0',
148
- 'carousel' => '0',
149
- 'carouselpag' => '0',
150
- 'carouselautoplay' => '0',
151
- 'autoscroll' => '1',
152
- 'width' => '100',
153
- 'width_unit' => '%',
154
- 'height' => '',
155
- 'height_unit' => '%',
156
- 'class' => '',
157
- 'layout' => 'list',
158
- 'masonrycols' => '3',
159
- 'masonrymobilecols' => '1',
160
- 'carouselcols' => '3',
161
- 'carouselmobilecols' => '1',
162
- 'carouselloop' => 'none',
163
- 'carouselarrows' => 'onhover',
164
- 'carouselheight' => 'tallest',
165
- 'carouseltime' => '5000',
166
- 'maxmedia' => '4',
167
- 'imagecols' => 'auto',
168
- 'autoscrolldistance' => '200',
169
- 'includewords' => '',
170
- 'excludewords' => '',
171
- 'includeanyall' => 'any',
172
- 'filterandor' => 'and',
173
- 'excludeanyall' => 'any',
174
- 'remove_by_id' => '',
175
- 'custom_css' => '',
176
- 'custom_js' => '',
177
- 'request_method' => 'auto',
178
- 'cron_cache_clear' => 'unset',
179
- 'multiplier' => '1.25',
180
- 'font_method' => 'svg',
181
- 'include_media_placeholder' => '1',
182
- 'showbio' => '1',
183
- 'disablelinks' => '',
184
- 'linktexttotwitter' => '',
185
- 'bgcolor' => '#',
186
- 'tweetbgcolor' => '#',
187
- 'headerbgcolor' => '#',
188
- 'headertextcolor' => '#',
189
- 'headertext' => '',
190
- 'timezone' => 'default',
191
- 'dateformat' => '1',
192
- 'datecustom' => '',
193
- 'mtime' => '',
194
- 'htime' => '',
195
- 'nowtime' => '',
196
- 'datetextsize' => 'inherit',
197
- 'datetextweight' => 'inherit',
198
- 'authortextsize' => 'inherit',
199
- 'authortextweight' => 'inherit',
200
- 'logosize' => 'inherit',
201
- 'logocolor' => '#',
202
- 'tweettextsize' => 'inherit',
203
- 'tweettextweight' => 'inherit',
204
- 'textcolor' => '#',
205
- 'textlength' => '280',
206
- 'retweetedtext' => 'Retweeted',
207
- 'linktextcolor' => '#',
208
- 'quotedauthorsize' => 'inherit',
209
- 'quotedauthorweight' => 'inherit',
210
- 'iconsize' => 'inherit',
211
- 'iconcolor' => '#',
212
- 'twitterlinktext' => 'Twitter',
213
- 'buttoncolor' => '#',
214
- 'buttontextcolor' => '#',
215
- 'buttontext' => 'Load More...',
216
- 'inreplytotext' => 'In reply to'
217
- );
218
-
219
- if ( is_array( $value ) ) {
220
- if ( empty( $value ) ) {
221
- return 0;
222
- }
223
- return count( $value );
224
- // 0 for anything that might be false, 1 for everything else
225
- } elseif ( in_array( $key, $normal_bools, true ) ) {
226
- if ( in_array( $value, array( false, 0, '0', 'false', '' ), true ) ) {
227
- return 0;
228
- }
229
- return 1;
230
-
231
- // if a custom text setting, we just want to know if it's different than the default
232
- } elseif ( in_array( $key, $custom_text_settings, true ) ) {
233
- if ( $defaults[ $key ] === $value ) {
234
- return 0;
235
- }
236
- return 1;
237
- } elseif ( in_array( $key, $comma_separate_counts_settings, true ) ) {
238
- if ( str_replace( ' ', '', $value ) === '' ) {
239
- return 0;
240
- }
241
- $split_at_comma = explode( ',', $value );
242
- return count( $split_at_comma );
243
- }
244
-
245
- return $value;
246
-
247
- }
248
-
249
- private function get_data() {
250
- $data = array();
251
-
252
- // Retrieve current theme info
253
- $theme_data = wp_get_theme();
254
-
255
- $count_b = 1;
256
- if ( is_multisite() ) {
257
- if ( function_exists( 'get_blog_count' ) ) {
258
- $count_b = get_blog_count();
259
- } else {
260
- $count_b = 'Not Set';
261
- }
262
- }
263
-
264
- $php_version = rtrim( ltrim( sanitize_text_field( phpversion() ) ) );
265
- $php_version = ! empty( $php_version ) ? substr( $php_version, 0, strpos( $php_version, '.', strpos( $php_version, '.' ) + 1 ) ) : phpversion();
266
-
267
- global $wp_version;
268
- $data['this_plugin'] = 'tw';
269
- $data['php_version'] = $php_version;
270
- $data['mi_version'] = CTF_VERSION;
271
- $data['wp_version'] = $wp_version;
272
- $data['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
273
- $data['multisite'] = is_multisite();
274
- $data['url'] = home_url();
275
- $data['themename'] = $theme_data->Name;
276
- $data['themeversion'] = $theme_data->Version;
277
- $data['settings'] = array();
278
- $data['pro'] = ctf_is_pro_version() ? '1' : '';
279
- $data['sites'] = $count_b;
280
- $data['usagetracking'] = get_option( 'ctf_usage_tracking_config', false );
281
- $num_users = function_exists( 'count_users' ) ? count_users() : 'Not Set';
282
- $data['usercount'] = is_array( $num_users ) ? $num_users['total_users'] : 1;
283
- $data['timezoneoffset']= date('P');
284
-
285
- $settings_to_send = array();
286
- $raw_settings = get_option( 'ctf_options', array() );
287
-
288
- foreach ( $raw_settings as $key => $value ) {
289
- if ( $key === 'consumer_key'
290
- || $key === 'consumer_secret'
291
- || $key === 'access_token'
292
- || $key === 'access_token_secret'
293
- || $key === 'tab' ) {
294
- // do not sent
295
- } elseif ( $key === 'connected_accounts' ) {
296
- if ( is_array( $raw_settings['connected_accounts'] ) ) {
297
- $settings_to_send['connected_accounts'] = count( $raw_settings['connected_accounts'] );
298
- } else {
299
- $settings_to_send['connected_accounts'] = 0;
300
- }
301
- } else {
302
- $value = $this->normalize_and_format( $key, $value );
303
- if ( $value !== false ) {
304
- $settings_to_send[ $key ] = $value;
305
- }
306
- }
307
-
308
- }
309
- global $wpdb;
310
- $feed_caches = array();
311
-
312
- $results = $wpdb->get_results( "
313
- SELECT option_name
314
- FROM $wpdb->options
315
- WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
316
- AND `option_name` NOT LIKE ('%\_transient\_ctf\_header%');", ARRAY_A );
317
-
318
- if ( isset( $results[0] ) ) {
319
- $feed_caches = $results;
320
- }
321
- $settings_to_send['num_found_feed_caches'] = count( $feed_caches );
322
-
323
- $data['settings'] = $settings_to_send;
324
-
325
- // Retrieve current plugin information
326
- if( ! function_exists( 'get_plugins' ) ) {
327
- include ABSPATH . '/wp-admin/includes/plugin.php';
328
- }
329
-
330
- $plugins = get_plugins();
331
- $active_plugins = get_option( 'active_plugins', array() );
332
- $plugins_to_send = array();
333
-
334
- foreach ( $plugins as $plugin_path => $plugin ) {
335
- // If the plugin isn't active, don't show it.
336
- if ( ! in_array( $plugin_path, $active_plugins ) )
337
- continue;
338
-
339
- $plugins_to_send[] = $plugin['Name'];
340
- }
341
-
342
- $data['active_plugins'] = $plugins_to_send;
343
- $data['locale'] = get_locale();
344
-
345
- return $data;
346
- }
347
-
348
- public function send_checkin( $override = false, $ignore_last_checkin = false ) {
349
-
350
- $home_url = trailingslashit( home_url() );
351
-
352
- if ( strpos( $home_url, 'smashballoon.com' ) !== false ) {
353
- return false;
354
- }
355
-
356
- if( ! $this->tracking_allowed() && ! $override ) {
357
- return false;
358
- }
359
-
360
- // Send a maximum of once per week
361
- $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => ctf_is_pro_version() ) );
362
- if ( is_numeric( $usage_tracking['last_send'] ) && $usage_tracking['last_send'] > strtotime( '-1 week' ) && ! $ignore_last_checkin ) {
363
- return false;
364
- }
365
-
366
- $request = wp_remote_post( 'https://usage.smashballoon.com/v1/checkin/', array(
367
- 'method' => 'POST',
368
- 'timeout' => 5,
369
- 'redirection' => 5,
370
- 'httpversion' => '1.1',
371
- 'blocking' => false,
372
- 'body' => $this->get_data(),
373
- 'user-agent' => 'MI/' . CTF_VERSION . '; ' . get_bloginfo( 'url' )
374
- ) );
375
-
376
- // If we have completed successfully, recheck in 1 week
377
- $usage_tracking['last_send'] = time();
378
- update_option( 'ctf_usage_tracking', $usage_tracking, false );
379
- return true;
380
- }
381
-
382
- private function tracking_allowed() {
383
- $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => ctf_is_pro_version() ) );
384
- $tracking_allowed = isset( $usage_tracking['enabled'] ) ? $usage_tracking['enabled'] : ctf_is_pro_version();
385
-
386
- return $tracking_allowed;
387
- }
388
-
389
- public function schedule_send() {
390
- if ( ! wp_next_scheduled( 'ctf_usage_tracking_cron' ) ) {
391
- $tracking = array();
392
- $tracking['day'] = rand( 0, 6 );
393
- $tracking['hour'] = rand( 0, 23 );
394
- $tracking['minute'] = rand( 0, 59 );
395
- $tracking['second'] = rand( 0, 59 );
396
- $tracking['offset'] = ( $tracking['day'] * DAY_IN_SECONDS ) +
397
- ( $tracking['hour'] * HOUR_IN_SECONDS ) +
398
- ( $tracking['minute'] * MINUTE_IN_SECONDS ) +
399
- $tracking['second'];
400
- $last_sunday = strtotime("next sunday") - (7 * DAY_IN_SECONDS);
401
- if ( ($last_sunday + $tracking['offset']) > time() + 6 * HOUR_IN_SECONDS ) {
402
- $tracking['initsend'] = $last_sunday + $tracking['offset'];
403
- } else {
404
- $tracking['initsend'] = strtotime("next sunday") + $tracking['offset'];
405
- }
406
-
407
- wp_schedule_event( $tracking['initsend'], 'weekly', 'ctf_usage_tracking_cron' );
408
- update_option( 'ctf_usage_tracking_config', $tracking );
409
- }
410
- }
411
-
412
- public function add_schedules( $schedules = array() ) {
413
- // Adds once weekly to the existing schedules.
414
- $schedules['weekly'] = array(
415
- 'interval' => 604800,
416
- 'display' => __( 'Once Weekly', 'custom-twitter-feeds' )
417
- );
418
- return $schedules;
419
- }
420
-
421
- public function save_setting() {
422
- if ( isset( $_POST['ctf_usage_tracking_enable'] ) ) {
423
- $usage_tracking['enabled'] = false;
424
- if ( isset( $_POST['ctf_usage_tracking_enable'] ) && $_POST['ctf_usage_tracking_enable'] === 'on' ) {
425
- $usage_tracking['enabled'] = true;
426
- }
427
- update_option( 'ctf_usage_tracking', $usage_tracking, false );
428
- }
429
- }
430
- }
431
  new CTF_Tracking();
1
+ <?php
2
+ /**
3
+ * Tracking functions for reporting plugin usage to the Smash Balloon site for users that have opted in
4
+ *
5
+ * @copyright Copyright (c) 2018, Chris Christoff
6
+ * @since
7
+ */
8
+
9
+ // Exit if accessed directly
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Usage tracking
16
+ *
17
+ * @access public
18
+ * @since 5.6
19
+ * @return void
20
+ */
21
+ class CTF_Tracking {
22
+
23
+ public function __construct() {
24
+ add_action( 'init', array( $this, 'schedule_send' ) );
25
+ add_filter( 'cron_schedules', array( $this, 'add_schedules' ) );
26
+ add_action( 'ctf_usage_tracking_cron', array( $this, 'send_checkin' ) );
27
+ add_action( 'admin_init', array( $this, 'save_setting' ) );
28
+ }
29
+
30
+ private function normalize_and_format( $key, $value ) {
31
+ $normal_bools = array(
32
+ 'ajax_theme',
33
+ 'have_own_tokens',
34
+ 'use_own_consumer',
35
+ 'preserve_settings',
36
+ 'usertimeline_includereplies',
37
+ 'hometimeline_includereplies',
38
+ 'mentionstimeline_includereplies',
39
+ 'usertimeline_includeretweets',
40
+ 'hometimeline_includeretweets',
41
+ 'mentionstimeline_includeretweets',
42
+ 'includereplies',
43
+ 'includeretweets',
44
+ 'include_retweeter',
45
+ 'include_avatar',
46
+ 'include_author',
47
+ 'include_text',
48
+ 'include_date',
49
+ 'include_actions',
50
+ 'include_twitterlink',
51
+ 'include_linkbox',
52
+ 'include_logo',
53
+ 'creditctf',
54
+ 'showbutton',
55
+ 'showheader',
56
+ 'persistentcache',
57
+ 'selfreplies',
58
+ 'autores',
59
+ 'disableintents',
60
+ 'disableawesome',
61
+ 'shorturls',
62
+ 'curlcards',
63
+ 'sslonly',
64
+ 'disablelightbox',
65
+ 'include_media',
66
+ 'include_twittercards',
67
+ 'include_replied_to',
68
+ 'masonry',
69
+ 'carousel',
70
+ 'carouselpag',
71
+ 'autoscroll',
72
+ 'showbio',
73
+ 'disablelinks',
74
+ 'linktexttotwitter',
75
+ 'include_media_placeholder'
76
+ );
77
+ $custom_text_settings = array(
78
+ 'retweetedtext',
79
+ 'twitterlinktext',
80
+ 'buttontext',
81
+ 'usertimeline_text',
82
+ 'hashtag_text',
83
+ 'search_text',
84
+ 'lists_id',
85
+ 'lists_owner',
86
+ 'inreplytotext',
87
+ 'custom_css',
88
+ 'custom_js'
89
+ );
90
+ $comma_separate_counts_settings = array(
91
+ 'includewords',
92
+ 'excludewords',
93
+ 'remove_by_id'
94
+ );
95
+ $defaults = array(
96
+ 'ajax_theme' => '0',
97
+ 'have_own_tokens' => '',
98
+ 'use_own_consumer' => '',
99
+ 'preserve_settings' => '',
100
+ 'usertimeline_includereplies' => '',
101
+ 'hometimeline_includereplies' => '',
102
+ 'mentionstimeline_includereplies' => '',
103
+ 'usertimeline_includeretweets' => '',
104
+ 'hometimeline_includeretweets' => '1',
105
+ 'mentionstimeline_includeretweets' => '',
106
+ 'tab' => 'configure',
107
+ 'consumer_key' => '',
108
+ 'consumer_secret' => '',
109
+ 'access_token' => '',
110
+ 'access_token_secret' => '',
111
+ 'type' => 'usertimeline',
112
+ 'usertimeline_text' => '',
113
+ 'hashtag_text' => '',
114
+ 'search_text' => '',
115
+ 'lists_id' => '',
116
+ 'lists_owner' => '',
117
+ 'num' => '5',
118
+ 'cache_time' => '1',
119
+ 'cache_time_unit' => '3600',
120
+ 'includereplies' => '',
121
+ 'includeretweets' => '',
122
+ 'width_mobile_no_fixed' => '0',
123
+ 'include_retweeter' => '1',
124
+ 'include_avatar' => '1',
125
+ 'include_author' => '1',
126
+ 'include_text' => '1',
127
+ 'include_date' => '1',
128
+ 'include_actions' => '1',
129
+ 'include_twitterlink' => '1',
130
+ 'include_linkbox' => '1',
131
+ 'include_logo' => '1',
132
+ 'creditctf' => '0',
133
+ 'showbutton' => '1',
134
+ 'showheader' => '1',
135
+ 'persistentcache' => '1',
136
+ 'selfreplies' => '1',
137
+ 'autores' => '1',
138
+ 'disableintents' => '0',
139
+ 'disableawesome' => '0',
140
+ 'shorturls' => '0',
141
+ 'curlcards' => '1',
142
+ 'sslonly' => '0',
143
+ 'disablelightbox' => '0',
144
+ 'include_media' => '1',
145
+ 'include_twittercards' => '1',
146
+ 'include_replied_to' => '1',
147
+ 'masonry' => '0',
148
+ 'carousel' => '0',
149
+ 'carouselpag' => '0',
150
+ 'carouselautoplay' => '0',
151
+ 'autoscroll' => '1',
152
+ 'width' => '100',
153
+ 'width_unit' => '%',
154
+ 'height' => '',
155
+ 'height_unit' => '%',
156
+ 'class' => '',
157
+ 'layout' => 'list',
158
+ 'masonrycols' => '3',
159
+ 'masonrymobilecols' => '1',
160
+ 'carouselcols' => '3',
161
+ 'carouselmobilecols' => '1',
162
+ 'carouselloop' => 'none',
163
+ 'carouselarrows' => 'onhover',
164
+ 'carouselheight' => 'tallest',
165
+ 'carouseltime' => '5000',
166
+ 'maxmedia' => '4',
167
+ 'imagecols' => 'auto',
168
+ 'autoscrolldistance' => '200',
169
+ 'includewords' => '',
170
+ 'excludewords' => '',
171
+ 'includeanyall' => 'any',
172
+ 'filterandor' => 'and',
173
+ 'excludeanyall' => 'any',
174
+ 'remove_by_id' => '',
175
+ 'custom_css' => '',
176
+ 'custom_js' => '',
177
+ 'request_method' => 'auto',
178
+ 'cron_cache_clear' => 'unset',
179
+ 'multiplier' => '1.25',
180
+ 'font_method' => 'svg',
181
+ 'include_media_placeholder' => '1',
182
+ 'showbio' => '1',
183
+ 'disablelinks' => '',
184
+ 'linktexttotwitter' => '',
185
+ 'bgcolor' => '#',
186
+ 'tweetbgcolor' => '#',
187
+ 'headerbgcolor' => '#',
188
+ 'headertextcolor' => '#',
189
+ 'headertext' => '',
190
+ 'timezone' => 'default',
191
+ 'dateformat' => '1',
192
+ 'datecustom' => '',
193
+ 'mtime' => '',
194
+ 'htime' => '',
195
+ 'nowtime' => '',
196
+ 'datetextsize' => 'inherit',
197
+ 'datetextweight' => 'inherit',
198
+ 'authortextsize' => 'inherit',
199
+ 'authortextweight' => 'inherit',
200
+ 'logosize' => 'inherit',
201
+ 'logocolor' => '#',
202
+ 'tweettextsize' => 'inherit',
203
+ 'tweettextweight' => 'inherit',
204
+ 'textcolor' => '#',
205
+ 'textlength' => '280',
206
+ 'retweetedtext' => 'Retweeted',
207
+ 'linktextcolor' => '#',
208
+ 'quotedauthorsize' => 'inherit',
209
+ 'quotedauthorweight' => 'inherit',
210
+ 'iconsize' => 'inherit',
211
+ 'iconcolor' => '#',
212
+ 'twitterlinktext' => 'Twitter',
213
+ 'buttoncolor' => '#',
214
+ 'buttontextcolor' => '#',
215
+ 'buttontext' => 'Load More...',
216
+ 'inreplytotext' => 'In reply to'
217
+ );
218
+
219
+ if ( is_array( $value ) ) {
220
+ if ( empty( $value ) ) {
221
+ return 0;
222
+ }
223
+ return count( $value );
224
+ // 0 for anything that might be false, 1 for everything else
225
+ } elseif ( in_array( $key, $normal_bools, true ) ) {
226
+ if ( in_array( $value, array( false, 0, '0', 'false', '' ), true ) ) {
227
+ return 0;
228
+ }
229
+ return 1;
230
+
231
+ // if a custom text setting, we just want to know if it's different than the default
232
+ } elseif ( in_array( $key, $custom_text_settings, true ) ) {
233
+ if ( $defaults[ $key ] === $value ) {
234
+ return 0;
235
+ }
236
+ return 1;
237
+ } elseif ( in_array( $key, $comma_separate_counts_settings, true ) ) {
238
+ if ( str_replace( ' ', '', $value ) === '' ) {
239
+ return 0;
240
+ }
241
+ $split_at_comma = explode( ',', $value );
242
+ return count( $split_at_comma );
243
+ }
244
+
245
+ return $value;
246
+
247
+ }
248
+
249
+ private function get_data() {
250
+ $data = array();
251
+
252
+ // Retrieve current theme info
253
+ $theme_data = wp_get_theme();
254
+
255
+ $count_b = 1;
256
+ if ( is_multisite() ) {
257
+ if ( function_exists( 'get_blog_count' ) ) {
258
+ $count_b = get_blog_count();
259
+ } else {
260
+ $count_b = 'Not Set';
261
+ }
262
+ }
263
+
264
+ $php_version = rtrim( ltrim( sanitize_text_field( phpversion() ) ) );
265
+ $php_version = ! empty( $php_version ) ? substr( $php_version, 0, strpos( $php_version, '.', strpos( $php_version, '.' ) + 1 ) ) : phpversion();
266
+
267
+ global $wp_version;
268
+ $data['this_plugin'] = 'tw';
269
+ $data['php_version'] = $php_version;
270
+ $data['mi_version'] = CTF_VERSION;
271
+ $data['wp_version'] = $wp_version;
272
+ $data['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
273
+ $data['multisite'] = is_multisite();
274
+ $data['url'] = home_url();
275
+ $data['themename'] = $theme_data->Name;
276
+ $data['themeversion'] = $theme_data->Version;
277
+ $data['settings'] = array();
278
+ $data['pro'] = ctf_is_pro_version() ? '1' : '';
279
+ $data['sites'] = $count_b;
280
+ $data['usagetracking'] = get_option( 'ctf_usage_tracking_config', false );
281
+ $num_users = function_exists( 'count_users' ) ? count_users() : 'Not Set';
282
+ $data['usercount'] = is_array( $num_users ) ? $num_users['total_users'] : 1;
283
+ $data['timezoneoffset']= date('P');
284
+
285
+ $settings_to_send = array();
286
+ $raw_settings = get_option( 'ctf_options', array() );
287
+
288
+ foreach ( $raw_settings as $key => $value ) {
289
+ if ( $key === 'consumer_key'
290
+ || $key === 'consumer_secret'
291
+ || $key === 'access_token'
292
+ || $key === 'access_token_secret'
293
+ || $key === 'tab' ) {
294
+ // do not sent
295
+ } elseif ( $key === 'connected_accounts' ) {
296
+ if ( is_array( $raw_settings['connected_accounts'] ) ) {
297
+ $settings_to_send['connected_accounts'] = count( $raw_settings['connected_accounts'] );
298
+ } else {
299
+ $settings_to_send['connected_accounts'] = 0;
300
+ }
301
+ } else {
302
+ $value = $this->normalize_and_format( $key, $value );
303
+ if ( $value !== false ) {
304
+ $settings_to_send[ $key ] = $value;
305
+ }
306
+ }
307
+
308
+ }
309
+ global $wpdb;
310
+ $feed_caches = array();
311
+
312
+ $results = $wpdb->get_results( "
313
+ SELECT option_name
314
+ FROM $wpdb->options
315
+ WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
316
+ AND `option_name` NOT LIKE ('%\_transient\_ctf\_header%');", ARRAY_A );
317
+
318
+ if ( isset( $results[0] ) ) {
319
+ $feed_caches = $results;
320
+ }
321
+ $settings_to_send['num_found_feed_caches'] = count( $feed_caches );
322
+
323
+ $data['settings'] = $settings_to_send;
324
+
325
+ // Retrieve current plugin information
326
+ if( ! function_exists( 'get_plugins' ) ) {
327
+ include ABSPATH . '/wp-admin/includes/plugin.php';
328
+ }
329
+
330
+ $plugins = get_plugins();
331
+ $active_plugins = get_option( 'active_plugins', array() );
332
+ $plugins_to_send = array();
333
+
334
+ foreach ( $plugins as $plugin_path => $plugin ) {
335
+ // If the plugin isn't active, don't show it.
336
+ if ( ! in_array( $plugin_path, $active_plugins ) )
337
+ continue;
338
+
339
+ $plugins_to_send[] = $plugin['Name'];
340
+ }
341
+
342
+ $data['active_plugins'] = $plugins_to_send;
343
+ $data['locale'] = get_locale();
344
+
345
+ return $data;
346
+ }
347
+
348
+ public function send_checkin( $override = false, $ignore_last_checkin = false ) {
349
+
350
+ $home_url = trailingslashit( home_url() );
351
+
352
+ if ( strpos( $home_url, 'smashballoon.com' ) !== false ) {
353
+ return false;
354
+ }
355
+
356
+ if( ! $this->tracking_allowed() && ! $override ) {
357
+ return false;
358
+ }
359
+
360
+ // Send a maximum of once per week
361
+ $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => ctf_is_pro_version() ) );
362
+ if ( is_numeric( $usage_tracking['last_send'] ) && $usage_tracking['last_send'] > strtotime( '-1 week' ) && ! $ignore_last_checkin ) {
363
+ return false;
364
+ }
365
+
366
+ $request = wp_remote_post( 'https://usage.smashballoon.com/v1/checkin/', array(
367
+ 'method' => 'POST',
368
+ 'timeout' => 5,
369
+ 'redirection' => 5,
370
+ 'httpversion' => '1.1',
371
+ 'blocking' => false,
372
+ 'body' => $this->get_data(),
373
+ 'user-agent' => 'MI/' . CTF_VERSION . '; ' . get_bloginfo( 'url' )
374
+ ) );
375
+
376
+ // If we have completed successfully, recheck in 1 week
377
+ $usage_tracking['last_send'] = time();
378
+ update_option( 'ctf_usage_tracking', $usage_tracking, false );
379
+ return true;
380
+ }
381
+
382
+ private function tracking_allowed() {
383
+ $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => ctf_is_pro_version() ) );
384
+ $tracking_allowed = isset( $usage_tracking['enabled'] ) ? $usage_tracking['enabled'] : ctf_is_pro_version();
385
+
386
+ return $tracking_allowed;
387
+ }
388
+
389
+ public function schedule_send() {
390
+ if ( ! wp_next_scheduled( 'ctf_usage_tracking_cron' ) ) {
391
+ $tracking = array();
392
+ $tracking['day'] = rand( 0, 6 );
393
+ $tracking['hour'] = rand( 0, 23 );
394
+ $tracking['minute'] = rand( 0, 59 );
395
+ $tracking['second'] = rand( 0, 59 );
396
+ $tracking['offset'] = ( $tracking['day'] * DAY_IN_SECONDS ) +
397
+ ( $tracking['hour'] * HOUR_IN_SECONDS ) +
398
+ ( $tracking['minute'] * MINUTE_IN_SECONDS ) +
399
+ $tracking['second'];
400
+ $last_sunday = strtotime("next sunday") - (7 * DAY_IN_SECONDS);
401
+ if ( ($last_sunday + $tracking['offset']) > time() + 6 * HOUR_IN_SECONDS ) {
402
+ $tracking['initsend'] = $last_sunday + $tracking['offset'];
403
+ } else {
404
+ $tracking['initsend'] = strtotime("next sunday") + $tracking['offset'];
405
+ }
406
+
407
+ wp_schedule_event( $tracking['initsend'], 'weekly', 'ctf_usage_tracking_cron' );
408
+ update_option( 'ctf_usage_tracking_config', $tracking );
409
+ }
410
+ }
411
+
412
+ public function add_schedules( $schedules = array() ) {
413
+ // Adds once weekly to the existing schedules.
414
+ $schedules['weekly'] = array(
415
+ 'interval' => 604800,
416
+ 'display' => __( 'Once Weekly', 'custom-twitter-feeds' )
417
+ );
418
+ return $schedules;
419
+ }
420
+
421
+ public function save_setting() {
422
+ if ( isset( $_POST['ctf_usage_tracking_enable'] ) ) {
423
+ $usage_tracking['enabled'] = false;
424
+ if ( isset( $_POST['ctf_usage_tracking_enable'] ) && $_POST['ctf_usage_tracking_enable'] === 'on' ) {
425
+ $usage_tracking['enabled'] = true;
426
+ }
427
+ update_option( 'ctf_usage_tracking', $usage_tracking, false );
428
+ }
429
+ }
430
+ }
431
  new CTF_Tracking();
inc/notices.php CHANGED
@@ -1,126 +1,126 @@
1
- <?php
2
- function ctf_get_current_time() {
3
- $current_time = time();
4
-
5
- // where to do tests
6
- //$current_time = strtotime( 'November 25, 2020' ) + 1;
7
-
8
- return $current_time;
9
- }
10
-
11
- // generates the html for the admin notices
12
- function ctf_notices_html() {
13
- //delete_option( 'ctf_rating_notice');
14
- //delete_transient( 'instagram_feed_rating_notice_waiting' );
15
- }
16
- //add_action( 'admin_notices', 'ctf_notices_html', 12 ); // priority 8 for Instagram, priority 10 for Facebook
17
-
18
- function ctf_get_future_date( $month, $year, $week, $day, $direction ) {
19
- if ( $direction > 0 ) {
20
- $startday = 1;
21
- } else {
22
- $startday = date( 't', mktime(0, 0, 0, $month, 1, $year ) );
23
- }
24
-
25
- $start = mktime( 0, 0, 0, $month, $startday, $year );
26
- $weekday = date( 'N', $start );
27
-
28
- $offset = 0;
29
- if ( $direction * $day >= $direction * $weekday ) {
30
- $offset = -$direction * 7;
31
- }
32
-
33
- $offset += $direction * ($week * 7) + ($day - $weekday);
34
- return mktime( 0, 0, 0, $month, $startday + $offset, $year );
35
- }
36
-
37
-
38
- function ctf_admin_database_warning() {
39
- if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'custom-twitter-feeds', '' ) ) ) {
40
-
41
-
42
- if ( ! current_user_can( 'manage_options' ) ) {
43
- return;
44
- }
45
-
46
- global $wpdb;
47
- $table_name = $wpdb->prefix . "options";
48
- $result = $wpdb->get_var("
49
- SELECT COUNT(*)
50
- FROM $table_name
51
- WHERE option_name LIKE '%ctf_!%'
52
- ");
53
-
54
- if ( (int) $result < 500 ) {
55
- return;
56
- }
57
- ?>
58
- <div class="notice notice-warning is-dismissible ctf-admin-notice">
59
- <p>
60
- <?php echo esc_html__( 'Heads up! It looks like you have over 500 Twitter feeds stored in your WordPress database. This is typically caused by a large number of hashtag feeds on your site, as the plugin permanently stores older Tweets to work around Twitter\'s 7 day hashtag feed limit. This many caches may lead to performance issues.', 'custom-twitter-feeds' ); ?>
61
- </p>
62
- <p>
63
- <?php echo sprintf( __( 'For a solution, please follow the directions %shere%s.', 'custom-twitter-feeds' ), '<a href="https://smashballoon.com/why-does-my-database-have-a-lot-of-twitter-feed-caches/" target="_blank" rel="noopener noreferrer">', '</a>' ); ?>
64
- </p>
65
- </div>
66
- <?php
67
- }
68
- }
69
- add_action( 'admin_notices', 'ctf_admin_database_warning' );
70
-
71
- /* Usage */
72
- add_action( 'admin_notices', 'ctf_usage_opt_in' );
73
- function ctf_usage_opt_in() {
74
-
75
- if ( isset( $_GET['trackingdismiss'] ) ) {
76
- $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => false ) );
77
-
78
- $usage_tracking['enabled'] = false;
79
-
80
- update_option( 'ctf_usage_tracking', $usage_tracking, false );
81
-
82
- return;
83
- }
84
-
85
- $cap = 'manage_options';
86
-
87
- $cap = apply_filters( 'ctf_settings_pages_capability', $cap );
88
- if ( ! current_user_can( $cap ) ) {
89
- return;
90
- }
91
- $usage_tracking = get_option( 'ctf_usage_tracking', false );
92
- if ( $usage_tracking ) {
93
- return;
94
- }
95
- ?>
96
- <div class="notice notice-warning is-dismissible ctf-admin-notice">
97
-
98
- <p>
99
- <strong><?php echo __( 'Help us improve the Custom Twitter Feed plugin', 'custom-twitter-feeds' ); ?></strong><br>
100
- <?php echo __( 'Understanding how you are using the plugin allows us to further improve it. Opt-in below to agree to send a weekly report of plugin usage data.', 'custom-twitter-feeds' ); ?>
101
- <a target="_blank" rel="noopener noreferrer" href="https://smashballoon.com/custom-twitter-feeds/docs/usage-tracking/"><?php echo __( 'More information', 'custom-twitter-feeds' ); ?></a>
102
- </p>
103
- <p>
104
- <a href="<?php echo admin_url('admin.php?page=custom-twitter-feeds&trackingdismiss=1') ?>" type="submit" class="button button-primary ctf-opt-in"><?php echo __( 'Yes, I\'d like to help', 'custom-twitter-feeds' ); ?></a>
105
- <a href="<?php echo admin_url('admin.php?page=custom-twitter-feeds&trackingdismiss=1') ?>" type="submit" class="ctf-no-usage-opt-out ctf-space-left button button-secondary"><?php echo __( 'No, thanks', 'custom-twitter-feeds' ); ?></a>
106
- </p>
107
-
108
- </div>
109
-
110
- <?php
111
- }
112
-
113
- function ctf_usage_opt_in_or_out() {
114
- if ( ! isset( $_POST['opted_in'] ) ) {
115
- die ( 'You did not do this the right way!' );
116
- }
117
-
118
- $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => false ) );
119
-
120
- $usage_tracking['enabled'] = isset( $_POST['opted_in'] ) ? $_POST['opted_in'] === 'true' : false;
121
-
122
- update_option( 'ctf_usage_tracking', $usage_tracking, false );
123
-
124
- die();
125
- }
126
  add_action( 'wp_ajax_ctf_usage_opt_in_or_out', 'ctf_usage_opt_in_or_out' );
1
+ <?php
2
+ function ctf_get_current_time() {
3
+ $current_time = time();
4
+
5
+ // where to do tests
6
+ //$current_time = strtotime( 'November 25, 2020' ) + 1;
7
+
8
+ return $current_time;
9
+ }
10
+
11
+ // generates the html for the admin notices
12
+ function ctf_notices_html() {
13
+ //delete_option( 'ctf_rating_notice');
14
+ //delete_transient( 'instagram_feed_rating_notice_waiting' );
15
+ }
16
+ //add_action( 'admin_notices', 'ctf_notices_html', 12 ); // priority 8 for Instagram, priority 10 for Facebook
17
+
18
+ function ctf_get_future_date( $month, $year, $week, $day, $direction ) {
19
+ if ( $direction > 0 ) {
20
+ $startday = 1;
21
+ } else {
22
+ $startday = date( 't', mktime(0, 0, 0, $month, 1, $year ) );
23
+ }
24
+
25
+ $start = mktime( 0, 0, 0, $month, $startday, $year );
26
+ $weekday = date( 'N', $start );
27
+
28
+ $offset = 0;
29
+ if ( $direction * $day >= $direction * $weekday ) {
30
+ $offset = -$direction * 7;
31
+ }
32
+
33
+ $offset += $direction * ($week * 7) + ($day - $weekday);
34
+ return mktime( 0, 0, 0, $month, $startday + $offset, $year );
35
+ }
36
+
37
+
38
+ function ctf_admin_database_warning() {
39
+ if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'custom-twitter-feeds', '' ) ) ) {
40
+
41
+
42
+ if ( ! current_user_can( 'manage_options' ) ) {
43
+ return;
44
+ }
45
+
46
+ global $wpdb;
47
+ $table_name = $wpdb->prefix . "options";
48
+ $result = $wpdb->get_var("
49
+ SELECT COUNT(*)
50
+ FROM $table_name
51
+ WHERE option_name LIKE '%ctf_!%'
52
+ ");
53
+
54
+ if ( (int) $result < 500 ) {
55
+ return;
56
+ }
57
+ ?>
58
+ <div class="notice notice-warning is-dismissible ctf-admin-notice">
59
+ <p>
60
+ <?php echo esc_html__( 'Heads up! It looks like you have over 500 Twitter feeds stored in your WordPress database. This is typically caused by a large number of hashtag feeds on your site, as the plugin permanently stores older Tweets to work around Twitter\'s 7 day hashtag feed limit. This many caches may lead to performance issues.', 'custom-twitter-feeds' ); ?>
61
+ </p>
62
+ <p>
63
+ <?php echo sprintf( __( 'For a solution, please follow the directions %shere%s.', 'custom-twitter-feeds' ), '<a href="https://smashballoon.com/why-does-my-database-have-a-lot-of-twitter-feed-caches/" target="_blank" rel="noopener noreferrer">', '</a>' ); ?>
64
+ </p>
65
+ </div>
66
+ <?php
67
+ }
68
+ }
69
+ add_action( 'admin_notices', 'ctf_admin_database_warning' );
70
+
71
+ /* Usage */
72
+ add_action( 'admin_notices', 'ctf_usage_opt_in' );
73
+ function ctf_usage_opt_in() {
74
+
75
+ if ( isset( $_GET['trackingdismiss'] ) ) {
76
+ $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => false ) );
77
+
78
+ $usage_tracking['enabled'] = false;
79
+
80
+ update_option( 'ctf_usage_tracking', $usage_tracking, false );
81
+
82
+ return;
83
+ }
84
+
85
+ $cap = 'manage_options';
86
+
87
+ $cap = apply_filters( 'ctf_settings_pages_capability', $cap );
88
+ if ( ! current_user_can( $cap ) ) {
89
+ return;
90
+ }
91
+ $usage_tracking = get_option( 'ctf_usage_tracking', false );
92
+ if ( $usage_tracking ) {
93
+ return;
94
+ }
95
+ ?>
96
+ <div class="notice notice-warning is-dismissible ctf-admin-notice">
97
+
98
+ <p>
99
+ <strong><?php echo __( 'Help us improve the Custom Twitter Feed plugin', 'custom-twitter-feeds' ); ?></strong><br>
100
+ <?php echo __( 'Understanding how you are using the plugin allows us to further improve it. Opt-in below to agree to send a weekly report of plugin usage data.', 'custom-twitter-feeds' ); ?>
101
+ <a target="_blank" rel="noopener noreferrer" href="https://smashballoon.com/custom-twitter-feeds/docs/usage-tracking/"><?php echo __( 'More information', 'custom-twitter-feeds' ); ?></a>
102
+ </p>
103
+ <p>
104
+ <a href="<?php echo admin_url('admin.php?page=custom-twitter-feeds&trackingdismiss=1') ?>" type="submit" class="button button-primary ctf-opt-in"><?php echo __( 'Yes, I\'d like to help', 'custom-twitter-feeds' ); ?></a>
105
+ <a href="<?php echo admin_url('admin.php?page=custom-twitter-feeds&trackingdismiss=1') ?>" type="submit" class="ctf-no-usage-opt-out ctf-space-left button button-secondary"><?php echo __( 'No, thanks', 'custom-twitter-feeds' ); ?></a>
106
+ </p>
107
+
108
+ </div>
109
+
110
+ <?php
111
+ }
112
+
113
+ function ctf_usage_opt_in_or_out() {
114
+ if ( ! isset( $_POST['opted_in'] ) ) {
115
+ die ( 'You did not do this the right way!' );
116
+ }
117
+
118
+ $usage_tracking = get_option( 'ctf_usage_tracking', array( 'last_send' => 0, 'enabled' => false ) );
119
+
120
+ $usage_tracking['enabled'] = isset( $_POST['opted_in'] ) ? $_POST['opted_in'] === 'true' : false;
121
+
122
+ update_option( 'ctf_usage_tracking', $usage_tracking, false );
123
+
124
+ die();
125
+ }
126
  add_action( 'wp_ajax_ctf_usage_opt_in_or_out', 'ctf_usage_opt_in_or_out' );
inc/widget.php CHANGED
@@ -1,63 +1,63 @@
1
- <?php
2
- /**
3
- * Class CtfWidget
4
- *
5
- * Creates a text widget with the custom-twitter-feeds shortcode inside
6
- */
7
-
8
- class CtfWidget extends WP_Widget
9
- {
10
- function __construct() {
11
- parent::__construct(
12
- 'custom-twitter-feeds-widget',
13
- __( 'Custom Twitter Feeds', 'custom-twitter-feeds' ),
14
- array( 'description' => __( 'Display your Twitter feed', 'custom-twitter-feeds' ), )
15
- );
16
- }
17
-
18
- public function widget( $args, $instance ) {
19
-
20
- $title = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) : '';
21
- $content = isset( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
22
-
23
- echo $args['before_widget'];
24
-
25
- if ( ! empty( $title ) ) {
26
- echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
27
- }
28
-
29
- echo do_shortcode( $content );
30
-
31
- echo $args['after_widget'];
32
- }
33
-
34
- public function form( $instance ) {
35
-
36
- $title = isset( $instance['title'] ) ? $instance['title'] : '';
37
- $content = isset ( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
38
- ?>
39
- <p>
40
- <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title:' ); ?></label>
41
- <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( $title ); ?>" />
42
- </p>
43
- <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'content' ) ); ?>" rows="16"><?php echo strip_tags( $content ); ?></textarea>
44
- <?php
45
- }
46
-
47
- public function update( $new_instance, $old_instance ) {
48
- $instance = array();
49
- $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
50
- $instance['content'] = ( ! empty( $new_instance['content'] ) ) ? strip_tags( $new_instance['content'] ) : '';
51
-
52
- return $instance;
53
- }
54
- }
55
-
56
- // register and load the widget
57
- function ctf_load_widget() {
58
- register_widget( 'CtfWidget' );
59
- }
60
- add_action( 'widgets_init', 'ctf_load_widget' );
61
-
62
- // allow shortcode in widgets
63
- add_filter( 'widget_text', 'do_shortcode' );
1
+ <?php
2
+ /**
3
+ * Class CtfWidget
4
+ *
5
+ * Creates a text widget with the custom-twitter-feeds shortcode inside
6
+ */
7
+
8
+ class CtfWidget extends WP_Widget
9
+ {
10
+ function __construct() {
11
+ parent::__construct(
12
+ 'custom-twitter-feeds-widget',
13
+ __( 'Custom Twitter Feeds', 'custom-twitter-feeds' ),
14
+ array( 'description' => __( 'Display your Twitter feed', 'custom-twitter-feeds' ), )
15
+ );
16
+ }
17
+
18
+ public function widget( $args, $instance ) {
19
+
20
+ $title = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) : '';
21
+ $content = isset( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
22
+
23
+ echo $args['before_widget'];
24
+
25
+ if ( ! empty( $title ) ) {
26
+ echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
27
+ }
28
+
29
+ echo do_shortcode( $content );
30
+
31
+ echo $args['after_widget'];
32
+ }
33
+
34
+ public function form( $instance ) {
35
+
36
+ $title = isset( $instance['title'] ) ? $instance['title'] : '';
37
+ $content = isset ( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
38
+ ?>
39
+ <p>
40
+ <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title:' ); ?></label>
41
+ <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( $title ); ?>" />
42
+ </p>
43
+ <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'content' ) ); ?>" rows="16"><?php echo strip_tags( $content ); ?></textarea>
44
+ <?php
45
+ }
46
+
47
+ public function update( $new_instance, $old_instance ) {
48
+ $instance = array();
49
+ $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
50
+ $instance['content'] = ( ! empty( $new_instance['content'] ) ) ? strip_tags( $new_instance['content'] ) : '';
51
+
52
+ return $instance;
53
+ }
54
+ }
55
+
56
+ // register and load the widget
57
+ function ctf_load_widget() {
58
+ register_widget( 'CtfWidget' );
59
+ }
60
+ add_action( 'widgets_init', 'ctf_load_widget' );
61
+
62
+ // allow shortcode in widgets
63
+ add_filter( 'widget_text', 'do_shortcode' );
js/admin-notifications.js CHANGED
@@ -1,208 +1,208 @@
1
- /**
2
- * CTF Admin Notifications.
3
- *
4
- * @since 2.18
5
- */
6
-
7
- 'use strict';
8
-
9
- var CTFAdminNotifications = window.CTFAdminNotifications || ( function( document, window, $ ) {
10
-
11
- /**
12
- * Elements holder.
13
- *
14
- * @since 2.18
15
- *
16
- * @type {object}
17
- */
18
- var el = {
19
-
20
- $notifications: $( '#ctf-notifications' ),
21
- $nextButton: $( '#ctf-notifications .navigation .next' ),
22
- $prevButton: $( '#ctf-notifications .navigation .prev' ),
23
- $adminBarCounter: $( '#wp-admin-bar-wpforms-menu .ctf-menu-notification-counter' ),
24
- $adminBarMenuItem: $( '#wp-admin-bar-ctf-notifications' ),
25
-
26
- };
27
-
28
- /**
29
- * Public functions and properties.
30
- *
31
- * @since 2.18
32
- *
33
- * @type {object}
34
- */
35
- var app = {
36
-
37
- /**
38
- * Start the engine.
39
- *
40
- * @since 2.18
41
- */
42
- init: function() {
43
- el.$notifications.find( '.messages a').each(function() {
44
- if ($(this).attr('href').indexOf('dismiss=') > -1 ) {
45
- $(this).addClass('button-dismiss');
46
- }
47
- })
48
-
49
- $( app.ready );
50
- },
51
-
52
- /**
53
- * Document ready.
54
- *
55
- * @since 2.18
56
- */
57
- ready: function() {
58
-
59
- app.updateNavigation();
60
- app.events();
61
- },
62
-
63
- /**
64
- * Register JS events.
65
- *
66
- * @since 2.18
67
- */
68
- events: function() {
69
-
70
- el.$notifications
71
- .on( 'click', '.dismiss', app.dismiss )
72
- .on( 'click', '.button-dismiss', app.buttonDismiss )
73
- .on( 'click', '.next', app.navNext )
74
- .on( 'click', '.prev', app.navPrev );
75
- },
76
-
77
- /**
78
- * Click on a dismiss button.
79
- *
80
- * @since 2.18
81
- */
82
- buttonDismiss: function( event ) {
83
- event.preventDefault();
84
- app.dismiss();
85
- },
86
-
87
- /**
88
- * Click on the Dismiss notification button.
89
- *
90
- * @since 2.18
91
- *
92
- * @param {object} event Event object.
93
- */
94
- dismiss: function( event ) {
95
-
96
- if ( el.$currentMessage.length === 0 ) {
97
- return;
98
- }
99
-
100
- // Update counter.
101
- var count = parseInt( el.$adminBarCounter.text(), 10 );
102
- if ( count > 1 ) {
103
- --count;
104
- el.$adminBarCounter.html( '<span>' + count + '</span>' );
105
- } else {
106
- el.$adminBarCounter.remove();
107
- el.$adminBarMenuItem.remove();
108
- }
109
-
110
- // Remove notification.
111
- var $nextMessage = el.$nextMessage.length < 1 ? el.$prevMessage : el.$nextMessage,
112
- messageId = el.$currentMessage.data( 'message-id' );
113
-
114
- if ( $nextMessage.length === 0 ) {
115
- el.$notifications.remove();
116
- } else {
117
- el.$currentMessage.remove();
118
- $nextMessage.addClass( 'current' );
119
- app.updateNavigation();
120
- }
121
-
122
- // AJAX call - update option.
123
- var data = {
124
- action: 'ctf_dashboard_notification_dismiss',
125
- nonce: ctf_admin.nonce,
126
- id: messageId,
127
- };
128
-
129
- $.post( ctf_admin.ajax_url, data, function( res ) {
130
-
131
- if ( ! res.success ) {
132
- //CTFAdmin.debug( res );
133
- }
134
- } ).fail( function( xhr, textStatus, e ) {
135
-
136
- //CTFAdmin.debug( xhr.responseText );
137
- } );
138
- },
139
-
140
- /**
141
- * Click on the Next notification button.
142
- *
143
- * @since 2.18
144
- *
145
- * @param {object} event Event object.
146
- */
147
- navNext: function( event ) {
148
-
149
- if ( el.$nextButton.hasClass( 'disabled' ) ) {
150
- return;
151
- }
152
-
153
- el.$currentMessage.removeClass( 'current' );
154
- el.$nextMessage.addClass( 'current' );
155
-
156
- app.updateNavigation();
157
- },
158
-
159
- /**
160
- * Click on the Previous notification button.
161
- *
162
- * @since 2.18
163
- *
164
- * @param {object} event Event object.
165
- */
166
- navPrev: function( event ) {
167
-
168
- if ( el.$prevButton.hasClass( 'disabled' ) ) {
169
- return;
170
- }
171
-
172
- el.$currentMessage.removeClass( 'current' );
173
- el.$prevMessage.addClass( 'current' );
174
-
175
- app.updateNavigation();
176
- },
177
-
178
- /**
179
- * Update navigation buttons.
180
- *
181
- * @since 2.18
182
- */
183
- updateNavigation: function() {
184
-
185
- el.$currentMessage = el.$notifications.find( '.message.current' );
186
- el.$nextMessage = el.$currentMessage.next( '.message' );
187
- el.$prevMessage = el.$currentMessage.prev( '.message' );
188
-
189
- if ( el.$nextMessage.length === 0 ) {
190
- el.$nextButton.addClass( 'disabled' );
191
- } else {
192
- el.$nextButton.removeClass( 'disabled' );
193
- }
194
-
195
- if ( el.$prevMessage.length === 0 ) {
196
- el.$prevButton.addClass( 'disabled' );
197
- } else {
198
- el.$prevButton.removeClass( 'disabled' );
199
- }
200
- },
201
- };
202
-
203
- return app;
204
-
205
- }( document, window, jQuery ) );
206
-
207
- // Initialize.
208
- CTFAdminNotifications.init();
1
+ /**
2
+ * CTF Admin Notifications.
3
+ *
4
+ * @since 2.18
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ var CTFAdminNotifications = window.CTFAdminNotifications || ( function( document, window, $ ) {
10
+
11
+ /**
12
+ * Elements holder.
13
+ *
14
+ * @since 2.18
15
+ *
16
+ * @type {object}
17
+ */
18
+ var el = {
19
+
20
+ $notifications: $( '#ctf-notifications' ),
21
+ $nextButton: $( '#ctf-notifications .navigation .next' ),
22
+ $prevButton: $( '#ctf-notifications .navigation .prev' ),
23
+ $adminBarCounter: $( '#wp-admin-bar-wpforms-menu .ctf-menu-notification-counter' ),
24
+ $adminBarMenuItem: $( '#wp-admin-bar-ctf-notifications' ),
25
+
26
+ };
27
+
28
+ /**
29
+ * Public functions and properties.
30
+ *
31
+ * @since 2.18
32
+ *
33
+ * @type {object}
34
+ */
35
+ var app = {
36
+
37
+ /**
38
+ * Start the engine.
39
+ *
40
+ * @since 2.18
41
+ */
42
+ init: function() {
43
+ el.$notifications.find( '.messages a').each(function() {
44
+ if ($(this).attr('href').indexOf('dismiss=') > -1 ) {
45
+ $(this).addClass('button-dismiss');
46
+ }
47
+ })
48
+
49
+ $( app.ready );
50
+ },
51
+
52
+ /**
53
+ * Document ready.
54
+ *
55
+ * @since 2.18
56
+ */
57
+ ready: function() {
58
+
59
+ app.updateNavigation();
60
+ app.events();
61
+ },
62
+
63
+ /**
64
+ * Register JS events.
65
+ *
66
+ * @since 2.18
67
+ */
68
+ events: function() {
69
+
70
+ el.$notifications
71
+ .on( 'click', '.dismiss', app.dismiss )
72
+ .on( 'click', '.button-dismiss', app.buttonDismiss )
73
+ .on( 'click', '.next', app.navNext )
74
+ .on( 'click', '.prev', app.navPrev );
75
+ },
76
+
77
+ /**
78
+ * Click on a dismiss button.
79
+ *
80
+ * @since 2.18
81
+ */
82
+ buttonDismiss: function( event ) {
83
+ event.preventDefault();
84
+ app.dismiss();
85
+ },
86
+
87
+ /**
88
+ * Click on the Dismiss notification button.
89
+ *
90
+ * @since 2.18
91
+ *
92
+ * @param {object} event Event object.
93
+ */
94
+ dismiss: function( event ) {
95
+
96
+ if ( el.$currentMessage.length === 0 ) {
97
+ return;
98
+ }
99
+
100
+ // Update counter.
101
+ var count = parseInt( el.$adminBarCounter.text(), 10 );
102
+ if ( count > 1 ) {
103
+ --count;
104
+ el.$adminBarCounter.html( '<span>' + count + '</span>' );
105
+ } else {
106
+ el.$adminBarCounter.remove();
107
+ el.$adminBarMenuItem.remove();
108
+ }
109
+
110
+ // Remove notification.
111
+ var $nextMessage = el.$nextMessage.length < 1 ? el.$prevMessage : el.$nextMessage,
112
+ messageId = el.$currentMessage.data( 'message-id' );
113
+
114
+ if ( $nextMessage.length === 0 ) {
115
+ el.$notifications.remove();
116
+ } else {
117
+ el.$currentMessage.remove();
118
+ $nextMessage.addClass( 'current' );
119
+ app.updateNavigation();
120
+ }
121
+
122
+ // AJAX call - update option.
123
+ var data = {
124
+ action: 'ctf_dashboard_notification_dismiss',
125
+ nonce: ctf_admin.nonce,
126
+ id: messageId,
127
+ };
128
+
129
+ $.post( ctf_admin.ajax_url, data, function( res ) {
130
+
131
+ if ( ! res.success ) {
132
+ //CTFAdmin.debug( res );
133
+ }
134
+ } ).fail( function( xhr, textStatus, e ) {
135
+
136
+ //CTFAdmin.debug( xhr.responseText );
137
+ } );
138
+ },
139
+
140
+ /**
141
+ * Click on the Next notification button.
142
+ *
143
+ * @since 2.18
144
+ *
145
+ * @param {object} event Event object.
146
+ */
147
+ navNext: function( event ) {
148
+
149
+ if ( el.$nextButton.hasClass( 'disabled' ) ) {
150
+ return;
151
+ }
152
+
153
+ el.$currentMessage.removeClass( 'current' );
154
+ el.$nextMessage.addClass( 'current' );
155
+
156
+ app.updateNavigation();
157
+ },
158
+
159
+ /**
160
+ * Click on the Previous notification button.
161
+ *
162
+ * @since 2.18
163
+ *
164
+ * @param {object} event Event object.
165
+ */
166
+ navPrev: function( event ) {
167
+
168
+ if ( el.$prevButton.hasClass( 'disabled' ) ) {
169
+ return;
170
+ }
171
+
172
+ el.$currentMessage.removeClass( 'current' );
173
+ el.$prevMessage.addClass( 'current' );
174
+
175
+ app.updateNavigation();
176
+ },
177
+
178
+ /**
179
+ * Update navigation buttons.
180
+ *
181
+ * @since 2.18
182
+ */
183
+ updateNavigation: function() {
184
+
185
+ el.$currentMessage = el.$notifications.find( '.message.current' );
186
+ el.$nextMessage = el.$currentMessage.next( '.message' );
187
+ el.$prevMessage = el.$currentMessage.prev( '.message' );
188
+
189
+ if ( el.$nextMessage.length === 0 ) {
190
+ el.$nextButton.addClass( 'disabled' );
191
+ } else {
192
+ el.$nextButton.removeClass( 'disabled' );
193
+ }
194
+
195
+ if ( el.$prevMessage.length === 0 ) {
196
+ el.$prevButton.addClass( 'disabled' );
197
+ } else {
198
+ el.$prevButton.removeClass( 'disabled' );
199
+ }
200
+ },
201
+ };
202
+
203
+ return app;
204
+
205
+ }( document, window, jQuery ) );
206
+
207
+ // Initialize.
208
+ CTFAdminNotifications.init();
js/ctf-admin-scripts.js CHANGED
@@ -1,671 +1,671 @@
1
- jQuery(document).ready(function($){
2
-
3
- // access token retrieving
4
- var $ctfRetrievedAccessToken = $('#ctf-retrieved-access-token'),
5
- $ctfRetrievedAccessTokenSecret = $('#ctf-retrieved-access-token-secret'),
6
- $ctfRetrievedDefaultScreenName = $('#ctf-retrieved-default-screen-name'),
7
-
8
- // toggle token input fields
9
- $ctfConsumerFields = $('.ctf-toggle-consumer'),
10
- $ctfAccessFields = $('.ctf-toggle-access'),
11
- $ctfHaveOwnTokens = $('#ctf_have_own_tokens');
12
-
13
- if ( $ctfRetrievedAccessToken.length ) {
14
- $('#ctf_access_token').val($ctfRetrievedAccessToken.val());
15
- $('#ctf_access_token_secret').val($ctfRetrievedAccessTokenSecret.val());
16
- if($('#ctf_usertimeline_text').val() == '') {
17
- $('#ctf_usertimeline_text').val($ctfRetrievedDefaultScreenName.val());
18
- }
19
-
20
- if (!$ctfHaveOwnTokens.is(':checked')) {
21
- $.ajax({
22
- url: ctf.ajax_url,
23
- type: 'post',
24
- data: {
25
- action: 'ctf_auto_save_tokens',
26
- security: ctf.sb_nonce,
27
- access_token: $ctfRetrievedAccessToken.val(),
28
- access_token_secret: $ctfRetrievedAccessTokenSecret.val(),
29
- just_tokens: true
30
- },
31
- success: function (data) {
32
- $('#ctf_access_token').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
33
- $('#ctf_access_token_secret').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
34
- }
35
- });
36
- }
37
- }
38
-
39
- function toggleAccessInputs() {
40
- if($ctfHaveOwnTokens.is(':checked')) {
41
- $ctfAccessFields.show();
42
- $ctfConsumerFields.show();
43
- } else {
44
- $ctfConsumerFields.hide();
45
- if($ctfAccessFields.find('#ctf_access_token').val() == '' && $ctfAccessFields.find('#ctf_access_token_secret').val() == '') {
46
- $ctfAccessFields.hide();
47
- $ctfConsumerFields.hide();
48
- }
49
- }
50
- }
51
- toggleAccessInputs();
52
-
53
- $ctfHaveOwnTokens.on('change', function() {
54
- toggleAccessInputs();
55
- });
56
-
57
- // variables for time triggered validator
58
- var typingTimer,
59
- doneTypingInterval = 1000,
60
- $ctfSearchText = $('#ctf-admin #ctf_search_text'),
61
- $ctfUserText = $('#ctf-admin #ctf_usertimeline_text'),
62
- $ctfSearchError = $('#ctf-admin .ctf_search_error'),
63
- $ctfUserError= $('#ctf-admin .ctf_usertimeline_error');
64
-
65
- // hide elements when page loads
66
- $ctfSearchError.hide();
67
- $ctfUserError.hide();
68
-
69
- // on search text keyup, start timer to trigger validator
70
- $ctfSearchText.keyup(function(){
71
- clearTimeout(typingTimer);
72
- if($ctfSearchText.val){
73
- typingTimer = setTimeout(searchValidator, doneTypingInterval);
74
- }
75
- });
76
-
77
- // on usertimeline text keyup, start timer to trigger validator
78
- $ctfUserText.keyup(function(){
79
- clearTimeout(typingTimer);
80
- if($ctfUserText.val){
81
- typingTimer = setTimeout(userValidator, doneTypingInterval);
82
- }
83
- });
84
-
85
- // validate search input when user is done typing
86
- var internationalHashtagRegexString = "[A-z\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC",
87
- hashtagRegex = new RegExp("^"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$|^#+"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$");
88
-
89
- function searchValidator() {
90
- var ctfSearch = $ctfSearchText.val();
91
-
92
- if ( ctfSearch.indexOf(',') > -1 || ctfSearch.indexOf(' ') > -1 ){
93
- $ctfSearchError.slideDown();
94
- } else {
95
- $ctfSearchError.slideUp();
96
- }
97
- }
98
-
99
- // validate screen name input when user is done typing
100
- function userValidator(){
101
- var ctfUser = $ctfUserText.val();
102
-
103
- if(ctfUser.match(/^@[A-Za-z0-9_]{1,15}$/) || ctfUser.match(/^[A-Za-z0-9_]{1,15}$/)){
104
- $ctfUserError.slideUp();
105
- } else {
106
- $ctfUserError.slideDown();
107
- }
108
- }
109
-
110
- // search term guide toggle
111
- var $ctfToggleSearchGuide = $('#ctf-admin .ctf-toggle-search-guide');
112
-
113
- // hide initially
114
- $ctfToggleSearchGuide.closest('h4').next('div').hide();
115
-
116
- // show on click
117
- $ctfToggleSearchGuide.on('click',function(){
118
- $(this).closest('h4').next('div').slideToggle();
119
- });
120
-
121
- // tooltips
122
- $('#ctf-admin .ctf-tooltip-link').on('click',function(){
123
- $(this).closest('tr, h3, .ctf-tooltip-wrap').find('.ctf-tooltip').slideToggle();
124
- });
125
-
126
- // include replies
127
- $('.ctf_include_replies_toggle').hide();
128
- $('.ctf_include_replies_toggle input').prop('disabled', true);
129
-
130
- function toggleIncludeReplies() {
131
- $('.ctf_include_replies_toggle').each(function() {
132
- if($(this).closest('td').find('.ctf-feed-settings-radio').is(':checked')) {
133
- $(this).slideDown();
134
- } else {
135
- $(this).slideUp();
136
- }
137
- });
138
- }
139
- toggleIncludeReplies();
140
-
141
- $('.ctf-feed-settings-radio').on('change', function() {
142
- toggleIncludeReplies();
143
-
144
- if( $('#ctf-admin #ctf_usertimeline_radio').is(':checked') ) {
145
- userValidator();
146
- // $ctfSearchError.slideUp();
147
- } else if( $('#ctf-admin #ctf_search_radio').is(':checked') ) {
148
- searchValidator();
149
- // $ctfUserError.slideUp();
150
- }
151
- });
152
-
153
- // color picker
154
- var $ctfColorpicker = $('.ctf-colorpicker');
155
-
156
- if($ctfColorpicker.length > 0){
157
- $ctfColorpicker.wpColorPicker();
158
- }
159
-
160
- // shortcode tooltips
161
- var $ctfAdminLabel = $('#ctf-admin label');
162
-
163
- $ctfAdminLabel.on('click',function(){
164
- var $sbi_shortcode = $(this).siblings('.ctf_shortcode');
165
- if($sbi_shortcode.is(':visible')){
166
- $(this).removeClass('ctf_shortcode_visible');
167
- $(this).siblings('.ctf_shortcode').css('display','none');
168
- } else {
169
- $(this).addClass('ctf_shortcode_visible');
170
- $(this).siblings('.ctf_shortcode').css('display','block');
171
- }
172
- });
173
-
174
- $ctfAdminLabel.on('mouseenter mouseleave', function(e) {
175
- switch(e.type) {
176
- case 'mouseenter':
177
- if($(this).siblings('.ctf_shortcode').length > 0 ){
178
- $(this).attr('title', 'Click for shortcode option').append('<code class="ctf_shortcode_symbol">[]</code>');
179
- }
180
- break;
181
- case 'mouseleave':
182
- $(this).find('.ctf_shortcode_symbol').remove();
183
- break;
184
- }
185
- });
186
-
187
- //Scroll to hash for quick links
188
- $('#ctf-admin a').on('click',function() {
189
- if(location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
190
- var target = $(this.hash);
191
- target = target.length ? target : this.hash.slice(1);
192
- if(target.length) {
193
- $('html,body').animate({
194
- scrollTop: target.offset().top
195
- }, 500);
196
- return false;
197
- }
198
- }
199
- });
200
-
201
- //Mobile width
202
- var ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
203
- ctfWidth = $('#ctf-admin #ctf_width').val(),
204
- $ctfWidthOptions = $('#ctf-admin #ctf_width_options');
205
-
206
- if (typeof ctfWidth !== 'undefined') {
207
- //Show initially if a width is set
208
- if(ctfWidth.length > 1 && !(ctfWidth == '100' && ctfWidthUnit == '%')) $ctfWidthOptions.show();
209
-
210
- $('#ctf_width, #ctf_width_unit').on('change',function(){
211
- ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
212
- ctfWidth = $('#ctf-admin #ctf_width').val();
213
-
214
- if(ctfWidth.length < 2 || (ctfWidth == '100' && ctfWidthUnit == '%')) {
215
- $ctfWidthOptions.slideUp();
216
- } else {
217
- $ctfWidthOptions.slideDown();
218
- }
219
- });
220
- }
221
-
222
- // clear cache
223
- var $ctfClearCacheButton = $('#ctf-admin #ctf-clear-cache');
224
-
225
- $ctfClearCacheButton.on('click',function(event) {
226
- event.preventDefault();
227
-
228
- $('#ctf-clear-cache-success').remove();
229
- $(this).prop("disabled",true);
230
-
231
- $.ajax({
232
- url : ctf.ajax_url,
233
- type : 'post',
234
- data : {
235
- action : 'ctf_clear_cache_admin'
236
- },
237
- success : function(data) {
238
- $ctfClearCacheButton.prop('disabled',false);
239
- if(!data===false) {
240
- $ctfClearCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
241
- } else {
242
- $ctfClearCacheButton.after('<span>error</span>');
243
- }
244
- }
245
- }); // ajax call
246
- }); // clear-cache click
247
-
248
- // clear persistent cache
249
- var $ctfClearPersistentCacheButton = $('#ctf-admin #ctf-clear-persistent-cache');
250
-
251
- $ctfClearPersistentCacheButton.on('click',function(event) {
252
- event.preventDefault();
253
-
254
- $('#ctf-clear-cache-success').remove();
255
- $(this).prop("disabled",true);
256
-
257
- $.ajax({
258
- url : ctf.ajax_url,
259
- type : 'post',
260
- data : {
261
- action : 'ctf_clear_persistent_cache'
262
- },
263
- success : function(data) {
264
- $ctfClearPersistentCacheButton.prop('disabled',false);
265
- if(!data===false) {
266
- $ctfClearPersistentCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
267
- } else {
268
- $ctfClearPersistentCacheButton.after('<span>error</span>');
269
- }
270
- }
271
- }); // ajax call
272
- }); // clear-persistent-cache click
273
-
274
- $('.ctf-opt-in').on('click',function(event) {
275
- event.preventDefault();
276
-
277
- var $btn = jQuery(this);
278
- $btn.prop( 'disabled', true ).addClass( 'loading' ).html('<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>');
279
-
280
- ctfSubmitOptIn(true);
281
- }); // clear_comment_cache click
282
-
283
- $('.ctf-no-usage-opt-out').on('click',function(event) {
284
- event.preventDefault();
285
-
286
- var $btn = jQuery(this);
287
- $btn.prop( 'disabled', true ).addClass( 'loading' ).html('<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>');
288
-
289
- ctfSubmitOptIn(false);
290
- }); // clear_comment_cache click
291
-
292
- function ctfSubmitOptIn(choice) {
293
- $.ajax({
294
- url : ctf.ajax_url,
295
- type : 'post',
296
- data : {
297
- action : 'ctf_usage_opt_in_or_out',
298
- opted_in: choice,
299
- },
300
- success : function(data) {
301
- $('.ctf-no-usage-opt-out').closest('.ctf-admin-notice').fadeOut();
302
- }
303
- }); // ajax call
304
- }
305
-
306
- //Pro version notices
307
- var ctfUpgradeNote = '<span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=twitter-free&utm_source=settings&utm_medium=layout" target="_blank">Available in Pro version</a></span>';
308
- $('.ctf_pro').each(function(){
309
- var $pro = $(this);
310
- if (!$pro.find('.ctf_layout_options_wrap').length) {
311
- $pro.find('td').last().append(ctfUpgradeNote);
312
- $pro.find('input, select, textarea').attr('disabled', 'true');
313
- }
314
- });
315
- $('#ctf_include_twittercards, #ctf_include_media, #ctf_include_replied_to').prop('disabled', true).prop('checked',false).next('label').css('color', '#999').after(ctfUpgradeNote);
316
-
317
- $('#ctf-admin .ctf-show-pro').closest('span').next('.ctf-pro-options').hide();
318
- $('#ctf-admin .ctf-show-pro').on('click',function() {
319
- if ($(this).closest('span').next('.ctf-pro-options').is(':visible')) {
320
- $(this).closest('span').next('.ctf-pro-options').hide();
321
- } else {
322
- $(this).closest('span').next('.ctf-pro-options').show();
323
- }
324
- });
325
-
326
- function ctfUpdateLayoutTypeOptionsDisplay() {
327
- setTimeout(function(){
328
- jQuery('.ctf_layout_settings').hide();
329
- jQuery('.ctf_layout_settings.ctf_layout_type_'+jQuery('.ctf_layout_type:checked').val()).show();
330
- }, 1);
331
- }
332
- ctfUpdateLayoutTypeOptionsDisplay();
333
- jQuery('.ctf_layout_type').on('change',ctfUpdateLayoutTypeOptionsDisplay);
334
-
335
- // notices
336
-
337
- if (jQuery('#ctf-notice-bar').length) {
338
- jQuery('#wpadminbar').after(jQuery('#ctf-notice-bar'));
339
- jQuery('#wpcontent').css('padding-left', 0);
340
- jQuery('#wpbody').css('padding-left', '20px');
341
- jQuery('#ctf-notice-bar').show();
342
- }
343
-
344
- jQuery('#ctf-notice-bar .dismiss').on('click',function(e) {
345
- e.preventDefault();
346
- jQuery('#ctf-notice-bar').remove();
347
- jQuery.ajax({
348
- url: ctf.ajax_url,
349
- type: 'post',
350
- data: {
351
- action : 'ctf_lite_dismiss',
352
- ctf_nonce: ctf.sb_nonce
353
- },
354
- success: function (data) {
355
- }
356
- });
357
- });
358
-
359
- jQuery('.ctf_show_gdpr_list').on('click', function(){
360
- jQuery(this).closest('div').find('.ctf_gdpr_list').slideToggle();
361
- });
362
-
363
- //Selecting a post style
364
- jQuery('#ctf_gdpr_setting').on('change', function(){
365
- ctfCheckGdprSetting( jQuery(this).val() );
366
- });
367
- function ctfCheckGdprSetting(option) {
368
- if( option == 'yes' ){
369
- jQuery('.ctf_gdpr_yes').show();
370
- jQuery('.ctf_gdpr_no, .ctf_gdpr_auto').hide();
371
- }
372
- if( option == 'no' ){
373
- jQuery('.ctf_gdpr_no').show();
374
- jQuery('.ctf_gdpr_yes, .ctf_gdpr_auto').hide();
375
- }
376
- if( option == 'auto' ){
377
- jQuery('.ctf_gdpr_auto').show();
378
- jQuery('.ctf_gdpr_yes, .ctf_gdpr_no').hide();
379
- }
380
- }
381
- ctfCheckGdprSetting();
382
-
383
- // Locator
384
- jQuery('.ctf-locator-more').click(function(e) {
385
- e.preventDefault();
386
- jQuery(this).closest('td').find('.ctf-full-wrap').show();
387
- jQuery(this).closest('td').find('.ctf-condensed-wrap').hide();
388
- jQuery(this).remove();
389
- });
390
-
391
- //Click event for other plugins in menu
392
- $('.ctf_get_sbi, .ctf_get_cff, .ctf_get_ctf, .ctf_get_yt').parent().on('click', function(e){
393
- e.preventDefault();
394
-
395
- jQuery('.sb_cross_install_modal').remove();
396
-
397
- $('#wpbody-content').prepend('<div class="sb_cross_install_modal"><div class="sb_cross_install_inner" id="ctf-admin-about"><div id="ctf-admin-addons"><div class="addons-container"><i class="fa fa-spinner fa-spin ctf-loader" aria-hidden="true"></i></div></div></div></div>');
398
-
399
- var $self = $(this).find('span'),
400
- sb_get_plugin = 'custom_twitter_feeds';
401
-
402
- if( $self.hasClass('ctf_get_cff') ){
403
- sb_get_plugin = 'custom_facebook_feed';
404
- } else if( $self.hasClass('ctf_get_sbi') ){
405
- sb_get_plugin = 'instagram_feed';
406
- } else if( $self.hasClass('ctf_get_yt') ){
407
- sb_get_plugin = 'feeds_for_youtube';
408
- }
409
-
410
- $get_plugins_url = ctf.ajax_url.replace('admin-ajax.php', '');
411
-
412
- // Get the quick install box from the about page
413
- $('.sb_cross_install_modal .addons-container').load($get_plugins_url+'admin.php?page=custom-twitter-feeds&tab=more #install_'+sb_get_plugin);
414
- });
415
- //Close the modal if clicking anywhere outside it
416
- jQuery('body').on('click', '.sb_cross_install_modal', function(e){
417
- if (e.target !== this) return;
418
- jQuery('.sb_cross_install_modal').remove();
419
- });
420
-
421
- //Add class to Pro menu item
422
- $('.ctf_get_pro').parent().attr({'class':'ctf_get_pro_highlight', 'target':'_blank'});
423
- });
424
-
425
- /* global smash_admin, jconfirm, wpCookies, Choices, List */
426
-
427
- (function($) {
428
-
429
- 'use strict';
430
-
431
- // Global settings access.
432
- var s;
433
-
434
- // Admin object.
435
- var SmashAdmin = {
436
-
437
- // Settings.
438
- settings: {
439
- iconActivate: '<i class="fa fa-toggle-on fa-flip-horizontal" aria-hidden="true"></i>',
440
- iconDeactivate: '<i class="fa fa-toggle-on" aria-hidden="true"></i>',
441
- iconInstall: '<i class="fa fa-cloud-download" aria-hidden="true"></i>',
442
- iconSpinner: '<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>',
443
- mediaFrame: false
444
- },
445
-
446
- /**
447
- * Start the engine.
448
- *
449
- * @since 1.3.9
450
- */
451
- init: function() {
452
-
453
- // Settings shortcut.
454
- s = this.settings;
455
-
456
- // Document ready.
457
- $( document ).ready( SmashAdmin.ready );
458
-
459
- // Addons List.
460
- SmashAdmin.initAddons();
461
- },
462
-
463
- /**
464
- * Document ready.
465
- *
466
- * @since 1.3.9
467
- */
468
- ready: function() {
469
-
470
- // Action available for each binding.
471
- $( document ).trigger( 'smashReady' );
472
- },
473
-
474
- //--------------------------------------------------------------------//
475
- // Addons List.
476
- //--------------------------------------------------------------------//
477
-
478
- /**
479
- * Element bindings for Addons List page.
480
- *
481
- * @since 1.3.9
482
- */
483
- initAddons: function() {
484
-
485
- // Some actions have to be delayed to document.ready.
486
- $( document ).on( 'smashReady', function() {
487
-
488
- // Only run on the addons page.
489
- if ( ! $( '#ctf-admin-addons' ).length ) {
490
- return;
491
- }
492
-
493
- // Display all addon boxes as the same height.
494
- if( $( '#ctf-admin-about .addon-item').length ){
495
- $( '#ctf-admin-about .addon-item .details' ).matchHeight( { byrow: false, property: 'height' } );
496
- }
497
-
498
- // Addons searching.
499
- if ( $('#ctf-admin-addons-list').length ) {
500
- var addonSearch = new List( 'ctf-admin-addons-list', {
501
- valueNames: [ 'addon-name' ]
502
- } );
503
-
504
- $( '#ctf-admin-addons-search' ).on( 'keyup', function () {
505
- var searchTerm = $( this ).val(),
506
- $heading = $( '#addons-heading' );
507
-
508
- if ( searchTerm ) {
509
- $heading.text( ctf_admin_strings.addon_search );
510
- }
511
- else {
512
- $heading.text( $heading.data( 'text' ) );
513
- }
514
-
515
- addonSearch.search( searchTerm );
516
- } );
517
- }
518
- });
519
-
520
- // Toggle an addon state.
521
- $( document ).on( 'click', '#ctf-admin-addons .addon-item button', function( event ) {
522
-
523
- event.preventDefault();
524
-
525
- if ( $( this ).hasClass( 'disabled' ) ) {
526
- return false;
527
- }
528
-
529
- SmashAdmin.addonToggle( $( this ) );
530
- });
531
- },
532
-
533
- /**
534
- * Toggle addon state.
535
- *
536
- * @since 1.3.9
537
- */
538
- addonToggle: function( $btn ) {
539
-
540
- var $addon = $btn.closest( '.addon-item' ),
541
- plugin = $btn.attr( 'data-plugin' ),
542
- plugin_type = $btn.attr( 'data-type' ),
543
- action,
544
- cssClass,
545
- statusText,
546
- buttonText,
547
- errorText,
548
- successText;
549
-
550
- if ( $btn.hasClass( 'status-go-to-url' ) ) {
551
- // Open url in new tab.
552
- window.open( $btn.attr('data-plugin'), '_blank' );
553
- return;
554
- }
555
-
556
- $btn.prop( 'disabled', true ).addClass( 'loading' );
557
- $btn.html( s.iconSpinner );
558
-
559
- if ( $btn.hasClass( 'status-active' ) ) {
560
- // Deactivate.
561
- action = 'ctf_deactivate_addon';
562
- cssClass = 'status-inactive';
563
- if ( plugin_type === 'plugin' ) {
564
- cssClass += ' button button-secondary';
565
- }
566
- statusText = ctf_admin_strings.addon_inactive;
567
- buttonText = ctf_admin_strings.addon_activate;
568
- if ( plugin_type === 'addon' ) {
569
- buttonText = s.iconActivate + buttonText;
570
- }
571
- errorText = s.iconDeactivate + ctf_admin_strings.addon_deactivate;
572
-
573
- } else if ( $btn.hasClass( 'status-inactive' ) ) {
574
- // Activate.
575
- action = 'ctf_activate_addon';
576
- cssClass = 'status-active';
577
- if ( plugin_type === 'plugin' ) {
578
- cssClass += ' button button-secondary disabled';
579
- }
580
- statusText = ctf_admin_strings.addon_active;
581
- buttonText = ctf_admin_strings.addon_deactivate;
582
- if ( plugin_type === 'addon' ) {
583
- buttonText = s.iconDeactivate + buttonText;
584
- } else if ( plugin_type === 'plugin' ) {
585
- buttonText = ctf_admin_strings.addon_activated;
586
- }
587
- errorText = s.iconActivate + ctf_admin_strings.addon_activate;
588
-
589
- } else if ( $btn.hasClass( 'status-download' ) ) {
590
- // Install & Activate.
591
- action = 'ctf_install_addon';
592
- cssClass = 'status-active';
593
- if ( plugin_type === 'plugin' ) {
594
- cssClass += ' button disabled';
595
- }
596
- statusText = ctf_admin_strings.addon_active;
597
- buttonText = ctf_admin_strings.addon_activated;
598
- if ( plugin_type === 'addon' ) {
599
- buttonText = s.iconActivate + ctf_admin_strings.addon_deactivate;
600
- }
601
- errorText = s.iconInstall + ctf_admin_strings.addon_activate;
602
-
603
- } else {
604
- return;
605
- }
606
-
607
- var data = {
608
- action: action,
609
- nonce : ctf_admin_strings.nonce,
610
- plugin: plugin,
611
- type : plugin_type
612
- };
613
- $.post( ctf_admin_strings.ajax_url, data, function( res ) {
614
-
615
- if ( res.success ) {
616
- if ( 'ctf_install_addon' === action ) {
617
- $btn.attr( 'data-plugin', res.data.basename );
618
- successText = res.data.msg;
619
- if ( ! res.data.is_activated ) {
620
- cssClass = 'status-inactive';
621
- if ( plugin_type === 'plugin' ) {
622
- cssClass = 'button';
623
- }
624
- statusText = ctf_admin_strings.addon_inactive;
625
- buttonText = s.iconActivate + ctf_admin_strings.addon_activate;
626
- }
627
- } else {
628
- successText = res.data;
629
- }
630
- $addon.find( '.actions' ).append( '<div class="msg success">'+successText+'</div>' );
631
- $addon.find( 'span.status-label' )
632
- .removeClass( 'status-active status-inactive status-download' )
633
- .addClass( cssClass )
634
- .removeClass( 'button button-primary button-secondary disabled' )
635
- .text( statusText );
636
- $btn
637
- .removeClass( 'status-active status-inactive status-download' )
638
- .removeClass( 'button button-primary button-secondary disabled' )
639
- .addClass( cssClass ).html( buttonText );
640
- } else {
641
- if ( 'download_failed' === res.data[0].code ) {
642
- if ( plugin_type === 'addon' ) {
643
- $addon.find( '.actions' ).append( '<div class="msg error">'+ctf_admin_strings.addon_error+'</div>' );
644
- } else {
645
- $addon.find( '.actions' ).append( '<div class="msg error">'+ctf_admin_strings.plugin_error+'</div>' );
646
- }
647
- } else {
648
- $addon.find( '.actions' ).append( '<div class="msg error">'+res.data+'</div>' );
649
- }
650
- $btn.html( errorText );
651
- }
652
-
653
- $btn.prop( 'disabled', false ).removeClass( 'loading' );
654
-
655
- // Automatically clear addon messages after 3 seconds.
656
- setTimeout( function() {
657
- $( '.addon-item .msg' ).remove();
658
- }, 3000 );
659
-
660
- }).fail( function( xhr ) {
661
- console.log( xhr.responseText );
662
- });
663
- },
664
-
665
- };
666
-
667
- SmashAdmin.init();
668
-
669
- window.SmashAdmin = SmashAdmin;
670
-
1
+ jQuery(document).ready(function($){
2
+
3
+ // access token retrieving
4
+ var $ctfRetrievedAccessToken = $('#ctf-retrieved-access-token'),
5
+ $ctfRetrievedAccessTokenSecret = $('#ctf-retrieved-access-token-secret'),
6
+ $ctfRetrievedDefaultScreenName = $('#ctf-retrieved-default-screen-name'),
7
+
8
+ // toggle token input fields
9
+ $ctfConsumerFields = $('.ctf-toggle-consumer'),
10
+ $ctfAccessFields = $('.ctf-toggle-access'),
11
+ $ctfHaveOwnTokens = $('#ctf_have_own_tokens');
12
+
13
+ if ( $ctfRetrievedAccessToken.length ) {
14
+ $('#ctf_access_token').val($ctfRetrievedAccessToken.val());
15
+ $('#ctf_access_token_secret').val($ctfRetrievedAccessTokenSecret.val());
16
+ if($('#ctf_usertimeline_text').val() == '') {
17
+ $('#ctf_usertimeline_text').val($ctfRetrievedDefaultScreenName.val());
18
+ }
19
+
20
+ if (!$ctfHaveOwnTokens.is(':checked')) {
21
+ $.ajax({
22
+ url: ctf.ajax_url,
23
+ type: 'post',
24
+ data: {
25
+ action: 'ctf_auto_save_tokens',
26
+ security: ctf.sb_nonce,
27
+ access_token: $ctfRetrievedAccessToken.val(),
28
+ access_token_secret: $ctfRetrievedAccessTokenSecret.val(),
29
+ just_tokens: true
30
+ },
31
+ success: function (data) {
32
+ $('#ctf_access_token').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
33
+ $('#ctf_access_token_secret').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
34
+ }
35
+ });
36
+ }
37
+ }
38
+
39
+ function toggleAccessInputs() {
40
+ if($ctfHaveOwnTokens.is(':checked')) {
41
+ $ctfAccessFields.show();
42
+ $ctfConsumerFields.show();
43
+ } else {
44
+ $ctfConsumerFields.hide();
45
+ if($ctfAccessFields.find('#ctf_access_token').val() == '' && $ctfAccessFields.find('#ctf_access_token_secret').val() == '') {
46
+ $ctfAccessFields.hide();
47
+ $ctfConsumerFields.hide();
48
+ }
49
+ }
50
+ }
51
+ toggleAccessInputs();
52
+
53
+ $ctfHaveOwnTokens.on('change', function() {
54
+ toggleAccessInputs();
55
+ });
56
+
57
+ // variables for time triggered validator
58
+ var typingTimer,
59
+ doneTypingInterval = 1000,
60
+ $ctfSearchText = $('#ctf-admin #ctf_search_text'),
61
+ $ctfUserText = $('#ctf-admin #ctf_usertimeline_text'),
62
+ $ctfSearchError = $('#ctf-admin .ctf_search_error'),
63
+ $ctfUserError= $('#ctf-admin .ctf_usertimeline_error');
64
+
65
+ // hide elements when page loads
66
+ $ctfSearchError.hide();
67
+ $ctfUserError.hide();
68
+
69
+ // on search text keyup, start timer to trigger validator
70
+ $ctfSearchText.keyup(function(){
71
+ clearTimeout(typingTimer);
72
+ if($ctfSearchText.val){
73
+ typingTimer = setTimeout(searchValidator, doneTypingInterval);
74
+ }
75
+ });
76
+
77
+ // on usertimeline text keyup, start timer to trigger validator
78
+ $ctfUserText.keyup(function(){
79
+ clearTimeout(typingTimer);
80
+ if($ctfUserText.val){
81
+ typingTimer = setTimeout(userValidator, doneTypingInterval);
82
+ }
83
+ });
84
+
85
+ // validate search input when user is done typing
86
+ var internationalHashtagRegexString = "[A-z\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC",
87
+ hashtagRegex = new RegExp("^"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$|^#+"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$");
88
+
89
+ function searchValidator() {
90
+ var ctfSearch = $ctfSearchText.val();
91
+
92
+ if ( ctfSearch.indexOf(',') > -1 || ctfSearch.indexOf(' ') > -1 ){
93
+ $ctfSearchError.slideDown();
94
+ } else {
95
+ $ctfSearchError.slideUp();
96
+ }
97
+ }
98
+
99
+ // validate screen name input when user is done typing
100
+ function userValidator(){
101
+ var ctfUser = $ctfUserText.val();
102
+
103
+ if(ctfUser.match(/^@[A-Za-z0-9_]{1,15}$/) || ctfUser.match(/^[A-Za-z0-9_]{1,15}$/)){
104
+ $ctfUserError.slideUp();
105
+ } else {
106
+ $ctfUserError.slideDown();
107
+ }
108
+ }
109
+
110
+ // search term guide toggle
111
+ var $ctfToggleSearchGuide = $('#ctf-admin .ctf-toggle-search-guide');
112
+
113
+ // hide initially
114
+ $ctfToggleSearchGuide.closest('h4').next('div').hide();
115
+
116
+ // show on click
117
+ $ctfToggleSearchGuide.on('click',function(){
118
+ $(this).closest('h4').next('div').slideToggle();
119
+ });
120
+
121
+ // tooltips
122
+ $('#ctf-admin .ctf-tooltip-link').on('click',function(){
123
+ $(this).closest('tr, h3, .ctf-tooltip-wrap').find('.ctf-tooltip').slideToggle();
124
+ });
125
+
126
+ // include replies
127
+ $('.ctf_include_replies_toggle').hide();
128
+ $('.ctf_include_replies_toggle input').prop('disabled', true);
129
+
130
+ function toggleIncludeReplies() {
131
+ $('.ctf_include_replies_toggle').each(function() {
132
+ if($(this).closest('td').find('.ctf-feed-settings-radio').is(':checked')) {
133
+ $(this).slideDown();
134
+ } else {
135
+ $(this).slideUp();
136
+ }
137
+ });
138
+ }
139
+ toggleIncludeReplies();
140
+
141
+ $('.ctf-feed-settings-radio').on('change', function() {
142
+ toggleIncludeReplies();
143
+
144
+ if( $('#ctf-admin #ctf_usertimeline_radio').is(':checked') ) {
145
+ userValidator();
146
+ // $ctfSearchError.slideUp();
147
+ } else if( $('#ctf-admin #ctf_search_radio').is(':checked') ) {
148
+ searchValidator();
149
+ // $ctfUserError.slideUp();
150
+ }
151
+ });
152
+
153
+ // color picker
154
+ var $ctfColorpicker = $('.ctf-colorpicker');
155
+
156
+ if($ctfColorpicker.length > 0){
157
+ $ctfColorpicker.wpColorPicker();
158
+ }
159
+
160
+ // shortcode tooltips
161
+ var $ctfAdminLabel = $('#ctf-admin label');
162
+
163
+ $ctfAdminLabel.on('click',function(){
164
+ var $sbi_shortcode = $(this).siblings('.ctf_shortcode');
165
+ if($sbi_shortcode.is(':visible')){
166
+ $(this).removeClass('ctf_shortcode_visible');
167
+ $(this).siblings('.ctf_shortcode').css('display','none');
168
+ } else {
169
+ $(this).addClass('ctf_shortcode_visible');
170
+ $(this).siblings('.ctf_shortcode').css('display','block');
171
+ }
172
+ });
173
+
174
+ $ctfAdminLabel.on('mouseenter mouseleave', function(e) {
175
+ switch(e.type) {
176
+ case 'mouseenter':
177
+ if($(this).siblings('.ctf_shortcode').length > 0 ){
178
+ $(this).attr('title', 'Click for shortcode option').append('<code class="ctf_shortcode_symbol">[]</code>');
179
+ }
180
+ break;
181
+ case 'mouseleave':
182
+ $(this).find('.ctf_shortcode_symbol').remove();
183
+ break;
184
+ }
185
+ });
186
+
187
+ //Scroll to hash for quick links
188
+ $('#ctf-admin a').on('click',function() {
189
+ if(location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
190
+ var target = $(this.hash);
191
+ target = target.length ? target : this.hash.slice(1);
192
+ if(target.length) {
193
+ $('html,body').animate({
194
+ scrollTop: target.offset().top
195
+ }, 500);
196
+ return false;
197
+ }
198
+ }
199
+ });
200
+
201
+ //Mobile width
202
+ var ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
203
+ ctfWidth = $('#ctf-admin #ctf_width').val(),
204
+ $ctfWidthOptions = $('#ctf-admin #ctf_width_options');
205
+
206
+ if (typeof ctfWidth !== 'undefined') {
207
+ //Show initially if a width is set
208
+ if(ctfWidth.length > 1 && !(ctfWidth == '100' && ctfWidthUnit == '%')) $ctfWidthOptions.show();
209
+
210
+ $('#ctf_width, #ctf_width_unit').on('change',function(){
211
+ ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
212
+ ctfWidth = $('#ctf-admin #ctf_width').val();
213
+
214
+ if(ctfWidth.length < 2 || (ctfWidth == '100' && ctfWidthUnit == '%')) {
215
+ $ctfWidthOptions.slideUp();
216
+ } else {
217
+ $ctfWidthOptions.slideDown();
218
+ }
219
+ });
220
+ }
221
+
222
+ // clear cache
223
+ var $ctfClearCacheButton = $('#ctf-admin #ctf-clear-cache');
224
+
225
+ $ctfClearCacheButton.on('click',function(event) {
226
+ event.preventDefault();
227
+
228
+ $('#ctf-clear-cache-success').remove();
229
+ $(this).prop("disabled",true);
230
+
231
+ $.ajax({
232
+ url : ctf.ajax_url,
233
+ type : 'post',
234
+ data : {
235
+ action : 'ctf_clear_cache_admin'
236
+ },
237
+ success : function(data) {
238
+ $ctfClearCacheButton.prop('disabled',false);
239
+ if(!data===false) {
240
+ $ctfClearCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
241
+ } else {
242
+ $ctfClearCacheButton.after('<span>error</span>');
243
+ }
244
+ }
245
+ }); // ajax call
246
+ }); // clear-cache click
247
+
248
+ // clear persistent cache
249
+ var $ctfClearPersistentCacheButton = $('#ctf-admin #ctf-clear-persistent-cache');
250
+
251
+ $ctfClearPersistentCacheButton.on('click',function(event) {
252
+ event.preventDefault();
253
+
254
+ $('#ctf-clear-cache-success').remove();
255
+ $(this).prop("disabled",true);
256
+
257
+ $.ajax({
258
+ url : ctf.ajax_url,
259
+ type : 'post',
260
+ data : {
261
+ action : 'ctf_clear_persistent_cache'
262
+ },
263
+ success : function(data) {
264
+ $ctfClearPersistentCacheButton.prop('disabled',false);
265
+ if(!data===false) {
266
+ $ctfClearPersistentCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
267
+ } else {
268
+ $ctfClearPersistentCacheButton.after('<span>error</span>');
269
+ }
270
+ }
271
+ }); // ajax call
272
+ }); // clear-persistent-cache click
273
+
274
+ $('.ctf-opt-in').on('click',function(event) {
275
+ event.preventDefault();
276
+
277
+ var $btn = jQuery(this);
278
+ $btn.prop( 'disabled', true ).addClass( 'loading' ).html('<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>');
279
+
280
+ ctfSubmitOptIn(true);
281
+ }); // clear_comment_cache click
282
+
283
+ $('.ctf-no-usage-opt-out').on('click',function(event) {
284
+ event.preventDefault();
285
+
286
+ var $btn = jQuery(this);
287
+ $btn.prop( 'disabled', true ).addClass( 'loading' ).html('<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>');
288
+
289
+ ctfSubmitOptIn(false);
290
+ }); // clear_comment_cache click
291
+
292
+ function ctfSubmitOptIn(choice) {
293
+ $.ajax({
294
+ url : ctf.ajax_url,
295
+ type : 'post',
296
+ data : {
297
+ action : 'ctf_usage_opt_in_or_out',
298
+ opted_in: choice,
299
+ },
300
+ success : function(data) {
301
+ $('.ctf-no-usage-opt-out').closest('.ctf-admin-notice').fadeOut();
302
+ }
303
+ }); // ajax call
304
+ }
305
+
306
+ //Pro version notices
307
+ var ctfUpgradeNote = '<span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=twitter-free&utm_source=settings&utm_medium=layout" target="_blank">Available in Pro version</a></span>';
308
+ $('.ctf_pro').each(function(){
309
+ var $pro = $(this);
310
+ if (!$pro.find('.ctf_layout_options_wrap').length) {
311
+ $pro.find('td').last().append(ctfUpgradeNote);
312
+ $pro.find('input, select, textarea').attr('disabled', 'true');
313
+ }
314
+ });
315
+ $('#ctf_include_twittercards, #ctf_include_media, #ctf_include_replied_to').prop('disabled', true).prop('checked',false).next('label').css('color', '#999').after(ctfUpgradeNote);
316
+
317
+ $('#ctf-admin .ctf-show-pro').closest('span').next('.ctf-pro-options').hide();
318
+ $('#ctf-admin .ctf-show-pro').on('click',function() {
319
+ if ($(this).closest('span').next('.ctf-pro-options').is(':visible')) {
320
+ $(this).closest('span').next('.ctf-pro-options').hide();
321
+ } else {
322
+ $(this).closest('span').next('.ctf-pro-options').show();
323
+ }
324
+ });
325
+
326
+ function ctfUpdateLayoutTypeOptionsDisplay() {
327
+ setTimeout(function(){
328
+ jQuery('.ctf_layout_settings').hide();
329
+ jQuery('.ctf_layout_settings.ctf_layout_type_'+jQuery('.ctf_layout_type:checked').val()).show();
330
+ }, 1);
331
+ }
332
+ ctfUpdateLayoutTypeOptionsDisplay();
333
+ jQuery('.ctf_layout_type').on('change',ctfUpdateLayoutTypeOptionsDisplay);
334
+
335
+ // notices
336
+
337
+ if (jQuery('#ctf-notice-bar').length) {
338
+ jQuery('#wpadminbar').after(jQuery('#ctf-notice-bar'));
339
+ jQuery('#wpcontent').css('padding-left', 0);
340
+ jQuery('#wpbody').css('padding-left', '20px');
341
+ jQuery('#ctf-notice-bar').show();
342
+ }
343
+
344
+ jQuery('#ctf-notice-bar .d