Custom Twitter Feeds - Version 1.2

Version Description

  • New: The plugin now uses persistent tweet caching for 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.
  • New: Tweets with media will have an icon and label that links to the tweet on twitter.com
  • Fix: Fixed an issue with checkbox settings being changed unintentionally after obtaining a new access token
Download this release

Release Info

Developer smashballoon
Plugin Icon 128x128 Custom Twitter Feeds
Version 1.2
Comparing to
See all releases

Code changes from version 1.1.8 to 1.2

README.txt CHANGED
@@ -5,7 +5,7 @@ Support Website: http://smashballoon/custom-twitter-feeds/
5
  Tags: Twitter, Twitter feed, Custom Twitter Feed, Twitter feeds, Custom Twitter Feeds, Tweets, Custom Tweets, Tweets feed, Twitter widget, Custom Twitter widget, Twitter plugin, Twitter API, Twitter tweets
6
  Requires at least: 3.0
7
  Tested up to: 4.7
8
- Stable tag: 1.1.8
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -14,25 +14,51 @@ Custom Twitter Feeds allows you to display completely customizable Twitter feeds
14
  == Description ==
15
  Display **completely customizable**, **responsive** and **search engine crawlable** versions of your Twitter feed on your website. Completely match the look and feel of the site with tons of customization options!
16
 
 
17
  * **Completely Customizable** - by default inherits your theme's styles
18
- * Feed content is **crawlable by search engines** adding SEO value to your site
19
  * **Completely responsive and mobile optimized** - works on any screen size
20
- * Display tweets from any user, your own account and those you follow, or from a specific hashtag
21
  * Display multiple feeds from different Twitter users on multiple pages or widgets
22
- * Post caching means that your feed loads lightning fast and minimizes Twitter API requests
23
  * **Infinitely load more** of your Tweets with the 'Load More' button
24
  * Built-in easy to use "Custom Twitter Feeds" Widget
25
  * Fully internationalized and translatable into any language
26
- * Display a beautiful header at the top of your feed
27
  * Enter your own custom CSS for even deeper customization
28
 
29
  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/ 'Custom Twitter Feeds setup guide').
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  = Feedback or Support =
32
  We're dedicated to providing the most customizable, 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/ 'Twitter Feed Support').
33
 
34
  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/ 'Twitter Feed Support') what the problem is and we'll get back to you right away.
35
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  We recently released a [Pro version](http://smashballoon.com/custom-twitter-feeds/ 'Custom Twitter Feeds Pro') which includes some awesome additional features:
37
 
38
  * Display Tweets from **multiple users or hashtags in the same feed**
@@ -50,21 +76,21 @@ Try the Pro version [demo here](http://smashballoon.com/custom-twitter-feeds/dem
50
  == Installation ==
51
  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).
52
  2. Activate the plugin through the 'Plugins' menu in WordPress.
53
- 3. Navigate to the 'Twitter Feed' settings page to configure your feed.
54
  4. Use the shortcode [custom-twitter-feeds] in your page, post or widget to display your feed.
55
- 5. You can display multiple feeds with different configurations by specifying the necessary parameters directly in the shortcode: [custom-twitter-feeds hashtag=#smashballoon].
56
 
57
- For simple step-by-step directions on how to set up Custom Twitter Feeds plugin please refer to our [setup guide](http://smashballoon.com/custom-twitter-feeds/free/ 'Custom Twitter Feeds setup guide').
58
 
59
  = Setting up the Free Custom Twitter Feeds WordPress Plugin =
60
 
61
  The Custom Twitter Feeds plugin is brand new and so we're currently working on improving our documentation for it. If you have an issue or question please submit a support ticket and we'll get back to you as soon as we can.
62
 
63
- 1) Once you've installed the plugin click on the Twitter Feed item in your WordPress menu
64
 
65
  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.
66
 
67
- 3) Authorize the plugin to read your Tweets.
68
  Note; the plugin does not obtain permission to edit or write to your Twitter account, only to read your Twitter content.
69
 
70
  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.
@@ -73,7 +99,7 @@ Note; the plugin does not obtain permission to edit or write to your Twitter acc
73
 
74
  6) Navigate to the Customize and Style pages to customize your Twitter feed.
75
 
76
- 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 feeds).
77
 
78
  8) Copy the [custom-twitter-feeds] shortcode and paste it into any page, post or widget where you want the Twitter feed to appear.
79
 
@@ -93,6 +119,43 @@ Yep. You can display multiple Twitter feeds by using our built-in shortcode opti
93
 
94
  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]'); ?>`.
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  == Other Notes ==
97
 
98
  = Twitter API Error Message Reference =
@@ -124,6 +187,18 @@ Sorry, that page does not exist
124
  **Causes:**
125
  - There may be a typo in the Twitter screen name or hashtag you are attempting to use
126
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  **Solutions**
128
 
129
  **#1 - Your Twitter access tokens might not be valid**
@@ -146,6 +221,11 @@ If you're still having trouble displaying your Tweets after trying the common is
146
  2. Custom Twitter Feeds plugin Settings pages
147
 
148
  == Changelog ==
 
 
 
 
 
149
  = 1.1.8 =
150
  * Tweak: Updated plugin links for new WordPress.org repo
151
  * Fix: Minor bug fixes
5
  Tags: Twitter, Twitter feed, Custom Twitter Feed, Twitter feeds, Custom Twitter Feeds, Tweets, Custom Tweets, Tweets feed, Twitter widget, Custom Twitter widget, Twitter plugin, Twitter API, Twitter tweets
6
  Requires at least: 3.0
7
  Tested up to: 4.7
8
+ Stable tag: 1.2
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
14
  == Description ==
15
  Display **completely customizable**, **responsive** and **search engine crawlable** versions of your Twitter feed on your website. Completely match the look and feel of the site with tons of customization options!
16
 
17
+ = Features =
18
  * **Completely Customizable** - by default inherits 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** - works 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 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
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 for even deeper customization
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/ '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 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 customizable, 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/ '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/ '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 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 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
+ = Pro Version =
62
  We recently released a [Pro version](http://smashballoon.com/custom-twitter-feeds/ 'Custom Twitter Feeds Pro') which includes some awesome additional features:
63
 
64
  * Display Tweets from **multiple users or hashtags in the same feed**
76
  == Installation ==
77
  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).
78
  2. Activate the plugin through the 'Plugins' menu in WordPress.
79
+ 3. Navigate to the 'Twitter Feed' settings page to configure your Twitter feed.
80
  4. Use the shortcode [custom-twitter-feeds] in your page, post or widget to display your feed.
81
+ 5. You can display multiple Twitter feeds with different configurations by specifying the necessary parameters directly in the shortcode: [custom-twitter-feeds hashtag=#smashballoon].
82
 
83
+ 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/ 'Custom Twitter Feeds setup guide').
84
 
85
  = Setting up the Free Custom Twitter Feeds WordPress Plugin =
86
 
87
  The Custom Twitter Feeds plugin is brand new and so we're currently working on improving our documentation for it. If you have an issue or question please submit a support ticket and we'll get back to you as soon as we can.
88
 
89
+ 1) Once you've installed the Custom Twitter Feeds plugin click on the Twitter Feed item in your WordPress menu
90
 
91
  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.
92
 
93
+ 3) Authorize the Custom Twitter Feeds plugin to read your Tweets.
94
  Note; the plugin does not obtain permission to edit or write to your Twitter account, only to read your Twitter content.
95
 
96
  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.
99
 
100
  6) Navigate to the Customize and Style pages to customize your Twitter feed.
101
 
102
+ 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).
103
 
104
  8) Copy the [custom-twitter-feeds] shortcode and paste it into any page, post or widget where you want the Twitter feed to appear.
105
 
119
 
120
  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]'); ?>`.
121
 
122
+ = Is the content of my Twitter feed crawlable by search engines and how does it help improve my SEO? =
123
+
124
+ Unlike other Twitter plugins which use iframes to embed your Twitter feed into your page once it's loaded, the Custom Twitter Feeds uses server-side code to embed your Twitter feed content directly into your page. This adds dynamic, search engine crawlable content to your site.
125
+
126
+ = Will Custom Twitter Feeds work with W3 Total Cache or other caching plugins? =
127
+
128
+ 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 posts. If you are experiencing problems with your Twitter feed 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.
129
+
130
+ = The font in my Twitter feed isn't inherited from my website =
131
+
132
+ 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 HTML by either the theme, another plugin, or perhaps even the shortcode being wrapped in code tags in your page editor. To fix this;
133
+
134
+ 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.
135
+
136
+ 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):
137
+
138
+ `#ctf code { font-family: sans-serif; }`
139
+
140
+ You can replace the font-family with whatever font your theme is using.
141
+
142
+ = Differences between the free version and Pro version of the Custom Twitter Feeds plugin =
143
+
144
+ 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:
145
+
146
+ - Display photos, videos, and gifs in your Tweets, with support for 3rd party services such as YouTube, Vimeo, Vine, and SoundCloud
147
+ - View media in a pop-up lightbox directly on your site
148
+ - Display Tweets in a multi-column Masonry layout
149
+ - Display your Twitter feed in rotating Carousels / Slideshows
150
+ - Twitter Cards (rich, standout links) are displayed in Tweets for links that support them
151
+ - Create advanced Twitter "Search" feeds or use the "Mentions" timeline
152
+ - Combine feeds of multiple types (i.e. User feeds and Hashtag Twitter feeds combined)
153
+ - Filter feeds by hashtag, word(s), or remove specific tweets
154
+ - Autoload more tweets when scrolling to the bottom of the Twitter feed
155
+ - Include Tweet replies ("in reply to")
156
+
157
+ You can see the demo of the Custom Twitter Feeds Pro version [here](https://smashballoon.com/custom-twitter-feeds/demo/), and a video screencast [here](https://smashballoon.com/custom-twitter-feeds/#watch-video).
158
+
159
  == Other Notes ==
160
 
161
  = Twitter API Error Message Reference =
187
  **Causes:**
188
  - There may be a typo in the Twitter screen name or hashtag you are attempting to use
189
 
190
+ **Error:**
191
+ Over capacity
192
+
193
+ **Causes:**
194
+ - Twitter’s servers are over capacity so the API is unusable. This will correct itself after some time
195
+
196
+ **Error:**
197
+ Connection timed out after 10000 milliseconds
198
+
199
+ **Causes:**
200
+ - Most likely this is due to your server blocking access to the Twitter API – See #4 below
201
+
202
  **Solutions**
203
 
204
  **#1 - Your Twitter access tokens might not be valid**
221
  2. Custom Twitter Feeds plugin Settings pages
222
 
223
  == Changelog ==
224
+ = 1.2 =
225
+ * New: The plugin now uses persistent tweet caching for 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.
226
+ * New: Tweets with media will have an icon and label that links to the tweet on twitter.com
227
+ * Fix: Fixed an issue with checkbox settings being changed unintentionally after obtaining a new access token
228
+
229
  = 1.1.8 =
230
  * Tweak: Updated plugin links for new WordPress.org repo
231
  * Fix: Minor bug fixes
css/ctf-styles.css CHANGED
@@ -273,7 +273,8 @@
273
  color: inherit;
274
  font-weight: inherit;
275
  }
276
- #ctf p.ctf-tweet-text{
 
277
  padding: 0 !important;
278
  margin: 0 !important;
279
  }
@@ -387,6 +388,43 @@
387
  font-size: 11px;
388
  line-height: 14px;
389
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
 
391
  /* Load more */
392
  #ctf #ctf-more{
@@ -654,6 +692,8 @@
654
  #ctf .fa-heart:before{content:"\f004"}
655
  #ctf .fa-check-circle:before{content:"\f058"}
656
  #ctf .fa-twitter:before{content:"\f099"}
 
 
657
 
658
  /* On mobile make the min-width 100% */
659
  @media all and (max-width: 640px){
273
  color: inherit;
274
  font-weight: inherit;
275
  }
276
+ #ctf p.ctf-tweet-text,
277
+ #ctf p.ctf-media-link {
278
  padding: 0 !important;
279
  margin: 0 !important;
280
  }
388
  font-size: 11px;
389
  line-height: 14px;
390
  }
391
+ #ctf .ctf-tweet-text-media-wrap,
392
+ #ctf .ctf-quoted-tweet-text-media-wrap{
393
+ margin-left: 5px;
394
+ padding: 0 1px 0 4px;
395
+ display: inline-block;
396
+ border: 1px solid #ddd;
397
+ border: 1px solid rgba(0,0,0,0.15);
398
+
399
+ color: inherit;
400
+ text-decoration: none;
401
+ line-height: 1.5;
402
+
403
+ -moz-border-radius: 2px;
404
+ -webkit-border-radius: 2px;
405
+ border-radius: 2px;
406
+
407
+ -moz-transition: background 0.1s ease-in-out;
408
+ -webkit-transition: background 0.1s ease-in-out;
409
+ -o-transition: background 0.1s ease-in-out;
410
+ transition: background 0.1s ease-in-out;
411
+ }
412
+ #ctf .ctf-tweet-text-media-wrap i,
413
+ #ctf .ctf-quoted-tweet-text-media-wrap i{
414
+ padding-left: 3px;
415
+ }
416
+ #ctf .ctf-tweet-text-media-wrap:hover,
417
+ #ctf .ctf-tweet-text-media-wrap:focus{
418
+ background: #eee;
419
+ background: rgba(0,0,0,0.05);
420
+ border: 1px solid #ddd;
421
+ border: 1px solid rgba(0,0,0,0.1);
422
+
423
+ -moz-transition: background 0.1s ease-in-out;
424
+ -webkit-transition: background 0.1s ease-in-out;
425
+ -o-transition: background 0.1s ease-in-out;
426
+ transition: background 0.1s ease-in-out;
427
+ }
428
 
429
  /* Load more */
430
  #ctf #ctf-more{
692
  #ctf .fa-heart:before{content:"\f004"}
693
  #ctf .fa-check-circle:before{content:"\f058"}
694
  #ctf .fa-twitter:before{content:"\f099"}
695
+ #ctf .fa-file-video-o:before{content:"\f1c8"}
696
+ #ctf .fa-picture-o:before{content:"\f03e"}
697
 
698
  /* On mobile make the min-width 100% */
699
  @media all and (max-width: 640px){
custom-twitter-feed.php CHANGED
@@ -3,7 +3,7 @@
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.1.8
7
  Author: Smash Balloon
8
  Author URI: http://smashballoon.com/
9
  Text Domain: custom-twitter-feeds
@@ -24,7 +24,7 @@ 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.1.8' );
28
  define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
  define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.js?ver=' . CTF_VERSION , __FILE__ ) );
30
  define( 'OAUTH_PROCESSOR_URL', 'https://smashballoon.com/ctf-at-retriever/?return_uri=' );
@@ -78,7 +78,9 @@ function ctf_init( $atts ) {
78
  include_once( CTF_URL . '/inc/CtfFeed.php' );
79
 
80
  $twitter_feed = CtfFeed::init( $atts );
81
-
 
 
82
  // if there is an error, display the error html, otherwise the feed
83
  if ( ! $twitter_feed->tweet_set || $twitter_feed->missing_credentials ) {
84
  return $twitter_feed->getErrorHtml();
@@ -104,12 +106,15 @@ function ctf_get_more_posts() {
104
  $num_needed = isset( $_POST['num_needed'] ) ? (int)$_POST['num_needed'] : 0;
105
  $ids_to_remove = isset( $_POST['ids_to_remove'] ) ? $_POST['ids_to_remove'] : array();
106
  $is_pagination = empty( $last_id_data ) ? 0 : 1;
 
107
 
108
  include_once( CTF_URL . '/inc/CtfFeed.php' );
109
 
110
- $twitter_feed = CtfFeed::init( $shortcode_data, $last_id_data, $num_needed, $ids_to_remove );
111
 
112
- $twitter_feed->maybeCacheTweets();
 
 
113
 
114
  echo $twitter_feed->getTweetSetHtml( $is_pagination );
115
 
@@ -220,16 +225,17 @@ function ctf_get_formatted_date( $raw_date, $feed_options, $utc_offset ) {
220
  */
221
  function ctf_auto_save_tokens() {
222
  if ( current_user_can( 'edit_posts' ) ) {
223
- wp_verify_nonce( 'ctf-smash-balloon' );
 
 
224
 
225
- $options = get_option( 'ctf_options' );
226
  $options['access_token'] = sanitize_text_field( $_POST['access_token'] );
227
  $options['access_token_secret'] = sanitize_text_field( $_POST['access_token_secret'] );
228
 
229
  update_option( 'ctf_options', $options );
230
- } else {
231
- return false;
232
  }
 
233
  }
234
  add_action( 'wp_ajax_ctf_auto_save_tokens', 'ctf_auto_save_tokens' );
235
 
@@ -261,6 +267,32 @@ function ctf_clear_cache() {
261
  add_action( 'ctf_cron_job', 'ctf_clear_cache' );
262
  add_action( 'wp_ajax_ctf_clear_cache', 'ctf_clear_cache' );
263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  /**
265
  * clear the cache and unschedule an cron jobs when deactivated
266
  */
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.2
7
  Author: Smash Balloon
8
  Author URI: http://smashballoon.com/
9
  Text Domain: custom-twitter-feeds
24
  */
25
 
26
  define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
27
+ define( 'CTF_VERSION', '1.2' );
28
  define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
  define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.js?ver=' . CTF_VERSION , __FILE__ ) );
30
  define( 'OAUTH_PROCESSOR_URL', 'https://smashballoon.com/ctf-at-retriever/?return_uri=' );
78
  include_once( CTF_URL . '/inc/CtfFeed.php' );
79
 
80
  $twitter_feed = CtfFeed::init( $atts );
81
+ /*echo '<pre>';
82
+ var_dump( $twitter_feed->tweet_set);
83
+ echo '</pre>';*/
84
  // if there is an error, display the error html, otherwise the feed
85
  if ( ! $twitter_feed->tweet_set || $twitter_feed->missing_credentials ) {
86
  return $twitter_feed->getErrorHtml();
106
  $num_needed = isset( $_POST['num_needed'] ) ? (int)$_POST['num_needed'] : 0;
107
  $ids_to_remove = isset( $_POST['ids_to_remove'] ) ? $_POST['ids_to_remove'] : array();
108
  $is_pagination = empty( $last_id_data ) ? 0 : 1;
109
+ $persistent_index = isset( $_POST['persistent_index'] ) ? sanitize_text_field( $_POST['persistent_index'] ) : '';
110
 
111
  include_once( CTF_URL . '/inc/CtfFeed.php' );
112
 
113
+ $twitter_feed = CtfFeed::init( $shortcode_data, $last_id_data, $num_needed, $ids_to_remove, $persistent_index );
114
 
115
+ if ( ! $twitter_feed->feed_options['persistentcache'] ) {
116
+ $twitter_feed->maybeCacheTweets();
117
+ }
118
 
119
  echo $twitter_feed->getTweetSetHtml( $is_pagination );
120
 
225
  */
226
  function ctf_auto_save_tokens() {
227
  if ( current_user_can( 'edit_posts' ) ) {
228
+ wp_cache_delete ( 'alloptions', 'options' );
229
+
230
+ $options = get_option( 'ctf_options', array() );
231
 
 
232
  $options['access_token'] = sanitize_text_field( $_POST['access_token'] );
233
  $options['access_token_secret'] = sanitize_text_field( $_POST['access_token_secret'] );
234
 
235
  update_option( 'ctf_options', $options );
236
+ die();
 
237
  }
238
+ die();
239
  }
240
  add_action( 'wp_ajax_ctf_auto_save_tokens', 'ctf_auto_save_tokens' );
241
 
267
  add_action( 'ctf_cron_job', 'ctf_clear_cache' );
268
  add_action( 'wp_ajax_ctf_clear_cache', 'ctf_clear_cache' );
269
 
270
+ /**
271
+ * manually clears the persistent cached tweets
272
+ *
273
+ * @return mixed bool whether or not it was successful
274
+ */
275
+
276
+ function ctf_clear_persistent_cache() {
277
+ if ( current_user_can( 'edit_posts' ) ) {
278
+ //Delete all persistent caches (start with ctf_!)
279
+ global $wpdb;
280
+ $table_name = $wpdb->prefix . "options";
281
+ $result = $wpdb->query("
282
+ DELETE
283
+ FROM $table_name
284
+ WHERE `option_name` LIKE ('%ctf\_\!%')
285
+ ");
286
+ delete_option( 'ctf_cache_list' );
287
+ return $result;
288
+ } else {
289
+ return false;
290
+ }
291
+
292
+ die();
293
+ }
294
+ add_action( 'wp_ajax_ctf_clear_persistent_cache', 'ctf_clear_persistent_cache' );
295
+
296
  /**
297
  * clear the cache and unschedule an cron jobs when deactivated
298
  */
inc/CtfAdmin.php CHANGED
@@ -554,6 +554,29 @@ class CtfAdmin
554
  'default' => 1.25
555
  ));
556
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
  /**
558
  * "Style" tab
559
  */
@@ -1417,12 +1440,22 @@ class CtfAdmin
1417
  <?php
1418
  }
1419
 
 
 
 
 
 
 
 
 
1420
  public function validate_ctf_options( $input )
1421
  {
1422
- if ( isset( $input['tab'] ) ) {
 
 
1423
  $ctf_options = get_option( 'ctf_options', array() );
1424
 
1425
- if ( $input['tab'] === 'configure' ) {
1426
 
1427
  $feed_types = apply_filters( 'ctf_admin_feed_type_list', '' );
1428
  $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
@@ -1475,13 +1508,13 @@ class CtfAdmin
1475
 
1476
  wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1477
  }
1478
- } elseif ( $input['tab'] === 'customize' ) {
1479
 
1480
  $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1481
  $checkbox_settings = array( 'width_mobile_no_fixed', 'include_retweeter', 'include_avatar', 'include_author', 'include_text',
1482
- 'include_date', 'include_actions', 'include_twitterlink', 'include_linkbox', 'creditctf', 'showbutton', 'showheader' );
1483
  $checkbox_settings = apply_filters( 'ctf_admin_customize_checkbox_settings', $checkbox_settings );
1484
- $leave_spaces = array( 'headertext', 'translate_minute', 'translate_hour' );
1485
 
1486
  foreach ( $checkbox_settings as $checkbox_setting ) {
1487
  $ctf_options[$checkbox_setting] = 0;
@@ -1527,7 +1560,7 @@ class CtfAdmin
1527
 
1528
  wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1529
  }
1530
- } elseif ( $input['tab'] === 'style' ) {
1531
  $checkbox_settings = array( 'showbio', 'disablelinks', 'linktexttotwitter' );
1532
  $leave_spaces = array( 'headertext' );
1533
 
@@ -1554,13 +1587,19 @@ class CtfAdmin
1554
 
1555
  return $ctf_options;
1556
  } elseif ( isset( $input['access_token'] ) ) {
1557
- $new = array();
 
 
1558
  $new['access_token'] = $input['access_token'];
1559
  $new['access_token_secret'] = $input['access_token_secret'];
1560
 
1561
  return $new;
1562
 
1563
  }
 
 
 
1564
 
 
1565
  }
1566
  }
554
  'default' => 1.25
555
  ));
556
 
557
+ $this->create_settings_field( array(
558
+ 'name' => 'persistent',
559
+ 'title' => '<label for="ctf_multiplier">Clear Persistent Cache</label>', // label for the input field
560
+ 'callback' => 'clear_persistent_cache_button', // name of the function that outputs the html
561
+ 'page' => 'ctf_options_advanced', // matches the section name
562
+ 'section' => 'ctf_options_advanced', // matches the section name
563
+ 'option' => 'ctf_options', // matches the options name
564
+ 'class' => 'small-text' // class for the wrapper and input field
565
+ ));
566
+
567
+ // persistent cache
568
+ $this->create_settings_field( array(
569
+ 'name' => 'persistentcache',
570
+ 'title' => '<label for="ctf_persistentcache">Persistent cache enabled by default</label><code class="ctf_shortcode">persistentcache
571
+ Eg: persistentcache=false</code>', // label for the input field
572
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
573
+ 'page' => 'ctf_options_advanced', // matches the section name
574
+ 'section' => 'ctf_options_advanced', // matches the section name
575
+ 'option' => 'ctf_options', // matches the options name
576
+ 'class' => '',
577
+ '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."
578
+ ));
579
+
580
  /**
581
  * "Style" tab
582
  */
1440
  <?php
1441
  }
1442
 
1443
+ public function clear_persistent_cache_button( $args ) {
1444
+ ?>
1445
+ <input id="ctf-clear-persistent-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Persistent Caches' ); ?>" />
1446
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1447
+ <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>
1448
+ <?php
1449
+ }
1450
+
1451
  public function validate_ctf_options( $input )
1452
  {
1453
+ if ( isset( $input['tab'] ) && ! isset( $_POST['just_tokens'] ) ) {
1454
+ wp_cache_delete ( 'alloptions', 'options' );
1455
+
1456
  $ctf_options = get_option( 'ctf_options', array() );
1457
 
1458
+ if ( $input['tab'] === 'configure' && isset( $input['usertimeline_text'] ) ) {
1459
 
1460
  $feed_types = apply_filters( 'ctf_admin_feed_type_list', '' );
1461
  $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1508
 
1509
  wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1510
  }
1511
+ } elseif ( $input['tab'] === 'customize' && isset( $input['class'] ) ) {
1512
 
1513
  $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1514
  $checkbox_settings = array( 'width_mobile_no_fixed', 'include_retweeter', 'include_avatar', 'include_author', 'include_text',
1515
+ 'include_date', 'include_actions', 'include_twitterlink', 'include_linkbox', 'creditctf', 'showbutton', 'showheader', 'persistentcache' );
1516
  $checkbox_settings = apply_filters( 'ctf_admin_customize_checkbox_settings', $checkbox_settings );
1517
+ $leave_spaces = array( 'headertext', 'translate_minute', 'translate_hour', 'custom_css', 'custom_js' );
1518
 
1519
  foreach ( $checkbox_settings as $checkbox_setting ) {
1520
  $ctf_options[$checkbox_setting] = 0;
1560
 
1561
  wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1562
  }
1563
+ } elseif ( $input['tab'] === 'style' && isset( $input['headertext'] ) ) {
1564
  $checkbox_settings = array( 'showbio', 'disablelinks', 'linktexttotwitter' );
1565
  $leave_spaces = array( 'headertext' );
1566
 
1587
 
1588
  return $ctf_options;
1589
  } elseif ( isset( $input['access_token'] ) ) {
1590
+ wp_cache_delete ( 'alloptions', 'options' );
1591
+
1592
+ $new = get_option( 'ctf_options', array() );
1593
  $new['access_token'] = $input['access_token'];
1594
  $new['access_token_secret'] = $input['access_token_secret'];
1595
 
1596
  return $new;
1597
 
1598
  }
1599
+ wp_cache_delete ( 'alloptions', 'options' );
1600
+
1601
+ $new = get_option( 'ctf_options', array() );
1602
 
1603
+ return $new;
1604
  }
1605
  }
inc/CtfFeed.php CHANGED
@@ -61,8 +61,6 @@ class CtfFeed
61
 
62
  private $check_for_duplicates = false;
63
 
64
- private $ids_to_remove = array();
65
-
66
  /**
67
  * @var array
68
  */
@@ -78,19 +76,21 @@ class CtfFeed
78
  */
79
  public $feed_html;
80
 
81
- /**
 
 
 
82
  * retrieves and sets options that apply to the feed
83
  *
84
  * @param array $atts data from the shortcode
85
  * @param string $last_id_data the last visible tweet on the feed, empty string if first set
86
  * @param int $num_needed_input this number represents the number left to retrieve after the first set
87
  */
88
- public function __construct( $atts, $last_id_data, $num_needed_input, $ids_to_remove )
89
  {
90
  $this->atts = $atts;
91
  $this->last_id_data = $last_id_data;
92
  $this->num_needed_input = $num_needed_input;
93
- $this->ids_to_remove = $ids_to_remove;
94
  $this->db_options = get_option( 'ctf_options', array() );
95
  }
96
 
@@ -102,10 +102,16 @@ class CtfFeed
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() )
106
  {
107
- $feed = new CtfFeed( $atts, $last_id_data, $num_needed_input, $ids_to_remove );
108
  $feed->setFeedOptions();
 
 
 
 
 
 
109
  $feed->setTweetSet();
110
  return $feed;
111
  }
@@ -191,7 +197,8 @@ class CtfFeed
191
  $this->setStandardStyleProperty( $bg_color, 'background-color' );
192
 
193
  $bool_true = array(
194
- 'showbutton',
 
195
  'showbio',
196
  'showheader'
197
  );
@@ -492,6 +499,14 @@ class CtfFeed
492
  }
493
  }
494
 
 
 
 
 
 
 
 
 
495
  /**
496
  * checks the data available in the cache to make sure it seems to be valid
497
  *
@@ -514,11 +529,23 @@ class CtfFeed
514
  */
515
  public function maybeSetTweetsFromCache()
516
  {
517
- $this->transient_data = get_transient( $this->transient_name );
518
-
519
- if ( $this->feed_options['cache_time'] <= 0 ) {
520
- return $this->tweet_set = false;
521
- }
 
 
 
 
 
 
 
 
 
 
 
 
522
  // validate the transient data
523
  if ( $this->transient_data ) {
524
  $this->errors['cache_status'] = $this->validateCache();
@@ -533,39 +560,162 @@ class CtfFeed
533
  }
534
  }
535
 
536
- private function removeDuplicates()
537
- {
538
- $tweet_set = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
539
- $usable_tweets = 0;
540
- $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
541
- $ids_of_tweets_to_remove = $this->ids_to_remove;
542
-
543
- $i = 0; // index of tweet_set
544
- $still_setting_filtered_tweets = true;
545
- while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
546
- if ( isset( $tweet_set[$i] ) ) {
547
- $id = isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ? $tweet_set[$i]['retweeted_status']['id_str'] : $tweet_set[$i]['id_str'];
548
- if ( in_array( $id, $ids_of_tweets_to_remove ) ) {
549
- unset( $tweet_set[$i] );
550
- } else {
551
- $usable_tweets++;
552
- $ids_of_tweets_to_remove[] = $id;
553
- }
554
- } else {
555
- $still_setting_filtered_tweets = false;
556
- }
557
-
558
- // if there are no more tweets needed
559
- if ( $usable_tweets >= $tweets_needed ) {
560
- $still_setting_filtered_tweets = false;
561
- } else {
562
- $i++;
563
- }
564
-
565
- }
566
-
567
- $this->tweet_set = array_values( $tweet_set );
568
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
569
 
570
  /**
571
  * will attempt to connect to the api to retrieve current tweets
@@ -573,7 +723,7 @@ class CtfFeed
573
  public function maybeSetTweetsFromTwitter()
574
  {
575
  $this->setTweetsToRetrieve();
576
- $this->api_obj = $this->apiConnect( $this->feed_options['type'], $this->feed_options['feed_term'] );
577
  $this->tweet_set = json_decode( $this->api_obj->json , $assoc = true );
578
 
579
  // check for errors/tweets present
@@ -591,7 +741,7 @@ class CtfFeed
591
  }
592
 
593
  if ( $this->check_for_duplicates ) {
594
- $this->removeDuplicates();
595
  }
596
  }
597
 
@@ -610,12 +760,16 @@ class CtfFeed
610
  /**
611
  * trims the unused data retrieved for more efficient caching
612
  */
613
- protected function trimTweetData()
614
  {
615
  $is_pagination = !empty( $this->last_id_data ) ? 1 : 0;
616
  $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
617
- $len = min( $this->feed_options['num'] + $is_pagination, count( $tweets ) );
618
- $trimmed_tweets = array();
 
 
 
 
619
 
620
  // for header
621
  if ( $this->last_id_data == '' ) { // if this is the first set of tweets
@@ -631,7 +785,7 @@ class CtfFeed
631
  $trimmed_tweets[$i]['user']['verified'] = $tweets[$i]['user']['verified'];
632
  $trimmed_tweets[$i]['user']['profile_image_url_https'] = $tweets[$i]['user']['profile_image_url_https'];
633
  $trimmed_tweets[$i]['user']['utc_offset']= $tweets[$i]['user']['utc_offset'];
634
- $trimmed_tweets[$i]['text']= $tweets[$i]['text'];
635
  $trimmed_tweets[$i]['id_str']= $tweets[$i]['id_str'];
636
  $trimmed_tweets[$i]['created_at']= $tweets[$i]['created_at'];
637
  $trimmed_tweets[$i]['retweet_count']= $tweets[$i]['retweet_count'];
@@ -643,7 +797,7 @@ class CtfFeed
643
  $trimmed_tweets[$i]['retweeted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['user']['verified'];
644
  $trimmed_tweets[$i]['retweeted_status']['user']['profile_image_url_https'] = $tweets[$i]['retweeted_status']['user']['profile_image_url_https'];
645
  $trimmed_tweets[$i]['retweeted_status']['user']['utc_offset']= $tweets[$i]['retweeted_status']['user']['utc_offset'];
646
- $trimmed_tweets[$i]['retweeted_status']['text'] = $tweets[$i]['retweeted_status']['text'];
647
  $trimmed_tweets[$i]['retweeted_status']['id_str'] = $tweets[$i]['retweeted_status']['id_str'];
648
  $trimmed_tweets[$i]['retweeted_status']['created_at']= $tweets[$i]['retweeted_status']['created_at'];
649
  $trimmed_tweets[$i]['retweeted_status']['retweet_count']= $tweets[$i]['retweeted_status']['retweet_count'];
@@ -654,7 +808,7 @@ class CtfFeed
654
  $trimmed_tweets[$i]['quoted_status']['user']['name'] = $tweets[$i]['quoted_status']['user']['name'];
655
  $trimmed_tweets[$i]['quoted_status']['user']['screen_name'] = $tweets[$i]['quoted_status']['user']['screen_name'];
656
  $trimmed_tweets[$i]['quoted_status']['user']['verified'] = $tweets[$i]['quoted_status']['user']['verified'];
657
- $trimmed_tweets[$i]['quoted_status']['text'] = $tweets[$i]['quoted_status']['text'];
658
  $trimmed_tweets[$i]['quoted_status']['id_str'] = $tweets[$i]['quoted_status']['id_str'];
659
  }
660
 
@@ -664,16 +818,103 @@ class CtfFeed
664
  $this->tweet_set = $trimmed_tweets;
665
  }
666
 
 
 
 
 
667
  /**
668
  * method to be overridden by pro
669
  *
670
- * @param $trimmed current trimmed tweet araray
671
  * @param $tweet current tweet data to be trimmed
672
  * @return mixed final trimmed tweet
673
  */
674
  protected function filterTrimmedTweets( $trimmed, $tweet )
675
  {
676
- return $trimmed;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
  }
678
 
679
  /**
@@ -683,7 +924,8 @@ class CtfFeed
683
  {
684
  if ( ( ! $this->transient_data || $this->errors['cache_status'] ) && $this->feed_options['cache_time'] > 0 ) {
685
  $this->trimTweetData();
686
- set_transient( $this->transient_name, $this->tweet_set, $this->feed_options['cache_time'] );
 
687
  }
688
  }
689
 
@@ -722,7 +964,10 @@ class CtfFeed
722
  protected function setGetFieldsArray( $end_point, $feed_term )
723
  {
724
  $get_fields = array();
725
- if ( $end_point === 'usertimeline' ) {
 
 
 
726
  if ( ! empty ( $feed_term ) ) {
727
  $get_fields['screen_name'] = $feed_term;
728
  }
@@ -738,50 +983,75 @@ class CtfFeed
738
  return $get_fields;
739
  }
740
 
741
- /**
742
- * attempts to connect and retrieve tweets from the Twitter api
743
- *
744
- * @return mixed|string object containing the response
745
- */
746
- public function apiConnect( $end_point, $feed_term )
747
- {
748
- // Only can be set in the options page
749
- $request_settings = array(
750
- 'consumer_key' => $this->feed_options['consumer_key'],
751
- 'consumer_secret' => $this->feed_options['consumer_secret'],
752
- 'access_token' => $this->feed_options['access_token'],
753
- 'access_token_secret' => $this->feed_options['access_token_secret'],
754
- );
755
-
756
- // For pagination, an extra post needs to be retrieved since the last post is
757
- // included in the next set
758
- $count = $this->feed_options['count'];
759
-
760
- $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
761
-
762
- // if the last id is present, that means this is not the first set of tweets
763
- // retrieve only tweets made after the last tweet using it's id
764
- if ( ! empty( $this->last_id_data ) ) {
765
- $count++;
766
- $max_id = $this->last_id_data;
767
- }
768
- $get_fields['count'] = $count;
769
-
770
- // max_id parameter should only be included for the second set of posts
771
- if ( isset( $max_id ) ) {
772
- $get_fields['max_id'] = $max_id;
773
- }
774
-
775
- include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
776
-
777
- // actual connection
778
- $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
779
- $twitter_connect->setUrlBase();
780
- $twitter_connect->setGetFields( $get_fields );
781
- $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
782
-
783
- return $twitter_connect->performRequest();
784
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
785
 
786
  /**
787
  * If the feed runs out of tweets to display for some reason,
@@ -989,6 +1259,7 @@ class CtfFeed
989
  'screen_name' => $post['user']['screen_name']
990
  );
991
  $retweet_data_att = ( $this->check_for_duplicates ) ? ' data-ctfretweetid="'.$post['retweeted_status']['id_str'].'"' : '';
 
992
  $post = $post['retweeted_status'];
993
 
994
  // temporary workaround for cached http images
@@ -1002,10 +1273,55 @@ class CtfFeed
1002
  if ( isset( $post['quoted_status'] ) ) {
1003
  $tweet_classes .= ' ctf-quoted';
1004
  $quoted = $post['quoted_status'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1005
  } else {
1006
  unset( $quoted );
 
1007
  }
1008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1009
  // include tweet view
1010
  $tweet_html .= '<div class="'. $tweet_classes . '" id="' . $post_id . '" style="' . $feed_options['tweetbgcolor'] . '"' . $retweet_data_att . '>';
1011
 
@@ -1046,10 +1362,19 @@ class CtfFeed
1046
 
1047
  if ( $feed_options['linktexttotwitter'] ) {
1048
  $tweet_html .= '<a href="https://twitter.com/statuses/' . $post['id_str'] . '" target="_blank">';
1049
- $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . $post['text'] . '</p>';
 
1050
  $tweet_html .= '</a>';
1051
  } else {
1052
- $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . $post['text'] . '</p>';
 
 
 
 
 
 
 
 
1053
  } // link text to twitter option is selected
1054
 
1055
  $tweet_html .= '</div>';
@@ -1064,7 +1389,8 @@ class CtfFeed
1064
  } // user is verified
1065
 
1066
  $tweet_html .= '<span class="ctf-quoted-author-screenname">@' . $quoted['user']['screen_name'] . '</span>';
1067
- $tweet_html .= '<p class="ctf-quoted-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . $quoted['text'] . '</p>';
 
1068
  $tweet_html .= '</a>';
1069
  }// show link box
1070
 
61
 
62
  private $check_for_duplicates = false;
63
 
 
 
64
  /**
65
  * @var array
66
  */
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
 
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
  }
197
  $this->setStandardStyleProperty( $bg_color, 'background-color' );
198
 
199
  $bool_true = array(
200
+ 'persistentcache',
201
+ 'showbutton',
202
  'showbio',
203
  'showheader'
204
  );
499
  }
500
  }
501
 
502
+ public function setCacheTypeOption() {
503
+ if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
504
+ $this->feed_options['persistentcache'] = true;
505
+ } else {
506
+ $this->feed_options['persistentcache'] = false;
507
+ }
508
+ }
509
+
510
  /**
511
  * checks the data available in the cache to make sure it seems to be valid
512
  *
529
  */
530
  public function maybeSetTweetsFromCache()
531
  {
532
+ if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
533
+ $persistent_cache_tweets = $this->persistentCacheTweets();
534
+ if ( is_array( $persistent_cache_tweets ) ) {
535
+ $this->transient_data = array_slice( $persistent_cache_tweets, ( $this->persistent_index - $this->feed_options['num'] - 1 ) , $this->persistent_index );
536
+ } else {
537
+ $this->transient_data = $persistent_cache_tweets;
538
+ }
539
+ } else {
540
+ $this->transient_data = get_transient( $this->transient_name );
541
+ if ( ! is_array( $this->transient_data ) ) {
542
+ $this->transient_data = json_decode( $this->transient_data, $assoc = true );
543
+ }
544
+
545
+ if ( $this->feed_options['cache_time'] <= 0 ) {
546
+ return $this->tweet_set = false;
547
+ }
548
+ }
549
  // validate the transient data
550
  if ( $this->transient_data ) {
551
  $this->errors['cache_status'] = $this->validateCache();
560
  }
561
  }
562
 
563
+ private function persistentCacheTweets()
564
+ {
565
+ // if cache exists get cached data
566
+ $includewords = ! empty( $this->feed_options['includewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['includewords'] ), 0, 10 ) : '';
567
+ $excludewords = ! empty( $this->feed_options['excludewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['excludewords'] ), 0, 5 ) : '';
568
+ $cache_name = substr( 'ctf_!_' . $this->feed_options['feed_term'] . $includewords . $excludewords, 0, 45 );
569
+ $cache_time_limit_reached = get_transient( $cache_name ) ? false : true;
570
+
571
+ $existing_cache = get_option( $cache_name, false );
572
+ if ( $existing_cache && ! is_array( $existing_cache ) ) {
573
+ $existing_cache = json_decode( $existing_cache, $assoc = true );
574
+ }
575
+
576
+ $this->persistent_index = $this->persistent_index + $this->feed_options['num'];
577
+ $this->feed_options['includeretweets'] = true;
578
+
579
+ $this->feed_options['count'] = 200;
580
+
581
+ if ( ! empty( $this->last_id_data ) || ( ! $cache_time_limit_reached && $existing_cache ) ) {
582
+ return $existing_cache;
583
+ } elseif ( $existing_cache ) {
584
+ // use "since-id" to look for more in an api request
585
+ $since_id = $existing_cache[0]['id_str'];
586
+ $api_obj = $this->getTweetsSinceID( $since_id, 'search', $this->feed_options['feed_term'], $this->feed_options['count'] );
587
+ // add any new tweets to the cache
588
+ $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
589
+
590
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : array();
591
+
592
+ // add a transient to delay another api retrieval
593
+ set_transient( $cache_name, true, $this->feed_options['cache_time'] );
594
+
595
+ if ( empty( $tweets ) ) {
596
+ return $existing_cache;
597
+ } else {
598
+ $this->tweet_set = $this->reduceTweetSetData( $tweets, false );
599
+ }
600
+ $this->tweet_set = $this->appendPersistentCacheTweets( $existing_cache );
601
+ $tweet_set = json_encode( $this->appendPersistentCacheTweets( $this->tweet_set ) );
602
+
603
+ update_option( $cache_name, $tweet_set );
604
+
605
+ return $this->tweet_set;
606
+ // else if cached data doesn't exist
607
+ } else {
608
+ // make a request for last 200 tweets
609
+ $api_obj = $this->apiConnectionResponse( 'search', $this->feed_options['feed_term'] );
610
+ // cache them in a regular option
611
+ $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
612
+ // check for errors/tweets present
613
+ if ( isset( $this->tweet_set['errors'][0] ) ) {
614
+ if ( empty( $this->api_obj ) ) {
615
+ $this->api_obj = new stdClass();
616
+ }
617
+ $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
618
+ $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
619
+
620
+ $this->tweet_set = false;
621
+ }
622
+
623
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
624
+
625
+ if ( empty( $tweets ) ) {
626
+ $this->errors['error_message'] = 'No Tweets returned';
627
+ $this->tweet_set = false;
628
+ } else {
629
+ $this->tweet_set = $this->reduceTweetSetData( $tweets, false );
630
+ }
631
+
632
+ // create a new persistent cache
633
+ if ( $this->tweet_set ) {
634
+ $tweet_set = json_encode( $this->appendPersistentCacheTweets( $this->tweet_set ) );
635
+
636
+ update_option( $cache_name, $tweet_set );
637
+
638
+ // update list of persistent cache
639
+ $cache_list = get_option( 'ctf_cache_list', array() );
640
+
641
+ $cache_list[] = $cache_name;
642
+
643
+ update_option( 'ctf_cache_list', $cache_list );
644
+ }
645
+
646
+ return $this->tweet_set;
647
+ }
648
+
649
+ // add the search parameter to another option that contains a list of all persistent caches available
650
+ }
651
+
652
+ private function reduceTweetSetData( $tweet_set, $limit = true ) {
653
+ if ( $this->check_for_duplicates ) {
654
+ $tweet_set = $this->removeDuplicates( $tweet_set, $limit );
655
+ }
656
+
657
+ $this->tweet_set = $tweet_set;
658
+ $this->trimTweetData( false );
659
+ return $this->tweet_set;
660
+ }
661
+
662
+ private function appendPersistentCacheTweets( $existing_cache )
663
+ {
664
+ if ( is_array( $this->tweet_set ) ) {
665
+ $tweet_set = array_merge( $this->tweet_set, $existing_cache );
666
+ } else {
667
+ $tweet_set = $existing_cache;
668
+ }
669
+
670
+ $tweet_set = array_slice( $tweet_set, 0, 150 );
671
+
672
+ return $tweet_set;
673
+ }
674
+
675
+
676
+ private function removeDuplicates( $tweet_set, $limit = true )
677
+ {
678
+ $tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
679
+ $usable_tweets = 0;
680
+ if ( $limit ) {
681
+ $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
682
+ } else {
683
+ $tweets_needed = 200;
684
+ }
685
+ $ids_of_tweets_to_remove = array();
686
+
687
+ $i = 0; // index of tweet_set
688
+ $still_setting_filtered_tweets = true;
689
+ while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
690
+ if ( isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ) {
691
+ unset( $tweet_set[$i] );
692
+ } elseif ( isset( $tweet_set[$i] ) ) {
693
+ $id = isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ? $tweet_set[$i]['retweeted_status']['id_str'] : $tweet_set[$i]['id_str'];
694
+ if ( in_array( $id, $ids_of_tweets_to_remove ) ) {
695
+ unset( $tweet_set[$i] );
696
+ } else {
697
+ $usable_tweets++;
698
+ $ids_of_tweets_to_remove[] = $id;
699
+ }
700
+ } else {
701
+ $still_setting_filtered_tweets = false;
702
+ }
703
+
704
+ // if there are no more tweets needed
705
+ if ( $usable_tweets >= $tweets_needed ) {
706
+ $still_setting_filtered_tweets = false;
707
+ } else {
708
+ $i++;
709
+ }
710
+
711
+ }
712
+
713
+ if ( is_array( $tweet_set ) ) {
714
+ return array_values( $tweet_set );
715
+ } else {
716
+ return false;
717
+ }
718
+ }
719
 
720
  /**
721
  * will attempt to connect to the api to retrieve current tweets
723
  public function maybeSetTweetsFromTwitter()
724
  {
725
  $this->setTweetsToRetrieve();
726
+ $this->api_obj = $this->apiConnectionResponse( $this->feed_options['type'], $this->feed_options['feed_term'] );
727
  $this->tweet_set = json_decode( $this->api_obj->json , $assoc = true );
728
 
729
  // check for errors/tweets present
741
  }
742
 
743
  if ( $this->check_for_duplicates ) {
744
+ $this->tweet_set = $this->removeDuplicates( $this->tweet_set );
745
  }
746
  }
747
 
760
  /**
761
  * trims the unused data retrieved for more efficient caching
762
  */
763
+ protected function trimTweetData( $limit = true )
764
  {
765
  $is_pagination = !empty( $this->last_id_data ) ? 1 : 0;
766
  $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
767
+ if ( $limit ) {
768
+ $len = min( $this->feed_options['num'] + $is_pagination, count( $tweets ) );
769
+ } else {
770
+ $len = count( $tweets );
771
+ }
772
+ $trimmed_tweets = array();
773
 
774
  // for header
775
  if ( $this->last_id_data == '' ) { // if this is the first set of tweets
785
  $trimmed_tweets[$i]['user']['verified'] = $tweets[$i]['user']['verified'];
786
  $trimmed_tweets[$i]['user']['profile_image_url_https'] = $tweets[$i]['user']['profile_image_url_https'];
787
  $trimmed_tweets[$i]['user']['utc_offset']= $tweets[$i]['user']['utc_offset'];
788
+ $trimmed_tweets[$i]['text'] = isset( $tweets[$i]['text'] ) ? $tweets[$i]['text'] : $tweets[$i]['full_text'];
789
  $trimmed_tweets[$i]['id_str']= $tweets[$i]['id_str'];
790
  $trimmed_tweets[$i]['created_at']= $tweets[$i]['created_at'];
791
  $trimmed_tweets[$i]['retweet_count']= $tweets[$i]['retweet_count'];
797
  $trimmed_tweets[$i]['retweeted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['user']['verified'];
798
  $trimmed_tweets[$i]['retweeted_status']['user']['profile_image_url_https'] = $tweets[$i]['retweeted_status']['user']['profile_image_url_https'];
799
  $trimmed_tweets[$i]['retweeted_status']['user']['utc_offset']= $tweets[$i]['retweeted_status']['user']['utc_offset'];
800
+ $trimmed_tweets[$i]['retweeted_status']['text'] = isset( $tweets[$i]['retweeted_status']['text'] ) ? $tweets[$i]['retweeted_status']['text'] : $tweets[$i]['retweeted_status']['full_text'];
801
  $trimmed_tweets[$i]['retweeted_status']['id_str'] = $tweets[$i]['retweeted_status']['id_str'];
802
  $trimmed_tweets[$i]['retweeted_status']['created_at']= $tweets[$i]['retweeted_status']['created_at'];
803
  $trimmed_tweets[$i]['retweeted_status']['retweet_count']= $tweets[$i]['retweeted_status']['retweet_count'];
808
  $trimmed_tweets[$i]['quoted_status']['user']['name'] = $tweets[$i]['quoted_status']['user']['name'];
809
  $trimmed_tweets[$i]['quoted_status']['user']['screen_name'] = $tweets[$i]['quoted_status']['user']['screen_name'];
810
  $trimmed_tweets[$i]['quoted_status']['user']['verified'] = $tweets[$i]['quoted_status']['user']['verified'];
811
+ $trimmed_tweets[$i]['quoted_status']['text'] = isset( $tweets[$i]['quoted_status']['text'] ) ? $tweets[$i]['quoted_status']['text'] : $tweets[$i]['quoted_status']['full_text'];
812
  $trimmed_tweets[$i]['quoted_status']['id_str'] = $tweets[$i]['quoted_status']['id_str'];
813
  }
814
 
818
  $this->tweet_set = $trimmed_tweets;
819
  }
820
 
821
+ protected function removeStringFromText( $string, $text) {
822
+ return str_replace( $string, '', $text );
823
+ }
824
+
825
  /**
826
  * method to be overridden by pro
827
  *
828
+ * @param $trimmed current trimmed tweet array
829
  * @param $tweet current tweet data to be trimmed
830
  * @return mixed final trimmed tweet
831
  */
832
  protected function filterTrimmedTweets( $trimmed, $tweet )
833
  {
834
+ if ( isset( $tweet['extended_entities']['media'] ) ) {
835
+ // if there is media, we need to remove the media url from the tweet text
836
+ $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
837
+ if ( isset( $tweet['extended_entities']['media'][0]['url'] ) ) {
838
+ $trimmed['text'] = $this->removeStringFromText( $tweet['extended_entities']['media'][0]['url'], $text );
839
+ }
840
+
841
+ $num_media = count( $tweet['extended_entities']['media'] );
842
+ for ( $i = 0; $i < $num_media; $i++ ) {
843
+ $trimmed['extended_entities']['media'][$i]['type'] = $tweet['extended_entities']['media'][$i]['type'];
844
+ }
845
+
846
+ } elseif ( isset( $tweet['entities']['media'] ) ) {
847
+ // if there is media, we need to remove the media url from the tweet text
848
+ $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
849
+ if ( isset( $tweet['entities']['media'][0]['url'] ) ) {
850
+ $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['media'][0]['url'], $text );
851
+ }
852
+
853
+ $num_media = count( $tweet['entities']['media'] );
854
+ for ( $i = 0; $i < $num_media; $i++ ) {
855
+ $trimmed['entities']['media'][$i]['type'] = $tweet['entities']['media'][$i]['type'];
856
+ }
857
+ }
858
+
859
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'] ) ) {
860
+ // if there is media, we need to remove the media url from the tweet text
861
+ $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
862
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'][0]['url'] ) ) {
863
+ $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
864
+ }
865
+
866
+ $num_media = count( $tweet['retweeted_status']['extended_entities']['media'] );
867
+ for ( $i = 0; $i < $num_media; $i++ ) {
868
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['type'];
869
+ }
870
+
871
+ } elseif ( isset( $tweet['retweeted_status']['entities']['media'] ) ) {
872
+ // if there is media, we need to remove the media url from the tweet text
873
+ $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
874
+ if ( isset( $tweet['retweeted_status']['entities']['media'][0]['url'] ) ) {
875
+ $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['entities']['media'][0]['url'], $retweeted_text );
876
+ }
877
+
878
+ $num_media = count( $tweet['retweeted_status']['entities']['media'] );
879
+ for( $i = 0; $i < $num_media; $i++ ) {
880
+ $trimmed['retweeted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['entities']['media'][$i]['type'];
881
+ }
882
+
883
+ } elseif ( isset( $tweet['quoted_status']['extended_entities']['media'] ) ) {
884
+ // if there is media, we need to remove the media url from the tweet text
885
+ $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
886
+ if ( isset( $tweet['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
887
+ $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['extended_entities']['media'][0]['url'], $quoted_text );
888
+ }
889
+
890
+ $num_media = count( $tweet['quoted_status']['extended_entities']['media'] );
891
+ for( $i = 0; $i < $num_media; $i++ ) {
892
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['quoted_status']['extended_entities']['media'][$i]['type'];
893
+ }
894
+
895
+ } elseif ( isset( $tweet['quoted_status']['entities']['media'] ) ) {
896
+ // if there is media, we need to remove the media url from the tweet text
897
+ $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
898
+ if ( isset( $tweet['quoted_status']['entities']['media'][0]['url'] ) ) {
899
+ $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['entities']['media'][0]['url'], $quoted_text );
900
+ }
901
+
902
+ $num_media = count( $tweet['quoted_status']['entities']['media'] );
903
+ for( $i = 0; $i < $num_media; $i++ ) {
904
+ $trimmed['quoted_status']['entities']['media'][$i]['type'] = $tweet['quoted_status']['entities']['media'][$i]['type'];
905
+ }
906
+
907
+ }
908
+
909
+ //remove the url from the text if it links to a quoted tweet that is already linked to
910
+ if ( isset( $tweet['quoted_status'] ) ) {
911
+ $maybe_remove_index = count( $tweet['entities']['urls'] ) - 1;
912
+ if ( isset( $tweet['entities']['urls'][$maybe_remove_index]['url'] ) ) {
913
+ $text = isset( $trimmed['full_text'] ) ? $trimmed['full_text'] : $trimmed['text'];
914
+ $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['urls'][$maybe_remove_index]['url'], $text );
915
+ }
916
+ }
917
+ return $trimmed;
918
  }
919
 
920
  /**
924
  {
925
  if ( ( ! $this->transient_data || $this->errors['cache_status'] ) && $this->feed_options['cache_time'] > 0 ) {
926
  $this->trimTweetData();
927
+ $cache = json_encode( $this->tweet_set );
928
+ set_transient( $this->transient_name, $cache, $this->feed_options['cache_time'] );
929
  }
930
  }
931
 
964
  protected function setGetFieldsArray( $end_point, $feed_term )
965
  {
966
  $get_fields = array();
967
+
968
+ $get_fields['tweet_mode'] = 'extended';
969
+
970
+ if ( $end_point === 'usertimeline' ) {
971
  if ( ! empty ( $feed_term ) ) {
972
  $get_fields['screen_name'] = $feed_term;
973
  }
983
  return $get_fields;
984
  }
985
 
986
+ /**
987
+ * attempts to connect and retrieve tweets from the Twitter api
988
+ *
989
+ * @return mixed|string object containing the response
990
+ */
991
+ public function apiConnectionResponse( $end_point, $feed_term )
992
+ {
993
+ // Only can be set in the options page
994
+ $request_settings = array(
995
+ 'consumer_key' => $this->feed_options['consumer_key'],
996
+ 'consumer_secret' => $this->feed_options['consumer_secret'],
997
+ 'access_token' => $this->feed_options['access_token'],
998
+ 'access_token_secret' => $this->feed_options['access_token_secret'],
999
+ );
1000
+
1001
+ // For pagination, an extra post needs to be retrieved since the last post is
1002
+ // included in the next set
1003
+ $count = $this->feed_options['count'];
1004
+
1005
+ $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1006
+
1007
+ if ( ! empty( $this->last_id_data ) ) {
1008
+ $count++;
1009
+ $max_id = $this->last_id_data;
1010
+ }
1011
+ $get_fields['count'] = $count;
1012
+
1013
+ // max_id parameter should only be included for the second set of posts
1014
+ if ( isset( $max_id ) ) {
1015
+ $get_fields['max_id'] = $max_id;
1016
+ }
1017
+
1018
+ include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1019
+
1020
+ // actual connection
1021
+ $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1022
+ $twitter_connect->setUrlBase();
1023
+ $twitter_connect->setGetFields( $get_fields );
1024
+ $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1025
+
1026
+ return $twitter_connect->performRequest();
1027
+ }
1028
+
1029
+ private function getTweetsSinceID( $since_id, $end_point = 'search', $feed_term, $count )
1030
+ {
1031
+ // Only can be set in the options page
1032
+ $request_settings = array(
1033
+ 'consumer_key' => $this->feed_options['consumer_key'],
1034
+ 'consumer_secret' => $this->feed_options['consumer_secret'],
1035
+ 'access_token' => $this->feed_options['access_token'],
1036
+ 'access_token_secret' => $this->feed_options['access_token_secret'],
1037
+ );
1038
+
1039
+ $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1040
+
1041
+ $get_fields['since_id'] = $since_id;
1042
+
1043
+ $get_fields['count'] = $count;
1044
+
1045
+ include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1046
+
1047
+ // actual connection
1048
+ $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1049
+ $twitter_connect->setUrlBase();
1050
+ $twitter_connect->setGetFields( $get_fields );
1051
+ $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1052
+
1053
+ return $twitter_connect->performRequest();
1054
+ }
1055
 
1056
  /**
1057
  * If the feed runs out of tweets to display for some reason,
1259
  'screen_name' => $post['user']['screen_name']
1260
  );
1261
  $retweet_data_att = ( $this->check_for_duplicates ) ? ' data-ctfretweetid="'.$post['retweeted_status']['id_str'].'"' : '';
1262
+ if ( isset( $post['retweeted_status'] ))
1263
  $post = $post['retweeted_status'];
1264
 
1265
  // temporary workaround for cached http images
1273
  if ( isset( $post['quoted_status'] ) ) {
1274
  $tweet_classes .= ' ctf-quoted';
1275
  $quoted = $post['quoted_status'];
1276
+ $quoted_media_text = '';
1277
+ if ( isset( $quoted['extended_entities']['media'][0] ) || isset( $quoted['entities']['media'][0] ) ) {
1278
+ $quoted_media = isset( $quoted['extended_entities']['media'] ) ? $quoted['extended_entities']['media'] : $quoted['entities']['media'];
1279
+ $quoted_media_count = count( $quoted_media );
1280
+ switch ( $quoted_media[0]['type'] ) {
1281
+ case 'video':
1282
+ case 'animated_gif':
1283
+ $quoted_media_text .= '<i style="padding-right: 5px;" class="fa fa-file-video-o ctf-tweet-text-media"></i>';
1284
+ break;
1285
+ default:
1286
+ if ( $quoted_media_count > 1 ) {
1287
+ $quoted_media_text .= $quoted_media_count . '<i style="padding-right: 5px;" class="fa fa-picture-o ctf-tweet-text-media"></i>';
1288
+ } else {
1289
+ $quoted_media_text .= '<i style="padding-right: 5px;" class="fa fa-picture-o ctf-tweet-text-media"></i>';
1290
+ }
1291
+ break;
1292
+ }
1293
+ } else {
1294
+ unset( $quoted_media );
1295
+ }
1296
  } else {
1297
  unset( $quoted );
1298
+ unset( $quoted_media_text );
1299
  }
1300
 
1301
+ // check for media [0]['type']
1302
+ $post_media_text = '';
1303
+ $post_media_count = 0;
1304
+ if ( isset( $post['extended_entities']['media'][0] ) || isset( $post['entities']['media'][0] ) ) {
1305
+ $post_media = isset( $post['extended_entities']['media'] ) ? $post['extended_entities']['media'] : $post['entities']['media'];
1306
+ $post_media_count = count( $post_media );
1307
+ switch ( $post_media[0]['type'] ) {
1308
+ case 'video':
1309
+ case 'animated_gif':
1310
+ $post_media_text .= '<i style="padding-right: 5px;" class="fa fa-file-video-o ctf-tweet-text-media"></i>';
1311
+ break;
1312
+ default:
1313
+ if ( $post_media_count > 1 ) {
1314
+ $post_media_text .= $post_media_count . '<i style="padding-right: 5px;" class="fa fa-picture-o ctf-tweet-text-media"></i>';
1315
+ } else {
1316
+ $post_media_text .= '<i style="padding-right: 5px;" class="fa fa-picture-o ctf-tweet-text-media"></i>';
1317
+ }
1318
+ break;
1319
+ }
1320
+ } else {
1321
+ unset( $post_media );
1322
+ unset( $post_media_text );
1323
+ }
1324
+
1325
  // include tweet view
1326
  $tweet_html .= '<div class="'. $tweet_classes . '" id="' . $post_id . '" style="' . $feed_options['tweetbgcolor'] . '"' . $retweet_data_att . '>';
1327
 
1362
 
1363
  if ( $feed_options['linktexttotwitter'] ) {
1364
  $tweet_html .= '<a href="https://twitter.com/statuses/' . $post['id_str'] . '" target="_blank">';
1365
+ $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . $post['text'] . $post_media_text .'</p>';
1366
+ //$tweet_html .= $post_media_text;
1367
  $tweet_html .= '</a>';
1368
  } else {
1369
+ $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . $post['text'];
1370
+
1371
+ if( $post_media_count > 0 ){
1372
+ if ( $feed_options['disablelinks'] ) {
1373
+ $tweet_html .= '<span class="ctf-tweet-text-media-wrap">' . $post_media_text . '</span>' . '</p>';
1374
+ } else {
1375
+ $tweet_html .= '</p><a href="https://twitter.com/statuses/' . $post['id_str'] . '" target="_blank" class="ctf-tweet-text-media-wrap">' . $post_media_text . '</a>';
1376
+ }
1377
+ }
1378
  } // link text to twitter option is selected
1379
 
1380
  $tweet_html .= '</div>';
1389
  } // user is verified
1390
 
1391
  $tweet_html .= '<span class="ctf-quoted-author-screenname">@' . $quoted['user']['screen_name'] . '</span>';
1392
+ $tweet_html .= '<p class="ctf-quoted-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . $quoted['text'] . '<span class="ctf-quoted-tweet-text-media-wrap">' . $quoted_media_text .'</span></p>';
1393
+ //$tweet_html .= ;
1394
  $tweet_html .= '</a>';
1395
  }// show link box
1396
 
js/ctf-admin-scripts.js CHANGED
@@ -17,7 +17,7 @@ jQuery(document).ready(function($){
17
  $('#ctf_usertimeline_text').val($ctfRetrievedDefaultScreenName.val());
18
  }
19
 
20
- if(!$ctfHaveOwnTokens.is(':checked')) {
21
  $.ajax({
22
  url: ctf.ajax_url,
23
  type: 'post',
@@ -25,15 +25,12 @@ jQuery(document).ready(function($){
25
  action: 'ctf_auto_save_tokens',
26
  security: ctf.sb_nonce,
27
  access_token: $ctfRetrievedAccessToken.val(),
28
- access_token_secret: $ctfRetrievedAccessTokenSecret.val()
 
29
  },
30
  success: function (data) {
31
- if (!data === false) {
32
- $('#ctf_access_token').after('<span class="ctf-success"><i class="fa fa-check-circle"></i> saved</span>');
33
- $('#ctf_access_token_secret').after('<span class="ctf-success"><i class="fa fa-check-circle"></i> saved</span>');
34
- } else {
35
- $('#ctf_access_token_secret').after('<span>Be sure to save your tokens</span>');
36
- }
37
  }
38
  });
39
  }
@@ -240,6 +237,31 @@ jQuery(document).ready(function($){
240
  }); // ajax call
241
  }); // clear-cache click
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
  //Pro version notices
245
  var ctfUpgradeNote = '<span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/" target="_blank">Available in Pro version</a></span>';
17
  $('#ctf_usertimeline_text').val($ctfRetrievedDefaultScreenName.val());
18
  }
19
 
20
+ if (!$ctfHaveOwnTokens.is(':checked')) {
21
  $.ajax({
22
  url: ctf.ajax_url,
23
  type: 'post',
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"><i class="fa fa-check-circle"></i> saved</span>');
33
+ $('#ctf_access_token_secret').after('<span class="ctf-success"><i class="fa fa-check-circle"></i> saved</span>');
 
 
 
 
34
  }
35
  });
36
  }
237
  }); // ajax call
238
  }); // clear-cache click
239
 
240
+ // clear persistent cache
241
+ var $ctfClearPersistentCacheButton = $('#ctf-admin #ctf-clear-persistent-cache');
242
+
243
+ $ctfClearPersistentCacheButton.click(function(event) {
244
+ event.preventDefault();
245
+
246
+ $('#ctf-clear-cache-success').remove();
247
+ $(this).prop("disabled",true);
248
+
249
+ $.ajax({
250
+ url : ctf.ajax_url,
251
+ type : 'post',
252
+ data : {
253
+ action : 'ctf_clear_persistent_cache'
254
+ },
255
+ success : function(data) {
256
+ $ctfClearPersistentCacheButton.prop('disabled',false);
257
+ if(!data===false) {
258
+ $ctfClearPersistentCacheButton.after('<i id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></i>');
259
+ } else {
260
+ $ctfClearPersistentCacheButton.after('<span>error</span>');
261
+ }
262
+ }
263
+ }); // ajax call
264
+ }); // clear-persistent-cache click
265
 
266
  //Pro version notices
267
  var ctfUpgradeNote = '<span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/" target="_blank">Available in Pro version</a></span>';
js/ctf-scripts.js CHANGED
@@ -10,7 +10,8 @@ if(!ctf_js_exists){
10
  $ctf.find('.ctf-item.ctf-new').each(function(){
11
 
12
  var $ctfItem = $(this),
13
- $ctfText = $ctfItem.find('.ctf-tweet-text'),
 
14
  ctfTextStr = ' ' + $ctfText.html();
15
 
16
  if( $ctf.attr('data-ctfdisablelinks') != 'true' && typeof ctfTextStr !== 'undefined' ){
@@ -55,9 +56,11 @@ if(!ctf_js_exists){
55
 
56
  //Replace text with linked version
57
  $ctfText.html( ctfTextStr.trim() );
 
58
 
59
  //Add link color
60
  $ctfText.find('a').css('color', '#' + ctfLinkColorHex);
 
61
 
62
  } // End "ctfdata-disablelinks" check
63
 
@@ -91,25 +94,12 @@ if(!ctf_js_exists){
91
 
92
  } // end ctfScripts()
93
 
94
- function ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, numNeeded ) {
95
 
96
  //Display loader
97
  $ctfMore.addClass('ctf-loading').append('<div class="ctf-loader"></div>');
98
  $ctfMore.find('.ctf-loader').css('background-color', $ctfMore.css('color'));
99
 
100
- var idsToRemove = [];
101
-
102
- if($ctf.hasClass('ctf-no-duplicates')) {
103
- $ctf.find('.ctf-item').each(function() {
104
- if($(this).attr('data-ctfretweetid')) {
105
- idsToRemove.push($(this).attr('data-ctfretweetid'));
106
- }else{
107
- var id = $(this).attr('id');
108
- idsToRemove.push(id.replace('ctf_', ''));
109
- }
110
- });
111
- }
112
-
113
  jQuery.ajax({
114
  url : ctf.ajax_url,
115
  type : 'post',
@@ -118,7 +108,7 @@ if(!ctf_js_exists){
118
  last_id_data : lastIDData,
119
  shortcode_data : shortcodeData,
120
  num_needed : numNeeded,
121
- ids_to_remove : idsToRemove
122
  },
123
  success : function(data) {
124
  if(lastIDData !== '') {
@@ -163,9 +153,10 @@ if(!ctf_js_exists){
163
  if(numNeeded > 0) {
164
  var $ctfMore = $ctf.find('.ctf-more'),
165
  lastIDData = $ctf.find('.ctf-item').last().attr('id'),
 
166
  shortcodeData = $ctf.attr('data-ctfshortcode');
167
 
168
- ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, numNeeded );
169
  }
170
  },500);
171
 
@@ -174,9 +165,10 @@ if(!ctf_js_exists){
174
  // read the json that is in the ctf-shortcode-data that contains all of the shortcode arguments
175
  var $ctfMore = $(this),
176
  lastIDData = $ctf.find('.ctf-item').last().attr('id'),
 
177
  shortcodeData = $ctf.attr('data-ctfshortcode');
178
 
179
- ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, 0 );
180
 
181
  });
182
 
10
  $ctf.find('.ctf-item.ctf-new').each(function(){
11
 
12
  var $ctfItem = $(this),
13
+ $ctfTextMedia = $ctfItem.find('.ctf-tweet-text-media-wrap'),
14
+ $ctfText = $ctfItem.find('.ctf-tweet-text').remove('.ctf-tweet-text-media-wrap'),
15
  ctfTextStr = ' ' + $ctfText.html();
16
 
17
  if( $ctf.attr('data-ctfdisablelinks') != 'true' && typeof ctfTextStr !== 'undefined' ){
56
 
57
  //Replace text with linked version
58
  $ctfText.html( ctfTextStr.trim() );
59
+ $ctfText.append($ctfTextMedia);
60
 
61
  //Add link color
62
  $ctfText.find('a').css('color', '#' + ctfLinkColorHex);
63
+ $ctfTextMedia.css('color', '#' + ctfLinkColorHex);
64
 
65
  } // End "ctfdata-disablelinks" check
66
 
94
 
95
  } // end ctfScripts()
96
 
97
+ function ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, numNeeded, persistentIndex ) {
98
 
99
  //Display loader
100
  $ctfMore.addClass('ctf-loading').append('<div class="ctf-loader"></div>');
101
  $ctfMore.find('.ctf-loader').css('background-color', $ctfMore.css('color'));
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  jQuery.ajax({
104
  url : ctf.ajax_url,
105
  type : 'post',
108
  last_id_data : lastIDData,
109
  shortcode_data : shortcodeData,
110
  num_needed : numNeeded,
111
+ persistent_index: persistentIndex
112
  },
113
  success : function(data) {
114
  if(lastIDData !== '') {
153
  if(numNeeded > 0) {
154
  var $ctfMore = $ctf.find('.ctf-more'),
155
  lastIDData = $ctf.find('.ctf-item').last().attr('id'),
156
+ persistentIndex = $ctf.find('.ctf-item').length,
157
  shortcodeData = $ctf.attr('data-ctfshortcode');
158
 
159
+ ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, numNeeded, persistentIndex );
160
  }
161
  },500);
162
 
165
  // read the json that is in the ctf-shortcode-data that contains all of the shortcode arguments
166
  var $ctfMore = $(this),
167
  lastIDData = $ctf.find('.ctf-item').last().attr('id'),
168
+ persistentIndex = $ctf.find('.ctf-item').length,
169
  shortcodeData = $ctf.attr('data-ctfshortcode');
170
 
171
+ ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, 0, persistentIndex );
172
 
173
  });
174
 
uninstall.php CHANGED
@@ -32,6 +32,16 @@ if ( ! $ctf_preserve_settings ) {
32
  WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
33
  " );
34
 
 
 
 
 
 
 
 
 
 
 
35
  // remove any scheduled cron jobs
36
  wp_clear_scheduled_hook( 'ctf_cron_job' );
37
  }
32
  WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
33
  " );
34
 
35
+ //Delete all persistent caches (start with ctf_!)
36
+ global $wpdb;
37
+ $table_name = $wpdb->prefix . "options";
38
+ $result = $wpdb->query("
39
+ DELETE
40
+ FROM $table_name
41
+ WHERE `option_name` LIKE ('%ctf\_\!%')
42
+ ");
43
+ delete_option( 'ctf_cache_list' );
44
+
45
  // remove any scheduled cron jobs
46
  wp_clear_scheduled_hook( 'ctf_cron_job' );
47
  }