|
1 <?php |
|
2 |
|
3 class AKTT { |
|
4 // settings: aktt_v3_settings |
|
5 static $ver = '3.0.1'; |
|
6 static $enabled = false; |
|
7 static $prefix = 'aktt_'; |
|
8 static $post_type = 'aktt_tweet'; |
|
9 static $text_domain = 'twitter-tools'; |
|
10 static $menu_page_slug = 'twitter-tools'; |
|
11 static $plugin_settings_section_slug = 'aktt_plugin_settings_group'; |
|
12 static $account_settings_section_slug = 'aktt_account_settings'; |
|
13 static $cap_options = 'manage_options'; |
|
14 static $cap_download = 'publish_posts'; |
|
15 static $admin_notices = array(); |
|
16 static $settings = array(); |
|
17 static $accounts = array(); |
|
18 static $debug = false; |
|
19 |
|
20 /** |
|
21 * Sets whether or not the plugin should be enabled. Also initialize the plugin's settings. |
|
22 * |
|
23 * @return void |
|
24 */ |
|
25 static function after_setup_theme() { |
|
26 self::add_thumbnail_support(); |
|
27 } |
|
28 |
|
29 static function add_thumbnail_support() { |
|
30 $thumbnails = get_theme_support('post-thumbnails'); |
|
31 if (is_array($thumbnails)) { |
|
32 add_theme_support('post-thumbnails', array_merge($thumbnails[0], array(self::$post_type))); |
|
33 } |
|
34 else if (!$thumbnails) { |
|
35 add_theme_support('post-thumbnails', array(self::$post_type)); |
|
36 } |
|
37 // else already enabled for all post types |
|
38 } |
|
39 |
|
40 /** |
|
41 * Sets whether or not the plugin should be enabled. Also initialize the plugin's settings. |
|
42 * |
|
43 * @return void |
|
44 */ |
|
45 static function init() { |
|
46 add_action('admin_notices', array('AKTT', 'admin_notices')); |
|
47 |
|
48 self::$enabled = class_exists('Social'); |
|
49 if (!self::$enabled) { |
|
50 self::add_admin_notice(sprintf(__('Twitter Tools relies on the <a href="%s">Social plugin</a>, please install this plugin.', 'twitter-tools'), 'http://wordpress.org/extend/plugins/social/'), 'error'); |
|
51 return; |
|
52 } |
|
53 |
|
54 /* Set our default settings. We need to do this at init() so |
|
55 that any text domains (i18n) are registered prior to us setting |
|
56 the labels. */ |
|
57 self::set_default_settings(); |
|
58 |
|
59 self::register_post_type(); |
|
60 self::register_taxonomies(); |
|
61 |
|
62 add_shortcode('aktt_tweets', 'aktt_shortcode_tweets'); |
|
63 add_shortcode('aktt_tweet', 'aktt_shortcode_tweet'); |
|
64 |
|
65 // General Hooks |
|
66 add_action('wp', array('AKTT', 'controller'), 1); |
|
67 add_filter('the_post', array('AKTT', 'the_post')); |
|
68 add_filter('the_posts', array('AKTT', 'the_posts')); |
|
69 add_action('social_account_disconnected', array('AKTT', 'social_account_disconnected'), 10, 2); |
|
70 add_action('social_broadcast_response', array('AKTT', 'social_broadcast_response'), 10, 3); |
|
71 |
|
72 // Admin Hooks |
|
73 add_action('admin_init', array('AKTT', 'init_settings'), 0); |
|
74 add_action('admin_init', array('AKTT', 'admin_controller'), 1); |
|
75 add_action('admin_menu', array('AKTT', 'admin_menu')); |
|
76 add_filter('plugin_action_links', array('AKTT', 'plugin_action_links'), 10, 2); |
|
77 add_action('admin_enqueue_scripts', array('AKTT', 'admin_enqueue_scripts')); |
|
78 |
|
79 // Cron Hooks |
|
80 add_action('social_cron_15', array('AKTT', 'import_tweets')); |
|
81 add_action('aktt_backfill_tweets', array('AKTT', 'backfill_tweets')); |
|
82 |
|
83 // Set logging to admin screen settings |
|
84 self::$debug = self::option('debug'); |
|
85 } |
|
86 |
|
87 |
|
88 /** |
|
89 * Sets the default settings for the plugin |
|
90 * |
|
91 * @return void |
|
92 */ |
|
93 static function set_default_settings() { |
|
94 // Set default settings |
|
95 $yn_options = array( |
|
96 '1' => __('Yes', 'twitter-tools'), |
|
97 '0' => __('No', 'twitter-tools') |
|
98 ); |
|
99 $settings = array( |
|
100 'tweet_admin_ui' => array( |
|
101 'name' => 'tweet_admin_ui', |
|
102 'value' => 1, |
|
103 'label' => __('Show admin screens for tweets', 'twitter-tools'), |
|
104 'type' => 'radio', |
|
105 'options' => $yn_options, |
|
106 ), |
|
107 'tweet_visibility' => array( |
|
108 'name' => 'tweet_visibility', |
|
109 'value' => 1, |
|
110 'label' => __('Create URLs for tweets', 'twitter-tools'), |
|
111 'type' => 'radio', |
|
112 'options' => array( |
|
113 '1' => sprintf(__('Yes <span class="help">(%s)</span>', 'twitter-tools'), home_url('tweet/{tweet-id}')), |
|
114 '0' => __('No', 'twitter-tools') |
|
115 ), |
|
116 ), |
|
117 'credit' => array( |
|
118 'name' => 'credit', |
|
119 'value' => 1, |
|
120 'label' => __('Give Twitter Tools credit', 'twitter-tools'), |
|
121 'type' => 'radio', |
|
122 'options' => $yn_options, |
|
123 ), |
|
124 'debug' => array( |
|
125 'name' => 'debug', |
|
126 'value' => 0, |
|
127 'label' => __('Debug logging', 'twitter-tools'), |
|
128 'type' => 'radio', |
|
129 'options' => array( |
|
130 '0' => __('Disabled', 'twitter-tools'), |
|
131 '1' => __('Enabled <span class="help">(written to the PHP error log)</span>', 'twitter-tools'), |
|
132 ), |
|
133 ), |
|
134 ); |
|
135 self::$settings = apply_filters('aktt_default_settings', $settings); |
|
136 } |
|
137 |
|
138 |
|
139 /** |
|
140 * Append a message of a certain type to the admin notices. |
|
141 * |
|
142 * @param string $msg |
|
143 * @param string $type |
|
144 * @return void |
|
145 */ |
|
146 static function add_admin_notice($msg, $type = 'updated') { |
|
147 self::$admin_notices[] = array( |
|
148 'type' => $type == 'error' ? $type : 'updated', // If it's not an error, set it to updated |
|
149 'msg' => $msg |
|
150 ); |
|
151 } |
|
152 |
|
153 |
|
154 /** |
|
155 * Displays admin notices |
|
156 * |
|
157 * @return void |
|
158 */ |
|
159 static function admin_notices() { |
|
160 if (is_array(self::$admin_notices)) { |
|
161 foreach (self::$admin_notices as $notice) { |
|
162 extract($notice); |
|
163 ?> |
|
164 <div class="<?php echo esc_attr($type); ?>"> |
|
165 <p><?php echo $msg; ?></p> |
|
166 </div><!-- /<?php echo esc_html($type); ?> --> |
|
167 <?php |
|
168 } |
|
169 } |
|
170 } |
|
171 |
|
172 |
|
173 /** |
|
174 * Registers the aktt_tweet post type |
|
175 * |
|
176 * @return void |
|
177 */ |
|
178 static function register_post_type() { |
|
179 register_post_type(self::$post_type, array( |
|
180 'labels' => array( |
|
181 'name' => __('Tweets', 'twitter-tools'), |
|
182 'singular_name' => __('Tweet', 'twitter-tools') |
|
183 ), |
|
184 'supports' => array( |
|
185 'editor', |
|
186 'thumbnail', |
|
187 ), |
|
188 'public' => (bool) self::option('tweet_visibility'), |
|
189 'show_ui' => (bool) self::option('tweet_admin_ui'), |
|
190 'rewrite' => array( |
|
191 'slug' => 'tweets', |
|
192 'with_front' => false |
|
193 ), |
|
194 'has_archive' => true, |
|
195 )); |
|
196 } |
|
197 |
|
198 |
|
199 /** |
|
200 * Register our taxonomies. |
|
201 * |
|
202 * @return void |
|
203 */ |
|
204 static function register_taxonomies() { |
|
205 $defaults = array( |
|
206 'public' => (bool) self::option('tweet_visibility'), |
|
207 'show_ui' => (bool) self::option('tweet_admin_ui'), |
|
208 ); |
|
209 $taxonomies = array( |
|
210 'aktt_accounts' => array_merge($defaults, array( |
|
211 'labels' => array( |
|
212 'name' => __('Accounts', 'twitter-tools'), |
|
213 'singular_name' => __('Account', 'twitter-tools') |
|
214 ), |
|
215 'rewrite' => array( |
|
216 'slug' => 'tweet-accounts', |
|
217 'with_front' => false |
|
218 ), |
|
219 )), |
|
220 'aktt_mentions' => array_merge($defaults, array( |
|
221 'labels' => array( |
|
222 'name' => __('Mentions', 'twitter-tools'), |
|
223 'singular_name' => __('Mention', 'twitter-tools') |
|
224 ), |
|
225 'rewrite' => array( |
|
226 'slug' => 'tweet-mentions', |
|
227 'with_front' => false |
|
228 ), |
|
229 )), |
|
230 'aktt_hashtags' => array_merge($defaults, array( |
|
231 'labels' => array( |
|
232 'name' => __('Hashtags', 'twitter-tools'), |
|
233 'singular_name' => __('Hashtag', 'twitter-tools') |
|
234 ), |
|
235 'rewrite' => array( |
|
236 'slug' => 'tweet-hashtags', |
|
237 'with_front' => false |
|
238 ), |
|
239 )), |
|
240 'aktt_types' => array_merge($defaults, array( |
|
241 'labels' => array( |
|
242 'name' => __('Types', 'twitter-tools'), |
|
243 'singular_name' => __('Type', 'twitter-tools') |
|
244 ), |
|
245 'rewrite' => array( |
|
246 'slug' => 'tweet-types', |
|
247 'with_front' => false |
|
248 ), |
|
249 'public' => false, |
|
250 'show_ui' => false, |
|
251 )), |
|
252 ); |
|
253 foreach ($taxonomies as $tax => $args) { |
|
254 register_taxonomy($tax, self::$post_type, $args); |
|
255 } |
|
256 } |
|
257 |
|
258 |
|
259 /** |
|
260 * Get or update an option from the DB, and fall back to the default setting |
|
261 * |
|
262 * @param string $setting |
|
263 * @return mixed |
|
264 */ |
|
265 static function option($key, $value = null) { |
|
266 // Do we have an option? |
|
267 $option = get_option('aktt_v3_settings'); |
|
268 if (!is_null($value)) { |
|
269 $option[$key] = $value; |
|
270 return update_option('aktt_v3_settings', $option); |
|
271 } |
|
272 if (!empty($option) && is_array($option) && isset($option[$key])) { |
|
273 $val = $option[$key]; |
|
274 } |
|
275 else { // Get a default |
|
276 $val = isset(self::$settings[$key]) ? self::$settings[$key]['value'] : null; |
|
277 } |
|
278 return apply_filters('aktt_get_option', $val, $key); |
|
279 } |
|
280 |
|
281 |
|
282 /** |
|
283 * Utility function to get tweets (used by shortcode, widget, etc.) |
|
284 * |
|
285 * @param array $args |
|
286 * @return array |
|
287 */ |
|
288 static function get_tweets($args) { |
|
289 $defaults = array( |
|
290 'account' => array(), |
|
291 'id' => null, |
|
292 'count' => 5, |
|
293 'offset' => 0, |
|
294 'mentions' => array(), |
|
295 'hashtags' => array(), |
|
296 'include_rts' => 0, |
|
297 'include_replies' => 0, |
|
298 ); |
|
299 $taxonomies = array( |
|
300 'aktt_accounts' => array( |
|
301 'var' => 'account', |
|
302 'strip' => array() |
|
303 ), |
|
304 'aktt_hashtags' => array( |
|
305 'var' => 'hashtags', |
|
306 'strip' => array('#') |
|
307 ), |
|
308 'aktt_mentions' => array( |
|
309 'var' => 'mentions', |
|
310 'strip' => array('@') |
|
311 ) |
|
312 ); |
|
313 foreach ($taxonomies as $data) { |
|
314 $tax = $data['var']; |
|
315 $strip = $data['strip']; |
|
316 if (isset($args[$tax])) { |
|
317 $terms = array(); |
|
318 foreach(explode(',', $args[$tax]) as $term) { |
|
319 $term = trim(str_replace($strip, '', $term)); |
|
320 if (!empty($term)) { |
|
321 $terms[] = $term; |
|
322 } |
|
323 } |
|
324 $args[$tax] = $terms; |
|
325 } |
|
326 } |
|
327 $params = array_merge($defaults, $args); |
|
328 $query_data = array( |
|
329 'post_type' => 'aktt_tweet', |
|
330 'posts_per_page' => $params['count'], |
|
331 'offset' => $params['offset'], |
|
332 ); |
|
333 // set tweet ID |
|
334 if (!empty($params['id'])) { |
|
335 $query_data['meta_query'] = array(array( |
|
336 'key' => '_aktt_tweet_id', |
|
337 'value' => $params['id'], |
|
338 'compare' => '=' |
|
339 )); |
|
340 } |
|
341 else { |
|
342 // process tax data |
|
343 $tax_query = array( |
|
344 'relation' => 'AND' |
|
345 ); |
|
346 // set accounts, mentions, hashtags |
|
347 foreach ($taxonomies as $tax => $data) { |
|
348 $var = $data['var']; |
|
349 if (isset($params[$var]) && count($params[$var])) { |
|
350 $query = array( |
|
351 'taxonomy' => $tax, |
|
352 'field' => 'slug', |
|
353 'terms' => array() |
|
354 ); |
|
355 foreach ($params[$var] as $term) { |
|
356 $query['terms'][] = $term; |
|
357 } |
|
358 $tax_query[] = $query; |
|
359 } |
|
360 } |
|
361 // always hide broadcasts - can be overridden with filter below |
|
362 $type_terms = array( |
|
363 'social-broadcast' |
|
364 ); |
|
365 // other exclusions - this is a NOT IN query |
|
366 if (!$params['include_rts']) { |
|
367 $type_terms[] = 'retweet'; |
|
368 } |
|
369 if (!$params['include_replies']) { |
|
370 $type_terms[] = 'reply'; |
|
371 } |
|
372 $tax_query[] = array( |
|
373 'taxonomy' => 'aktt_types', |
|
374 'field' => 'slug', |
|
375 'terms' => $type_terms, |
|
376 'operator' => 'NOT IN' |
|
377 ); |
|
378 $query_data['tax_query'] = $tax_query; |
|
379 } |
|
380 $query = new WP_Query(apply_filters('aktt_get_tweets', $query_data)); |
|
381 return $query->posts; |
|
382 } |
|
383 |
|
384 /** |
|
385 * Attach tweet data to post and replace entities in the post content |
|
386 * |
|
387 * @param stdClass $post |
|
388 * @return stdClass |
|
389 */ |
|
390 static function the_post($post) { |
|
391 if ($post->post_type == self::$post_type && empty($post->tweet)) { |
|
392 if ($raw_data = get_post_meta($post->ID, '_aktt_tweet_raw_data', true)) { |
|
393 $post->tweet = new AKTT_Tweet(json_decode($raw_data)); |
|
394 $post->post_content = $post->tweet->link_entities(); |
|
395 } |
|
396 if (has_post_thumbnail($post->ID)) { |
|
397 $size = apply_filters('aktt_featured_image_size', 'medium'); |
|
398 $post->post_content .= "\n\n".get_the_post_thumbnail(null, $size); |
|
399 } |
|
400 } |
|
401 return $post; |
|
402 } |
|
403 |
|
404 /** |
|
405 * Attach tweet data to posts |
|
406 * |
|
407 * @param array $posts |
|
408 * @return array |
|
409 */ |
|
410 static function the_posts($posts) { |
|
411 foreach ($posts as &$post) { |
|
412 AKTT::the_post($post); |
|
413 } |
|
414 return $posts; |
|
415 } |
|
416 |
|
417 /** |
|
418 * Prepends a "settings" link for our plugin on the plugins.php page |
|
419 * |
|
420 * @param array $links |
|
421 * @param string $file -- filename of plugin |
|
422 * @return array |
|
423 */ |
|
424 function plugin_action_links($links, $file) { |
|
425 if (basename($file) == basename(AKTT_FILE)) { |
|
426 $settings_link = '<a href="options-general.php?page='.self::$menu_page_slug.'">'.__('Settings', 'twitter-tools').'</a>'; |
|
427 array_unshift($links, $settings_link); |
|
428 } |
|
429 return $links; |
|
430 } |
|
431 |
|
432 |
|
433 /** |
|
434 * Adds a link to the "Settings" menu in WP-Admin. |
|
435 */ |
|
436 public function admin_menu() { |
|
437 add_options_page( |
|
438 __('Twitter Tools Options', 'twitter-tools'), |
|
439 __('Twitter Tools', 'twitter-tools'), |
|
440 self::$cap_options, |
|
441 self::$menu_page_slug, |
|
442 array('AKTT', 'settings_page') |
|
443 ); |
|
444 } |
|
445 |
|
446 static function maybe_create_db_index($col, $key_name = null, $table_name = null) { |
|
447 global $wpdb; |
|
448 if (empty($key_name)) { |
|
449 $key_name = $col; |
|
450 } |
|
451 if (empty($table_name)) { |
|
452 $table_name = $wpdb->posts; |
|
453 } |
|
454 // Add a GUID index if none exists |
|
455 $results = $wpdb->get_results($wpdb->prepare(" |
|
456 SHOW INDEX |
|
457 FROM $table_name |
|
458 WHERE KEY_NAME = '%s' |
|
459 ", $key_name)); |
|
460 if (!count($results)) { |
|
461 $wpdb->query(" |
|
462 ALTER TABLE $table_name |
|
463 ADD INDEX ($col) |
|
464 "); // can's use $wpdb->prepare here |
|
465 } |
|
466 } |
|
467 |
|
468 /** |
|
469 * Initializes the plugin settings in WP admin, using the Settings API |
|
470 * |
|
471 * @return void |
|
472 */ |
|
473 static function init_settings() { |
|
474 |
|
475 // Register our parent setting (it contains an array of all our plugin-wide settings) |
|
476 register_setting( |
|
477 self::$menu_page_slug, // Page it belongs to |
|
478 'aktt_v3_settings', // option name |
|
479 array('AKTT', 'sanitize_plugin_settings') // Sanitize callback |
|
480 ); |
|
481 |
|
482 // Register our account settings |
|
483 register_setting( |
|
484 self::$menu_page_slug, // Page it belongs to |
|
485 'aktt_v3_accounts', // option name |
|
486 array('AKTT', 'sanitize_account_settings') // Sanitize callback |
|
487 ); |
|
488 |
|
489 } |
|
490 |
|
491 |
|
492 /** |
|
493 * Sanitization of values |
|
494 * |
|
495 * @param mixed $value |
|
496 * @return int |
|
497 */ |
|
498 static function sanitize_plugin_settings($value) { |
|
499 self::maybe_create_db_index('guid'); |
|
500 flush_rewrite_rules(false); |
|
501 if (is_array($value)) { |
|
502 foreach ($value as $k => $v) { |
|
503 $value[$k] = self::sanitize_plugin_setting($k, $v); |
|
504 } |
|
505 } |
|
506 return $value; |
|
507 } |
|
508 |
|
509 /** |
|
510 * Sanitizes the ACCOUNT settings from the Twitter Tools' admin page. |
|
511 * |
|
512 * ** Option Storage Format ** |
|
513 * |
|
514 * $option_value = array( |
|
515 * $this->id => array( |
|
516 * 'settings' => array( |
|
517 * 'post_author' => 0, |
|
518 * 'post_cats' => array(), |
|
519 * 'post_tags' => array(), |
|
520 * 'hashtag' => '', |
|
521 * ), |
|
522 * ), |
|
523 * ); |
|
524 * |
|
525 * @param array $value |
|
526 * @return array |
|
527 */ |
|
528 static function sanitize_account_settings($value) { |
|
529 if (is_array($value)) { |
|
530 foreach ($value as $id => &$acct) { |
|
531 // If we don't have a settings array, get rid of it |
|
532 if (!isset($acct['settings'])) { |
|
533 unset($value[$id]); |
|
534 continue; |
|
535 } |
|
536 |
|
537 // Loop over each setting and sanitize |
|
538 foreach (array_keys(AKTT_Account::$settings) as $key) { |
|
539 if (!isset($acct['settings'][$key])) { |
|
540 $acct['settings'][$key] = null; |
|
541 } |
|
542 $acct['settings'][$key] = self::sanitize_account_setting($key, $acct['settings'][$key]); |
|
543 } |
|
544 } |
|
545 } |
|
546 else { |
|
547 $value = null; |
|
548 } |
|
549 return $value; |
|
550 } |
|
551 |
|
552 |
|
553 static function sanitize_plugin_setting($key, $value) { |
|
554 return self::sanitize_setting($key, $value, self::$settings[$key]['type']); |
|
555 } |
|
556 |
|
557 static function sanitize_account_setting($key, $value) { |
|
558 return self::sanitize_setting($key, $value, AKTT_Account::$settings[$key]['type']); |
|
559 } |
|
560 |
|
561 |
|
562 /** |
|
563 * Sanitizes a setting, based on a big switch statement |
|
564 * that has each setting, and how to clean it. |
|
565 * |
|
566 * @param string $key |
|
567 * @param mixed $value |
|
568 * @param string $type - type of setting (int, etc.) |
|
569 * @return mixed - Clean value **If it matched a switch case** |
|
570 */ |
|
571 static function sanitize_setting($key, $value, $type) { |
|
572 switch ($type) { |
|
573 case 'int': |
|
574 $value = is_array($value) ? array_map('intval', $value) : intval($value); |
|
575 break; |
|
576 case 'no_html': |
|
577 $value = is_array($value) ? array_map('wp_filter_nohtml_kses', $value) : wp_filter_nohtml_kses($value); |
|
578 break; |
|
579 case 'tags': |
|
580 $value = trim($value); |
|
581 if (!empty($value)) { |
|
582 $tags_clean = array(); |
|
583 $tags_input = array_map('trim', explode(',', $value)); |
|
584 foreach ($tags_input as $tag) { |
|
585 if (!empty($tag)) { |
|
586 $tags_clean[] = $tag; |
|
587 if (!get_term_by('name', $tag, 'post_tag')) { |
|
588 wp_insert_term($tag, 'post_tag'); |
|
589 } |
|
590 } |
|
591 } |
|
592 unset($tags_input); |
|
593 $value = implode(', ', $tags_clean); |
|
594 } |
|
595 break; |
|
596 case 'is_cat': |
|
597 $term = get_term_by('id', $value, 'category'); |
|
598 $value = (!$term) ? 0 : $term->term_id; |
|
599 break; |
|
600 default: |
|
601 $value = apply_filters('aktt_sanitize_setting', $value, $key, $type); |
|
602 } |
|
603 return $value; |
|
604 } |
|
605 |
|
606 |
|
607 /** |
|
608 * Outputs the plugin's settings form. Utilizes the "settings" API in WP |
|
609 * |
|
610 * @return void |
|
611 */ |
|
612 static function settings_page() { |
|
613 global $wpdb; |
|
614 $wpdb->aktt = $wpdb->prefix.'ak_twitter'; |
|
615 $upgrade_needed = in_array($wpdb->aktt, $wpdb->get_col(" |
|
616 SHOW TABLES |
|
617 ")); |
|
618 if ($upgrade_needed) { |
|
619 $upgrade_col = false; |
|
620 $cols = $wpdb->get_results(" |
|
621 DESCRIBE $wpdb->aktt |
|
622 "); |
|
623 foreach ($cols as $col) { |
|
624 if ($col->Field == 'upgrade_30') { |
|
625 $upgrade_col = true; |
|
626 break; |
|
627 } |
|
628 } |
|
629 if ($upgrade_col) { |
|
630 $upgrade_needed = (bool) $wpdb->get_var(" |
|
631 SELECT COUNT(*) |
|
632 FROM $wpdb->aktt |
|
633 WHERE upgrade_30 = 0 |
|
634 "); |
|
635 } |
|
636 } |
|
637 // check to see if CRON for backfilling data is scheduled |
|
638 if (wp_next_scheduled('aktt_backfill_tweets') === false) { |
|
639 // check to see if it should be |
|
640 $query = new WP_Query(array( |
|
641 'post_type' => AKTT::$post_type, |
|
642 'posts_per_page' => 10, |
|
643 'meta_key' => '_aktt_30_backfill_needed', |
|
644 )); |
|
645 if (count($query->posts)) { |
|
646 // schedule |
|
647 wp_schedule_event(time() + 900, 'hourly', 'aktt_backfill_tweets'); |
|
648 } |
|
649 unset($query); |
|
650 } |
|
651 self::get_social_accounts(); |
|
652 include(AKTT_PATH.'/views/admin.php'); |
|
653 } |
|
654 |
|
655 |
|
656 /** |
|
657 * Returns the nonce'd URL for manually kicking off updates |
|
658 * |
|
659 * @return string |
|
660 */ |
|
661 static function get_manual_update_url() { |
|
662 $url = add_query_arg(array('aktt_action' => 'manual_tweet_download'), admin_url('index.php')); |
|
663 return wp_nonce_url($url, 'manual_tweet_download'); |
|
664 } |
|
665 |
|
666 |
|
667 /** |
|
668 * Loads the social twitter accounts into a static variable |
|
669 * |
|
670 * @return void |
|
671 */ |
|
672 static function get_social_accounts() { |
|
673 $social_twitter = Social::instance()->service('twitter'); |
|
674 |
|
675 // If we don't have a Social_Twitter object, get out |
|
676 if (is_null($social_twitter)) { |
|
677 return; |
|
678 } |
|
679 |
|
680 // If we don't have any Social Twitter accounts, get out |
|
681 $social_accounts = $social_twitter->accounts(); |
|
682 if (empty($social_accounts)) { |
|
683 return; |
|
684 } |
|
685 |
|
686 /* Loop over our social twitter accounts and create AKTT_Account objects |
|
687 that will store the various configuration options for the twitter accounts. */ |
|
688 foreach ($social_accounts as $obj_id => $acct_obj) { |
|
689 // If this account has already been assigned, continue on |
|
690 if (isset(self::$accounts[$obj_id]) || !$acct_obj->universal()) { |
|
691 continue; |
|
692 } |
|
693 |
|
694 /* Call a static method to load the object, so we |
|
695 can ensure it was instantiated properly */ |
|
696 $o = AKTT_Account::load($acct_obj); |
|
697 |
|
698 // Assign the object, only if we were successfully created |
|
699 if (is_a($o, 'AKTT_Account')) { |
|
700 self::$accounts[$obj_id] = $o; |
|
701 } |
|
702 } |
|
703 } |
|
704 |
|
705 /** |
|
706 * Return the first account from the list, at random. |
|
707 * |
|
708 * @return mixed AKTT_Account object|bool |
|
709 */ |
|
710 static function default_account() { |
|
711 self::get_social_accounts(); |
|
712 if (count(self::$accounts)) { |
|
713 foreach (self::$accounts as $account) { |
|
714 if ($account->option('enabled')) { |
|
715 return $account; |
|
716 } |
|
717 } |
|
718 } |
|
719 return false; |
|
720 } |
|
721 |
|
722 /** |
|
723 * Remove an account when it is removed from Social |
|
724 * |
|
725 * @return void |
|
726 */ |
|
727 static function social_account_disconnected($service, $id) { |
|
728 if ($service == 'twitter') { |
|
729 $accounts = get_option('aktt_v3_accounts'); |
|
730 if (is_array($accounts) && count($accounts) && isset($accounts[$id])) { |
|
731 $account = Social::instance()->service('twitter')->account($id); |
|
732 // If the account being removed was only a universal account, it will no longer |
|
733 // be available (false). If it is still around as a personal account (but is not |
|
734 // a universal account), then the !universal() check will handle that. |
|
735 if ($account === false or !$account->universal()) { |
|
736 unset($accounts[$id]); |
|
737 update_option('aktt_v3_accounts', $accounts); |
|
738 } |
|
739 } |
|
740 } |
|
741 } |
|
742 |
|
743 /** |
|
744 * Iterates over all the twitter accounts in social and downloads and imports the tweets. |
|
745 * |
|
746 * @return void |
|
747 */ |
|
748 function import_tweets() { |
|
749 // load our accounts |
|
750 self::get_social_accounts(); |
|
751 |
|
752 // See if we have any accounts to loop over |
|
753 if (!is_array(self::$accounts) || empty(self::$accounts)) { |
|
754 return; |
|
755 } |
|
756 |
|
757 // iterate over each account and download the tweets |
|
758 foreach (self::$accounts as $id => $acct) { |
|
759 // Download the tweets for that acct |
|
760 if ($acct->option('enabled')) { |
|
761 // could time out with lots of accounts, so a new request for each |
|
762 $url = home_url('index.php').'?'.http_build_query(array( |
|
763 'aktt_action' => 'download_account_tweets', |
|
764 'acct_id' => $id, |
|
765 'social_api_key' => Social::option('system_cron_api_key') |
|
766 ), null, '&'); |
|
767 self::log('Downloading tweets for '.$acct->social_acct->name().': '.$url); |
|
768 wp_remote_get( |
|
769 $url, |
|
770 array( |
|
771 'timeout' => 0.01, |
|
772 'blocking' => false, |
|
773 'sslverify' => apply_filters('https_local_ssl_verify', true), |
|
774 ) |
|
775 ); |
|
776 } |
|
777 } |
|
778 } |
|
779 |
|
780 |
|
781 /** |
|
782 * Find 10 tweets, backfill the data from Twitter |
|
783 * |
|
784 * @param int $count |
|
785 * @return bool |
|
786 */ |
|
787 function backfill_tweets($count = 10) { |
|
788 self::log('#### Backfilling tweets ####'); |
|
789 $query = new WP_Query(array( |
|
790 'post_type' => AKTT::$post_type, |
|
791 'posts_per_page' => 10, |
|
792 'meta_key' => '_aktt_30_backfill_needed', |
|
793 )); |
|
794 if (!count($query->posts)) { |
|
795 if (($timestamp = wp_next_scheduled('aktt_backfill_tweets')) !== false) { |
|
796 wp_unschedule_event($timestamp, 'aktt_backfill_tweets'); |
|
797 } |
|
798 return false; |
|
799 } |
|
800 foreach ($query->posts as $post) { |
|
801 $tweet_id = get_post_meta($post->ID, '_aktt_tweet_id', true); |
|
802 if (empty($tweet_id)) { |
|
803 continue; |
|
804 } |
|
805 $url = home_url('index.php').'?'.http_build_query(array( |
|
806 'aktt_action' => 'backfill_tweet_data', |
|
807 'tweet_id' => $tweet_id, |
|
808 'social_api_key' => Social::option('system_cron_api_key') |
|
809 ), null, '&'); |
|
810 self::log('Backfilling tweet '.$tweet_id.' '.$url); |
|
811 wp_remote_get( |
|
812 $url, |
|
813 array( |
|
814 'timeout' => 0.01, |
|
815 'blocking' => false, |
|
816 'sslverify' => apply_filters('https_local_ssl_verify', true), |
|
817 ) |
|
818 ); |
|
819 } |
|
820 return true; |
|
821 } |
|
822 |
|
823 /** |
|
824 * Create tweet when Social does a broadcast |
|
825 * |
|
826 * @param Social_Response $response |
|
827 * @param string $key |
|
828 * @param stdClass $post |
|
829 * @return void |
|
830 */ |
|
831 static function social_broadcast_response($response, $key, $post) { |
|
832 // get tweet |
|
833 $data = $response->body(); |
|
834 $tweet = $data->response; |
|
835 // check if it's one of our enabled accounts |
|
836 self::get_social_accounts(); |
|
837 foreach (self::$accounts as $account) { |
|
838 if ($account->option('enabled') && $account->social_acct->id() == $tweet->user->id) { |
|
839 // populate AKTT_Tweet object, save |
|
840 $t = new AKTT_Tweet($tweet); |
|
841 $t->add(); |
|
842 break; |
|
843 } |
|
844 } |
|
845 } |
|
846 |
|
847 /** |
|
848 * Check for auth against Social's api key |
|
849 * |
|
850 * @return book |
|
851 */ |
|
852 static function social_key_auth() { |
|
853 return (bool) (!empty($_GET['social_api_key']) && stripslashes($_GET['social_api_key']) == Social::option('system_cron_api_key')); |
|
854 } |
|
855 |
|
856 |
|
857 /** |
|
858 * Request handler |
|
859 * |
|
860 * @return void |
|
861 */ |
|
862 function controller(){ |
|
863 if (isset($_GET['aktt_action'])) { |
|
864 switch ($_GET['aktt_action']) { |
|
865 case 'download_account_tweets': |
|
866 if (empty($_GET['acct_id']) || !AKTT::social_key_auth()) { |
|
867 wp_die(__('Sorry, try again.', 'twitter-tools')); |
|
868 } |
|
869 $acct_id = intval($_GET['acct_id']); |
|
870 self::get_social_accounts(); |
|
871 if (isset(self::$accounts[$acct_id])) { |
|
872 if ($tweets = self::$accounts[$acct_id]->download_tweets()) { |
|
873 self::$accounts[$acct_id]->save_tweets($tweets); |
|
874 } |
|
875 } |
|
876 die(); |
|
877 break; |
|
878 case 'import_tweet': |
|
879 // check for status_id && auth key |
|
880 if (empty($_GET['tweet_id']) || !AKTT::social_key_auth()) { |
|
881 wp_die(__('Sorry, try again.', 'twitter-tools')); |
|
882 } |
|
883 // check for account_name |
|
884 $username = (!empty($_GET['username']) ? stripslashes($_GET['username']) : null); |
|
885 // download tweet |
|
886 $tweet = self::download_tweet($_GET['tweet_id'], $username); |
|
887 if (!is_a($tweet, 'stdClass')) { |
|
888 wp_die('Failed to download tweet.'); |
|
889 } |
|
890 // store tweet |
|
891 $t = new AKTT_Tweet($tweet); |
|
892 if (!$t->exists_by_guid()) { |
|
893 $t->add(); |
|
894 } |
|
895 die(); |
|
896 break; |
|
897 case 'backfill_tweet_data': |
|
898 if (empty($_GET['tweet_id']) || !AKTT::social_key_auth()) { |
|
899 wp_die(__('Sorry, try again.', 'twitter-tools')); |
|
900 } |
|
901 $t = new AKTT_Tweet(stripslashes($_GET['tweet_id'])); |
|
902 if (!$t->get_post()) { |
|
903 die(); |
|
904 } |
|
905 $usernames = wp_get_object_terms($t->post->ID, 'aktt_accounts'); |
|
906 $username = $usernames[0]->slug; |
|
907 |
|
908 $tweet = self::download_tweet($_GET['tweet_id'], $username); |
|
909 |
|
910 if (!is_a($tweet, 'stdClass')) { |
|
911 wp_die('Failed to download tweet'); |
|
912 } |
|
913 $t->update_twitter_data($tweet); |
|
914 die(); |
|
915 break; |
|
916 } |
|
917 } |
|
918 } |
|
919 |
|
920 |
|
921 /** |
|
922 * Request handler for admin |
|
923 * |
|
924 * @return void |
|
925 */ |
|
926 function admin_controller(){ |
|
927 if (isset($_GET['aktt_action'])) { |
|
928 switch ($_GET['aktt_action']) { |
|
929 case 'manual_tweet_download': |
|
930 // Permission & nonce checking |
|
931 if (!check_admin_referer('manual_tweet_download') || !current_user_can(self::$cap_download)) { |
|
932 wp_die(__('Sorry, try again.', 'twitter-tools')); |
|
933 } |
|
934 |
|
935 self::import_tweets(); |
|
936 echo json_encode(array( |
|
937 'result' => 'success', |
|
938 'msg' => __('Tweets are downloading…', 'twitter-tools') |
|
939 )); |
|
940 die(); |
|
941 break; |
|
942 case 'upgrade-3.0': |
|
943 // Permission checking |
|
944 if (!current_user_can(self::$cap_options)) { |
|
945 wp_die(__('Sorry, try again.', 'twitter-tools')); |
|
946 } |
|
947 include(AKTT_PATH.'/upgrade/3.0.php'); |
|
948 aktt_upgrade_30(); |
|
949 die(); |
|
950 break; |
|
951 case 'upgrade-3.0-run': |
|
952 // Permission checking |
|
953 if (!current_user_can(self::$cap_options) || !wp_verify_nonce($_GET['nonce'], 'upgrade-3.0-run')) { |
|
954 header('Content-type: application/json'); |
|
955 echo json_encode(array( |
|
956 'result' => 'error', |
|
957 'message' => __('Sorry, try again.', 'twitter-tools') |
|
958 )); |
|
959 die(); |
|
960 } |
|
961 include(AKTT_PATH.'/upgrade/3.0.php'); |
|
962 $to_upgrade = aktt_upgrade_30_run(); |
|
963 header('Content-type: application/json'); |
|
964 echo json_encode(array( |
|
965 'result' => 'success', |
|
966 'to_upgrade' => $to_upgrade |
|
967 )); |
|
968 die(); |
|
969 break; |
|
970 case 'tweets_updated': |
|
971 self::add_admin_notice(__('Tweets are downloading...', 'twitter-tools')); |
|
972 break; |
|
973 } |
|
974 } |
|
975 } |
|
976 |
|
977 |
|
978 /** |
|
979 * Load JS resources necessary for admin... only on the twitter tools' settings page |
|
980 * |
|
981 * @param string $hook_suffix |
|
982 * @return void |
|
983 */ |
|
984 function admin_enqueue_scripts($hook_suffix) { |
|
985 add_action('admin_footer', array('AKTT', 'admin_js')); |
|
986 if ($hook_suffix == 'settings_page_twitter-tools') { |
|
987 wp_enqueue_script('suggest'); |
|
988 add_action('admin_footer', array('AKTT', 'admin_js_suggest')); |
|
989 } |
|
990 } |
|
991 |
|
992 |
|
993 /** |
|
994 * Output the admin-side JavaScript |
|
995 * |
|
996 * @return void |
|
997 */ |
|
998 static function admin_js() { |
|
999 ?> |
|
1000 <script type="text/javascript"> |
|
1001 jQuery(function($) { |
|
1002 $('a[href="post-new.php?post_type=aktt_tweet"]').hide().parent('li').hide(); |
|
1003 if (location.href.indexOf('edit-tags.php?taxonomy=aktt_accounts') != -1 || |
|
1004 location.href.indexOf('edit-tags.php?taxonomy=aktt_mentions') != -1 || |
|
1005 location.href.indexOf('edit-tags.php?taxonomy=aktt_hashtags') != -1 || |
|
1006 location.href.indexOf('edit-tags.php?taxonomy=aktt_types') != -1 |
|
1007 ) { |
|
1008 $('#col-left .form-wrap').hide(); |
|
1009 } |
|
1010 }); |
|
1011 </script> |
|
1012 <?php |
|
1013 } |
|
1014 |
|
1015 /** |
|
1016 * Output the admin-side JavaScript for auto-suggest |
|
1017 * |
|
1018 * @return void |
|
1019 */ |
|
1020 static function admin_js_suggest() { |
|
1021 ?> |
|
1022 <script type="text/javascript"> |
|
1023 jQuery(function($) { |
|
1024 $('.type-ahead').each(function() { |
|
1025 var tax = $(this).data('tax'); |
|
1026 $(this).suggest( |
|
1027 ajaxurl + '?action=ajax-tag-search&tax=' + tax, |
|
1028 { |
|
1029 delay: 500, |
|
1030 minchars: 2, |
|
1031 multiple: true |
|
1032 } |
|
1033 ); |
|
1034 }); |
|
1035 }); |
|
1036 </script> |
|
1037 <?php |
|
1038 } |
|
1039 |
|
1040 function log($msg) { |
|
1041 if (self::$debug) { |
|
1042 error_log($msg); |
|
1043 } |
|
1044 } |
|
1045 |
|
1046 static function profile_url($username) { |
|
1047 return 'http://twitter.com/'.urlencode($username); |
|
1048 } |
|
1049 |
|
1050 static function profile_link($username) { |
|
1051 return '<a href="'.esc_url(self::profile_url($username)).'">'.esc_html(self::profile_prefix($username)).'</a>'; |
|
1052 } |
|
1053 |
|
1054 static function profile_prefix($username, $prefix = '@') { |
|
1055 if (AKTT::substr($username, 0, 1) != '#') { |
|
1056 $username = '@'.$username; |
|
1057 } |
|
1058 return $username; |
|
1059 } |
|
1060 |
|
1061 static function hashtag_url($hashtag) { |
|
1062 $hashtag = self::hashtag_prefix($hashtag); |
|
1063 return 'http://twitter.com/search?q='.urlencode($hashtag); |
|
1064 } |
|
1065 |
|
1066 static function hashtag_link($hashtag) { |
|
1067 $hashtag = self::hashtag_prefix($hashtag); |
|
1068 return '<a href="'.esc_url(self::hashtag_url($hashtag)).'">'.esc_html($hashtag).'</a>'; |
|
1069 } |
|
1070 |
|
1071 static function hashtag_prefix($hashtag, $prefix = '#') { |
|
1072 if (AKTT::substr($hashtag, 0, 1) != '#') { |
|
1073 $hashtag = '#'.$hashtag; |
|
1074 } |
|
1075 return $hashtag; |
|
1076 } |
|
1077 |
|
1078 static function status_url($username, $id) { |
|
1079 return 'http://twitter.com/'.urlencode($username).'/status/'.urlencode($id); |
|
1080 } |
|
1081 |
|
1082 static function download_tweet($status_id, $username = null) { |
|
1083 if (empty(AKTT::$accounts)) { |
|
1084 return false; |
|
1085 } |
|
1086 $account_found = $tweet = false; |
|
1087 if (!empty($username)) { |
|
1088 AKTT::get_social_accounts(); |
|
1089 foreach (AKTT::$accounts as $id => $account) { |
|
1090 if ($username == $account->social_acct->name()) { |
|
1091 // proper account stored as $account |
|
1092 $account_found = true; |
|
1093 break; |
|
1094 } |
|
1095 } |
|
1096 if (!$account_found) { |
|
1097 $account = AKTT::$accounts[0]; // use any account |
|
1098 } |
|
1099 $response = Social::instance()->service('twitter')->request( |
|
1100 $account->social_acct, |
|
1101 'statuses/show/'.urlencode($t->id).'.json', |
|
1102 array( |
|
1103 'include_entities' => 1, // include explicit hashtags and mentions |
|
1104 'include_rts' => 1, // include retweets |
|
1105 ) |
|
1106 ); |
|
1107 $content = $response->body(); |
|
1108 if ($content->result == 'success') { |
|
1109 $tweets = $content->response; |
|
1110 if (!$tweets || !is_array($tweets) || count($tweets) != 1) { |
|
1111 $tweet = $tweet[0]; |
|
1112 } |
|
1113 } |
|
1114 } |
|
1115 return $tweet; |
|
1116 } |
|
1117 |
|
1118 static function gmt_to_wp_time($gmt_time) { |
|
1119 $timezone_string = get_option('timezone_string'); |
|
1120 if (!empty($timezone_string)) { |
|
1121 // Not using get_option('gmt_offset') because it gets the offset for the |
|
1122 // current date/time which doesn't work for timezones with daylight savings time. |
|
1123 $gmt_date = date('Y-m-d H:i:s', $gmt_time); |
|
1124 $datetime = new DateTime($gmt_date); |
|
1125 $datetime->setTimezone(new DateTimeZone(get_option('timezone_string'))); |
|
1126 $offset_in_secs = $datetime->getOffset(); |
|
1127 |
|
1128 return $gmt_time + $offset_in_secs; |
|
1129 } |
|
1130 else { |
|
1131 return $gmt_time + (get_option('gmt_offset') * 3600); |
|
1132 } |
|
1133 } |
|
1134 |
|
1135 static function substr_replace($string, $replacement, $start, $length = null, $encoding = null) { |
|
1136 // from http://www.php.net/manual/en/function.substr-replace.php#90146 |
|
1137 // via https://github.com/ruanyf/wp-twitter-tools/commit/56d1a4497483b2b39f434fdfab4797d8574088e5 |
|
1138 if (extension_loaded('mbstring') === true) { |
|
1139 $string_length = (is_null($encoding) === true) ? mb_strlen($string) : mb_strlen($string, $encoding); |
|
1140 |
|
1141 if ($start < 0) { |
|
1142 $start = max(0, $string_length + $start); |
|
1143 } |
|
1144 else if ($start > $string_length) { |
|
1145 $start = $string_length; |
|
1146 } |
|
1147 if ($length < 0) { |
|
1148 $length = max(0, $string_length - $start + $length); |
|
1149 } |
|
1150 else if ((is_null($length) === true) || ($length > $string_length)) { |
|
1151 $length = $string_length; |
|
1152 } |
|
1153 if (($start + $length) > $string_length) { |
|
1154 $length = $string_length - $start; |
|
1155 } |
|
1156 if (is_null($encoding) === true) { |
|
1157 return mb_substr($string, 0, $start) . $replacement |
|
1158 . mb_substr($string, $start + $length, $string_length - $start - $length); |
|
1159 } |
|
1160 return mb_substr($string, 0, $start, $encoding) . $replacement |
|
1161 . mb_substr($string, $start + $length, $string_length - $start - $length, $encoding); |
|
1162 } |
|
1163 else { |
|
1164 return (is_null($length) === true) ? substr_replace($string, $replacement, $start) : substr_replace($string, $replacement, $start, $length); |
|
1165 } |
|
1166 } |
|
1167 |
|
1168 static function strlen($str, $encoding = null) { |
|
1169 if (function_exists('mb_strlen')) { |
|
1170 if (is_null($encoding) === true) { |
|
1171 return mb_strlen($str); |
|
1172 } |
|
1173 else { |
|
1174 return mb_strlen($str, $encoding); |
|
1175 } |
|
1176 } |
|
1177 else { |
|
1178 return strlen($str); |
|
1179 } |
|
1180 } |
|
1181 |
|
1182 static function substr($str, $start, $length) { |
|
1183 if (function_exists('mb_substr')) { |
|
1184 return mb_substr($str, $start, $length); |
|
1185 } |
|
1186 else { |
|
1187 return substr($str, $start, $length); |
|
1188 } |
|
1189 } |
|
1190 |
|
1191 } |
|
1192 |
|
1193 |