1 <?php |
1 <?php |
2 /* |
2 /* |
3 Plugin Name: Twitter Tools |
3 Plugin Name: Twitter Tools |
4 Plugin URI: http://alexking.org/projects/wordpress |
4 Plugin URI: http://crowdfavorite.com/wordpress/plugins/twitter-tools/ |
5 Description: A complete integration between your WordPress blog and <a href="http://twitter.com">Twitter</a>. Bring your tweets into your blog and pass your blog posts to Twitter. Show your tweets in your sidebar, and post tweets from your WordPress admin. |
5 Description: An integration between your WordPress site and Twitter. Create posts from your tweets. Show your tweets in your sidebar. Relies on <a href="http://wordpress.org/extend/plugins/social/">Social</a>. |
6 Version: 2.0 |
6 Version: 3.0.3 |
7 Author: Alex King |
7 Author: Crowd Favorite |
8 Author URI: http://alexking.org |
8 Author URI: http://crowdfavorite.com |
9 */ |
9 */ |
10 |
10 |
11 // Copyright (c) 2007-2009 Crowd Favorite, Ltd., Alex King. All rights reserved. |
11 // Copyright (c) 2007-2012 Crowd Favorite, Ltd. All rights reserved. |
12 // |
12 // |
13 // Released under the GPL license |
13 // Released under the GPL license |
14 // http://www.opensource.org/licenses/gpl-license.php |
14 // http://www.opensource.org/licenses/gpl-license.php |
15 // |
15 // |
16 // This is an add-on for WordPress |
16 // This is an add-on for WordPress |
17 // http://wordpress.org/ |
17 // http://wordpress.org/ |
18 // |
18 // |
19 // Thanks to John Ford ( http://www.aldenta.com ) for his contributions. |
|
20 // Thanks to Dougal Campbell ( http://dougal.gunters.org ) for his contributions. |
|
21 // Thanks to Silas Sewell ( http://silas.sewell.ch ) for his contributions. |
|
22 // Thanks to Greg Grubbs for his contributions. |
|
23 // |
|
24 // ********************************************************************** |
19 // ********************************************************************** |
25 // This program is distributed in the hope that it will be useful, but |
20 // This program is distributed in the hope that it will be useful, but |
26 // WITHOUT ANY WARRANTY; without even the implied warranty of |
21 // WITHOUT ANY WARRANTY; without even the implied warranty of |
27 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
28 // ********************************************************************** |
23 // ********************************************************************** |
29 |
24 |
30 load_plugin_textdomain('twitter-tools'); |
25 $aktt_file = __FILE__; |
|
26 if (isset($plugin)) { |
|
27 $aktt_file = $plugin; |
|
28 } |
|
29 else if (isset($mu_plugin)) { |
|
30 $aktt_file = $mu_plugin; |
|
31 } |
|
32 else if (isset($network_plugin)) { |
|
33 $aktt_file = $network_plugin; |
|
34 } |
|
35 @define('AKTT_FILE', $aktt_file); |
|
36 @define('AKTT_PATH', WP_PLUGIN_DIR.'/'.basename(dirname($aktt_file)).'/'); |
31 |
37 |
32 if (!defined('PLUGINDIR')) { |
38 require_once(AKTT_PATH.'/classes/aktt.php'); |
33 define('PLUGINDIR','wp-content/plugins'); |
39 require_once(AKTT_PATH.'/classes/aktt_account.php'); |
|
40 require_once(AKTT_PATH.'/classes/aktt_tweet.php'); |
|
41 require_once(AKTT_PATH.'/widget.php'); |
|
42 |
|
43 add_action('after_setup_theme', array('AKTT', 'after_setup_theme'), 9999); |
|
44 add_action('init', array('AKTT', 'init'), 0); |
|
45 |
|
46 /* Shortcode syntax |
|
47 * [aktt_tweets |
|
48 * account="alexkingorg" |
|
49 * count="5" |
|
50 * offset="0" |
|
51 * include_rts="0" |
|
52 * include_replies="0" |
|
53 * mentions="crowdfavorite,twittertools" |
|
54 * hashtags="wordpress,plugin,twittertools" |
|
55 * ] |
|
56 */ |
|
57 function aktt_shortcode_tweets($args) { |
|
58 if (!AKTT::$enabled) { |
|
59 return ''; |
|
60 } |
|
61 if ($account = AKTT::default_account()) { |
|
62 $username = $account->social_acct->name(); |
|
63 } |
|
64 else { // no accounts, get out |
|
65 return ''; |
|
66 } |
|
67 $args = shortcode_atts(array( |
|
68 'account' => $username, |
|
69 'include_rts' => 0, |
|
70 'include_replies' => 0, |
|
71 'count' => 5, |
|
72 'mentions' => '', |
|
73 'hashtags' => '', |
|
74 ), $args); |
|
75 $tweets = AKTT::get_tweets($args); |
|
76 ob_start(); |
|
77 include(AKTT_PATH.'/views/tweet-list.php'); |
|
78 return ob_get_clean(); |
34 } |
79 } |
35 |
80 |
36 if (is_file(trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools.php')) { |
81 /* Shortcode syntax |
37 define('AKTT_FILE', trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools.php'); |
82 * [aktt_tweet account="alexkingorg"] |
38 } |
83 * [aktt_tweet id="138741523272577028"] |
39 else if (is_file(trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools/twitter-tools.php')) { |
84 */ |
40 define('AKTT_FILE', trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools/twitter-tools.php'); |
85 function aktt_shortcode_tweet($args) { |
|
86 if (!AKTT::$enabled) { |
|
87 return ''; |
|
88 } |
|
89 if ($account = AKTT::default_account()) { |
|
90 $username = $account->social_acct->name(); |
|
91 } |
|
92 else { // no accounts, get out |
|
93 return ''; |
|
94 } |
|
95 $args = shortcode_atts(array( |
|
96 'account' => $username, |
|
97 'id' => null |
|
98 ), $args); |
|
99 // if we have an ID, only search by that |
|
100 if (!empty($args['id'])) { |
|
101 unset($args['account']); |
|
102 } |
|
103 $args['count'] = 1; |
|
104 $tweets = AKTT::get_tweets($args); |
|
105 if (count($tweets) != 1) { |
|
106 return ''; |
|
107 } |
|
108 $tweet = $tweets[0]; |
|
109 ob_start(); |
|
110 include(AKTT_PATH.'/views/tweet.php'); |
|
111 return ob_get_clean(); |
41 } |
112 } |
42 |
113 |
43 if (!function_exists('wp_prototype_before_jquery')) { |
114 // included for compatibility only |
44 function wp_prototype_before_jquery( $js_array ) { |
115 function aktt_sidebar_tweets($count = 5, $form = null) { |
45 if ( false === $jquery = array_search( 'jquery', $js_array ) ) |
116 _deprecated_function(__FUNCTION__, '3.0', 'aktt_shortcode_tweets()'); |
46 return $js_array; |
117 echo do_shortcode('[aktt_tweets count="'.intval($count).'"]'); |
47 |
|
48 if ( false === $prototype = array_search( 'prototype', $js_array ) ) |
|
49 return $js_array; |
|
50 |
|
51 if ( $prototype < $jquery ) |
|
52 return $js_array; |
|
53 |
|
54 unset($js_array[$prototype]); |
|
55 |
|
56 array_splice( $js_array, $jquery, 0, 'prototype' ); |
|
57 |
|
58 return $js_array; |
|
59 } |
|
60 |
|
61 add_filter( 'print_scripts_array', 'wp_prototype_before_jquery' ); |
|
62 } |
118 } |
63 |
119 |
64 define('AKTT_API_POST_STATUS', 'http://twitter.com/statuses/update.json'); |
120 function aktt_sideload_image($file, $post_id, $desc = null) { |
65 define('AKTT_API_USER_TIMELINE', 'http://twitter.com/statuses/user_timeline.json'); |
121 if (!function_exists('wp_sideload_image') && !function_exists('download_url')) { |
66 define('AKTT_API_STATUS_SHOW', 'http://twitter.com/statuses/show/###ID###.json'); |
122 include(ABSPATH.'wp-admin/includes/file.php'); |
67 define('AKTT_PROFILE_URL', 'http://twitter.com/###USERNAME###'); |
|
68 define('AKTT_STATUS_URL', 'http://twitter.com/###USERNAME###/statuses/###STATUS###'); |
|
69 define('AKTT_HASHTAG_URL', 'http://search.twitter.com/search?q=###HASHTAG###'); |
|
70 |
|
71 function aktt_install() { |
|
72 global $wpdb; |
|
73 |
|
74 $aktt_install = new twitter_tools; |
|
75 $wpdb->aktt = $wpdb->prefix.'ak_twitter'; |
|
76 $charset_collate = ''; |
|
77 if ( version_compare(mysql_get_server_info(), '4.1.0', '>=') ) { |
|
78 if (!empty($wpdb->charset)) { |
|
79 $charset_collate .= " DEFAULT CHARACTER SET $wpdb->charset"; |
|
80 } |
|
81 if (!empty($wpdb->collate)) { |
|
82 $charset_collate .= " COLLATE $wpdb->collate"; |
|
83 } |
|
84 } |
123 } |
85 $result = $wpdb->query(" |
124 // Can be replaced with `wp_sideload_image` once WP 3.5 is released |
86 CREATE TABLE `$wpdb->aktt` ( |
125 if (function_exists('wp_sideload_image')) { |
87 `id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , |
126 return wp_sideload_image($file, $post_id, $desc); |
88 `tw_id` VARCHAR( 255 ) NOT NULL , |
|
89 `tw_text` VARCHAR( 255 ) NOT NULL , |
|
90 `tw_reply_username` VARCHAR( 255 ) DEFAULT NULL , |
|
91 `tw_reply_tweet` VARCHAR( 255 ) DEFAULT NULL , |
|
92 `tw_created_at` DATETIME NOT NULL , |
|
93 `modified` DATETIME NOT NULL , |
|
94 INDEX ( `tw_id` ) |
|
95 ) $charset_collate |
|
96 "); |
|
97 foreach ($aktt_install->options as $option) { |
|
98 add_option('aktt_'.$option, $aktt_install->$option); |
|
99 } |
127 } |
100 add_option('aktt_update_hash', ''); |
128 if ( empty( $file ) ) { |
101 } |
129 return new WP_Error( '', 'File URL cannot be empty.' ); |
102 register_activation_hook(AKTT_FILE, 'aktt_install'); |
|
103 |
|
104 class twitter_tools { |
|
105 function twitter_tools() { |
|
106 $this->options = array( |
|
107 'twitter_username' |
|
108 , 'twitter_password' |
|
109 , 'create_blog_posts' |
|
110 , 'create_digest' |
|
111 , 'create_digest_weekly' |
|
112 , 'digest_daily_time' |
|
113 , 'digest_weekly_time' |
|
114 , 'digest_weekly_day' |
|
115 , 'digest_title' |
|
116 , 'digest_title_weekly' |
|
117 , 'blog_post_author' |
|
118 , 'blog_post_category' |
|
119 , 'blog_post_tags' |
|
120 , 'notify_twitter' |
|
121 , 'sidebar_tweet_count' |
|
122 , 'tweet_from_sidebar' |
|
123 , 'give_tt_credit' |
|
124 , 'exclude_reply_tweets' |
|
125 , 'tweet_prefix' |
|
126 , 'last_tweet_download' |
|
127 , 'doing_tweet_download' |
|
128 , 'doing_digest_post' |
|
129 , 'install_date' |
|
130 , 'js_lib' |
|
131 , 'digest_tweet_order' |
|
132 , 'notify_twitter_default' |
|
133 ); |
|
134 $this->twitter_username = ''; |
|
135 $this->twitter_password = ''; |
|
136 $this->create_blog_posts = '0'; |
|
137 $this->create_digest = '0'; |
|
138 $this->create_digest_weekly = '0'; |
|
139 $this->digest_daily_time = null; |
|
140 $this->digest_weekly_time = null; |
|
141 $this->digest_weekly_day = null; |
|
142 $this->digest_title = __("Twitter Updates for %s", 'twitter-tools'); |
|
143 $this->digest_title_weekly = __("Twitter Weekly Updates for %s", 'twitter-tools'); |
|
144 $this->blog_post_author = '1'; |
|
145 $this->blog_post_category = '1'; |
|
146 $this->blog_post_tags = ''; |
|
147 $this->notify_twitter = '0'; |
|
148 $this->notify_twitter_default = '0'; |
|
149 $this->sidebar_tweet_count = '3'; |
|
150 $this->tweet_from_sidebar = '1'; |
|
151 $this->give_tt_credit = '1'; |
|
152 $this->exclude_reply_tweets = '0'; |
|
153 $this->install_date = ''; |
|
154 $this->js_lib = 'jquery'; |
|
155 $this->digest_tweet_order = 'ASC'; |
|
156 $this->tweet_prefix = 'New blog post'; |
|
157 // not included in options |
|
158 $this->update_hash = ''; |
|
159 $this->tweet_format = $this->tweet_prefix.': %s %s'; |
|
160 $this->last_digest_post = ''; |
|
161 $this->last_tweet_download = ''; |
|
162 $this->doing_tweet_download = '0'; |
|
163 $this->doing_digest_post = '0'; |
|
164 $this->version = '1.6'; |
|
165 } |
|
166 |
|
167 function upgrade() { |
|
168 global $wpdb; |
|
169 $wpdb->aktt = $wpdb->prefix.'ak_twitter'; |
|
170 |
|
171 $col_data = $wpdb->get_results(" |
|
172 SHOW COLUMNS FROM $wpdb->aktt |
|
173 "); |
|
174 $cols = array(); |
|
175 foreach ($col_data as $col) { |
|
176 $cols[] = $col->Field; |
|
177 } |
|
178 // 1.2 schema upgrade |
|
179 if (!in_array('tw_reply_username', $cols)) { |
|
180 $wpdb->query(" |
|
181 ALTER TABLE `$wpdb->aktt` |
|
182 ADD `tw_reply_username` VARCHAR( 255 ) DEFAULT NULL |
|
183 AFTER `tw_text` |
|
184 "); |
|
185 } |
|
186 if (!in_array('tw_reply_tweet', $cols)) { |
|
187 $wpdb->query(" |
|
188 ALTER TABLE `$wpdb->aktt` |
|
189 ADD `tw_reply_tweet` VARCHAR( 255 ) DEFAULT NULL |
|
190 AFTER `tw_reply_username` |
|
191 "); |
|
192 } |
|
193 $this->upgrade_default_tweet_prefix(); |
|
194 } |
|
195 |
|
196 function upgrade_default_tweet_prefix() { |
|
197 $prefix = get_option('aktt_tweet_prefix'); |
|
198 if (empty($prefix)) { |
|
199 $aktt_defaults = new twitter_tools; |
|
200 update_option('aktt_tweet_prefix', $aktt_defaults->tweet_prefix); |
|
201 } |
|
202 } |
130 } |
203 |
131 |
204 function get_settings() { |
132 // Download file to temp location |
205 foreach ($this->options as $option) { |
133 $tmp = download_url( $file ); |
206 $value = get_option('aktt_'.$option); |
|
207 if ($option != 'tweet_prefix' || !empty($value)) { |
|
208 $this->$option = $value; |
|
209 } |
|
210 } |
|
211 $this->tweet_format = $this->tweet_prefix.': %s %s'; |
|
212 } |
|
213 |
|
214 // puts post fields into object propps |
|
215 function populate_settings() { |
|
216 foreach ($this->options as $option) { |
|
217 $value = stripslashes($_POST['aktt_'.$option]); |
|
218 if (isset($_POST['aktt_'.$option]) && ($option != 'tweet_prefix' || !empty($value))) { |
|
219 $this->$option = $value; |
|
220 } |
|
221 } |
|
222 } |
|
223 |
|
224 // puts object props into wp option storage |
|
225 function update_settings() { |
|
226 if (current_user_can('manage_options')) { |
|
227 $this->sidebar_tweet_count = intval($this->sidebar_tweet_count); |
|
228 if ($this->sidebar_tweet_count == 0) { |
|
229 $this->sidebar_tweet_count = '3'; |
|
230 } |
|
231 foreach ($this->options as $option) { |
|
232 update_option('aktt_'.$option, $this->$option); |
|
233 } |
|
234 if (empty($this->install_date)) { |
|
235 update_option('aktt_install_date', current_time('mysql')); |
|
236 } |
|
237 $this->initiate_digests(); |
|
238 $this->upgrade(); |
|
239 $this->upgrade_default_tweet_prefix(); |
|
240 } |
|
241 } |
|
242 |
|
243 // figure out when the next weekly and daily digests will be |
|
244 function initiate_digests() { |
|
245 $next = ($this->create_digest) ? $this->calculate_next_daily_digest() : null; |
|
246 $this->next_daily_digest = $next; |
|
247 update_option('aktt_next_daily_digest', $next); |
|
248 |
|
249 $next = ($this->create_digest_weekly) ? $this->calculate_next_weekly_digest() : null; |
|
250 $this->next_weekly_digest = $next; |
|
251 update_option('aktt_next_weekly_digest', $next); |
|
252 } |
|
253 |
|
254 function calculate_next_daily_digest() { |
|
255 $optionDate = strtotime($this->digest_daily_time); |
|
256 $hour_offset = date("G", $optionDate); |
|
257 $minute_offset = date("i", $optionDate); |
|
258 $next = mktime($hour_offset, $minute_offset, 0); |
|
259 |
|
260 // may have to move to next day |
|
261 $now = time(); |
|
262 while($next < $now) { |
|
263 $next += 60 * 60 * 24; |
|
264 } |
|
265 return $next; |
|
266 } |
|
267 |
|
268 function calculate_next_weekly_digest() { |
|
269 $optionDate = strtotime($this->digest_weekly_time); |
|
270 $hour_offset = date("G", $optionDate); |
|
271 $minute_offset = date("i", $optionDate); |
|
272 |
|
273 $current_day_of_month = date("j"); |
|
274 $current_day_of_week = date("w"); |
|
275 $current_month = date("n"); |
|
276 |
|
277 // if this week's day is less than today, go for next week |
|
278 $nextDay = $current_day_of_month - $current_day_of_week + $this->digest_weekly_day; |
|
279 $next = mktime($hour_offset, $minute_offset, 0, $current_month, $nextDay); |
|
280 if ($this->digest_weekly_day <= $current_day_of_week) { |
|
281 $next = strtotime('+1 week', $next); |
|
282 } |
|
283 return $next; |
|
284 } |
|
285 |
|
286 function ping_digests() { |
|
287 // still busy |
|
288 if (get_option('aktt_doing_digest_post') == '1') { |
|
289 return; |
|
290 } |
|
291 // check all the digest schedules |
|
292 if ($this->create_digest == 1) { |
|
293 $this->ping_digest('aktt_next_daily_digest', 'aktt_last_digest_post', $this->digest_title, 60 * 60 * 24 * 1); |
|
294 } |
|
295 if ($this->create_digest_weekly == 1) { |
|
296 $this->ping_digest('aktt_next_weekly_digest', 'aktt_last_digest_post_weekly', $this->digest_title_weekly, 60 * 60 * 24 * 7); |
|
297 } |
|
298 return; |
|
299 } |
|
300 |
|
301 function ping_digest($nextDateField, $lastDateField, $title, $defaultDuration) { |
|
302 |
134 |
303 $next = get_option($nextDateField); |
135 // Set variables for storage |
304 |
136 // fix file filename for query strings |
305 if ($next) { |
137 preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches ); |
306 $next = $this->validateDate($next); |
138 $file_array['name'] = basename($matches[0]); |
307 $rightNow = time(); |
139 $file_array['tmp_name'] = $tmp; |
308 if ($rightNow >= $next) { |
140 |
309 $start = get_option($lastDateField); |
141 // If error storing temporarily, unlink |
310 $start = $this->validateDate($start, $rightNow - $defaultDuration); |
142 if ( is_wp_error( $tmp ) ) { |
311 if ($this->do_digest_post($start, $next, $title)) { |
143 @unlink($file_array['tmp_name']); |
312 update_option($lastDateField, $rightNow); |
144 $file_array['tmp_name'] = ''; |
313 update_option($nextDateField, $next + $defaultDuration); |
|
314 } else { |
|
315 update_option($lastDateField, null); |
|
316 } |
|
317 } |
|
318 } |
|
319 } |
|
320 |
|
321 function validateDate($in, $default = 0) { |
|
322 if (!is_numeric($in)) { |
|
323 // try to convert what they gave us into a date |
|
324 $out = strtotime($in); |
|
325 // if that doesn't work, return the default |
|
326 if (!is_numeric($out)) { |
|
327 return $default; |
|
328 } |
|
329 return $out; |
|
330 } |
|
331 return $in; |
|
332 } |
145 } |
333 |
146 |
334 function do_digest_post($start, $end, $title) { |
147 if (!function_exists('media_handle_sideload')) { |
335 |
148 include(ABSPATH.'wp-admin/includes/media.php'); |
336 if (!$start || !$end) return false; |
|
337 |
|
338 // flag us as busy |
|
339 update_option('aktt_doing_digest_post', '1'); |
|
340 remove_action('publish_post', 'aktt_notify_twitter', 99); |
|
341 remove_action('publish_post', 'aktt_store_post_options', 1, 2); |
|
342 remove_action('save_post', 'aktt_store_post_options', 1, 2); |
|
343 // see if there's any tweets in the time range |
|
344 global $wpdb; |
|
345 |
|
346 $startGMT = gmdate("Y-m-d H:i:s", $start); |
|
347 $endGMT = gmdate("Y-m-d H:i:s", $end); |
|
348 |
|
349 // build sql |
|
350 $conditions = array(); |
|
351 $conditions[] = "tw_created_at >= '{$startGMT}'"; |
|
352 $conditions[] = "tw_created_at <= '{$endGMT}'"; |
|
353 $conditions[] = "tw_text NOT LIKE '$this->tweet_prefix%'"; |
|
354 if ($this->exclude_reply_tweets) { |
|
355 $conditions[] = "tw_text NOT LIKE '@%'"; |
|
356 } |
|
357 $where = implode(' AND ', $conditions); |
|
358 |
|
359 $sql = " |
|
360 SELECT * FROM {$wpdb->aktt} |
|
361 WHERE {$where} |
|
362 GROUP BY tw_id |
|
363 ORDER BY tw_created_at {$this->digest_tweet_order} |
|
364 "; |
|
365 |
|
366 $tweets = $wpdb->get_results($sql); |
|
367 |
|
368 if (count($tweets) > 0) { |
|
369 |
|
370 $tweets_to_post = array(); |
|
371 foreach ($tweets as $data) { |
|
372 $tweet = new aktt_tweet; |
|
373 $tweet->tw_text = $data->tw_text; |
|
374 $tweet->tw_reply_tweet = $data->tw_reply_tweet; |
|
375 if (!$tweet->tweet_is_post_notification() || ($tweet->tweet_is_reply() && $this->exclude_reply_tweets)) { |
|
376 $tweets_to_post[] = $data; |
|
377 } |
|
378 } |
|
379 |
|
380 $tweets_to_post = apply_filters('aktt_tweets_to_digest_post', $tweets_to_post); // here's your chance to alter the tweet list that will be posted as the digest |
|
381 |
|
382 if (count($tweets_to_post) > 0) { |
|
383 $content = '<ul class="aktt_tweet_digest">'."\n"; |
|
384 foreach ($tweets_to_post as $tweet) { |
|
385 $content .= ' <li>'.aktt_tweet_display($tweet, 'absolute').'</li>'."\n"; |
|
386 } |
|
387 $content .= '</ul>'."\n"; |
|
388 if ($this->give_tt_credit == '1') { |
|
389 $content .= '<p class="aktt_credit">'.__('Powered by <a href="http://alexking.org/projects/wordpress">Twitter Tools</a>', 'twitter-tools').'</p>'; |
|
390 } |
|
391 $post_data = array( |
|
392 'post_content' => $wpdb->escape($content), |
|
393 'post_title' => $wpdb->escape(sprintf($title, date('Y-m-d'))), |
|
394 'post_date' => date('Y-m-d H:i:s', $end), |
|
395 'post_category' => array($this->blog_post_category), |
|
396 'post_status' => 'publish', |
|
397 'post_author' => $wpdb->escape($this->blog_post_author) |
|
398 ); |
|
399 $post_data = apply_filters('aktt_digest_post_data', $post_data); // last chance to alter the digest content |
|
400 |
|
401 $post_id = wp_insert_post($post_data); |
|
402 |
|
403 add_post_meta($post_id, 'aktt_tweeted', '1', true); |
|
404 wp_set_post_tags($post_id, $this->blog_post_tags); |
|
405 } |
|
406 |
|
407 } |
|
408 add_action('publish_post', 'aktt_notify_twitter', 99); |
|
409 add_action('publish_post', 'aktt_store_post_options', 1, 2); |
|
410 add_action('save_post', 'aktt_store_post_options', 1, 2); |
|
411 update_option('aktt_doing_digest_post', '0'); |
|
412 return true; |
|
413 } |
149 } |
414 |
150 if (!function_exists('wp_read_image_metadata')) { |
415 function tweet_download_interval() { |
151 include(ABSPATH.'wp-admin/includes/image.php'); |
416 return 600; |
|
417 } |
152 } |
418 |
153 return media_handle_sideload( $file_array, $post_id, $desc ); |
419 function do_tweet($tweet = '') { |
|
420 if (empty($this->twitter_username) |
|
421 || empty($this->twitter_password) |
|
422 || empty($tweet) |
|
423 || empty($tweet->tw_text) |
|
424 ) { |
|
425 return; |
|
426 } |
|
427 $tweet = apply_filters('aktt_do_tweet', $tweet); // return false here to not tweet |
|
428 if (!$tweet) { |
|
429 return; |
|
430 } |
|
431 require_once(ABSPATH.WPINC.'/class-snoopy.php'); |
|
432 $snoop = new Snoopy; |
|
433 $snoop->agent = 'Twitter Tools http://alexking.org/projects/wordpress'; |
|
434 $snoop->rawheaders = array( |
|
435 'X-Twitter-Client' => 'Twitter Tools' |
|
436 , 'X-Twitter-Client-Version' => $this->version |
|
437 , 'X-Twitter-Client-URL' => 'http://alexking.org/projects/wordpress/twitter-tools.xml' |
|
438 ); |
|
439 $snoop->user = $this->twitter_username; |
|
440 $snoop->pass = $this->twitter_password; |
|
441 $snoop->submit( |
|
442 AKTT_API_POST_STATUS |
|
443 , array( |
|
444 'status' => $tweet->tw_text |
|
445 , 'source' => 'twittertools' |
|
446 ) |
|
447 ); |
|
448 if (strpos($snoop->response_code, '200')) { |
|
449 update_option('aktt_last_tweet_download', strtotime('-28 minutes')); |
|
450 return true; |
|
451 } |
|
452 return false; |
|
453 } |
|
454 |
|
455 function do_blog_post_tweet($post_id = 0) { |
|
456 // this is only called on the publish_post hook |
|
457 if ($this->notify_twitter == '0' |
|
458 || $post_id == 0 |
|
459 || get_post_meta($post_id, 'aktt_tweeted', true) == '1' |
|
460 || get_post_meta($post_id, 'aktt_notify_twitter', true) == 'no' |
|
461 ) { |
|
462 return; |
|
463 } |
|
464 $post = get_post($post_id); |
|
465 // check for an edited post before TT was installed |
|
466 if ($post->post_date <= $this->install_date) { |
|
467 return; |
|
468 } |
|
469 // check for private posts |
|
470 if ($post->post_status == 'private') { |
|
471 return; |
|
472 } |
|
473 $tweet = new aktt_tweet; |
|
474 $url = apply_filters('tweet_blog_post_url', get_permalink($post_id)); |
|
475 $tweet->tw_text = sprintf(__($this->tweet_format, 'twitter-tools'), @html_entity_decode($post->post_title, ENT_COMPAT, 'UTF-8'), $url); |
|
476 $tweet = apply_filters('aktt_do_blog_post_tweet', $tweet, $post); // return false here to not tweet |
|
477 if (!$tweet) { |
|
478 return; |
|
479 } |
|
480 $this->do_tweet($tweet); |
|
481 add_post_meta($post_id, 'aktt_tweeted', '1', true); |
|
482 } |
|
483 |
|
484 function do_tweet_post($tweet) { |
|
485 global $wpdb; |
|
486 remove_action('publish_post', 'aktt_notify_twitter', 99); |
|
487 $data = array( |
|
488 'post_content' => $wpdb->escape(aktt_make_clickable($tweet->tw_text)) |
|
489 , 'post_title' => $wpdb->escape(trim_add_elipsis($tweet->tw_text, 30)) |
|
490 , 'post_date' => get_date_from_gmt(date('Y-m-d H:i:s', $tweet->tw_created_at)) |
|
491 , 'post_category' => array($this->blog_post_category) |
|
492 , 'post_status' => 'publish' |
|
493 , 'post_author' => $wpdb->escape($this->blog_post_author) |
|
494 ); |
|
495 $data = apply_filters('aktt_do_tweet_post', $data, $tweet); // return false here to not make a blog post |
|
496 if (!$data) { |
|
497 return; |
|
498 } |
|
499 $post_id = wp_insert_post($data); |
|
500 add_post_meta($post_id, 'aktt_twitter_id', $tweet->tw_id, true); |
|
501 wp_set_post_tags($post_id, $this->blog_post_tags); |
|
502 add_action('publish_post', 'aktt_notify_twitter', 99); |
|
503 } |
|
504 } |
154 } |
505 |
|
506 class aktt_tweet { |
|
507 function aktt_tweet( |
|
508 $tw_id = '' |
|
509 , $tw_text = '' |
|
510 , $tw_created_at = '' |
|
511 , $tw_reply_username = null |
|
512 , $tw_reply_tweet = null |
|
513 ) { |
|
514 $this->id = ''; |
|
515 $this->modified = ''; |
|
516 $this->tw_created_at = $tw_created_at; |
|
517 $this->tw_text = $tw_text; |
|
518 $this->tw_reply_username = $tw_reply_username; |
|
519 $this->tw_reply_tweet = $tw_reply_tweet; |
|
520 $this->tw_id = $tw_id; |
|
521 } |
|
522 |
|
523 function twdate_to_time($date) { |
|
524 $parts = explode(' ', $date); |
|
525 $date = strtotime($parts[1].' '.$parts[2].', '.$parts[5].' '.$parts[3]); |
|
526 return $date; |
|
527 } |
|
528 |
|
529 function tweet_post_exists() { |
|
530 global $wpdb; |
|
531 $test = $wpdb->get_results(" |
|
532 SELECT * |
|
533 FROM $wpdb->postmeta |
|
534 WHERE meta_key = 'aktt_twitter_id' |
|
535 AND meta_value = '".$wpdb->escape($this->tw_id)."' |
|
536 "); |
|
537 if (count($test) > 0) { |
|
538 return true; |
|
539 } |
|
540 return false; |
|
541 } |
|
542 |
|
543 function tweet_is_post_notification() { |
|
544 global $aktt; |
|
545 if (substr($this->tw_text, 0, strlen($aktt->tweet_prefix)) == $aktt->tweet_prefix) { |
|
546 return true; |
|
547 } |
|
548 return false; |
|
549 } |
|
550 |
|
551 function tweet_is_reply() { |
|
552 // Twitter data changed - users still expect anything starting with @ is a reply |
|
553 // return !empty($this->tw_reply_tweet); |
|
554 return (substr($this->tw_text, 0, 1) == '@'); |
|
555 } |
|
556 |
|
557 function add() { |
|
558 global $wpdb, $aktt; |
|
559 $wpdb->query(" |
|
560 INSERT |
|
561 INTO $wpdb->aktt |
|
562 ( tw_id |
|
563 , tw_text |
|
564 , tw_reply_username |
|
565 , tw_reply_tweet |
|
566 , tw_created_at |
|
567 , modified |
|
568 ) |
|
569 VALUES |
|
570 ( '".$wpdb->escape($this->tw_id)."' |
|
571 , '".$wpdb->escape($this->tw_text)."' |
|
572 , '".$wpdb->escape($this->tw_reply_username)."' |
|
573 , '".$wpdb->escape($this->tw_reply_tweet)."' |
|
574 , '".date('Y-m-d H:i:s', $this->tw_created_at)."' |
|
575 , NOW() |
|
576 ) |
|
577 "); |
|
578 do_action('aktt_add_tweet', $this); |
|
579 if ($aktt->create_blog_posts == '1' && !$this->tweet_post_exists() && !$this->tweet_is_post_notification() && (!$aktt->exclude_reply_tweets || !$this->tweet_is_reply())) { |
|
580 $aktt->do_tweet_post($this); |
|
581 } |
|
582 } |
|
583 } |
|
584 |
|
585 function aktt_api_status_show_url($id) { |
|
586 return str_replace('###ID###', $id, AKTT_API_STATUS_SHOW); |
|
587 } |
|
588 |
|
589 function aktt_profile_url($username) { |
|
590 return str_replace('###USERNAME###', $username, AKTT_PROFILE_URL); |
|
591 } |
|
592 |
|
593 function aktt_profile_link($username, $prefix = '', $suffix = '') { |
|
594 return $prefix.'<a href="'.aktt_profile_url($username).'" class="aktt_username">'.$username.'</a>'.$suffix; |
|
595 } |
|
596 |
|
597 function aktt_hashtag_url($hashtag) { |
|
598 $hashtag = urlencode('#'.$hashtag); |
|
599 return str_replace('###HASHTAG###', $hashtag, AKTT_HASHTAG_URL); |
|
600 } |
|
601 |
|
602 function aktt_hashtag_link($hashtag, $prefix = '', $suffix = '') { |
|
603 return $prefix.'<a href="'.aktt_hashtag_url($hashtag).'" class="aktt_hashtag">'.htmlspecialchars($hashtag).'</a>'.$suffix; |
|
604 } |
|
605 |
|
606 function aktt_status_url($username, $status) { |
|
607 return str_replace( |
|
608 array( |
|
609 '###USERNAME###' |
|
610 , '###STATUS###' |
|
611 ) |
|
612 , array( |
|
613 $username |
|
614 , $status |
|
615 ) |
|
616 , AKTT_STATUS_URL |
|
617 ); |
|
618 } |
|
619 |
|
620 function aktt_login_test($username, $password) { |
|
621 require_once(ABSPATH.WPINC.'/class-snoopy.php'); |
|
622 $snoop = new Snoopy; |
|
623 $snoop->agent = 'Twitter Tools http://alexking.org/projects/wordpress'; |
|
624 $snoop->user = $username; |
|
625 $snoop->pass = $password; |
|
626 $snoop->fetch(AKTT_API_USER_TIMELINE); |
|
627 if (strpos($snoop->response_code, '200')) { |
|
628 return __("Login succeeded, you're good to go.", 'twitter-tools'); |
|
629 } else { |
|
630 $json = new Services_JSON(); |
|
631 $results = $json->decode($snoop->results); |
|
632 return sprintf(__('Sorry, login failed. Error message from Twitter: %s', 'twitter-tools'), $results->error); |
|
633 } |
|
634 } |
|
635 |
|
636 |
|
637 function aktt_ping_digests() { |
|
638 global $aktt; |
|
639 $aktt->ping_digests(); |
|
640 } |
|
641 |
|
642 function aktt_update_tweets() { |
|
643 global $aktt; |
|
644 // let the last update run for 10 minutes |
|
645 if (time() - intval(get_option('aktt_doing_tweet_download')) < $aktt->tweet_download_interval()) { |
|
646 return; |
|
647 } |
|
648 // wait 10 min between downloads |
|
649 if (time() - intval(get_option('aktt_last_tweet_download')) < $aktt->tweet_download_interval()) { |
|
650 return; |
|
651 } |
|
652 update_option('aktt_doing_tweet_download', time()); |
|
653 global $wpdb, $aktt; |
|
654 if (empty($aktt->twitter_username) || empty($aktt->twitter_password)) { |
|
655 update_option('aktt_doing_tweet_download', '0'); |
|
656 return; |
|
657 } |
|
658 require_once(ABSPATH.WPINC.'/class-snoopy.php'); |
|
659 $snoop = new Snoopy; |
|
660 $snoop->agent = 'Twitter Tools http://alexking.org/projects/wordpress'; |
|
661 $snoop->user = $aktt->twitter_username; |
|
662 $snoop->pass = $aktt->twitter_password; |
|
663 $snoop->fetch(AKTT_API_USER_TIMELINE); |
|
664 |
|
665 if (!strpos($snoop->response_code, '200')) { |
|
666 update_option('aktt_doing_tweet_download', '0'); |
|
667 return; |
|
668 } |
|
669 |
|
670 $data = $snoop->results; |
|
671 // hash results to see if they're any different than the last update, if so, return |
|
672 $hash = md5($data); |
|
673 if ($hash == get_option('aktt_update_hash')) { |
|
674 update_option('aktt_last_tweet_download', time()); |
|
675 update_option('aktt_doing_tweet_download', '0'); |
|
676 return; |
|
677 } |
|
678 $json = new Services_JSON(); |
|
679 $tweets = $json->decode($data); |
|
680 |
|
681 if (is_array($tweets) && count($tweets) > 0) { |
|
682 $tweet_ids = array(); |
|
683 foreach ($tweets as $tweet) { |
|
684 $tweet_ids[] = $wpdb->escape($tweet->id); |
|
685 } |
|
686 $existing_ids = $wpdb->get_col(" |
|
687 SELECT tw_id |
|
688 FROM $wpdb->aktt |
|
689 WHERE tw_id |
|
690 IN ('".implode("', '", $tweet_ids)."') |
|
691 "); |
|
692 $new_tweets = array(); |
|
693 foreach ($tweets as $tw_data) { |
|
694 if (!$existing_ids || !in_array($tw_data->id, $existing_ids)) { |
|
695 $tweet = new aktt_tweet( |
|
696 $tw_data->id |
|
697 , $tw_data->text |
|
698 ); |
|
699 $tweet->tw_created_at = $tweet->twdate_to_time($tw_data->created_at); |
|
700 if (!empty($tw_data->in_reply_to_status_id)) { |
|
701 $tweet->tw_reply_tweet = $tw_data->in_reply_to_status_id; |
|
702 $url = aktt_api_status_show_url($tw_data->in_reply_to_status_id); |
|
703 $snoop->fetch($url); |
|
704 if (strpos($snoop->response_code, '200') !== false) { |
|
705 $data = $snoop->results; |
|
706 $status = $json->decode($data); |
|
707 $tweet->tw_reply_username = $status->user->screen_name; |
|
708 } |
|
709 } |
|
710 // make sure we haven't downloaded someone else's tweets - happens sometimes due to Twitter hiccups |
|
711 if (strtolower($tw_data->user->screen_name) == strtolower($aktt->twitter_username)) { |
|
712 $new_tweets[] = $tweet; |
|
713 $tweet->add(); |
|
714 } |
|
715 } |
|
716 } |
|
717 } |
|
718 aktt_reset_tweet_checking($hash, time()); |
|
719 } |
|
720 |
|
721 |
|
722 function aktt_reset_tweet_checking($hash = '', $time = 0) { |
|
723 if (!current_user_can('manage_options')) { |
|
724 return; |
|
725 } |
|
726 update_option('aktt_update_hash', $hash); |
|
727 update_option('aktt_last_tweet_download', $time); |
|
728 update_option('aktt_doing_tweet_download', '0'); |
|
729 } |
|
730 |
|
731 function aktt_reset_digests() { |
|
732 if (!current_user_can('manage_options')) { |
|
733 return; |
|
734 } |
|
735 update_option('aktt_doing_digest_post', '0'); |
|
736 } |
|
737 |
|
738 function aktt_notify_twitter($post_id) { |
|
739 global $aktt; |
|
740 $aktt->do_blog_post_tweet($post_id); |
|
741 } |
|
742 add_action('publish_post', 'aktt_notify_twitter', 99); |
|
743 |
|
744 function aktt_sidebar_tweets() { |
|
745 global $wpdb, $aktt; |
|
746 if ($aktt->exclude_reply_tweets) { |
|
747 $where = "AND tw_text NOT LIKE '@%' "; |
|
748 } |
|
749 else { |
|
750 $where = ''; |
|
751 } |
|
752 $tweets = $wpdb->get_results(" |
|
753 SELECT * |
|
754 FROM $wpdb->aktt |
|
755 WHERE tw_text NOT LIKE '$aktt->tweet_prefix%' |
|
756 $where |
|
757 GROUP BY tw_id |
|
758 ORDER BY tw_created_at DESC |
|
759 LIMIT $aktt->sidebar_tweet_count |
|
760 "); |
|
761 $output = '<div class="aktt_tweets">'."\n" |
|
762 .' <ul>'."\n"; |
|
763 if (count($tweets) > 0) { |
|
764 foreach ($tweets as $tweet) { |
|
765 $output .= ' <li>'.aktt_tweet_display($tweet).'</li>'."\n"; |
|
766 } |
|
767 } |
|
768 else { |
|
769 $output .= ' <li>'.__('No tweets available at the moment.', 'twitter-tools').'</li>'."\n"; |
|
770 } |
|
771 if (!empty($aktt->twitter_username)) { |
|
772 $output .= ' <li class="aktt_more_updates"><a href="'.aktt_profile_url($aktt->twitter_username).'">'.__('More updates...', 'twitter-tools').'</a></li>'."\n"; |
|
773 } |
|
774 $output .= '</ul>'; |
|
775 if ($aktt->tweet_from_sidebar == '1' && !empty($aktt->twitter_username) && !empty($aktt->twitter_password)) { |
|
776 $output .= aktt_tweet_form('input', 'onsubmit="akttPostTweet(); return false;"'); |
|
777 $output .= ' <p id="aktt_tweet_posted_msg">'.__('Posting tweet...', 'twitter-tools').'</p>'; |
|
778 } |
|
779 if ($aktt->give_tt_credit == '1') { |
|
780 $output .= '<p class="aktt_credit">'.__('Powered by <a href="http://alexking.org/projects/wordpress">Twitter Tools</a>', 'twitter-tools').'</p>'; |
|
781 } |
|
782 $output .= '</div>'; |
|
783 print($output); |
|
784 } |
|
785 |
|
786 function aktt_latest_tweet() { |
|
787 global $wpdb, $aktt; |
|
788 $tweets = $wpdb->get_results(" |
|
789 SELECT * |
|
790 FROM $wpdb->aktt |
|
791 WHERE tw_text NOT LIKE '$aktt->tweet_prefix%' |
|
792 GROUP BY tw_id |
|
793 ORDER BY tw_created_at DESC |
|
794 LIMIT 1 |
|
795 "); |
|
796 if (count($tweets) == 1) { |
|
797 foreach ($tweets as $tweet) { |
|
798 $output = aktt_tweet_display($tweet); |
|
799 } |
|
800 } |
|
801 else { |
|
802 $output = __('No tweets available at the moment.', 'twitter-tools'); |
|
803 } |
|
804 print($output); |
|
805 } |
|
806 |
|
807 function aktt_tweet_display($tweet, $time = 'relative') { |
|
808 global $aktt; |
|
809 $output = aktt_make_clickable(wp_specialchars($tweet->tw_text)); |
|
810 if (!empty($tweet->tw_reply_username)) { |
|
811 $output .= ' <a href="'.aktt_status_url($tweet->tw_reply_username, $tweet->tw_reply_tweet).'" class="aktt_tweet_reply">'.sprintf(__('in reply to %s', 'twitter-tools'), $tweet->tw_reply_username).'</a>'; |
|
812 } |
|
813 switch ($time) { |
|
814 case 'relative': |
|
815 $time_display = aktt_relativeTime($tweet->tw_created_at, 3); |
|
816 break; |
|
817 case 'absolute': |
|
818 $time_display = '#'; |
|
819 break; |
|
820 } |
|
821 $output .= ' <a href="'.aktt_status_url($aktt->twitter_username, $tweet->tw_id).'" class="aktt_tweet_time">'.$time_display.'</a>'; |
|
822 $output = apply_filters('aktt_tweet_display', $output, $tweet); // allows you to alter the tweet display output |
|
823 return $output; |
|
824 } |
|
825 |
|
826 function aktt_make_clickable($tweet) { |
|
827 $tweet .= ' '; |
|
828 $tweet = preg_replace_callback( |
|
829 '/@([a-zA-Z0-9_]{1,15})([) ])/' |
|
830 , create_function( |
|
831 '$matches' |
|
832 , 'return aktt_profile_link($matches[1], \'@\', $matches[2]);' |
|
833 ) |
|
834 , $tweet |
|
835 ); |
|
836 $tweet = preg_replace_callback( |
|
837 '/\ #([a-zA-Z0-9_]{1,15})/' |
|
838 , create_function( |
|
839 '$matches' |
|
840 , 'return aktt_hashtag_link($matches[1], \' #\', \'\');' |
|
841 ) |
|
842 , $tweet |
|
843 ); |
|
844 |
|
845 if (function_exists('make_chunky')) { |
|
846 return make_chunky($tweet); |
|
847 } |
|
848 else { |
|
849 return make_clickable($tweet); |
|
850 } |
|
851 } |
|
852 |
|
853 function aktt_tweet_form($type = 'input', $extra = '') { |
|
854 $output = ''; |
|
855 if (current_user_can('publish_posts')) { |
|
856 $output .= ' |
|
857 <form action="'.get_bloginfo('wpurl').'/index.php" method="post" id="aktt_tweet_form" '.$extra.'> |
|
858 <fieldset> |
|
859 '; |
|
860 switch ($type) { |
|
861 case 'input': |
|
862 $output .= ' |
|
863 <p><input type="text" size="20" maxlength="140" id="aktt_tweet_text" name="aktt_tweet_text" onkeyup="akttCharCount();" /></p> |
|
864 <input type="hidden" name="ak_action" value="aktt_post_tweet_sidebar" /> |
|
865 <script type="text/javascript"> |
|
866 //<![CDATA[ |
|
867 function akttCharCount() { |
|
868 var count = document.getElementById("aktt_tweet_text").value.length; |
|
869 if (count > 0) { |
|
870 document.getElementById("aktt_char_count").innerHTML = 140 - count; |
|
871 } |
|
872 else { |
|
873 document.getElementById("aktt_char_count").innerHTML = ""; |
|
874 } |
|
875 } |
|
876 setTimeout("akttCharCount();", 500); |
|
877 document.getElementById("aktt_tweet_form").setAttribute("autocomplete", "off"); |
|
878 //]]> |
|
879 </script> |
|
880 '; |
|
881 break; |
|
882 case 'textarea': |
|
883 $output .= ' |
|
884 <p><textarea type="text" cols="60" rows="5" maxlength="140" id="aktt_tweet_text" name="aktt_tweet_text" onkeyup="akttCharCount();"></textarea></p> |
|
885 <input type="hidden" name="ak_action" value="aktt_post_tweet_admin" /> |
|
886 <script type="text/javascript"> |
|
887 //<![CDATA[ |
|
888 function akttCharCount() { |
|
889 var count = document.getElementById("aktt_tweet_text").value.length; |
|
890 if (count > 0) { |
|
891 document.getElementById("aktt_char_count").innerHTML = (140 - count) + "'.__(' characters remaining', 'twitter-tools').'"; |
|
892 } |
|
893 else { |
|
894 document.getElementById("aktt_char_count").innerHTML = ""; |
|
895 } |
|
896 } |
|
897 setTimeout("akttCharCount();", 500); |
|
898 document.getElementById("aktt_tweet_form").setAttribute("autocomplete", "off"); |
|
899 //]]> |
|
900 </script> |
|
901 '; |
|
902 break; |
|
903 } |
|
904 $output .= ' |
|
905 <p> |
|
906 <input type="submit" id="aktt_tweet_submit" name="aktt_tweet_submit" value="'.__('Post Tweet!', 'twitter-tools').'" class="button-primary" /> |
|
907 <span id="aktt_char_count"></span> |
|
908 </p> |
|
909 <div class="clear"></div> |
|
910 </fieldset> |
|
911 </form> |
|
912 '; |
|
913 } |
|
914 return $output; |
|
915 } |
|
916 |
|
917 function aktt_widget_init() { |
|
918 if (!function_exists('register_sidebar_widget')) { |
|
919 return; |
|
920 } |
|
921 function aktt_widget($args) { |
|
922 extract($args); |
|
923 $options = get_option('aktt_widget'); |
|
924 $title = $options['title']; |
|
925 if (empty($title)) { |
|
926 } |
|
927 echo $before_widget . $before_title . $title . $after_title; |
|
928 aktt_sidebar_tweets(); |
|
929 echo $after_widget; |
|
930 } |
|
931 register_sidebar_widget(array(__('Twitter Tools', 'twitter-tools'), 'widgets'), 'aktt_widget'); |
|
932 |
|
933 function aktt_widget_control() { |
|
934 $options = get_option('aktt_widget'); |
|
935 if (!is_array($options)) { |
|
936 $options = array( |
|
937 'title' => __("What I'm Doing...", 'twitter-tools') |
|
938 ); |
|
939 } |
|
940 if (isset($_POST['ak_action']) && $_POST['ak_action'] == 'aktt_update_widget_options') { |
|
941 $options['title'] = strip_tags(stripslashes($_POST['aktt_widget_title'])); |
|
942 update_option('aktt_widget', $options); |
|
943 // reset checking so that sidebar isn't blank if this is the first time activating |
|
944 aktt_reset_tweet_checking(); |
|
945 aktt_update_tweets(); |
|
946 } |
|
947 |
|
948 // Be sure you format your options to be valid HTML attributes. |
|
949 $title = htmlspecialchars($options['title'], ENT_QUOTES); |
|
950 |
|
951 // Here is our little form segment. Notice that we don't need a |
|
952 // complete form. This will be embedded into the existing form. |
|
953 print(' |
|
954 <p style="text-align:right;"><label for="aktt_widget_title">' . __('Title:') . ' <input style="width: 200px;" id="aktt_widget_title" name="aktt_widget_title" type="text" value="'.$title.'" /></label></p> |
|
955 <p>'.__('Find additional Twitter Tools options on the <a href="options-general.php?page=twitter-tools.php">Twitter Tools Options page</a>.', 'twitter-tools').' |
|
956 <input type="hidden" id="ak_action" name="ak_action" value="aktt_update_widget_options" /> |
|
957 '); |
|
958 } |
|
959 register_widget_control(array(__('Twitter Tools', 'twitter-tools'), 'widgets'), 'aktt_widget_control', 300, 100); |
|
960 |
|
961 } |
|
962 add_action('widgets_init', 'aktt_widget_init'); |
|
963 |
|
964 function aktt_init() { |
|
965 global $wpdb, $aktt; |
|
966 $aktt = new twitter_tools; |
|
967 |
|
968 $wpdb->aktt = $wpdb->prefix.'ak_twitter'; |
|
969 |
|
970 $aktt->get_settings(); |
|
971 if (($aktt->last_tweet_download + $aktt->tweet_download_interval()) < time()) { |
|
972 add_action('shutdown', 'aktt_update_tweets'); |
|
973 add_action('shutdown', 'aktt_ping_digests'); |
|
974 } |
|
975 if (is_admin() || ($aktt->tweet_from_sidebar && current_user_can('publish_posts'))) { |
|
976 switch ($aktt->js_lib) { |
|
977 case 'jquery': |
|
978 wp_enqueue_script('jquery'); |
|
979 break; |
|
980 case 'prototype': |
|
981 wp_enqueue_script('prototype'); |
|
982 break; |
|
983 } |
|
984 } |
|
985 global $wp_version; |
|
986 if (isset($wp_version) && version_compare($wp_version, '2.5', '>=') && empty ($aktt->install_date)) { |
|
987 add_action('admin_notices', create_function( '', "echo '<div class=\"error\"><p>".sprintf(__('Please update your <a href="%s">Twitter Tools settings</a>', 'twitter-tools'), get_bloginfo('wpurl')."/wp-admin/options-general.php?page=twitter-tools.php")."</p></div>';" ) ); |
|
988 } |
|
989 if (!get_option('aktt_tweet_prefix')) { |
|
990 update_option('aktt_tweet_prefix', $aktt->tweet_prefix); |
|
991 add_action('admin_notices', create_function( '', "echo '<div class=\"error\"><p>".sprintf(__('Please update your <a href="%s">Twitter Tools settings</a>', 'twitter-tools'), get_bloginfo('wpurl')."/wp-admin/options-general.php?page=twitter-tools.php")."</p></div>';" ) ); |
|
992 } |
|
993 } |
|
994 add_action('init', 'aktt_init'); |
|
995 |
|
996 function aktt_head() { |
|
997 global $aktt; |
|
998 if ($aktt->tweet_from_sidebar) { |
|
999 print(' |
|
1000 <link rel="stylesheet" type="text/css" href="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_css" /> |
|
1001 <script type="text/javascript" src="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_js"></script> |
|
1002 '); |
|
1003 } |
|
1004 } |
|
1005 add_action('wp_head', 'aktt_head'); |
|
1006 |
|
1007 function aktt_head_admin() { |
|
1008 print(' |
|
1009 <link rel="stylesheet" type="text/css" href="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_css_admin" /> |
|
1010 <script type="text/javascript" src="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_js_admin"></script> |
|
1011 '); |
|
1012 } |
|
1013 add_action('admin_head', 'aktt_head_admin'); |
|
1014 |
|
1015 function aktt_request_handler() { |
|
1016 global $wpdb, $aktt; |
|
1017 if (!empty($_GET['ak_action'])) { |
|
1018 switch($_GET['ak_action']) { |
|
1019 case 'aktt_update_tweets': |
|
1020 aktt_update_tweets(); |
|
1021 wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&tweets-updated=true'); |
|
1022 die(); |
|
1023 break; |
|
1024 case 'aktt_reset_tweet_checking': |
|
1025 aktt_reset_tweet_checking(); |
|
1026 wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&tweet-checking-reset=true'); |
|
1027 die(); |
|
1028 break; |
|
1029 case 'aktt_reset_tweet_checking': |
|
1030 aktt_reset_digests(); |
|
1031 wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&digest-reset=true'); |
|
1032 die(); |
|
1033 break; |
|
1034 case 'aktt_js': |
|
1035 remove_action('shutdown', 'aktt_ping_digests'); |
|
1036 header("Content-type: text/javascript"); |
|
1037 switch ($aktt->js_lib) { |
|
1038 case 'jquery': |
|
1039 ?> |
|
1040 function akttPostTweet() { |
|
1041 var tweet_field = jQuery('#aktt_tweet_text'); |
|
1042 var tweet_text = tweet_field.val(); |
|
1043 if (tweet_text == '') { |
|
1044 return; |
|
1045 } |
|
1046 var tweet_msg = jQuery("#aktt_tweet_posted_msg"); |
|
1047 jQuery.post( |
|
1048 "<?php bloginfo('wpurl'); ?>/index.php" |
|
1049 , { |
|
1050 ak_action: "aktt_post_tweet_sidebar" |
|
1051 , aktt_tweet_text: tweet_text |
|
1052 } |
|
1053 , function(data) { |
|
1054 tweet_msg.html(data); |
|
1055 akttSetReset(); |
|
1056 } |
|
1057 ); |
|
1058 tweet_field.val('').focus(); |
|
1059 jQuery('#aktt_char_count').html(''); |
|
1060 jQuery("#aktt_tweet_posted_msg").show(); |
|
1061 } |
|
1062 function akttSetReset() { |
|
1063 setTimeout('akttReset();', 2000); |
|
1064 } |
|
1065 function akttReset() { |
|
1066 jQuery('#aktt_tweet_posted_msg').hide(); |
|
1067 } |
|
1068 <?php |
|
1069 break; |
|
1070 case 'prototype': |
|
1071 ?> |
|
1072 function akttPostTweet() { |
|
1073 var tweet_field = $('aktt_tweet_text'); |
|
1074 var tweet_text = tweet_field.value; |
|
1075 if (tweet_text == '') { |
|
1076 return; |
|
1077 } |
|
1078 var tweet_msg = $("aktt_tweet_posted_msg"); |
|
1079 var akttAjax = new Ajax.Updater( |
|
1080 tweet_msg, |
|
1081 "<?php bloginfo('wpurl'); ?>/index.php", |
|
1082 { |
|
1083 method: "post", |
|
1084 parameters: "ak_action=aktt_post_tweet_sidebar&aktt_tweet_text=" + tweet_text, |
|
1085 onComplete: akttSetReset |
|
1086 } |
|
1087 ); |
|
1088 tweet_field.value = ''; |
|
1089 tweet_field.focus(); |
|
1090 $('aktt_char_count').innerHTML = ''; |
|
1091 tweet_msg.style.display = 'block'; |
|
1092 } |
|
1093 function akttSetReset() { |
|
1094 setTimeout('akttReset();', 2000); |
|
1095 } |
|
1096 function akttReset() { |
|
1097 $('aktt_tweet_posted_msg').style.display = 'none'; |
|
1098 } |
|
1099 <?php |
|
1100 break; |
|
1101 } |
|
1102 die(); |
|
1103 break; |
|
1104 case 'aktt_css': |
|
1105 remove_action('shutdown', 'aktt_ping_digests'); |
|
1106 header("Content-Type: text/css"); |
|
1107 ?> |
|
1108 #aktt_tweet_form { |
|
1109 margin: 0; |
|
1110 padding: 5px 0; |
|
1111 } |
|
1112 #aktt_tweet_form fieldset { |
|
1113 border: 0; |
|
1114 } |
|
1115 #aktt_tweet_form fieldset #aktt_tweet_submit { |
|
1116 float: right; |
|
1117 margin-right: 10px; |
|
1118 } |
|
1119 #aktt_tweet_form fieldset #aktt_char_count { |
|
1120 color: #666; |
|
1121 } |
|
1122 #aktt_tweet_posted_msg { |
|
1123 background: #ffc; |
|
1124 display: none; |
|
1125 margin: 0 0 5px 0; |
|
1126 padding: 5px; |
|
1127 } |
|
1128 #aktt_tweet_form div.clear { |
|
1129 clear: both; |
|
1130 float: none; |
|
1131 } |
|
1132 <?php |
|
1133 die(); |
|
1134 break; |
|
1135 case 'aktt_js_admin': |
|
1136 remove_action('shutdown', 'aktt_ping_digests'); |
|
1137 header("Content-Type: text/javascript"); |
|
1138 switch ($aktt->js_lib) { |
|
1139 case 'jquery': |
|
1140 ?> |
|
1141 function akttTestLogin() { |
|
1142 var result = jQuery('#aktt_login_test_result'); |
|
1143 result.show().addClass('aktt_login_result_wait').html('<?php _e('Testing...', 'twitter-tools'); ?>'); |
|
1144 jQuery.post( |
|
1145 "<?php bloginfo('wpurl'); ?>/index.php" |
|
1146 , { |
|
1147 ak_action: "aktt_login_test" |
|
1148 , aktt_twitter_username: jQuery('#aktt_twitter_username').val() |
|
1149 , aktt_twitter_password: jQuery('#aktt_twitter_password').val() |
|
1150 } |
|
1151 , function(data) { |
|
1152 result.html(data).removeClass('aktt_login_result_wait'); |
|
1153 setTimeout('akttTestLoginResult();', 5000); |
|
1154 } |
|
1155 ); |
|
1156 }; |
|
1157 |
|
1158 function akttTestLoginResult() { |
|
1159 jQuery('#aktt_login_test_result').fadeOut('slow'); |
|
1160 }; |
|
1161 |
|
1162 (function($){ |
|
1163 |
|
1164 jQuery.fn.timepicker = function(){ |
|
1165 |
|
1166 var hrs = new Array(); |
|
1167 for(var h = 1; h <= 12; hrs.push(h++)); |
|
1168 |
|
1169 var mins = new Array(); |
|
1170 for(var m = 0; m < 60; mins.push(m++)); |
|
1171 |
|
1172 var ap = new Array('am', 'pm'); |
|
1173 |
|
1174 function pad(n) { |
|
1175 n = n.toString(); |
|
1176 return n.length == 1 ? '0' + n : n; |
|
1177 } |
|
1178 |
|
1179 this.each(function() { |
|
1180 |
|
1181 var v = $(this).val(); |
|
1182 if (!v) v = new Date(); |
|
1183 |
|
1184 var d = new Date(v); |
|
1185 var h = d.getHours(); |
|
1186 var m = d.getMinutes(); |
|
1187 var p = (h >= 12) ? "pm" : "am"; |
|
1188 h = (h > 12) ? h - 12 : h; |
|
1189 |
|
1190 var output = ''; |
|
1191 |
|
1192 output += '<select id="h_' + this.id + '" class="timepicker">'; |
|
1193 for (var hr in hrs){ |
|
1194 output += '<option value="' + pad(hrs[hr]) + '"'; |
|
1195 if(parseInt(hrs[hr], 10) == h || (parseInt(hrs[hr], 10) == 12 && h == 0)) output += ' selected'; |
|
1196 output += '>' + pad(hrs[hr]) + '</option>'; |
|
1197 } |
|
1198 output += '</select>'; |
|
1199 |
|
1200 output += '<select id="m_' + this.id + '" class="timepicker">'; |
|
1201 for (var mn in mins){ |
|
1202 output += '<option value="' + pad(mins[mn]) + '"'; |
|
1203 if(parseInt(mins[mn], 10) == m) output += ' selected'; |
|
1204 output += '>' + pad(mins[mn]) + '</option>'; |
|
1205 } |
|
1206 output += '</select>'; |
|
1207 |
|
1208 output += '<select id="p_' + this.id + '" class="timepicker">'; |
|
1209 for(var pp in ap){ |
|
1210 output += '<option value="' + ap[pp] + '"'; |
|
1211 if(ap[pp] == p) output += ' selected'; |
|
1212 output += '>' + ap[pp] + '</option>'; |
|
1213 } |
|
1214 output += '</select>'; |
|
1215 |
|
1216 $(this).after(output); |
|
1217 |
|
1218 var field = this; |
|
1219 $(this).siblings('select.timepicker').change(function() { |
|
1220 var h = parseInt($('#h_' + field.id).val(), 10); |
|
1221 var m = parseInt($('#m_' + field.id).val(), 10); |
|
1222 var p = $('#p_' + field.id).val(); |
|
1223 |
|
1224 if (p == "am") { |
|
1225 if (h == 12) { |
|
1226 h = 0; |
|
1227 } |
|
1228 } else if (p == "pm") { |
|
1229 if (h < 12) { |
|
1230 h += 12; |
|
1231 } |
|
1232 } |
|
1233 |
|
1234 var d = new Date(); |
|
1235 d.setHours(h); |
|
1236 d.setMinutes(m); |
|
1237 |
|
1238 $(field).val(d.toUTCString()); |
|
1239 }).change(); |
|
1240 |
|
1241 }); |
|
1242 |
|
1243 return this; |
|
1244 }; |
|
1245 |
|
1246 jQuery.fn.daypicker = function() { |
|
1247 |
|
1248 var days = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); |
|
1249 |
|
1250 this.each(function() { |
|
1251 |
|
1252 var v = $(this).val(); |
|
1253 if (!v) v = 0; |
|
1254 v = parseInt(v, 10); |
|
1255 |
|
1256 var output = ""; |
|
1257 output += '<select id="d_' + this.id + '" class="daypicker">'; |
|
1258 for (var i = 0; i < days.length; i++) { |
|
1259 output += '<option value="' + i + '"'; |
|
1260 if (v == i) output += ' selected'; |
|
1261 output += '>' + days[i] + '</option>'; |
|
1262 } |
|
1263 output += '</select>'; |
|
1264 |
|
1265 $(this).after(output); |
|
1266 |
|
1267 var field = this; |
|
1268 $(this).siblings('select.daypicker').change(function() { |
|
1269 $(field).val( $(this).val() ); |
|
1270 }).change(); |
|
1271 |
|
1272 }); |
|
1273 |
|
1274 }; |
|
1275 |
|
1276 jQuery.fn.forceToggleClass = function(classNames, bOn) { |
|
1277 return this.each(function() { |
|
1278 jQuery(this)[ bOn ? "addClass" : "removeClass" ](classNames); |
|
1279 }); |
|
1280 }; |
|
1281 |
|
1282 })(jQuery); |
|
1283 |
|
1284 jQuery(function() { |
|
1285 |
|
1286 // add in the time and day selects |
|
1287 jQuery('form#ak_twittertools input.time').timepicker(); |
|
1288 jQuery('form#ak_twittertools input.day').daypicker(); |
|
1289 |
|
1290 // togglers |
|
1291 jQuery('.time_toggle .toggler').change(function() { |
|
1292 var theSelect = jQuery(this); |
|
1293 theSelect.parent('.time_toggle').forceToggleClass('active', theSelect.val() === "1"); |
|
1294 }).change(); |
|
1295 |
|
1296 }); |
|
1297 <?php |
|
1298 break; |
|
1299 case 'prototype': |
|
1300 ?> |
|
1301 function akttTestLogin() { |
|
1302 var username = $('aktt_twitter_username').value; |
|
1303 var password = $('aktt_twitter_password').value; |
|
1304 var result = $('aktt_login_test_result'); |
|
1305 result.className = 'aktt_login_result_wait'; |
|
1306 result.innerHTML = '<?php _e('Testing...', 'twitter-tools'); ?>'; |
|
1307 var akttAjax = new Ajax.Updater( |
|
1308 result, |
|
1309 "<?php bloginfo('wpurl'); ?>/index.php", |
|
1310 { |
|
1311 method: "post", |
|
1312 parameters: "ak_action=aktt_login_test&aktt_twitter_username=" + username + "&aktt_twitter_password=" + password, |
|
1313 onComplete: akttTestLoginResult |
|
1314 } |
|
1315 ); |
|
1316 } |
|
1317 function akttTestLoginResult() { |
|
1318 $('aktt_login_test_result').className = 'aktt_login_result'; |
|
1319 Fat.fade_element('aktt_login_test_result'); |
|
1320 } |
|
1321 <?php |
|
1322 break; |
|
1323 } |
|
1324 die(); |
|
1325 break; |
|
1326 case 'aktt_css_admin': |
|
1327 remove_action('shutdown', 'aktt_ping_digests'); |
|
1328 header("Content-Type: text/css"); |
|
1329 ?> |
|
1330 #aktt_tweet_form { |
|
1331 margin: 0; |
|
1332 padding: 5px 0; |
|
1333 } |
|
1334 #aktt_tweet_form fieldset { |
|
1335 border: 0; |
|
1336 } |
|
1337 #aktt_tweet_form fieldset textarea { |
|
1338 width: 95%; |
|
1339 } |
|
1340 #aktt_tweet_form fieldset #aktt_tweet_submit { |
|
1341 float: right; |
|
1342 margin-right: 50px; |
|
1343 } |
|
1344 #aktt_tweet_form fieldset #aktt_char_count { |
|
1345 color: #666; |
|
1346 } |
|
1347 #ak_readme { |
|
1348 height: 300px; |
|
1349 width: 95%; |
|
1350 } |
|
1351 #ak_twittertools .options { |
|
1352 overflow: hidden; |
|
1353 border: none; |
|
1354 } |
|
1355 #ak_twittertools .option { |
|
1356 overflow: hidden; |
|
1357 padding-bottom: 9px; |
|
1358 padding-top: 9px; |
|
1359 } |
|
1360 #ak_twittertools .option label { |
|
1361 display: block; |
|
1362 float: left; |
|
1363 width: 200px; |
|
1364 margin-right: 24px; |
|
1365 text-align: right; |
|
1366 } |
|
1367 #ak_twittertools .option span { |
|
1368 display: block; |
|
1369 float: left; |
|
1370 margin-left: 230px; |
|
1371 margin-top: 6px; |
|
1372 clear: left; |
|
1373 } |
|
1374 #ak_twittertools select, |
|
1375 #ak_twittertools input { |
|
1376 float: left; |
|
1377 display: block; |
|
1378 margin-right: 6px; |
|
1379 } |
|
1380 #ak_twittertools p.submit { |
|
1381 overflow: hidden; |
|
1382 } |
|
1383 #ak_twittertools .option span { |
|
1384 color: #666; |
|
1385 display: block; |
|
1386 } |
|
1387 #ak_twittertools #aktt_login_test_result { |
|
1388 display: inline; |
|
1389 padding: 3px; |
|
1390 } |
|
1391 #ak_twittertools fieldset.options .option span.aktt_login_result_wait { |
|
1392 background: #ffc; |
|
1393 } |
|
1394 #ak_twittertools fieldset.options .option span.aktt_login_result { |
|
1395 background: #CFEBF7; |
|
1396 color: #000; |
|
1397 } |
|
1398 #ak_twittertools .timepicker, |
|
1399 #ak_twittertools .daypicker { |
|
1400 display: none; |
|
1401 } |
|
1402 #ak_twittertools .active .timepicker, |
|
1403 #ak_twittertools .active .daypicker { |
|
1404 display: block |
|
1405 } |
|
1406 <?php |
|
1407 die(); |
|
1408 break; |
|
1409 } |
|
1410 } |
|
1411 if (!empty($_POST['ak_action'])) { |
|
1412 switch($_POST['ak_action']) { |
|
1413 case 'aktt_update_settings': |
|
1414 $aktt->populate_settings(); |
|
1415 $aktt->update_settings(); |
|
1416 wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&updated=true'); |
|
1417 die(); |
|
1418 break; |
|
1419 case 'aktt_post_tweet_sidebar': |
|
1420 if (!empty($_POST['aktt_tweet_text']) && current_user_can('publish_posts')) { |
|
1421 $tweet = new aktt_tweet(); |
|
1422 $tweet->tw_text = stripslashes($_POST['aktt_tweet_text']); |
|
1423 if ($aktt->do_tweet($tweet)) { |
|
1424 die(__('Tweet posted.', 'twitter-tools')); |
|
1425 } |
|
1426 else { |
|
1427 die(__('Tweet post failed.', 'twitter-tools')); |
|
1428 } |
|
1429 } |
|
1430 break; |
|
1431 case 'aktt_post_tweet_admin': |
|
1432 if (!empty($_POST['aktt_tweet_text']) && current_user_can('publish_posts')) { |
|
1433 $tweet = new aktt_tweet(); |
|
1434 $tweet->tw_text = stripslashes($_POST['aktt_tweet_text']); |
|
1435 if ($aktt->do_tweet($tweet)) { |
|
1436 wp_redirect(get_bloginfo('wpurl').'/wp-admin/post-new.php?page=twitter-tools.php&tweet-posted=true'); |
|
1437 } |
|
1438 else { |
|
1439 wp_die(__('Oops, your tweet was not posted. Please check your username and password and that Twitter is up and running happily.', 'twitter-tools')); |
|
1440 } |
|
1441 die(); |
|
1442 } |
|
1443 break; |
|
1444 case 'aktt_login_test': |
|
1445 $test = @aktt_login_test( |
|
1446 @stripslashes($_POST['aktt_twitter_username']) |
|
1447 , @stripslashes($_POST['aktt_twitter_password']) |
|
1448 ); |
|
1449 die(__($test, 'twitter-tools')); |
|
1450 break; |
|
1451 } |
|
1452 } |
|
1453 } |
|
1454 add_action('init', 'aktt_request_handler', 10); |
|
1455 |
|
1456 function aktt_admin_tweet_form() { |
|
1457 global $aktt; |
|
1458 if ( $_GET['tweet-posted'] ) { |
|
1459 print(' |
|
1460 <div id="message" class="updated fade"> |
|
1461 <p>'.__('Tweet posted.', 'twitter-tools').'</p> |
|
1462 </div> |
|
1463 '); |
|
1464 } |
|
1465 print(' |
|
1466 <div class="wrap" id="aktt_write_tweet"> |
|
1467 '); |
|
1468 if (empty($aktt->twitter_username) || empty($aktt->twitter_password)) { |
|
1469 print(' |
|
1470 <p>'.__('Please enter your <a href="http://twitter.com">Twitter</a> account information in your <a href="options-general.php?page=twitter-tools.php">Twitter Tools Options</a>.', 'twitter-tools').'</p> |
|
1471 '); |
|
1472 } |
|
1473 else { |
|
1474 print(' |
|
1475 <h2>'.__('Write Tweet', 'twitter-tools').'</h2> |
|
1476 <p>'.__('This will create a new \'tweet\' in <a href="http://twitter.com">Twitter</a> using the account information in your <a href="options-general.php?page=twitter-tools.php">Twitter Tools Options</a>.', 'twitter-tools').'</p> |
|
1477 '.aktt_tweet_form('textarea').' |
|
1478 '); |
|
1479 } |
|
1480 print(' |
|
1481 </div> |
|
1482 '); |
|
1483 } |
|
1484 |
|
1485 function aktt_options_form() { |
|
1486 global $wpdb, $aktt; |
|
1487 |
|
1488 $categories = get_categories('hide_empty=0'); |
|
1489 $cat_options = ''; |
|
1490 foreach ($categories as $category) { |
|
1491 // WP < 2.3 compatibility |
|
1492 !empty($category->term_id) ? $cat_id = $category->term_id : $cat_id = $category->cat_ID; |
|
1493 !empty($category->name) ? $cat_name = $category->name : $cat_name = $category->cat_name; |
|
1494 if ($cat_id == $aktt->blog_post_category) { |
|
1495 $selected = 'selected="selected"'; |
|
1496 } |
|
1497 else { |
|
1498 $selected = ''; |
|
1499 } |
|
1500 $cat_options .= "\n\t<option value='$cat_id' $selected>$cat_name</option>"; |
|
1501 } |
|
1502 |
|
1503 $authors = get_users_of_blog(); |
|
1504 $author_options = ''; |
|
1505 foreach ($authors as $user) { |
|
1506 $usero = new WP_User($user->user_id); |
|
1507 $author = $usero->data; |
|
1508 // Only list users who are allowed to publish |
|
1509 if (! $usero->has_cap('publish_posts')) { |
|
1510 continue; |
|
1511 } |
|
1512 if ($author->ID == $aktt->blog_post_author) { |
|
1513 $selected = 'selected="selected"'; |
|
1514 } |
|
1515 else { |
|
1516 $selected = ''; |
|
1517 } |
|
1518 $author_options .= "\n\t<option value='$author->ID' $selected>$author->user_nicename</option>"; |
|
1519 } |
|
1520 |
|
1521 $js_libs = array( |
|
1522 'jquery' => 'jQuery' |
|
1523 , 'prototype' => 'Prototype' |
|
1524 ); |
|
1525 $js_lib_options = ''; |
|
1526 foreach ($js_libs as $js_lib => $js_lib_display) { |
|
1527 if ($js_lib == $aktt->js_lib) { |
|
1528 $selected = 'selected="selected"'; |
|
1529 } |
|
1530 else { |
|
1531 $selected = ''; |
|
1532 } |
|
1533 $js_lib_options .= "\n\t<option value='$js_lib' $selected>$js_lib_display</option>"; |
|
1534 } |
|
1535 $digest_tweet_orders = array( |
|
1536 'ASC' => __('Oldest first (Chronological order)', 'twitter-tools'), |
|
1537 'DESC' => __('Newest first (Reverse-chronological order)', 'twitter-tools') |
|
1538 ); |
|
1539 $digest_tweet_order_options = ''; |
|
1540 foreach ($digest_tweet_orders as $digest_tweet_order => $digest_tweet_order_display) { |
|
1541 if ($digest_tweet_order == $aktt->digest_tweet_order) { |
|
1542 $selected = 'selected="selected"'; |
|
1543 } |
|
1544 else { |
|
1545 $selected = ''; |
|
1546 } |
|
1547 $digest_tweet_order_options .= "\n\t<option value='$digest_tweet_order' $selected>$digest_tweet_order_display</option>"; |
|
1548 } |
|
1549 $yes_no = array( |
|
1550 'create_blog_posts' |
|
1551 , 'create_digest' |
|
1552 , 'create_digest_weekly' |
|
1553 , 'notify_twitter' |
|
1554 , 'notify_twitter_default' |
|
1555 , 'tweet_from_sidebar' |
|
1556 , 'give_tt_credit' |
|
1557 , 'exclude_reply_tweets' |
|
1558 ); |
|
1559 foreach ($yes_no as $key) { |
|
1560 $var = $key.'_options'; |
|
1561 if ($aktt->$key == '0') { |
|
1562 $$var = ' |
|
1563 <option value="0" selected="selected">'.__('No', 'twitter-tools').'</option> |
|
1564 <option value="1">'.__('Yes', 'twitter-tools').'</option> |
|
1565 '; |
|
1566 } |
|
1567 else { |
|
1568 $$var = ' |
|
1569 <option value="0">'.__('No', 'twitter-tools').'</option> |
|
1570 <option value="1" selected="selected">'.__('Yes', 'twitter-tools').'</option> |
|
1571 '; |
|
1572 } |
|
1573 } |
|
1574 if ( $_GET['tweets-updated'] ) { |
|
1575 print(' |
|
1576 <div id="message" class="updated fade"> |
|
1577 <p>'.__('Tweets updated.', 'twitter-tools').'</p> |
|
1578 </div> |
|
1579 '); |
|
1580 } |
|
1581 if ( $_GET['tweet-checking-reset'] ) { |
|
1582 print(' |
|
1583 <div id="message" class="updated fade"> |
|
1584 <p>'.__('Tweet checking has been reset.', 'twitter-tools').'</p> |
|
1585 </div> |
|
1586 '); |
|
1587 } |
|
1588 print(' |
|
1589 <div class="wrap" id="aktt_options_page"> |
|
1590 <h2>'.__('Twitter Tools Options', 'twitter-tools').'</h2> |
|
1591 <form id="ak_twittertools" name="ak_twittertools" action="'.get_bloginfo('wpurl').'/wp-admin/options-general.php" method="post"> |
|
1592 <fieldset class="options"> |
|
1593 <div class="option"> |
|
1594 <label for="aktt_twitter_username">'.__('Twitter Username', 'twitter-tools').'/'.__('Password', 'twitter-tools').'</label> |
|
1595 <input type="text" size="25" name="aktt_twitter_username" id="aktt_twitter_username" value="'.$aktt->twitter_username.'" autocomplete="off" /> |
|
1596 <input type="password" size="25" name="aktt_twitter_password" id="aktt_twitter_password" value="'.$aktt->twitter_password.'" autocomplete="off" /> |
|
1597 <input type="button" class="button" name="aktt_login_test" id="aktt_login_test" value="'.__('Test Login Info', 'twitter-tools').'" onclick="akttTestLogin(); return false;" /> |
|
1598 <span id="aktt_login_test_result"></span> |
|
1599 </div> |
|
1600 <div class="option"> |
|
1601 <label for="aktt_notify_twitter">'.__('Enable option to create a tweet when you post in your blog?', 'twitter-tools').'</label> |
|
1602 <select name="aktt_notify_twitter" id="aktt_notify_twitter">'.$notify_twitter_options.'</select> |
|
1603 </div> |
|
1604 <div class="option"> |
|
1605 <label for="aktt_tweet_prefix">'.__('Tweet prefix for new blog posts:', 'twitter-tools').'</label> |
|
1606 <input type="text" size="30" name="aktt_tweet_prefix" id="aktt_tweet_prefix" value="'.$aktt->tweet_prefix.'" /><span>'.__('Cannot be left blank. Will result in <b>{Your prefix}: Title URL</b>', 'twitter-tools').'</span> |
|
1607 </div> |
|
1608 <div class="option"> |
|
1609 <label for="aktt_notify_twitter_default">'.__('Set this on by default?', 'twitter-tools').'</label> |
|
1610 <select name="aktt_notify_twitter_default" id="aktt_notify_twitter_default">'.$notify_twitter_default_options.'</select><span>' .__('Also determines tweeting for posting via XML-RPC', 'twitter-tools').'</span> |
|
1611 </div> |
|
1612 <div class="option"> |
|
1613 <label for="aktt_create_blog_posts">'.__('Create a blog post from each of your tweets?', 'twitter-tools').'</label> |
|
1614 <select name="aktt_create_blog_posts" id="aktt_create_blog_posts">'.$create_blog_posts_options.'</select> |
|
1615 </div> |
|
1616 <div class="option time_toggle"> |
|
1617 <label>'.__('Create a daily digest blog post from your tweets?', 'twitter-tools').'</label> |
|
1618 <select name="aktt_create_digest" class="toggler">'.$create_digest_options.'</select> |
|
1619 <input type="hidden" class="time" id="aktt_digest_daily_time" name="aktt_digest_daily_time" value="'.$aktt->digest_daily_time.'" /> |
|
1620 </div> |
|
1621 <div class="option"> |
|
1622 <label for="aktt_digest_title">'.__('Title for daily digest posts:', 'twitter-tools').'</label> |
|
1623 <input type="text" size="30" name="aktt_digest_title" id="aktt_digest_title" value="'.$aktt->digest_title.'" /> |
|
1624 <span>'.__('Include %s where you want the date. Example: Tweets on %s', 'twitter-tools').'</span> |
|
1625 </div> |
|
1626 <div class="option time_toggle"> |
|
1627 <label>'.__('Create a weekly digest blog post from your tweets?', 'twitter-tools').'</label> |
|
1628 <select name="aktt_create_digest_weekly" class="toggler">'.$create_digest_weekly_options.'</select> |
|
1629 <input type="hidden" class="time" name="aktt_digest_weekly_time" id="aktt_digest_weekly_time" value="'.$aktt->digest_weekly_time.'" /> |
|
1630 <input type="hidden" class="day" name="aktt_digest_weekly_day" value="'.$aktt->digest_weekly_day.'" /> |
|
1631 </div> |
|
1632 <div class="option"> |
|
1633 <label for="aktt_digest_title_weekly">'.__('Title for weekly digest posts:', 'twitter-tools').'</label> |
|
1634 <input type="text" size="30" name="aktt_digest_title_weekly" id="aktt_digest_title_weekly" value="'.$aktt->digest_title_weekly.'" /> |
|
1635 <span>'.__('Include %s where you want the date. Example: Tweets on %s', 'twitter-tools').'</span> |
|
1636 </div> |
|
1637 <div class="option"> |
|
1638 <label for="aktt_digest_tweet_order">'.__('Order of tweets in digest?', 'twitter-tools').'</label> |
|
1639 <select name="aktt_digest_tweet_order" id="aktt_digest_tweet_order">'.$digest_tweet_order_options.'</select> |
|
1640 </div> |
|
1641 <div class="option"> |
|
1642 <label for="aktt_blog_post_category">'.__('Category for tweet posts:', 'twitter-tools').'</label> |
|
1643 <select name="aktt_blog_post_category" id="aktt_blog_post_category">'.$cat_options.'</select> |
|
1644 </div> |
|
1645 <div class="option"> |
|
1646 <label for="aktt_blog_post_tags">'.__('Tag(s) for your tweet posts:', 'twitter-tools').'</label> |
|
1647 <input name="aktt_blog_post_tags" id="aktt_blog_post_tags" value="'.$aktt->blog_post_tags.'"> |
|
1648 <span>'.__('Separate multiple tags with commas. Example: tweets, twitter', 'twitter-tools').'</span> |
|
1649 </div> |
|
1650 <div class="option"> |
|
1651 <label for="aktt_blog_post_author">'.__('Author for tweet posts:', 'twitter-tools').'</label> |
|
1652 <select name="aktt_blog_post_author" id="aktt_blog_post_author">'.$author_options.'</select> |
|
1653 </div> |
|
1654 <div class="option"> |
|
1655 <label for="aktt_exclude_reply_tweets">'.__('Exclude @reply tweets in your sidebar, digests and created blog posts?', 'twitter-tools').'</label> |
|
1656 <select name="aktt_exclude_reply_tweets" id="aktt_exclude_reply_tweets">'.$exclude_reply_tweets_options.'</select> |
|
1657 </div> |
|
1658 <div class="option"> |
|
1659 <label for="aktt_sidebar_tweet_count">'.__('Tweets to show in sidebar:', 'twitter-tools').'</label> |
|
1660 <input type="text" size="3" name="aktt_sidebar_tweet_count" id="aktt_sidebar_tweet_count" value="'.$aktt->sidebar_tweet_count.'" /> |
|
1661 <span>'.__('Numbers only please.', 'twitter-tools').'</span> |
|
1662 </div> |
|
1663 <div class="option"> |
|
1664 <label for="aktt_tweet_from_sidebar">'.__('Create tweets from your sidebar?', 'twitter-tools').'</label> |
|
1665 <select name="aktt_tweet_from_sidebar" id="aktt_tweet_from_sidebar">'.$tweet_from_sidebar_options.'</select> |
|
1666 </div> |
|
1667 <div class="option"> |
|
1668 <label for="aktt_js_lib">'.__('JS Library to use?', 'twitter-tools').'</label> |
|
1669 <select name="aktt_js_lib" id="aktt_js_lib">'.$js_lib_options.'</select> |
|
1670 </div> |
|
1671 <div class="option"> |
|
1672 <label for="aktt_give_tt_credit">'.__('Give Twitter Tools credit?', 'twitter-tools').'</label> |
|
1673 <select name="aktt_give_tt_credit" id="aktt_give_tt_credit">'.$give_tt_credit_options.'</select> |
|
1674 </div> |
|
1675 </fieldset> |
|
1676 <p class="submit"> |
|
1677 <input type="submit" name="submit" class="button-primary" value="'.__('Update Twitter Tools Options', 'twitter-tools').'" /> |
|
1678 </p> |
|
1679 <input type="hidden" name="ak_action" value="aktt_update_settings" class="hidden" style="display: none;" /> |
|
1680 </form> |
|
1681 <h2>'.__('Update Tweets / Reset Checking and Digests', 'twitter-tools').'</h2> |
|
1682 <form name="ak_twittertools_updatetweets" action="'.get_bloginfo('wpurl').'/wp-admin/options-general.php" method="get"> |
|
1683 <p>'.__('Use these buttons to manually update your tweets or reset the checking settings.', 'twitter-tools').'</p> |
|
1684 <p class="submit"> |
|
1685 <input type="submit" name="submit-button" value="'.__('Update Tweets', 'twitter-tools').'" /> |
|
1686 <input type="submit" name="reset-button-1" value="'.__('Reset Tweet Checking', 'twitter-tools').'" onclick="document.getElementById(\'ak_action_2\').value = \'aktt_reset_tweet_checking\';" /> |
|
1687 <input type="submit" name="reset-button-2" value="'.__('Reset Digests', 'twitter-tools').'" onclick="document.getElementById(\'ak_action_2\').value = \'aktt_reset_digests\';" /> |
|
1688 <input type="hidden" name="ak_action" id="ak_action_2" value="aktt_update_tweets" /> |
|
1689 </p> |
|
1690 </form> |
|
1691 '); |
|
1692 do_action('aktt_options_form'); |
|
1693 print(' |
|
1694 |
|
1695 <h2>'.__('README', 'twitter-tools').'</h2> |
|
1696 <p>'.__('Find answers to common questions here.', 'twitter-tools').'</p> |
|
1697 <iframe id="ak_readme" src="http://alexking.org/projects/wordpress/readme?project=twitter-tools"></iframe> |
|
1698 </div> |
|
1699 '); |
|
1700 } |
|
1701 |
|
1702 function aktt_post_options() { |
|
1703 global $aktt, $post; |
|
1704 if ($aktt->notify_twitter) { |
|
1705 echo '<div class="postbox"> |
|
1706 <h3>'.__('Twitter Tools', 'twitter-tools').'</h3> |
|
1707 <div class="inside"> |
|
1708 <p>'.__('Notify Twitter about this post?', 'twitter-tools'); |
|
1709 $notify = get_post_meta($post->ID, 'aktt_notify_twitter', true); |
|
1710 if ($notify == '') { |
|
1711 switch ($aktt->notify_twitter_default) { |
|
1712 case '1': |
|
1713 $notify = 'yes'; |
|
1714 break; |
|
1715 case '0': |
|
1716 $notify = 'no'; |
|
1717 break; |
|
1718 } |
|
1719 } |
|
1720 if ($notify == 'no') { |
|
1721 $yes = ''; |
|
1722 $no = 'checked="checked"'; |
|
1723 } |
|
1724 else { |
|
1725 $yes = 'checked="checked"'; |
|
1726 $no = ''; |
|
1727 } |
|
1728 echo ' |
|
1729 <input type="radio" name="aktt_notify_twitter" id="aktt_notify_twitter_yes" value="yes" '.$yes.' /> <label for="aktt_notify_twitter_yes">'.__('Yes', 'twitter-tools').'</label> |
|
1730 <input type="radio" name="aktt_notify_twitter" id="aktt_notify_twitter_no" value="no" '.$no.' /> <label for="aktt_notify_twitter_no">'.__('No', 'twitter-tools').'</label> |
|
1731 '; |
|
1732 echo ' |
|
1733 </p> |
|
1734 '; |
|
1735 do_action('aktt_post_options'); |
|
1736 echo ' |
|
1737 </div><!--.inside--> |
|
1738 </div><!--.postbox--> |
|
1739 '; |
|
1740 } |
|
1741 } |
|
1742 add_action('edit_form_advanced', 'aktt_post_options'); |
|
1743 |
|
1744 function aktt_store_post_options($post_id, $post = false) { |
|
1745 global $aktt; |
|
1746 $post = get_post($post_id); |
|
1747 if (!$post || $post->post_type == 'revision') { |
|
1748 return; |
|
1749 } |
|
1750 |
|
1751 $notify_meta = get_post_meta($post_id, 'aktt_notify_twitter', true); |
|
1752 $posted_meta = $_POST['aktt_notify_twitter']; |
|
1753 |
|
1754 $save = false; |
|
1755 if (!empty($posted_meta)) { |
|
1756 $posted_meta == 'yes' ? $meta = 'yes' : $meta = 'no'; |
|
1757 $save = true; |
|
1758 } |
|
1759 else if (empty($notify_meta)) { |
|
1760 $aktt->notify_twitter_default ? $meta = 'yes' : $meta = 'no'; |
|
1761 $save = true; |
|
1762 } |
|
1763 else { |
|
1764 $save = false; |
|
1765 } |
|
1766 |
|
1767 if ($save) { |
|
1768 if (!update_post_meta($post_id, 'aktt_notify_twitter', $meta)) { |
|
1769 add_post_meta($post_id, 'aktt_notify_twitter', $meta); |
|
1770 } |
|
1771 } |
|
1772 } |
|
1773 add_action('draft_post', 'aktt_store_post_options', 1, 2); |
|
1774 add_action('publish_post', 'aktt_store_post_options', 1, 2); |
|
1775 add_action('save_post', 'aktt_store_post_options', 1, 2); |
|
1776 |
|
1777 function aktt_menu_items() { |
|
1778 if (current_user_can('manage_options')) { |
|
1779 add_options_page( |
|
1780 __('Twitter Tools Options', 'twitter-tools') |
|
1781 , __('Twitter Tools', 'twitter-tools') |
|
1782 , 10 |
|
1783 , basename(__FILE__) |
|
1784 , 'aktt_options_form' |
|
1785 ); |
|
1786 } |
|
1787 if (current_user_can('publish_posts')) { |
|
1788 add_submenu_page( |
|
1789 'post-new.php' |
|
1790 , __('New Tweet', 'twitter-tools') |
|
1791 , __('Tweet', 'twitter-tools') |
|
1792 , 2 |
|
1793 , basename(__FILE__) |
|
1794 , 'aktt_admin_tweet_form' |
|
1795 ); |
|
1796 } |
|
1797 } |
|
1798 add_action('admin_menu', 'aktt_menu_items'); |
|
1799 |
|
1800 function aktt_plugin_action_links($links, $file) { |
|
1801 $plugin_file = basename(__FILE__); |
|
1802 if (basename($file) == $plugin_file) { |
|
1803 $settings_link = '<a href="options-general.php?page='.$plugin_file.'">'.__('Settings', 'twitter-tools').'</a>'; |
|
1804 array_unshift($links, $settings_link); |
|
1805 } |
|
1806 return $links; |
|
1807 } |
|
1808 add_filter('plugin_action_links', 'aktt_plugin_action_links', 10, 2); |
|
1809 |
|
1810 if (!function_exists('trim_add_elipsis')) { |
|
1811 function trim_add_elipsis($string, $limit = 100) { |
|
1812 if (strlen($string) > $limit) { |
|
1813 $string = substr($string, 0, $limit)."..."; |
|
1814 } |
|
1815 return $string; |
|
1816 } |
|
1817 } |
|
1818 |
|
1819 if (!function_exists('ak_gmmktime')) { |
|
1820 function ak_gmmktime() { |
|
1821 return gmmktime() - get_option('gmt_offset') * 3600; |
|
1822 } |
|
1823 } |
|
1824 |
|
1825 /** |
|
1826 |
|
1827 based on: http://www.gyford.com/phil/writing/2006/12/02/quick_twitter.php |
|
1828 |
|
1829 * Returns a relative date, eg "4 hrs ago". |
|
1830 * |
|
1831 * Assumes the passed-in can be parsed by strtotime. |
|
1832 * Precision could be one of: |
|
1833 * 1 5 hours, 3 minutes, 2 seconds ago (not yet implemented). |
|
1834 * 2 5 hours, 3 minutes |
|
1835 * 3 5 hours |
|
1836 * |
|
1837 * This is all a little overkill, but copied from other places I've used it. |
|
1838 * Also superfluous, now I've noticed that the Twitter API includes something |
|
1839 * similar, but this version is more accurate and less verbose. |
|
1840 * |
|
1841 * @access private. |
|
1842 * @param string date In a format parseable by strtotime(). |
|
1843 * @param integer precision |
|
1844 * @return string |
|
1845 */ |
|
1846 function aktt_relativeTime ($date, $precision=2) |
|
1847 { |
|
1848 |
|
1849 $now = time(); |
|
1850 |
|
1851 $time = gmmktime( |
|
1852 substr($date, 11, 2) |
|
1853 , substr($date, 14, 2) |
|
1854 , substr($date, 17, 2) |
|
1855 , substr($date, 5, 2) |
|
1856 , substr($date, 8, 2) |
|
1857 , substr($date, 0, 4) |
|
1858 ); |
|
1859 |
|
1860 $time = strtotime(date('Y-m-d H:i:s', $time)); |
|
1861 |
|
1862 $diff = $now - $time; |
|
1863 |
|
1864 $months = floor($diff/2419200); |
|
1865 $diff -= $months * 2419200; |
|
1866 $weeks = floor($diff/604800); |
|
1867 $diff -= $weeks*604800; |
|
1868 $days = floor($diff/86400); |
|
1869 $diff -= $days * 86400; |
|
1870 $hours = floor($diff/3600); |
|
1871 $diff -= $hours * 3600; |
|
1872 $minutes = floor($diff/60); |
|
1873 $diff -= $minutes * 60; |
|
1874 $seconds = $diff; |
|
1875 |
|
1876 if ($months > 0) { |
|
1877 return date_i18n( __('Y-m-d', 'twitter-tools'), $time); |
|
1878 } else { |
|
1879 $relative_date = ''; |
|
1880 if ($weeks > 0) { |
|
1881 // Weeks and days |
|
1882 $relative_date .= ($relative_date?', ':'').$weeks.' '.__ngettext('week', 'weeks', $weeks, 'twitter-tools'); |
|
1883 if ($precision <= 2) { |
|
1884 $relative_date .= $days>0? ($relative_date?', ':'').$days.' '.__ngettext('day', 'days', $days, 'twitter-tools'):''; |
|
1885 if ($precision == 1) { |
|
1886 $relative_date .= $hours>0?($relative_date?', ':'').$hours.' '.__ngettext('hr', 'hrs', $hours, 'twitter-tools'):''; |
|
1887 } |
|
1888 } |
|
1889 } elseif ($days > 0) { |
|
1890 // days and hours |
|
1891 $relative_date .= ($relative_date?', ':'').$days.' '.__ngettext('day', 'days', $days, 'twitter-tools'); |
|
1892 if ($precision <= 2) { |
|
1893 $relative_date .= $hours>0?($relative_date?', ':'').$hours.' '.__ngettext('hr', 'hrs', $hours, 'twitter-tools'):''; |
|
1894 if ($precision == 1) { |
|
1895 $relative_date .= $minutes>0?($relative_date?', ':'').$minutes.' '.__ngettext('min', 'mins', $minutes, 'twitter-tools'):''; |
|
1896 } |
|
1897 } |
|
1898 } elseif ($hours > 0) { |
|
1899 // hours and minutes |
|
1900 $relative_date .= ($relative_date?', ':'').$hours.' '.__ngettext('hr', 'hrs', $hours, 'twitter-tools'); |
|
1901 if ($precision <= 2) { |
|
1902 $relative_date .= $minutes>0?($relative_date?', ':'').$minutes.' '.__ngettext('min', 'mins', $minutes, 'twitter-tools'):''; |
|
1903 if ($precision == 1) { |
|
1904 $relative_date .= $seconds>0?($relative_date?', ':'').$seconds.' '.__ngettext('sec', 'secs', $seconds, 'twitter-tools'):''; |
|
1905 } |
|
1906 } |
|
1907 } elseif ($minutes > 0) { |
|
1908 // minutes only |
|
1909 $relative_date .= ($relative_date?', ':'').$minutes.' '.__ngettext('min', 'mins', $minutes, 'twitter-tools'); |
|
1910 if ($precision == 1) { |
|
1911 $relative_date .= $seconds>0?($relative_date?', ':'').$seconds.' '.__ngettext('sec', 'secs', $seconds, 'twitter-tools'):''; |
|
1912 } |
|
1913 } else { |
|
1914 // seconds only |
|
1915 $relative_date .= ($relative_date?', ':'').$seconds.' '.__ngettext('sec', 'secs', $seconds, 'twitter-tools'); |
|
1916 } |
|
1917 } |
|
1918 |
|
1919 // Return relative date and add proper verbiage |
|
1920 return sprintf(__('%s ago', 'twitter-tools'), $relative_date); |
|
1921 } |
|
1922 if (!class_exists('Services_JSON')) { |
|
1923 |
|
1924 // PEAR JSON class |
|
1925 |
|
1926 /** |
|
1927 * Converts to and from JSON format. |
|
1928 * |
|
1929 * JSON (JavaScript Object Notation) is a lightweight data-interchange |
|
1930 * format. It is easy for humans to read and write. It is easy for machines |
|
1931 * to parse and generate. It is based on a subset of the JavaScript |
|
1932 * Programming Language, Standard ECMA-262 3rd Edition - December 1999. |
|
1933 * This feature can also be found in Python. JSON is a text format that is |
|
1934 * completely language independent but uses conventions that are familiar |
|
1935 * to programmers of the C-family of languages, including C, C++, C#, Java, |
|
1936 * JavaScript, Perl, TCL, and many others. These properties make JSON an |
|
1937 * ideal data-interchange language. |
|
1938 * |
|
1939 * This package provides a simple encoder and decoder for JSON notation. It |
|
1940 * is intended for use with client-side Javascript applications that make |
|
1941 * use of HTTPRequest to perform server communication functions - data can |
|
1942 * be encoded into JSON notation for use in a client-side javascript, or |
|
1943 * decoded from incoming Javascript requests. JSON format is native to |
|
1944 * Javascript, and can be directly eval()'ed with no further parsing |
|
1945 * overhead |
|
1946 * |
|
1947 * All strings should be in ASCII or UTF-8 format! |
|
1948 * |
|
1949 * LICENSE: Redistribution and use in source and binary forms, with or |
|
1950 * without modification, are permitted provided that the following |
|
1951 * conditions are met: Redistributions of source code must retain the |
|
1952 * above copyright notice, this list of conditions and the following |
|
1953 * disclaimer. Redistributions in binary form must reproduce the above |
|
1954 * copyright notice, this list of conditions and the following disclaimer |
|
1955 * in the documentation and/or other materials provided with the |
|
1956 * distribution. |
|
1957 * |
|
1958 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
1959 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
1960 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
1961 * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
1962 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
1963 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
|
1964 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
1965 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
1966 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
|
1967 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
|
1968 * DAMAGE. |
|
1969 * |
|
1970 * @category |
|
1971 * @package Services_JSON |
|
1972 * @author Michal Migurski <mike-json@teczno.com> |
|
1973 * @author Matt Knapp <mdknapp[at]gmail[dot]com> |
|
1974 * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> |
|
1975 * @copyright 2005 Michal Migurski |
|
1976 * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ |
|
1977 * @license http://www.opensource.org/licenses/bsd-license.php |
|
1978 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 |
|
1979 */ |
|
1980 |
|
1981 /** |
|
1982 * Marker constant for Services_JSON::decode(), used to flag stack state |
|
1983 */ |
|
1984 define('SERVICES_JSON_SLICE', 1); |
|
1985 |
|
1986 /** |
|
1987 * Marker constant for Services_JSON::decode(), used to flag stack state |
|
1988 */ |
|
1989 define('SERVICES_JSON_IN_STR', 2); |
|
1990 |
|
1991 /** |
|
1992 * Marker constant for Services_JSON::decode(), used to flag stack state |
|
1993 */ |
|
1994 define('SERVICES_JSON_IN_ARR', 3); |
|
1995 |
|
1996 /** |
|
1997 * Marker constant for Services_JSON::decode(), used to flag stack state |
|
1998 */ |
|
1999 define('SERVICES_JSON_IN_OBJ', 4); |
|
2000 |
|
2001 /** |
|
2002 * Marker constant for Services_JSON::decode(), used to flag stack state |
|
2003 */ |
|
2004 define('SERVICES_JSON_IN_CMT', 5); |
|
2005 |
|
2006 /** |
|
2007 * Behavior switch for Services_JSON::decode() |
|
2008 */ |
|
2009 define('SERVICES_JSON_LOOSE_TYPE', 16); |
|
2010 |
|
2011 /** |
|
2012 * Behavior switch for Services_JSON::decode() |
|
2013 */ |
|
2014 define('SERVICES_JSON_SUPPRESS_ERRORS', 32); |
|
2015 |
|
2016 /** |
|
2017 * Converts to and from JSON format. |
|
2018 * |
|
2019 * Brief example of use: |
|
2020 * |
|
2021 * <code> |
|
2022 * // create a new instance of Services_JSON |
|
2023 * $json = new Services_JSON(); |
|
2024 * |
|
2025 * // convert a complexe value to JSON notation, and send it to the browser |
|
2026 * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); |
|
2027 * $output = $json->encode($value); |
|
2028 * |
|
2029 * print($output); |
|
2030 * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] |
|
2031 * |
|
2032 * // accept incoming POST data, assumed to be in JSON notation |
|
2033 * $input = file_get_contents('php://input', 1000000); |
|
2034 * $value = $json->decode($input); |
|
2035 * </code> |
|
2036 */ |
|
2037 class Services_JSON |
|
2038 { |
|
2039 /** |
|
2040 * constructs a new JSON instance |
|
2041 * |
|
2042 * @param int $use object behavior flags; combine with boolean-OR |
|
2043 * |
|
2044 * possible values: |
|
2045 * - SERVICES_JSON_LOOSE_TYPE: loose typing. |
|
2046 * "{...}" syntax creates associative arrays |
|
2047 * instead of objects in decode(). |
|
2048 * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. |
|
2049 * Values which can't be encoded (e.g. resources) |
|
2050 * appear as NULL instead of throwing errors. |
|
2051 * By default, a deeply-nested resource will |
|
2052 * bubble up with an error, so all return values |
|
2053 * from encode() should be checked with isError() |
|
2054 */ |
|
2055 function Services_JSON($use = 0) |
|
2056 { |
|
2057 $this->use = $use; |
|
2058 } |
|
2059 |
|
2060 /** |
|
2061 * convert a string from one UTF-16 char to one UTF-8 char |
|
2062 * |
|
2063 * Normally should be handled by mb_convert_encoding, but |
|
2064 * provides a slower PHP-only method for installations |
|
2065 * that lack the multibye string extension. |
|
2066 * |
|
2067 * @param string $utf16 UTF-16 character |
|
2068 * @return string UTF-8 character |
|
2069 * @access private |
|
2070 */ |
|
2071 function utf162utf8($utf16) |
|
2072 { |
|
2073 // oh please oh please oh please oh please oh please |
|
2074 if(function_exists('mb_convert_encoding')) { |
|
2075 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); |
|
2076 } |
|
2077 |
|
2078 $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); |
|
2079 |
|
2080 switch(true) { |
|
2081 case ((0x7F & $bytes) == $bytes): |
|
2082 // this case should never be reached, because we are in ASCII range |
|
2083 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2084 return chr(0x7F & $bytes); |
|
2085 |
|
2086 case (0x07FF & $bytes) == $bytes: |
|
2087 // return a 2-byte UTF-8 character |
|
2088 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2089 return chr(0xC0 | (($bytes >> 6) & 0x1F)) |
|
2090 . chr(0x80 | ($bytes & 0x3F)); |
|
2091 |
|
2092 case (0xFFFF & $bytes) == $bytes: |
|
2093 // return a 3-byte UTF-8 character |
|
2094 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2095 return chr(0xE0 | (($bytes >> 12) & 0x0F)) |
|
2096 . chr(0x80 | (($bytes >> 6) & 0x3F)) |
|
2097 . chr(0x80 | ($bytes & 0x3F)); |
|
2098 } |
|
2099 |
|
2100 // ignoring UTF-32 for now, sorry |
|
2101 return ''; |
|
2102 } |
|
2103 |
|
2104 /** |
|
2105 * convert a string from one UTF-8 char to one UTF-16 char |
|
2106 * |
|
2107 * Normally should be handled by mb_convert_encoding, but |
|
2108 * provides a slower PHP-only method for installations |
|
2109 * that lack the multibye string extension. |
|
2110 * |
|
2111 * @param string $utf8 UTF-8 character |
|
2112 * @return string UTF-16 character |
|
2113 * @access private |
|
2114 */ |
|
2115 function utf82utf16($utf8) |
|
2116 { |
|
2117 // oh please oh please oh please oh please oh please |
|
2118 if(function_exists('mb_convert_encoding')) { |
|
2119 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); |
|
2120 } |
|
2121 |
|
2122 switch(strlen($utf8)) { |
|
2123 case 1: |
|
2124 // this case should never be reached, because we are in ASCII range |
|
2125 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2126 return $utf8; |
|
2127 |
|
2128 case 2: |
|
2129 // return a UTF-16 character from a 2-byte UTF-8 char |
|
2130 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2131 return chr(0x07 & (ord($utf8{0}) >> 2)) |
|
2132 . chr((0xC0 & (ord($utf8{0}) << 6)) |
|
2133 | (0x3F & ord($utf8{1}))); |
|
2134 |
|
2135 case 3: |
|
2136 // return a UTF-16 character from a 3-byte UTF-8 char |
|
2137 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2138 return chr((0xF0 & (ord($utf8{0}) << 4)) |
|
2139 | (0x0F & (ord($utf8{1}) >> 2))) |
|
2140 . chr((0xC0 & (ord($utf8{1}) << 6)) |
|
2141 | (0x7F & ord($utf8{2}))); |
|
2142 } |
|
2143 |
|
2144 // ignoring UTF-32 for now, sorry |
|
2145 return ''; |
|
2146 } |
|
2147 |
|
2148 /** |
|
2149 * encodes an arbitrary variable into JSON format |
|
2150 * |
|
2151 * @param mixed $var any number, boolean, string, array, or object to be encoded. |
|
2152 * see argument 1 to Services_JSON() above for array-parsing behavior. |
|
2153 * if var is a strng, note that encode() always expects it |
|
2154 * to be in ASCII or UTF-8 format! |
|
2155 * |
|
2156 * @return mixed JSON string representation of input var or an error if a problem occurs |
|
2157 * @access public |
|
2158 */ |
|
2159 function encode($var) |
|
2160 { |
|
2161 switch (gettype($var)) { |
|
2162 case 'boolean': |
|
2163 return $var ? 'true' : 'false'; |
|
2164 |
|
2165 case 'NULL': |
|
2166 return 'null'; |
|
2167 |
|
2168 case 'integer': |
|
2169 return (int) $var; |
|
2170 |
|
2171 case 'double': |
|
2172 case 'float': |
|
2173 return (float) $var; |
|
2174 |
|
2175 case 'string': |
|
2176 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT |
|
2177 $ascii = ''; |
|
2178 $strlen_var = strlen($var); |
|
2179 |
|
2180 /* |
|
2181 * Iterate over every character in the string, |
|
2182 * escaping with a slash or encoding to UTF-8 where necessary |
|
2183 */ |
|
2184 for ($c = 0; $c < $strlen_var; ++$c) { |
|
2185 |
|
2186 $ord_var_c = ord($var{$c}); |
|
2187 |
|
2188 switch (true) { |
|
2189 case $ord_var_c == 0x08: |
|
2190 $ascii .= '\b'; |
|
2191 break; |
|
2192 case $ord_var_c == 0x09: |
|
2193 $ascii .= '\t'; |
|
2194 break; |
|
2195 case $ord_var_c == 0x0A: |
|
2196 $ascii .= '\n'; |
|
2197 break; |
|
2198 case $ord_var_c == 0x0C: |
|
2199 $ascii .= '\f'; |
|
2200 break; |
|
2201 case $ord_var_c == 0x0D: |
|
2202 $ascii .= '\r'; |
|
2203 break; |
|
2204 |
|
2205 case $ord_var_c == 0x22: |
|
2206 case $ord_var_c == 0x2F: |
|
2207 case $ord_var_c == 0x5C: |
|
2208 // double quote, slash, slosh |
|
2209 $ascii .= '\\'.$var{$c}; |
|
2210 break; |
|
2211 |
|
2212 case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): |
|
2213 // characters U-00000000 - U-0000007F (same as ASCII) |
|
2214 $ascii .= $var{$c}; |
|
2215 break; |
|
2216 |
|
2217 case (($ord_var_c & 0xE0) == 0xC0): |
|
2218 // characters U-00000080 - U-000007FF, mask 110XXXXX |
|
2219 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2220 $char = pack('C*', $ord_var_c, ord($var{$c + 1})); |
|
2221 $c += 1; |
|
2222 $utf16 = $this->utf82utf16($char); |
|
2223 $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
2224 break; |
|
2225 |
|
2226 case (($ord_var_c & 0xF0) == 0xE0): |
|
2227 // characters U-00000800 - U-0000FFFF, mask 1110XXXX |
|
2228 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2229 $char = pack('C*', $ord_var_c, |
|
2230 ord($var{$c + 1}), |
|
2231 ord($var{$c + 2})); |
|
2232 $c += 2; |
|
2233 $utf16 = $this->utf82utf16($char); |
|
2234 $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
2235 break; |
|
2236 |
|
2237 case (($ord_var_c & 0xF8) == 0xF0): |
|
2238 // characters U-00010000 - U-001FFFFF, mask 11110XXX |
|
2239 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2240 $char = pack('C*', $ord_var_c, |
|
2241 ord($var{$c + 1}), |
|
2242 ord($var{$c + 2}), |
|
2243 ord($var{$c + 3})); |
|
2244 $c += 3; |
|
2245 $utf16 = $this->utf82utf16($char); |
|
2246 $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
2247 break; |
|
2248 |
|
2249 case (($ord_var_c & 0xFC) == 0xF8): |
|
2250 // characters U-00200000 - U-03FFFFFF, mask 111110XX |
|
2251 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2252 $char = pack('C*', $ord_var_c, |
|
2253 ord($var{$c + 1}), |
|
2254 ord($var{$c + 2}), |
|
2255 ord($var{$c + 3}), |
|
2256 ord($var{$c + 4})); |
|
2257 $c += 4; |
|
2258 $utf16 = $this->utf82utf16($char); |
|
2259 $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
2260 break; |
|
2261 |
|
2262 case (($ord_var_c & 0xFE) == 0xFC): |
|
2263 // characters U-04000000 - U-7FFFFFFF, mask 1111110X |
|
2264 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2265 $char = pack('C*', $ord_var_c, |
|
2266 ord($var{$c + 1}), |
|
2267 ord($var{$c + 2}), |
|
2268 ord($var{$c + 3}), |
|
2269 ord($var{$c + 4}), |
|
2270 ord($var{$c + 5})); |
|
2271 $c += 5; |
|
2272 $utf16 = $this->utf82utf16($char); |
|
2273 $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
2274 break; |
|
2275 } |
|
2276 } |
|
2277 |
|
2278 return '"'.$ascii.'"'; |
|
2279 |
|
2280 case 'array': |
|
2281 /* |
|
2282 * As per JSON spec if any array key is not an integer |
|
2283 * we must treat the the whole array as an object. We |
|
2284 * also try to catch a sparsely populated associative |
|
2285 * array with numeric keys here because some JS engines |
|
2286 * will create an array with empty indexes up to |
|
2287 * max_index which can cause memory issues and because |
|
2288 * the keys, which may be relevant, will be remapped |
|
2289 * otherwise. |
|
2290 * |
|
2291 * As per the ECMA and JSON specification an object may |
|
2292 * have any string as a property. Unfortunately due to |
|
2293 * a hole in the ECMA specification if the key is a |
|
2294 * ECMA reserved word or starts with a digit the |
|
2295 * parameter is only accessible using ECMAScript's |
|
2296 * bracket notation. |
|
2297 */ |
|
2298 |
|
2299 // treat as a JSON object |
|
2300 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { |
|
2301 $properties = array_map(array($this, 'name_value'), |
|
2302 array_keys($var), |
|
2303 array_values($var)); |
|
2304 |
|
2305 foreach($properties as $property) { |
|
2306 if(Services_JSON::isError($property)) { |
|
2307 return $property; |
|
2308 } |
|
2309 } |
|
2310 |
|
2311 return '{' . join(',', $properties) . '}'; |
|
2312 } |
|
2313 |
|
2314 // treat it like a regular array |
|
2315 $elements = array_map(array($this, 'encode'), $var); |
|
2316 |
|
2317 foreach($elements as $element) { |
|
2318 if(Services_JSON::isError($element)) { |
|
2319 return $element; |
|
2320 } |
|
2321 } |
|
2322 |
|
2323 return '[' . join(',', $elements) . ']'; |
|
2324 |
|
2325 case 'object': |
|
2326 $vars = get_object_vars($var); |
|
2327 |
|
2328 $properties = array_map(array($this, 'name_value'), |
|
2329 array_keys($vars), |
|
2330 array_values($vars)); |
|
2331 |
|
2332 foreach($properties as $property) { |
|
2333 if(Services_JSON::isError($property)) { |
|
2334 return $property; |
|
2335 } |
|
2336 } |
|
2337 |
|
2338 return '{' . join(',', $properties) . '}'; |
|
2339 |
|
2340 default: |
|
2341 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) |
|
2342 ? 'null' |
|
2343 : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); |
|
2344 } |
|
2345 } |
|
2346 |
|
2347 /** |
|
2348 * array-walking function for use in generating JSON-formatted name-value pairs |
|
2349 * |
|
2350 * @param string $name name of key to use |
|
2351 * @param mixed $value reference to an array element to be encoded |
|
2352 * |
|
2353 * @return string JSON-formatted name-value pair, like '"name":value' |
|
2354 * @access private |
|
2355 */ |
|
2356 function name_value($name, $value) |
|
2357 { |
|
2358 $encoded_value = $this->encode($value); |
|
2359 |
|
2360 if(Services_JSON::isError($encoded_value)) { |
|
2361 return $encoded_value; |
|
2362 } |
|
2363 |
|
2364 return $this->encode(strval($name)) . ':' . $encoded_value; |
|
2365 } |
|
2366 |
|
2367 /** |
|
2368 * reduce a string by removing leading and trailing comments and whitespace |
|
2369 * |
|
2370 * @param $str string string value to strip of comments and whitespace |
|
2371 * |
|
2372 * @return string string value stripped of comments and whitespace |
|
2373 * @access private |
|
2374 */ |
|
2375 function reduce_string($str) |
|
2376 { |
|
2377 $str = preg_replace(array( |
|
2378 |
|
2379 // eliminate single line comments in '// ...' form |
|
2380 '#^\s*//(.+)$#m', |
|
2381 |
|
2382 // eliminate multi-line comments in '/* ... */' form, at start of string |
|
2383 '#^\s*/\*(.+)\*/#Us', |
|
2384 |
|
2385 // eliminate multi-line comments in '/* ... */' form, at end of string |
|
2386 '#/\*(.+)\*/\s*$#Us' |
|
2387 |
|
2388 ), '', $str); |
|
2389 |
|
2390 // eliminate extraneous space |
|
2391 return trim($str); |
|
2392 } |
|
2393 |
|
2394 /** |
|
2395 * decodes a JSON string into appropriate variable |
|
2396 * |
|
2397 * @param string $str JSON-formatted string |
|
2398 * |
|
2399 * @return mixed number, boolean, string, array, or object |
|
2400 * corresponding to given JSON input string. |
|
2401 * See argument 1 to Services_JSON() above for object-output behavior. |
|
2402 * Note that decode() always returns strings |
|
2403 * in ASCII or UTF-8 format! |
|
2404 * @access public |
|
2405 */ |
|
2406 function decode($str) |
|
2407 { |
|
2408 $str = $this->reduce_string($str); |
|
2409 |
|
2410 switch (strtolower($str)) { |
|
2411 case 'true': |
|
2412 return true; |
|
2413 |
|
2414 case 'false': |
|
2415 return false; |
|
2416 |
|
2417 case 'null': |
|
2418 return null; |
|
2419 |
|
2420 default: |
|
2421 $m = array(); |
|
2422 |
|
2423 if (is_numeric($str)) { |
|
2424 // Lookie-loo, it's a number |
|
2425 |
|
2426 // This would work on its own, but I'm trying to be |
|
2427 // good about returning integers where appropriate: |
|
2428 // return (float)$str; |
|
2429 |
|
2430 // Return float or int, as appropriate |
|
2431 return ((float)$str == (integer)$str) |
|
2432 ? (integer)$str |
|
2433 : (float)$str; |
|
2434 |
|
2435 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { |
|
2436 // STRINGS RETURNED IN UTF-8 FORMAT |
|
2437 $delim = substr($str, 0, 1); |
|
2438 $chrs = substr($str, 1, -1); |
|
2439 $utf8 = ''; |
|
2440 $strlen_chrs = strlen($chrs); |
|
2441 |
|
2442 for ($c = 0; $c < $strlen_chrs; ++$c) { |
|
2443 |
|
2444 $substr_chrs_c_2 = substr($chrs, $c, 2); |
|
2445 $ord_chrs_c = ord($chrs{$c}); |
|
2446 |
|
2447 switch (true) { |
|
2448 case $substr_chrs_c_2 == '\b': |
|
2449 $utf8 .= chr(0x08); |
|
2450 ++$c; |
|
2451 break; |
|
2452 case $substr_chrs_c_2 == '\t': |
|
2453 $utf8 .= chr(0x09); |
|
2454 ++$c; |
|
2455 break; |
|
2456 case $substr_chrs_c_2 == '\n': |
|
2457 $utf8 .= chr(0x0A); |
|
2458 ++$c; |
|
2459 break; |
|
2460 case $substr_chrs_c_2 == '\f': |
|
2461 $utf8 .= chr(0x0C); |
|
2462 ++$c; |
|
2463 break; |
|
2464 case $substr_chrs_c_2 == '\r': |
|
2465 $utf8 .= chr(0x0D); |
|
2466 ++$c; |
|
2467 break; |
|
2468 |
|
2469 case $substr_chrs_c_2 == '\\"': |
|
2470 case $substr_chrs_c_2 == '\\\'': |
|
2471 case $substr_chrs_c_2 == '\\\\': |
|
2472 case $substr_chrs_c_2 == '\\/': |
|
2473 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || |
|
2474 ($delim == "'" && $substr_chrs_c_2 != '\\"')) { |
|
2475 $utf8 .= $chrs{++$c}; |
|
2476 } |
|
2477 break; |
|
2478 |
|
2479 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): |
|
2480 // single, escaped unicode character |
|
2481 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) |
|
2482 . chr(hexdec(substr($chrs, ($c + 4), 2))); |
|
2483 $utf8 .= $this->utf162utf8($utf16); |
|
2484 $c += 5; |
|
2485 break; |
|
2486 |
|
2487 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): |
|
2488 $utf8 .= $chrs{$c}; |
|
2489 break; |
|
2490 |
|
2491 case ($ord_chrs_c & 0xE0) == 0xC0: |
|
2492 // characters U-00000080 - U-000007FF, mask 110XXXXX |
|
2493 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2494 $utf8 .= substr($chrs, $c, 2); |
|
2495 ++$c; |
|
2496 break; |
|
2497 |
|
2498 case ($ord_chrs_c & 0xF0) == 0xE0: |
|
2499 // characters U-00000800 - U-0000FFFF, mask 1110XXXX |
|
2500 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2501 $utf8 .= substr($chrs, $c, 3); |
|
2502 $c += 2; |
|
2503 break; |
|
2504 |
|
2505 case ($ord_chrs_c & 0xF8) == 0xF0: |
|
2506 // characters U-00010000 - U-001FFFFF, mask 11110XXX |
|
2507 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2508 $utf8 .= substr($chrs, $c, 4); |
|
2509 $c += 3; |
|
2510 break; |
|
2511 |
|
2512 case ($ord_chrs_c & 0xFC) == 0xF8: |
|
2513 // characters U-00200000 - U-03FFFFFF, mask 111110XX |
|
2514 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2515 $utf8 .= substr($chrs, $c, 5); |
|
2516 $c += 4; |
|
2517 break; |
|
2518 |
|
2519 case ($ord_chrs_c & 0xFE) == 0xFC: |
|
2520 // characters U-04000000 - U-7FFFFFFF, mask 1111110X |
|
2521 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|
2522 $utf8 .= substr($chrs, $c, 6); |
|
2523 $c += 5; |
|
2524 break; |
|
2525 |
|
2526 } |
|
2527 |
|
2528 } |
|
2529 |
|
2530 return $utf8; |
|
2531 |
|
2532 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { |
|
2533 // array, or object notation |
|
2534 |
|
2535 if ($str{0} == '[') { |
|
2536 $stk = array(SERVICES_JSON_IN_ARR); |
|
2537 $arr = array(); |
|
2538 } else { |
|
2539 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { |
|
2540 $stk = array(SERVICES_JSON_IN_OBJ); |
|
2541 $obj = array(); |
|
2542 } else { |
|
2543 $stk = array(SERVICES_JSON_IN_OBJ); |
|
2544 $obj = new stdClass(); |
|
2545 } |
|
2546 } |
|
2547 |
|
2548 array_push($stk, array('what' => SERVICES_JSON_SLICE, |
|
2549 'where' => 0, |
|
2550 'delim' => false)); |
|
2551 |
|
2552 $chrs = substr($str, 1, -1); |
|
2553 $chrs = $this->reduce_string($chrs); |
|
2554 |
|
2555 if ($chrs == '') { |
|
2556 if (reset($stk) == SERVICES_JSON_IN_ARR) { |
|
2557 return $arr; |
|
2558 |
|
2559 } else { |
|
2560 return $obj; |
|
2561 |
|
2562 } |
|
2563 } |
|
2564 |
|
2565 //print("\nparsing {$chrs}\n"); |
|
2566 |
|
2567 $strlen_chrs = strlen($chrs); |
|
2568 |
|
2569 for ($c = 0; $c <= $strlen_chrs; ++$c) { |
|
2570 |
|
2571 $top = end($stk); |
|
2572 $substr_chrs_c_2 = substr($chrs, $c, 2); |
|
2573 |
|
2574 if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { |
|
2575 // found a comma that is not inside a string, array, etc., |
|
2576 // OR we've reached the end of the character list |
|
2577 $slice = substr($chrs, $top['where'], ($c - $top['where'])); |
|
2578 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); |
|
2579 //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
|
2580 |
|
2581 if (reset($stk) == SERVICES_JSON_IN_ARR) { |
|
2582 // we are in an array, so just push an element onto the stack |
|
2583 array_push($arr, $this->decode($slice)); |
|
2584 |
|
2585 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { |
|
2586 // we are in an object, so figure |
|
2587 // out the property name and set an |
|
2588 // element in an associative array, |
|
2589 // for now |
|
2590 $parts = array(); |
|
2591 |
|
2592 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { |
|
2593 // "name":value pair |
|
2594 $key = $this->decode($parts[1]); |
|
2595 $val = $this->decode($parts[2]); |
|
2596 |
|
2597 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { |
|
2598 $obj[$key] = $val; |
|
2599 } else { |
|
2600 $obj->$key = $val; |
|
2601 } |
|
2602 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { |
|
2603 // name:value pair, where name is unquoted |
|
2604 $key = $parts[1]; |
|
2605 $val = $this->decode($parts[2]); |
|
2606 |
|
2607 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { |
|
2608 $obj[$key] = $val; |
|
2609 } else { |
|
2610 $obj->$key = $val; |
|
2611 } |
|
2612 } |
|
2613 |
|
2614 } |
|
2615 |
|
2616 } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { |
|
2617 // found a quote, and we are not inside a string |
|
2618 array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); |
|
2619 //print("Found start of string at {$c}\n"); |
|
2620 |
|
2621 } elseif (($chrs{$c} == $top['delim']) && |
|
2622 ($top['what'] == SERVICES_JSON_IN_STR) && |
|
2623 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { |
|
2624 // found a quote, we're in a string, and it's not escaped |
|
2625 // we know that it's not escaped becase there is _not_ an |
|
2626 // odd number of backslashes at the end of the string so far |
|
2627 array_pop($stk); |
|
2628 //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); |
|
2629 |
|
2630 } elseif (($chrs{$c} == '[') && |
|
2631 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { |
|
2632 // found a left-bracket, and we are in an array, object, or slice |
|
2633 array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); |
|
2634 //print("Found start of array at {$c}\n"); |
|
2635 |
|
2636 } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { |
|
2637 // found a right-bracket, and we're in an array |
|
2638 array_pop($stk); |
|
2639 //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
|
2640 |
|
2641 } elseif (($chrs{$c} == '{') && |
|
2642 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { |
|
2643 // found a left-brace, and we are in an array, object, or slice |
|
2644 array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); |
|
2645 //print("Found start of object at {$c}\n"); |
|
2646 |
|
2647 } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { |
|
2648 // found a right-brace, and we're in an object |
|
2649 array_pop($stk); |
|
2650 //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
|
2651 |
|
2652 } elseif (($substr_chrs_c_2 == '/*') && |
|
2653 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { |
|
2654 // found a comment start, and we are in an array, object, or slice |
|
2655 array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); |
|
2656 $c++; |
|
2657 //print("Found start of comment at {$c}\n"); |
|
2658 |
|
2659 } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { |
|
2660 // found a comment end, and we're in one now |
|
2661 array_pop($stk); |
|
2662 $c++; |
|
2663 |
|
2664 for ($i = $top['where']; $i <= $c; ++$i) |
|
2665 $chrs = substr_replace($chrs, ' ', $i, 1); |
|
2666 |
|
2667 //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
|
2668 |
|
2669 } |
|
2670 |
|
2671 } |
|
2672 |
|
2673 if (reset($stk) == SERVICES_JSON_IN_ARR) { |
|
2674 return $arr; |
|
2675 |
|
2676 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { |
|
2677 return $obj; |
|
2678 |
|
2679 } |
|
2680 |
|
2681 } |
|
2682 } |
|
2683 } |
|
2684 |
|
2685 /** |
|
2686 * @todo Ultimately, this should just call PEAR::isError() |
|
2687 */ |
|
2688 function isError($data, $code = null) |
|
2689 { |
|
2690 if (class_exists('pear')) { |
|
2691 return PEAR::isError($data, $code); |
|
2692 } elseif (is_object($data) && (get_class($data) == 'services_json_error' || |
|
2693 is_subclass_of($data, 'services_json_error'))) { |
|
2694 return true; |
|
2695 } |
|
2696 |
|
2697 return false; |
|
2698 } |
|
2699 } |
|
2700 |
|
2701 if (class_exists('PEAR_Error')) { |
|
2702 |
|
2703 class Services_JSON_Error extends PEAR_Error |
|
2704 { |
|
2705 function Services_JSON_Error($message = 'unknown error', $code = null, |
|
2706 $mode = null, $options = null, $userinfo = null) |
|
2707 { |
|
2708 parent::PEAR_Error($message, $code, $mode, $options, $userinfo); |
|
2709 } |
|
2710 } |
|
2711 |
|
2712 } else { |
|
2713 |
|
2714 /** |
|
2715 * @todo Ultimately, this class shall be descended from PEAR_Error |
|
2716 */ |
|
2717 class Services_JSON_Error |
|
2718 { |
|
2719 function Services_JSON_Error($message = 'unknown error', $code = null, |
|
2720 $mode = null, $options = null, $userinfo = null) |
|
2721 { |
|
2722 |
|
2723 } |
|
2724 } |
|
2725 |
|
2726 } |
|
2727 |
|
2728 } |
|
2729 |
|
2730 ?> |
|