wordpress 2.8 ()
with the following extensions :
- add-to-any
- categories page
- Event calendar (a custom version for IRI-Theme)
- Executable PHP widget
- FD feedburner
- ggis subscribe
- Google Xml site maple
- post of current category
- page redirection
- related post by category
AND IRI-Theme
<?php
/*
Plugin Name: Twitter Tools
Plugin URI: http://alexking.org/projects/wordpress
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.
Version: 2.0
Author: Alex King
Author URI: http://alexking.org
*/
// Copyright (c) 2007-2009 Crowd Favorite, Ltd., Alex King. All rights reserved.
//
// Released under the GPL license
// http://www.opensource.org/licenses/gpl-license.php
//
// This is an add-on for WordPress
// http://wordpress.org/
//
// Thanks to John Ford ( http://www.aldenta.com ) for his contributions.
// Thanks to Dougal Campbell ( http://dougal.gunters.org ) for his contributions.
// Thanks to Silas Sewell ( http://silas.sewell.ch ) for his contributions.
// Thanks to Greg Grubbs for his contributions.
//
// **********************************************************************
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// **********************************************************************
load_plugin_textdomain('twitter-tools');
if (!defined('PLUGINDIR')) {
define('PLUGINDIR','wp-content/plugins');
}
if (is_file(trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools.php')) {
define('AKTT_FILE', trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools.php');
}
else if (is_file(trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools/twitter-tools.php')) {
define('AKTT_FILE', trailingslashit(ABSPATH.PLUGINDIR).'twitter-tools/twitter-tools.php');
}
if (!function_exists('wp_prototype_before_jquery')) {
function wp_prototype_before_jquery( $js_array ) {
if ( false === $jquery = array_search( 'jquery', $js_array ) )
return $js_array;
if ( false === $prototype = array_search( 'prototype', $js_array ) )
return $js_array;
if ( $prototype < $jquery )
return $js_array;
unset($js_array[$prototype]);
array_splice( $js_array, $jquery, 0, 'prototype' );
return $js_array;
}
add_filter( 'print_scripts_array', 'wp_prototype_before_jquery' );
}
define('AKTT_API_POST_STATUS', 'http://twitter.com/statuses/update.json');
define('AKTT_API_USER_TIMELINE', 'http://twitter.com/statuses/user_timeline.json');
define('AKTT_API_STATUS_SHOW', 'http://twitter.com/statuses/show/###ID###.json');
define('AKTT_PROFILE_URL', 'http://twitter.com/###USERNAME###');
define('AKTT_STATUS_URL', 'http://twitter.com/###USERNAME###/statuses/###STATUS###');
define('AKTT_HASHTAG_URL', 'http://search.twitter.com/search?q=###HASHTAG###');
function aktt_install() {
global $wpdb;
$aktt_install = new twitter_tools;
$wpdb->aktt = $wpdb->prefix.'ak_twitter';
$charset_collate = '';
if ( version_compare(mysql_get_server_info(), '4.1.0', '>=') ) {
if (!empty($wpdb->charset)) {
$charset_collate .= " DEFAULT CHARACTER SET $wpdb->charset";
}
if (!empty($wpdb->collate)) {
$charset_collate .= " COLLATE $wpdb->collate";
}
}
$result = $wpdb->query("
CREATE TABLE `$wpdb->aktt` (
`id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`tw_id` VARCHAR( 255 ) NOT NULL ,
`tw_text` VARCHAR( 255 ) NOT NULL ,
`tw_reply_username` VARCHAR( 255 ) DEFAULT NULL ,
`tw_reply_tweet` VARCHAR( 255 ) DEFAULT NULL ,
`tw_created_at` DATETIME NOT NULL ,
`modified` DATETIME NOT NULL ,
INDEX ( `tw_id` )
) $charset_collate
");
foreach ($aktt_install->options as $option) {
add_option('aktt_'.$option, $aktt_install->$option);
}
add_option('aktt_update_hash', '');
}
register_activation_hook(AKTT_FILE, 'aktt_install');
class twitter_tools {
function twitter_tools() {
$this->options = array(
'twitter_username'
, 'twitter_password'
, 'create_blog_posts'
, 'create_digest'
, 'create_digest_weekly'
, 'digest_daily_time'
, 'digest_weekly_time'
, 'digest_weekly_day'
, 'digest_title'
, 'digest_title_weekly'
, 'blog_post_author'
, 'blog_post_category'
, 'blog_post_tags'
, 'notify_twitter'
, 'sidebar_tweet_count'
, 'tweet_from_sidebar'
, 'give_tt_credit'
, 'exclude_reply_tweets'
, 'tweet_prefix'
, 'last_tweet_download'
, 'doing_tweet_download'
, 'doing_digest_post'
, 'install_date'
, 'js_lib'
, 'digest_tweet_order'
, 'notify_twitter_default'
);
$this->twitter_username = '';
$this->twitter_password = '';
$this->create_blog_posts = '0';
$this->create_digest = '0';
$this->create_digest_weekly = '0';
$this->digest_daily_time = null;
$this->digest_weekly_time = null;
$this->digest_weekly_day = null;
$this->digest_title = __("Twitter Updates for %s", 'twitter-tools');
$this->digest_title_weekly = __("Twitter Weekly Updates for %s", 'twitter-tools');
$this->blog_post_author = '1';
$this->blog_post_category = '1';
$this->blog_post_tags = '';
$this->notify_twitter = '0';
$this->notify_twitter_default = '0';
$this->sidebar_tweet_count = '3';
$this->tweet_from_sidebar = '1';
$this->give_tt_credit = '1';
$this->exclude_reply_tweets = '0';
$this->install_date = '';
$this->js_lib = 'jquery';
$this->digest_tweet_order = 'ASC';
$this->tweet_prefix = 'New blog post';
// not included in options
$this->update_hash = '';
$this->tweet_format = $this->tweet_prefix.': %s %s';
$this->last_digest_post = '';
$this->last_tweet_download = '';
$this->doing_tweet_download = '0';
$this->doing_digest_post = '0';
$this->version = '1.6';
}
function upgrade() {
global $wpdb;
$wpdb->aktt = $wpdb->prefix.'ak_twitter';
$col_data = $wpdb->get_results("
SHOW COLUMNS FROM $wpdb->aktt
");
$cols = array();
foreach ($col_data as $col) {
$cols[] = $col->Field;
}
// 1.2 schema upgrade
if (!in_array('tw_reply_username', $cols)) {
$wpdb->query("
ALTER TABLE `$wpdb->aktt`
ADD `tw_reply_username` VARCHAR( 255 ) DEFAULT NULL
AFTER `tw_text`
");
}
if (!in_array('tw_reply_tweet', $cols)) {
$wpdb->query("
ALTER TABLE `$wpdb->aktt`
ADD `tw_reply_tweet` VARCHAR( 255 ) DEFAULT NULL
AFTER `tw_reply_username`
");
}
$this->upgrade_default_tweet_prefix();
}
function upgrade_default_tweet_prefix() {
$prefix = get_option('aktt_tweet_prefix');
if (empty($prefix)) {
$aktt_defaults = new twitter_tools;
update_option('aktt_tweet_prefix', $aktt_defaults->tweet_prefix);
}
}
function get_settings() {
foreach ($this->options as $option) {
$value = get_option('aktt_'.$option);
if ($option != 'tweet_prefix' || !empty($value)) {
$this->$option = $value;
}
}
$this->tweet_format = $this->tweet_prefix.': %s %s';
}
// puts post fields into object propps
function populate_settings() {
foreach ($this->options as $option) {
$value = stripslashes($_POST['aktt_'.$option]);
if (isset($_POST['aktt_'.$option]) && ($option != 'tweet_prefix' || !empty($value))) {
$this->$option = $value;
}
}
}
// puts object props into wp option storage
function update_settings() {
if (current_user_can('manage_options')) {
$this->sidebar_tweet_count = intval($this->sidebar_tweet_count);
if ($this->sidebar_tweet_count == 0) {
$this->sidebar_tweet_count = '3';
}
foreach ($this->options as $option) {
update_option('aktt_'.$option, $this->$option);
}
if (empty($this->install_date)) {
update_option('aktt_install_date', current_time('mysql'));
}
$this->initiate_digests();
$this->upgrade();
$this->upgrade_default_tweet_prefix();
}
}
// figure out when the next weekly and daily digests will be
function initiate_digests() {
$next = ($this->create_digest) ? $this->calculate_next_daily_digest() : null;
$this->next_daily_digest = $next;
update_option('aktt_next_daily_digest', $next);
$next = ($this->create_digest_weekly) ? $this->calculate_next_weekly_digest() : null;
$this->next_weekly_digest = $next;
update_option('aktt_next_weekly_digest', $next);
}
function calculate_next_daily_digest() {
$optionDate = strtotime($this->digest_daily_time);
$hour_offset = date("G", $optionDate);
$minute_offset = date("i", $optionDate);
$next = mktime($hour_offset, $minute_offset, 0);
// may have to move to next day
$now = time();
while($next < $now) {
$next += 60 * 60 * 24;
}
return $next;
}
function calculate_next_weekly_digest() {
$optionDate = strtotime($this->digest_weekly_time);
$hour_offset = date("G", $optionDate);
$minute_offset = date("i", $optionDate);
$current_day_of_month = date("j");
$current_day_of_week = date("w");
$current_month = date("n");
// if this week's day is less than today, go for next week
$nextDay = $current_day_of_month - $current_day_of_week + $this->digest_weekly_day;
$next = mktime($hour_offset, $minute_offset, 0, $current_month, $nextDay);
if ($this->digest_weekly_day <= $current_day_of_week) {
$next = strtotime('+1 week', $next);
}
return $next;
}
function ping_digests() {
// still busy
if (get_option('aktt_doing_digest_post') == '1') {
return;
}
// check all the digest schedules
if ($this->create_digest == 1) {
$this->ping_digest('aktt_next_daily_digest', 'aktt_last_digest_post', $this->digest_title, 60 * 60 * 24 * 1);
}
if ($this->create_digest_weekly == 1) {
$this->ping_digest('aktt_next_weekly_digest', 'aktt_last_digest_post_weekly', $this->digest_title_weekly, 60 * 60 * 24 * 7);
}
return;
}
function ping_digest($nextDateField, $lastDateField, $title, $defaultDuration) {
$next = get_option($nextDateField);
if ($next) {
$next = $this->validateDate($next);
$rightNow = time();
if ($rightNow >= $next) {
$start = get_option($lastDateField);
$start = $this->validateDate($start, $rightNow - $defaultDuration);
if ($this->do_digest_post($start, $next, $title)) {
update_option($lastDateField, $rightNow);
update_option($nextDateField, $next + $defaultDuration);
} else {
update_option($lastDateField, null);
}
}
}
}
function validateDate($in, $default = 0) {
if (!is_numeric($in)) {
// try to convert what they gave us into a date
$out = strtotime($in);
// if that doesn't work, return the default
if (!is_numeric($out)) {
return $default;
}
return $out;
}
return $in;
}
function do_digest_post($start, $end, $title) {
if (!$start || !$end) return false;
// flag us as busy
update_option('aktt_doing_digest_post', '1');
remove_action('publish_post', 'aktt_notify_twitter', 99);
remove_action('publish_post', 'aktt_store_post_options', 1, 2);
remove_action('save_post', 'aktt_store_post_options', 1, 2);
// see if there's any tweets in the time range
global $wpdb;
$startGMT = gmdate("Y-m-d H:i:s", $start);
$endGMT = gmdate("Y-m-d H:i:s", $end);
// build sql
$conditions = array();
$conditions[] = "tw_created_at >= '{$startGMT}'";
$conditions[] = "tw_created_at <= '{$endGMT}'";
$conditions[] = "tw_text NOT LIKE '$this->tweet_prefix%'";
if ($this->exclude_reply_tweets) {
$conditions[] = "tw_text NOT LIKE '@%'";
}
$where = implode(' AND ', $conditions);
$sql = "
SELECT * FROM {$wpdb->aktt}
WHERE {$where}
GROUP BY tw_id
ORDER BY tw_created_at {$this->digest_tweet_order}
";
$tweets = $wpdb->get_results($sql);
if (count($tweets) > 0) {
$tweets_to_post = array();
foreach ($tweets as $data) {
$tweet = new aktt_tweet;
$tweet->tw_text = $data->tw_text;
$tweet->tw_reply_tweet = $data->tw_reply_tweet;
if (!$tweet->tweet_is_post_notification() || ($tweet->tweet_is_reply() && $this->exclude_reply_tweets)) {
$tweets_to_post[] = $data;
}
}
$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
if (count($tweets_to_post) > 0) {
$content = '<ul class="aktt_tweet_digest">'."\n";
foreach ($tweets_to_post as $tweet) {
$content .= ' <li>'.aktt_tweet_display($tweet, 'absolute').'</li>'."\n";
}
$content .= '</ul>'."\n";
if ($this->give_tt_credit == '1') {
$content .= '<p class="aktt_credit">'.__('Powered by <a href="http://alexking.org/projects/wordpress">Twitter Tools</a>', 'twitter-tools').'</p>';
}
$post_data = array(
'post_content' => $wpdb->escape($content),
'post_title' => $wpdb->escape(sprintf($title, date('Y-m-d'))),
'post_date' => date('Y-m-d H:i:s', $end),
'post_category' => array($this->blog_post_category),
'post_status' => 'publish',
'post_author' => $wpdb->escape($this->blog_post_author)
);
$post_data = apply_filters('aktt_digest_post_data', $post_data); // last chance to alter the digest content
$post_id = wp_insert_post($post_data);
add_post_meta($post_id, 'aktt_tweeted', '1', true);
wp_set_post_tags($post_id, $this->blog_post_tags);
}
}
add_action('publish_post', 'aktt_notify_twitter', 99);
add_action('publish_post', 'aktt_store_post_options', 1, 2);
add_action('save_post', 'aktt_store_post_options', 1, 2);
update_option('aktt_doing_digest_post', '0');
return true;
}
function tweet_download_interval() {
return 600;
}
function do_tweet($tweet = '') {
if (empty($this->twitter_username)
|| empty($this->twitter_password)
|| empty($tweet)
|| empty($tweet->tw_text)
) {
return;
}
$tweet = apply_filters('aktt_do_tweet', $tweet); // return false here to not tweet
if (!$tweet) {
return;
}
require_once(ABSPATH.WPINC.'/class-snoopy.php');
$snoop = new Snoopy;
$snoop->agent = 'Twitter Tools http://alexking.org/projects/wordpress';
$snoop->rawheaders = array(
'X-Twitter-Client' => 'Twitter Tools'
, 'X-Twitter-Client-Version' => $this->version
, 'X-Twitter-Client-URL' => 'http://alexking.org/projects/wordpress/twitter-tools.xml'
);
$snoop->user = $this->twitter_username;
$snoop->pass = $this->twitter_password;
$snoop->submit(
AKTT_API_POST_STATUS
, array(
'status' => $tweet->tw_text
, 'source' => 'twittertools'
)
);
if (strpos($snoop->response_code, '200')) {
update_option('aktt_last_tweet_download', strtotime('-28 minutes'));
return true;
}
return false;
}
function do_blog_post_tweet($post_id = 0) {
// this is only called on the publish_post hook
if ($this->notify_twitter == '0'
|| $post_id == 0
|| get_post_meta($post_id, 'aktt_tweeted', true) == '1'
|| get_post_meta($post_id, 'aktt_notify_twitter', true) == 'no'
) {
return;
}
$post = get_post($post_id);
// check for an edited post before TT was installed
if ($post->post_date <= $this->install_date) {
return;
}
// check for private posts
if ($post->post_status == 'private') {
return;
}
$tweet = new aktt_tweet;
$url = apply_filters('tweet_blog_post_url', get_permalink($post_id));
$tweet->tw_text = sprintf(__($this->tweet_format, 'twitter-tools'), @html_entity_decode($post->post_title, ENT_COMPAT, 'UTF-8'), $url);
$tweet = apply_filters('aktt_do_blog_post_tweet', $tweet, $post); // return false here to not tweet
if (!$tweet) {
return;
}
$this->do_tweet($tweet);
add_post_meta($post_id, 'aktt_tweeted', '1', true);
}
function do_tweet_post($tweet) {
global $wpdb;
remove_action('publish_post', 'aktt_notify_twitter', 99);
$data = array(
'post_content' => $wpdb->escape(aktt_make_clickable($tweet->tw_text))
, 'post_title' => $wpdb->escape(trim_add_elipsis($tweet->tw_text, 30))
, 'post_date' => get_date_from_gmt(date('Y-m-d H:i:s', $tweet->tw_created_at))
, 'post_category' => array($this->blog_post_category)
, 'post_status' => 'publish'
, 'post_author' => $wpdb->escape($this->blog_post_author)
);
$data = apply_filters('aktt_do_tweet_post', $data, $tweet); // return false here to not make a blog post
if (!$data) {
return;
}
$post_id = wp_insert_post($data);
add_post_meta($post_id, 'aktt_twitter_id', $tweet->tw_id, true);
wp_set_post_tags($post_id, $this->blog_post_tags);
add_action('publish_post', 'aktt_notify_twitter', 99);
}
}
class aktt_tweet {
function aktt_tweet(
$tw_id = ''
, $tw_text = ''
, $tw_created_at = ''
, $tw_reply_username = null
, $tw_reply_tweet = null
) {
$this->id = '';
$this->modified = '';
$this->tw_created_at = $tw_created_at;
$this->tw_text = $tw_text;
$this->tw_reply_username = $tw_reply_username;
$this->tw_reply_tweet = $tw_reply_tweet;
$this->tw_id = $tw_id;
}
function twdate_to_time($date) {
$parts = explode(' ', $date);
$date = strtotime($parts[1].' '.$parts[2].', '.$parts[5].' '.$parts[3]);
return $date;
}
function tweet_post_exists() {
global $wpdb;
$test = $wpdb->get_results("
SELECT *
FROM $wpdb->postmeta
WHERE meta_key = 'aktt_twitter_id'
AND meta_value = '".$wpdb->escape($this->tw_id)."'
");
if (count($test) > 0) {
return true;
}
return false;
}
function tweet_is_post_notification() {
global $aktt;
if (substr($this->tw_text, 0, strlen($aktt->tweet_prefix)) == $aktt->tweet_prefix) {
return true;
}
return false;
}
function tweet_is_reply() {
// Twitter data changed - users still expect anything starting with @ is a reply
// return !empty($this->tw_reply_tweet);
return (substr($this->tw_text, 0, 1) == '@');
}
function add() {
global $wpdb, $aktt;
$wpdb->query("
INSERT
INTO $wpdb->aktt
( tw_id
, tw_text
, tw_reply_username
, tw_reply_tweet
, tw_created_at
, modified
)
VALUES
( '".$wpdb->escape($this->tw_id)."'
, '".$wpdb->escape($this->tw_text)."'
, '".$wpdb->escape($this->tw_reply_username)."'
, '".$wpdb->escape($this->tw_reply_tweet)."'
, '".date('Y-m-d H:i:s', $this->tw_created_at)."'
, NOW()
)
");
do_action('aktt_add_tweet', $this);
if ($aktt->create_blog_posts == '1' && !$this->tweet_post_exists() && !$this->tweet_is_post_notification() && (!$aktt->exclude_reply_tweets || !$this->tweet_is_reply())) {
$aktt->do_tweet_post($this);
}
}
}
function aktt_api_status_show_url($id) {
return str_replace('###ID###', $id, AKTT_API_STATUS_SHOW);
}
function aktt_profile_url($username) {
return str_replace('###USERNAME###', $username, AKTT_PROFILE_URL);
}
function aktt_profile_link($username, $prefix = '', $suffix = '') {
return $prefix.'<a href="'.aktt_profile_url($username).'" class="aktt_username">'.$username.'</a>'.$suffix;
}
function aktt_hashtag_url($hashtag) {
$hashtag = urlencode('#'.$hashtag);
return str_replace('###HASHTAG###', $hashtag, AKTT_HASHTAG_URL);
}
function aktt_hashtag_link($hashtag, $prefix = '', $suffix = '') {
return $prefix.'<a href="'.aktt_hashtag_url($hashtag).'" class="aktt_hashtag">'.htmlspecialchars($hashtag).'</a>'.$suffix;
}
function aktt_status_url($username, $status) {
return str_replace(
array(
'###USERNAME###'
, '###STATUS###'
)
, array(
$username
, $status
)
, AKTT_STATUS_URL
);
}
function aktt_login_test($username, $password) {
require_once(ABSPATH.WPINC.'/class-snoopy.php');
$snoop = new Snoopy;
$snoop->agent = 'Twitter Tools http://alexking.org/projects/wordpress';
$snoop->user = $username;
$snoop->pass = $password;
$snoop->fetch(AKTT_API_USER_TIMELINE);
if (strpos($snoop->response_code, '200')) {
return __("Login succeeded, you're good to go.", 'twitter-tools');
} else {
$json = new Services_JSON();
$results = $json->decode($snoop->results);
return sprintf(__('Sorry, login failed. Error message from Twitter: %s', 'twitter-tools'), $results->error);
}
}
function aktt_ping_digests() {
global $aktt;
$aktt->ping_digests();
}
function aktt_update_tweets() {
global $aktt;
// let the last update run for 10 minutes
if (time() - intval(get_option('aktt_doing_tweet_download')) < $aktt->tweet_download_interval()) {
return;
}
// wait 10 min between downloads
if (time() - intval(get_option('aktt_last_tweet_download')) < $aktt->tweet_download_interval()) {
return;
}
update_option('aktt_doing_tweet_download', time());
global $wpdb, $aktt;
if (empty($aktt->twitter_username) || empty($aktt->twitter_password)) {
update_option('aktt_doing_tweet_download', '0');
return;
}
require_once(ABSPATH.WPINC.'/class-snoopy.php');
$snoop = new Snoopy;
$snoop->agent = 'Twitter Tools http://alexking.org/projects/wordpress';
$snoop->user = $aktt->twitter_username;
$snoop->pass = $aktt->twitter_password;
$snoop->fetch(AKTT_API_USER_TIMELINE);
if (!strpos($snoop->response_code, '200')) {
update_option('aktt_doing_tweet_download', '0');
return;
}
$data = $snoop->results;
// hash results to see if they're any different than the last update, if so, return
$hash = md5($data);
if ($hash == get_option('aktt_update_hash')) {
update_option('aktt_last_tweet_download', time());
update_option('aktt_doing_tweet_download', '0');
return;
}
$json = new Services_JSON();
$tweets = $json->decode($data);
if (is_array($tweets) && count($tweets) > 0) {
$tweet_ids = array();
foreach ($tweets as $tweet) {
$tweet_ids[] = $wpdb->escape($tweet->id);
}
$existing_ids = $wpdb->get_col("
SELECT tw_id
FROM $wpdb->aktt
WHERE tw_id
IN ('".implode("', '", $tweet_ids)."')
");
$new_tweets = array();
foreach ($tweets as $tw_data) {
if (!$existing_ids || !in_array($tw_data->id, $existing_ids)) {
$tweet = new aktt_tweet(
$tw_data->id
, $tw_data->text
);
$tweet->tw_created_at = $tweet->twdate_to_time($tw_data->created_at);
if (!empty($tw_data->in_reply_to_status_id)) {
$tweet->tw_reply_tweet = $tw_data->in_reply_to_status_id;
$url = aktt_api_status_show_url($tw_data->in_reply_to_status_id);
$snoop->fetch($url);
if (strpos($snoop->response_code, '200') !== false) {
$data = $snoop->results;
$status = $json->decode($data);
$tweet->tw_reply_username = $status->user->screen_name;
}
}
// make sure we haven't downloaded someone else's tweets - happens sometimes due to Twitter hiccups
if (strtolower($tw_data->user->screen_name) == strtolower($aktt->twitter_username)) {
$new_tweets[] = $tweet;
$tweet->add();
}
}
}
}
aktt_reset_tweet_checking($hash, time());
}
function aktt_reset_tweet_checking($hash = '', $time = 0) {
if (!current_user_can('manage_options')) {
return;
}
update_option('aktt_update_hash', $hash);
update_option('aktt_last_tweet_download', $time);
update_option('aktt_doing_tweet_download', '0');
}
function aktt_reset_digests() {
if (!current_user_can('manage_options')) {
return;
}
update_option('aktt_doing_digest_post', '0');
}
function aktt_notify_twitter($post_id) {
global $aktt;
$aktt->do_blog_post_tweet($post_id);
}
add_action('publish_post', 'aktt_notify_twitter', 99);
function aktt_sidebar_tweets() {
global $wpdb, $aktt;
if ($aktt->exclude_reply_tweets) {
$where = "AND tw_text NOT LIKE '@%' ";
}
else {
$where = '';
}
$tweets = $wpdb->get_results("
SELECT *
FROM $wpdb->aktt
WHERE tw_text NOT LIKE '$aktt->tweet_prefix%'
$where
GROUP BY tw_id
ORDER BY tw_created_at DESC
LIMIT $aktt->sidebar_tweet_count
");
$output = '<div class="aktt_tweets">'."\n"
.' <ul>'."\n";
if (count($tweets) > 0) {
foreach ($tweets as $tweet) {
$output .= ' <li>'.aktt_tweet_display($tweet).'</li>'."\n";
}
}
else {
$output .= ' <li>'.__('No tweets available at the moment.', 'twitter-tools').'</li>'."\n";
}
if (!empty($aktt->twitter_username)) {
$output .= ' <li class="aktt_more_updates"><a href="'.aktt_profile_url($aktt->twitter_username).'">'.__('More updates...', 'twitter-tools').'</a></li>'."\n";
}
$output .= '</ul>';
if ($aktt->tweet_from_sidebar == '1' && !empty($aktt->twitter_username) && !empty($aktt->twitter_password)) {
$output .= aktt_tweet_form('input', 'onsubmit="akttPostTweet(); return false;"');
$output .= ' <p id="aktt_tweet_posted_msg">'.__('Posting tweet...', 'twitter-tools').'</p>';
}
if ($aktt->give_tt_credit == '1') {
$output .= '<p class="aktt_credit">'.__('Powered by <a href="http://alexking.org/projects/wordpress">Twitter Tools</a>', 'twitter-tools').'</p>';
}
$output .= '</div>';
print($output);
}
function aktt_latest_tweet() {
global $wpdb, $aktt;
$tweets = $wpdb->get_results("
SELECT *
FROM $wpdb->aktt
WHERE tw_text NOT LIKE '$aktt->tweet_prefix%'
GROUP BY tw_id
ORDER BY tw_created_at DESC
LIMIT 1
");
if (count($tweets) == 1) {
foreach ($tweets as $tweet) {
$output = aktt_tweet_display($tweet);
}
}
else {
$output = __('No tweets available at the moment.', 'twitter-tools');
}
print($output);
}
function aktt_tweet_display($tweet, $time = 'relative') {
global $aktt;
$output = aktt_make_clickable(wp_specialchars($tweet->tw_text));
if (!empty($tweet->tw_reply_username)) {
$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>';
}
switch ($time) {
case 'relative':
$time_display = aktt_relativeTime($tweet->tw_created_at, 3);
break;
case 'absolute':
$time_display = '#';
break;
}
$output .= ' <a href="'.aktt_status_url($aktt->twitter_username, $tweet->tw_id).'" class="aktt_tweet_time">'.$time_display.'</a>';
$output = apply_filters('aktt_tweet_display', $output, $tweet); // allows you to alter the tweet display output
return $output;
}
function aktt_make_clickable($tweet) {
$tweet .= ' ';
$tweet = preg_replace_callback(
'/@([a-zA-Z0-9_]{1,15})([) ])/'
, create_function(
'$matches'
, 'return aktt_profile_link($matches[1], \'@\', $matches[2]);'
)
, $tweet
);
$tweet = preg_replace_callback(
'/\ #([a-zA-Z0-9_]{1,15})/'
, create_function(
'$matches'
, 'return aktt_hashtag_link($matches[1], \' #\', \'\');'
)
, $tweet
);
if (function_exists('make_chunky')) {
return make_chunky($tweet);
}
else {
return make_clickable($tweet);
}
}
function aktt_tweet_form($type = 'input', $extra = '') {
$output = '';
if (current_user_can('publish_posts')) {
$output .= '
<form action="'.get_bloginfo('wpurl').'/index.php" method="post" id="aktt_tweet_form" '.$extra.'>
<fieldset>
';
switch ($type) {
case 'input':
$output .= '
<p><input type="text" size="20" maxlength="140" id="aktt_tweet_text" name="aktt_tweet_text" onkeyup="akttCharCount();" /></p>
<input type="hidden" name="ak_action" value="aktt_post_tweet_sidebar" />
<script type="text/javascript">
//<![CDATA[
function akttCharCount() {
var count = document.getElementById("aktt_tweet_text").value.length;
if (count > 0) {
document.getElementById("aktt_char_count").innerHTML = 140 - count;
}
else {
document.getElementById("aktt_char_count").innerHTML = "";
}
}
setTimeout("akttCharCount();", 500);
document.getElementById("aktt_tweet_form").setAttribute("autocomplete", "off");
//]]>
</script>
';
break;
case 'textarea':
$output .= '
<p><textarea type="text" cols="60" rows="5" maxlength="140" id="aktt_tweet_text" name="aktt_tweet_text" onkeyup="akttCharCount();"></textarea></p>
<input type="hidden" name="ak_action" value="aktt_post_tweet_admin" />
<script type="text/javascript">
//<![CDATA[
function akttCharCount() {
var count = document.getElementById("aktt_tweet_text").value.length;
if (count > 0) {
document.getElementById("aktt_char_count").innerHTML = (140 - count) + "'.__(' characters remaining', 'twitter-tools').'";
}
else {
document.getElementById("aktt_char_count").innerHTML = "";
}
}
setTimeout("akttCharCount();", 500);
document.getElementById("aktt_tweet_form").setAttribute("autocomplete", "off");
//]]>
</script>
';
break;
}
$output .= '
<p>
<input type="submit" id="aktt_tweet_submit" name="aktt_tweet_submit" value="'.__('Post Tweet!', 'twitter-tools').'" class="button-primary" />
<span id="aktt_char_count"></span>
</p>
<div class="clear"></div>
</fieldset>
</form>
';
}
return $output;
}
function aktt_widget_init() {
if (!function_exists('register_sidebar_widget')) {
return;
}
function aktt_widget($args) {
extract($args);
$options = get_option('aktt_widget');
$title = $options['title'];
if (empty($title)) {
}
echo $before_widget . $before_title . $title . $after_title;
aktt_sidebar_tweets();
echo $after_widget;
}
register_sidebar_widget(array(__('Twitter Tools', 'twitter-tools'), 'widgets'), 'aktt_widget');
function aktt_widget_control() {
$options = get_option('aktt_widget');
if (!is_array($options)) {
$options = array(
'title' => __("What I'm Doing...", 'twitter-tools')
);
}
if (isset($_POST['ak_action']) && $_POST['ak_action'] == 'aktt_update_widget_options') {
$options['title'] = strip_tags(stripslashes($_POST['aktt_widget_title']));
update_option('aktt_widget', $options);
// reset checking so that sidebar isn't blank if this is the first time activating
aktt_reset_tweet_checking();
aktt_update_tweets();
}
// Be sure you format your options to be valid HTML attributes.
$title = htmlspecialchars($options['title'], ENT_QUOTES);
// Here is our little form segment. Notice that we don't need a
// complete form. This will be embedded into the existing form.
print('
<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>
<p>'.__('Find additional Twitter Tools options on the <a href="options-general.php?page=twitter-tools.php">Twitter Tools Options page</a>.', 'twitter-tools').'
<input type="hidden" id="ak_action" name="ak_action" value="aktt_update_widget_options" />
');
}
register_widget_control(array(__('Twitter Tools', 'twitter-tools'), 'widgets'), 'aktt_widget_control', 300, 100);
}
add_action('widgets_init', 'aktt_widget_init');
function aktt_init() {
global $wpdb, $aktt;
$aktt = new twitter_tools;
$wpdb->aktt = $wpdb->prefix.'ak_twitter';
$aktt->get_settings();
if (($aktt->last_tweet_download + $aktt->tweet_download_interval()) < time()) {
add_action('shutdown', 'aktt_update_tweets');
add_action('shutdown', 'aktt_ping_digests');
}
if (is_admin() || ($aktt->tweet_from_sidebar && current_user_can('publish_posts'))) {
switch ($aktt->js_lib) {
case 'jquery':
wp_enqueue_script('jquery');
break;
case 'prototype':
wp_enqueue_script('prototype');
break;
}
}
global $wp_version;
if (isset($wp_version) && version_compare($wp_version, '2.5', '>=') && empty ($aktt->install_date)) {
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>';" ) );
}
if (!get_option('aktt_tweet_prefix')) {
update_option('aktt_tweet_prefix', $aktt->tweet_prefix);
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>';" ) );
}
}
add_action('init', 'aktt_init');
function aktt_head() {
global $aktt;
if ($aktt->tweet_from_sidebar) {
print('
<link rel="stylesheet" type="text/css" href="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_css" />
<script type="text/javascript" src="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_js"></script>
');
}
}
add_action('wp_head', 'aktt_head');
function aktt_head_admin() {
print('
<link rel="stylesheet" type="text/css" href="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_css_admin" />
<script type="text/javascript" src="'.get_bloginfo('wpurl').'/index.php?ak_action=aktt_js_admin"></script>
');
}
add_action('admin_head', 'aktt_head_admin');
function aktt_request_handler() {
global $wpdb, $aktt;
if (!empty($_GET['ak_action'])) {
switch($_GET['ak_action']) {
case 'aktt_update_tweets':
aktt_update_tweets();
wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&tweets-updated=true');
die();
break;
case 'aktt_reset_tweet_checking':
aktt_reset_tweet_checking();
wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&tweet-checking-reset=true');
die();
break;
case 'aktt_reset_tweet_checking':
aktt_reset_digests();
wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&digest-reset=true');
die();
break;
case 'aktt_js':
remove_action('shutdown', 'aktt_ping_digests');
header("Content-type: text/javascript");
switch ($aktt->js_lib) {
case 'jquery':
?>
function akttPostTweet() {
var tweet_field = jQuery('#aktt_tweet_text');
var tweet_text = tweet_field.val();
if (tweet_text == '') {
return;
}
var tweet_msg = jQuery("#aktt_tweet_posted_msg");
jQuery.post(
"<?php bloginfo('wpurl'); ?>/index.php"
, {
ak_action: "aktt_post_tweet_sidebar"
, aktt_tweet_text: tweet_text
}
, function(data) {
tweet_msg.html(data);
akttSetReset();
}
);
tweet_field.val('').focus();
jQuery('#aktt_char_count').html('');
jQuery("#aktt_tweet_posted_msg").show();
}
function akttSetReset() {
setTimeout('akttReset();', 2000);
}
function akttReset() {
jQuery('#aktt_tweet_posted_msg').hide();
}
<?php
break;
case 'prototype':
?>
function akttPostTweet() {
var tweet_field = $('aktt_tweet_text');
var tweet_text = tweet_field.value;
if (tweet_text == '') {
return;
}
var tweet_msg = $("aktt_tweet_posted_msg");
var akttAjax = new Ajax.Updater(
tweet_msg,
"<?php bloginfo('wpurl'); ?>/index.php",
{
method: "post",
parameters: "ak_action=aktt_post_tweet_sidebar&aktt_tweet_text=" + tweet_text,
onComplete: akttSetReset
}
);
tweet_field.value = '';
tweet_field.focus();
$('aktt_char_count').innerHTML = '';
tweet_msg.style.display = 'block';
}
function akttSetReset() {
setTimeout('akttReset();', 2000);
}
function akttReset() {
$('aktt_tweet_posted_msg').style.display = 'none';
}
<?php
break;
}
die();
break;
case 'aktt_css':
remove_action('shutdown', 'aktt_ping_digests');
header("Content-Type: text/css");
?>
#aktt_tweet_form {
margin: 0;
padding: 5px 0;
}
#aktt_tweet_form fieldset {
border: 0;
}
#aktt_tweet_form fieldset #aktt_tweet_submit {
float: right;
margin-right: 10px;
}
#aktt_tweet_form fieldset #aktt_char_count {
color: #666;
}
#aktt_tweet_posted_msg {
background: #ffc;
display: none;
margin: 0 0 5px 0;
padding: 5px;
}
#aktt_tweet_form div.clear {
clear: both;
float: none;
}
<?php
die();
break;
case 'aktt_js_admin':
remove_action('shutdown', 'aktt_ping_digests');
header("Content-Type: text/javascript");
switch ($aktt->js_lib) {
case 'jquery':
?>
function akttTestLogin() {
var result = jQuery('#aktt_login_test_result');
result.show().addClass('aktt_login_result_wait').html('<?php _e('Testing...', 'twitter-tools'); ?>');
jQuery.post(
"<?php bloginfo('wpurl'); ?>/index.php"
, {
ak_action: "aktt_login_test"
, aktt_twitter_username: jQuery('#aktt_twitter_username').val()
, aktt_twitter_password: jQuery('#aktt_twitter_password').val()
}
, function(data) {
result.html(data).removeClass('aktt_login_result_wait');
setTimeout('akttTestLoginResult();', 5000);
}
);
};
function akttTestLoginResult() {
jQuery('#aktt_login_test_result').fadeOut('slow');
};
(function($){
jQuery.fn.timepicker = function(){
var hrs = new Array();
for(var h = 1; h <= 12; hrs.push(h++));
var mins = new Array();
for(var m = 0; m < 60; mins.push(m++));
var ap = new Array('am', 'pm');
function pad(n) {
n = n.toString();
return n.length == 1 ? '0' + n : n;
}
this.each(function() {
var v = $(this).val();
if (!v) v = new Date();
var d = new Date(v);
var h = d.getHours();
var m = d.getMinutes();
var p = (h >= 12) ? "pm" : "am";
h = (h > 12) ? h - 12 : h;
var output = '';
output += '<select id="h_' + this.id + '" class="timepicker">';
for (var hr in hrs){
output += '<option value="' + pad(hrs[hr]) + '"';
if(parseInt(hrs[hr], 10) == h || (parseInt(hrs[hr], 10) == 12 && h == 0)) output += ' selected';
output += '>' + pad(hrs[hr]) + '</option>';
}
output += '</select>';
output += '<select id="m_' + this.id + '" class="timepicker">';
for (var mn in mins){
output += '<option value="' + pad(mins[mn]) + '"';
if(parseInt(mins[mn], 10) == m) output += ' selected';
output += '>' + pad(mins[mn]) + '</option>';
}
output += '</select>';
output += '<select id="p_' + this.id + '" class="timepicker">';
for(var pp in ap){
output += '<option value="' + ap[pp] + '"';
if(ap[pp] == p) output += ' selected';
output += '>' + ap[pp] + '</option>';
}
output += '</select>';
$(this).after(output);
var field = this;
$(this).siblings('select.timepicker').change(function() {
var h = parseInt($('#h_' + field.id).val(), 10);
var m = parseInt($('#m_' + field.id).val(), 10);
var p = $('#p_' + field.id).val();
if (p == "am") {
if (h == 12) {
h = 0;
}
} else if (p == "pm") {
if (h < 12) {
h += 12;
}
}
var d = new Date();
d.setHours(h);
d.setMinutes(m);
$(field).val(d.toUTCString());
}).change();
});
return this;
};
jQuery.fn.daypicker = function() {
var days = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
this.each(function() {
var v = $(this).val();
if (!v) v = 0;
v = parseInt(v, 10);
var output = "";
output += '<select id="d_' + this.id + '" class="daypicker">';
for (var i = 0; i < days.length; i++) {
output += '<option value="' + i + '"';
if (v == i) output += ' selected';
output += '>' + days[i] + '</option>';
}
output += '</select>';
$(this).after(output);
var field = this;
$(this).siblings('select.daypicker').change(function() {
$(field).val( $(this).val() );
}).change();
});
};
jQuery.fn.forceToggleClass = function(classNames, bOn) {
return this.each(function() {
jQuery(this)[ bOn ? "addClass" : "removeClass" ](classNames);
});
};
})(jQuery);
jQuery(function() {
// add in the time and day selects
jQuery('form#ak_twittertools input.time').timepicker();
jQuery('form#ak_twittertools input.day').daypicker();
// togglers
jQuery('.time_toggle .toggler').change(function() {
var theSelect = jQuery(this);
theSelect.parent('.time_toggle').forceToggleClass('active', theSelect.val() === "1");
}).change();
});
<?php
break;
case 'prototype':
?>
function akttTestLogin() {
var username = $('aktt_twitter_username').value;
var password = $('aktt_twitter_password').value;
var result = $('aktt_login_test_result');
result.className = 'aktt_login_result_wait';
result.innerHTML = '<?php _e('Testing...', 'twitter-tools'); ?>';
var akttAjax = new Ajax.Updater(
result,
"<?php bloginfo('wpurl'); ?>/index.php",
{
method: "post",
parameters: "ak_action=aktt_login_test&aktt_twitter_username=" + username + "&aktt_twitter_password=" + password,
onComplete: akttTestLoginResult
}
);
}
function akttTestLoginResult() {
$('aktt_login_test_result').className = 'aktt_login_result';
Fat.fade_element('aktt_login_test_result');
}
<?php
break;
}
die();
break;
case 'aktt_css_admin':
remove_action('shutdown', 'aktt_ping_digests');
header("Content-Type: text/css");
?>
#aktt_tweet_form {
margin: 0;
padding: 5px 0;
}
#aktt_tweet_form fieldset {
border: 0;
}
#aktt_tweet_form fieldset textarea {
width: 95%;
}
#aktt_tweet_form fieldset #aktt_tweet_submit {
float: right;
margin-right: 50px;
}
#aktt_tweet_form fieldset #aktt_char_count {
color: #666;
}
#ak_readme {
height: 300px;
width: 95%;
}
#ak_twittertools .options {
overflow: hidden;
border: none;
}
#ak_twittertools .option {
overflow: hidden;
padding-bottom: 9px;
padding-top: 9px;
}
#ak_twittertools .option label {
display: block;
float: left;
width: 200px;
margin-right: 24px;
text-align: right;
}
#ak_twittertools .option span {
display: block;
float: left;
margin-left: 230px;
margin-top: 6px;
clear: left;
}
#ak_twittertools select,
#ak_twittertools input {
float: left;
display: block;
margin-right: 6px;
}
#ak_twittertools p.submit {
overflow: hidden;
}
#ak_twittertools .option span {
color: #666;
display: block;
}
#ak_twittertools #aktt_login_test_result {
display: inline;
padding: 3px;
}
#ak_twittertools fieldset.options .option span.aktt_login_result_wait {
background: #ffc;
}
#ak_twittertools fieldset.options .option span.aktt_login_result {
background: #CFEBF7;
color: #000;
}
#ak_twittertools .timepicker,
#ak_twittertools .daypicker {
display: none;
}
#ak_twittertools .active .timepicker,
#ak_twittertools .active .daypicker {
display: block
}
<?php
die();
break;
}
}
if (!empty($_POST['ak_action'])) {
switch($_POST['ak_action']) {
case 'aktt_update_settings':
$aktt->populate_settings();
$aktt->update_settings();
wp_redirect(get_bloginfo('wpurl').'/wp-admin/options-general.php?page=twitter-tools.php&updated=true');
die();
break;
case 'aktt_post_tweet_sidebar':
if (!empty($_POST['aktt_tweet_text']) && current_user_can('publish_posts')) {
$tweet = new aktt_tweet();
$tweet->tw_text = stripslashes($_POST['aktt_tweet_text']);
if ($aktt->do_tweet($tweet)) {
die(__('Tweet posted.', 'twitter-tools'));
}
else {
die(__('Tweet post failed.', 'twitter-tools'));
}
}
break;
case 'aktt_post_tweet_admin':
if (!empty($_POST['aktt_tweet_text']) && current_user_can('publish_posts')) {
$tweet = new aktt_tweet();
$tweet->tw_text = stripslashes($_POST['aktt_tweet_text']);
if ($aktt->do_tweet($tweet)) {
wp_redirect(get_bloginfo('wpurl').'/wp-admin/post-new.php?page=twitter-tools.php&tweet-posted=true');
}
else {
wp_die(__('Oops, your tweet was not posted. Please check your username and password and that Twitter is up and running happily.', 'twitter-tools'));
}
die();
}
break;
case 'aktt_login_test':
$test = @aktt_login_test(
@stripslashes($_POST['aktt_twitter_username'])
, @stripslashes($_POST['aktt_twitter_password'])
);
die(__($test, 'twitter-tools'));
break;
}
}
}
add_action('init', 'aktt_request_handler', 10);
function aktt_admin_tweet_form() {
global $aktt;
if ( $_GET['tweet-posted'] ) {
print('
<div id="message" class="updated fade">
<p>'.__('Tweet posted.', 'twitter-tools').'</p>
</div>
');
}
print('
<div class="wrap" id="aktt_write_tweet">
');
if (empty($aktt->twitter_username) || empty($aktt->twitter_password)) {
print('
<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>
');
}
else {
print('
<h2>'.__('Write Tweet', 'twitter-tools').'</h2>
<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>
'.aktt_tweet_form('textarea').'
');
}
print('
</div>
');
}
function aktt_options_form() {
global $wpdb, $aktt;
$categories = get_categories('hide_empty=0');
$cat_options = '';
foreach ($categories as $category) {
// WP < 2.3 compatibility
!empty($category->term_id) ? $cat_id = $category->term_id : $cat_id = $category->cat_ID;
!empty($category->name) ? $cat_name = $category->name : $cat_name = $category->cat_name;
if ($cat_id == $aktt->blog_post_category) {
$selected = 'selected="selected"';
}
else {
$selected = '';
}
$cat_options .= "\n\t<option value='$cat_id' $selected>$cat_name</option>";
}
$authors = get_users_of_blog();
$author_options = '';
foreach ($authors as $user) {
$usero = new WP_User($user->user_id);
$author = $usero->data;
// Only list users who are allowed to publish
if (! $usero->has_cap('publish_posts')) {
continue;
}
if ($author->ID == $aktt->blog_post_author) {
$selected = 'selected="selected"';
}
else {
$selected = '';
}
$author_options .= "\n\t<option value='$author->ID' $selected>$author->user_nicename</option>";
}
$js_libs = array(
'jquery' => 'jQuery'
, 'prototype' => 'Prototype'
);
$js_lib_options = '';
foreach ($js_libs as $js_lib => $js_lib_display) {
if ($js_lib == $aktt->js_lib) {
$selected = 'selected="selected"';
}
else {
$selected = '';
}
$js_lib_options .= "\n\t<option value='$js_lib' $selected>$js_lib_display</option>";
}
$digest_tweet_orders = array(
'ASC' => __('Oldest first (Chronological order)', 'twitter-tools'),
'DESC' => __('Newest first (Reverse-chronological order)', 'twitter-tools')
);
$digest_tweet_order_options = '';
foreach ($digest_tweet_orders as $digest_tweet_order => $digest_tweet_order_display) {
if ($digest_tweet_order == $aktt->digest_tweet_order) {
$selected = 'selected="selected"';
}
else {
$selected = '';
}
$digest_tweet_order_options .= "\n\t<option value='$digest_tweet_order' $selected>$digest_tweet_order_display</option>";
}
$yes_no = array(
'create_blog_posts'
, 'create_digest'
, 'create_digest_weekly'
, 'notify_twitter'
, 'notify_twitter_default'
, 'tweet_from_sidebar'
, 'give_tt_credit'
, 'exclude_reply_tweets'
);
foreach ($yes_no as $key) {
$var = $key.'_options';
if ($aktt->$key == '0') {
$$var = '
<option value="0" selected="selected">'.__('No', 'twitter-tools').'</option>
<option value="1">'.__('Yes', 'twitter-tools').'</option>
';
}
else {
$$var = '
<option value="0">'.__('No', 'twitter-tools').'</option>
<option value="1" selected="selected">'.__('Yes', 'twitter-tools').'</option>
';
}
}
if ( $_GET['tweets-updated'] ) {
print('
<div id="message" class="updated fade">
<p>'.__('Tweets updated.', 'twitter-tools').'</p>
</div>
');
}
if ( $_GET['tweet-checking-reset'] ) {
print('
<div id="message" class="updated fade">
<p>'.__('Tweet checking has been reset.', 'twitter-tools').'</p>
</div>
');
}
print('
<div class="wrap" id="aktt_options_page">
<h2>'.__('Twitter Tools Options', 'twitter-tools').'</h2>
<form id="ak_twittertools" name="ak_twittertools" action="'.get_bloginfo('wpurl').'/wp-admin/options-general.php" method="post">
<fieldset class="options">
<div class="option">
<label for="aktt_twitter_username">'.__('Twitter Username', 'twitter-tools').'/'.__('Password', 'twitter-tools').'</label>
<input type="text" size="25" name="aktt_twitter_username" id="aktt_twitter_username" value="'.$aktt->twitter_username.'" autocomplete="off" />
<input type="password" size="25" name="aktt_twitter_password" id="aktt_twitter_password" value="'.$aktt->twitter_password.'" autocomplete="off" />
<input type="button" class="button" name="aktt_login_test" id="aktt_login_test" value="'.__('Test Login Info', 'twitter-tools').'" onclick="akttTestLogin(); return false;" />
<span id="aktt_login_test_result"></span>
</div>
<div class="option">
<label for="aktt_notify_twitter">'.__('Enable option to create a tweet when you post in your blog?', 'twitter-tools').'</label>
<select name="aktt_notify_twitter" id="aktt_notify_twitter">'.$notify_twitter_options.'</select>
</div>
<div class="option">
<label for="aktt_tweet_prefix">'.__('Tweet prefix for new blog posts:', 'twitter-tools').'</label>
<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>
</div>
<div class="option">
<label for="aktt_notify_twitter_default">'.__('Set this on by default?', 'twitter-tools').'</label>
<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>
</div>
<div class="option">
<label for="aktt_create_blog_posts">'.__('Create a blog post from each of your tweets?', 'twitter-tools').'</label>
<select name="aktt_create_blog_posts" id="aktt_create_blog_posts">'.$create_blog_posts_options.'</select>
</div>
<div class="option time_toggle">
<label>'.__('Create a daily digest blog post from your tweets?', 'twitter-tools').'</label>
<select name="aktt_create_digest" class="toggler">'.$create_digest_options.'</select>
<input type="hidden" class="time" id="aktt_digest_daily_time" name="aktt_digest_daily_time" value="'.$aktt->digest_daily_time.'" />
</div>
<div class="option">
<label for="aktt_digest_title">'.__('Title for daily digest posts:', 'twitter-tools').'</label>
<input type="text" size="30" name="aktt_digest_title" id="aktt_digest_title" value="'.$aktt->digest_title.'" />
<span>'.__('Include %s where you want the date. Example: Tweets on %s', 'twitter-tools').'</span>
</div>
<div class="option time_toggle">
<label>'.__('Create a weekly digest blog post from your tweets?', 'twitter-tools').'</label>
<select name="aktt_create_digest_weekly" class="toggler">'.$create_digest_weekly_options.'</select>
<input type="hidden" class="time" name="aktt_digest_weekly_time" id="aktt_digest_weekly_time" value="'.$aktt->digest_weekly_time.'" />
<input type="hidden" class="day" name="aktt_digest_weekly_day" value="'.$aktt->digest_weekly_day.'" />
</div>
<div class="option">
<label for="aktt_digest_title_weekly">'.__('Title for weekly digest posts:', 'twitter-tools').'</label>
<input type="text" size="30" name="aktt_digest_title_weekly" id="aktt_digest_title_weekly" value="'.$aktt->digest_title_weekly.'" />
<span>'.__('Include %s where you want the date. Example: Tweets on %s', 'twitter-tools').'</span>
</div>
<div class="option">
<label for="aktt_digest_tweet_order">'.__('Order of tweets in digest?', 'twitter-tools').'</label>
<select name="aktt_digest_tweet_order" id="aktt_digest_tweet_order">'.$digest_tweet_order_options.'</select>
</div>
<div class="option">
<label for="aktt_blog_post_category">'.__('Category for tweet posts:', 'twitter-tools').'</label>
<select name="aktt_blog_post_category" id="aktt_blog_post_category">'.$cat_options.'</select>
</div>
<div class="option">
<label for="aktt_blog_post_tags">'.__('Tag(s) for your tweet posts:', 'twitter-tools').'</label>
<input name="aktt_blog_post_tags" id="aktt_blog_post_tags" value="'.$aktt->blog_post_tags.'">
<span>'.__('Separate multiple tags with commas. Example: tweets, twitter', 'twitter-tools').'</span>
</div>
<div class="option">
<label for="aktt_blog_post_author">'.__('Author for tweet posts:', 'twitter-tools').'</label>
<select name="aktt_blog_post_author" id="aktt_blog_post_author">'.$author_options.'</select>
</div>
<div class="option">
<label for="aktt_exclude_reply_tweets">'.__('Exclude @reply tweets in your sidebar, digests and created blog posts?', 'twitter-tools').'</label>
<select name="aktt_exclude_reply_tweets" id="aktt_exclude_reply_tweets">'.$exclude_reply_tweets_options.'</select>
</div>
<div class="option">
<label for="aktt_sidebar_tweet_count">'.__('Tweets to show in sidebar:', 'twitter-tools').'</label>
<input type="text" size="3" name="aktt_sidebar_tweet_count" id="aktt_sidebar_tweet_count" value="'.$aktt->sidebar_tweet_count.'" />
<span>'.__('Numbers only please.', 'twitter-tools').'</span>
</div>
<div class="option">
<label for="aktt_tweet_from_sidebar">'.__('Create tweets from your sidebar?', 'twitter-tools').'</label>
<select name="aktt_tweet_from_sidebar" id="aktt_tweet_from_sidebar">'.$tweet_from_sidebar_options.'</select>
</div>
<div class="option">
<label for="aktt_js_lib">'.__('JS Library to use?', 'twitter-tools').'</label>
<select name="aktt_js_lib" id="aktt_js_lib">'.$js_lib_options.'</select>
</div>
<div class="option">
<label for="aktt_give_tt_credit">'.__('Give Twitter Tools credit?', 'twitter-tools').'</label>
<select name="aktt_give_tt_credit" id="aktt_give_tt_credit">'.$give_tt_credit_options.'</select>
</div>
</fieldset>
<p class="submit">
<input type="submit" name="submit" class="button-primary" value="'.__('Update Twitter Tools Options', 'twitter-tools').'" />
</p>
<input type="hidden" name="ak_action" value="aktt_update_settings" class="hidden" style="display: none;" />
</form>
<h2>'.__('Update Tweets / Reset Checking and Digests', 'twitter-tools').'</h2>
<form name="ak_twittertools_updatetweets" action="'.get_bloginfo('wpurl').'/wp-admin/options-general.php" method="get">
<p>'.__('Use these buttons to manually update your tweets or reset the checking settings.', 'twitter-tools').'</p>
<p class="submit">
<input type="submit" name="submit-button" value="'.__('Update Tweets', 'twitter-tools').'" />
<input type="submit" name="reset-button-1" value="'.__('Reset Tweet Checking', 'twitter-tools').'" onclick="document.getElementById(\'ak_action_2\').value = \'aktt_reset_tweet_checking\';" />
<input type="submit" name="reset-button-2" value="'.__('Reset Digests', 'twitter-tools').'" onclick="document.getElementById(\'ak_action_2\').value = \'aktt_reset_digests\';" />
<input type="hidden" name="ak_action" id="ak_action_2" value="aktt_update_tweets" />
</p>
</form>
');
do_action('aktt_options_form');
print('
<h2>'.__('README', 'twitter-tools').'</h2>
<p>'.__('Find answers to common questions here.', 'twitter-tools').'</p>
<iframe id="ak_readme" src="http://alexking.org/projects/wordpress/readme?project=twitter-tools"></iframe>
</div>
');
}
function aktt_post_options() {
global $aktt, $post;
if ($aktt->notify_twitter) {
echo '<div class="postbox">
<h3>'.__('Twitter Tools', 'twitter-tools').'</h3>
<div class="inside">
<p>'.__('Notify Twitter about this post?', 'twitter-tools');
$notify = get_post_meta($post->ID, 'aktt_notify_twitter', true);
if ($notify == '') {
switch ($aktt->notify_twitter_default) {
case '1':
$notify = 'yes';
break;
case '0':
$notify = 'no';
break;
}
}
if ($notify == 'no') {
$yes = '';
$no = 'checked="checked"';
}
else {
$yes = 'checked="checked"';
$no = '';
}
echo '
<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>
<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>
';
echo '
</p>
';
do_action('aktt_post_options');
echo '
</div><!--.inside-->
</div><!--.postbox-->
';
}
}
add_action('edit_form_advanced', 'aktt_post_options');
function aktt_store_post_options($post_id, $post = false) {
global $aktt;
$post = get_post($post_id);
if (!$post || $post->post_type == 'revision') {
return;
}
$notify_meta = get_post_meta($post_id, 'aktt_notify_twitter', true);
$posted_meta = $_POST['aktt_notify_twitter'];
$save = false;
if (!empty($posted_meta)) {
$posted_meta == 'yes' ? $meta = 'yes' : $meta = 'no';
$save = true;
}
else if (empty($notify_meta)) {
$aktt->notify_twitter_default ? $meta = 'yes' : $meta = 'no';
$save = true;
}
else {
$save = false;
}
if ($save) {
if (!update_post_meta($post_id, 'aktt_notify_twitter', $meta)) {
add_post_meta($post_id, 'aktt_notify_twitter', $meta);
}
}
}
add_action('draft_post', 'aktt_store_post_options', 1, 2);
add_action('publish_post', 'aktt_store_post_options', 1, 2);
add_action('save_post', 'aktt_store_post_options', 1, 2);
function aktt_menu_items() {
if (current_user_can('manage_options')) {
add_options_page(
__('Twitter Tools Options', 'twitter-tools')
, __('Twitter Tools', 'twitter-tools')
, 10
, basename(__FILE__)
, 'aktt_options_form'
);
}
if (current_user_can('publish_posts')) {
add_submenu_page(
'post-new.php'
, __('New Tweet', 'twitter-tools')
, __('Tweet', 'twitter-tools')
, 2
, basename(__FILE__)
, 'aktt_admin_tweet_form'
);
}
}
add_action('admin_menu', 'aktt_menu_items');
function aktt_plugin_action_links($links, $file) {
$plugin_file = basename(__FILE__);
if (basename($file) == $plugin_file) {
$settings_link = '<a href="options-general.php?page='.$plugin_file.'">'.__('Settings', 'twitter-tools').'</a>';
array_unshift($links, $settings_link);
}
return $links;
}
add_filter('plugin_action_links', 'aktt_plugin_action_links', 10, 2);
if (!function_exists('trim_add_elipsis')) {
function trim_add_elipsis($string, $limit = 100) {
if (strlen($string) > $limit) {
$string = substr($string, 0, $limit)."...";
}
return $string;
}
}
if (!function_exists('ak_gmmktime')) {
function ak_gmmktime() {
return gmmktime() - get_option('gmt_offset') * 3600;
}
}
/**
based on: http://www.gyford.com/phil/writing/2006/12/02/quick_twitter.php
* Returns a relative date, eg "4 hrs ago".
*
* Assumes the passed-in can be parsed by strtotime.
* Precision could be one of:
* 1 5 hours, 3 minutes, 2 seconds ago (not yet implemented).
* 2 5 hours, 3 minutes
* 3 5 hours
*
* This is all a little overkill, but copied from other places I've used it.
* Also superfluous, now I've noticed that the Twitter API includes something
* similar, but this version is more accurate and less verbose.
*
* @access private.
* @param string date In a format parseable by strtotime().
* @param integer precision
* @return string
*/
function aktt_relativeTime ($date, $precision=2)
{
$now = time();
$time = gmmktime(
substr($date, 11, 2)
, substr($date, 14, 2)
, substr($date, 17, 2)
, substr($date, 5, 2)
, substr($date, 8, 2)
, substr($date, 0, 4)
);
$time = strtotime(date('Y-m-d H:i:s', $time));
$diff = $now - $time;
$months = floor($diff/2419200);
$diff -= $months * 2419200;
$weeks = floor($diff/604800);
$diff -= $weeks*604800;
$days = floor($diff/86400);
$diff -= $days * 86400;
$hours = floor($diff/3600);
$diff -= $hours * 3600;
$minutes = floor($diff/60);
$diff -= $minutes * 60;
$seconds = $diff;
if ($months > 0) {
return date_i18n( __('Y-m-d', 'twitter-tools'), $time);
} else {
$relative_date = '';
if ($weeks > 0) {
// Weeks and days
$relative_date .= ($relative_date?', ':'').$weeks.' '.__ngettext('week', 'weeks', $weeks, 'twitter-tools');
if ($precision <= 2) {
$relative_date .= $days>0? ($relative_date?', ':'').$days.' '.__ngettext('day', 'days', $days, 'twitter-tools'):'';
if ($precision == 1) {
$relative_date .= $hours>0?($relative_date?', ':'').$hours.' '.__ngettext('hr', 'hrs', $hours, 'twitter-tools'):'';
}
}
} elseif ($days > 0) {
// days and hours
$relative_date .= ($relative_date?', ':'').$days.' '.__ngettext('day', 'days', $days, 'twitter-tools');
if ($precision <= 2) {
$relative_date .= $hours>0?($relative_date?', ':'').$hours.' '.__ngettext('hr', 'hrs', $hours, 'twitter-tools'):'';
if ($precision == 1) {
$relative_date .= $minutes>0?($relative_date?', ':'').$minutes.' '.__ngettext('min', 'mins', $minutes, 'twitter-tools'):'';
}
}
} elseif ($hours > 0) {
// hours and minutes
$relative_date .= ($relative_date?', ':'').$hours.' '.__ngettext('hr', 'hrs', $hours, 'twitter-tools');
if ($precision <= 2) {
$relative_date .= $minutes>0?($relative_date?', ':'').$minutes.' '.__ngettext('min', 'mins', $minutes, 'twitter-tools'):'';
if ($precision == 1) {
$relative_date .= $seconds>0?($relative_date?', ':'').$seconds.' '.__ngettext('sec', 'secs', $seconds, 'twitter-tools'):'';
}
}
} elseif ($minutes > 0) {
// minutes only
$relative_date .= ($relative_date?', ':'').$minutes.' '.__ngettext('min', 'mins', $minutes, 'twitter-tools');
if ($precision == 1) {
$relative_date .= $seconds>0?($relative_date?', ':'').$seconds.' '.__ngettext('sec', 'secs', $seconds, 'twitter-tools'):'';
}
} else {
// seconds only
$relative_date .= ($relative_date?', ':'').$seconds.' '.__ngettext('sec', 'secs', $seconds, 'twitter-tools');
}
}
// Return relative date and add proper verbiage
return sprintf(__('%s ago', 'twitter-tools'), $relative_date);
}
if (!class_exists('Services_JSON')) {
// PEAR JSON class
/**
* Converts to and from JSON format.
*
* JSON (JavaScript Object Notation) is a lightweight data-interchange
* format. It is easy for humans to read and write. It is easy for machines
* to parse and generate. It is based on a subset of the JavaScript
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
* This feature can also be found in Python. JSON is a text format that is
* completely language independent but uses conventions that are familiar
* to programmers of the C-family of languages, including C, C++, C#, Java,
* JavaScript, Perl, TCL, and many others. These properties make JSON an
* ideal data-interchange language.
*
* This package provides a simple encoder and decoder for JSON notation. It
* is intended for use with client-side Javascript applications that make
* use of HTTPRequest to perform server communication functions - data can
* be encoded into JSON notation for use in a client-side javascript, or
* decoded from incoming Javascript requests. JSON format is native to
* Javascript, and can be directly eval()'ed with no further parsing
* overhead
*
* All strings should be in ASCII or UTF-8 format!
*
* LICENSE: Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met: Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* @category
* @package Services_JSON
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_SLICE', 1);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_STR', 2);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_ARR', 3);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_OBJ', 4);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_CMT', 5);
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_LOOSE_TYPE', 16);
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
/**
* Converts to and from JSON format.
*
* Brief example of use:
*
* <code>
* // create a new instance of Services_JSON
* $json = new Services_JSON();
*
* // convert a complexe value to JSON notation, and send it to the browser
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
* $output = $json->encode($value);
*
* print($output);
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
*
* // accept incoming POST data, assumed to be in JSON notation
* $input = file_get_contents('php://input', 1000000);
* $value = $json->decode($input);
* </code>
*/
class Services_JSON
{
/**
* constructs a new JSON instance
*
* @param int $use object behavior flags; combine with boolean-OR
*
* possible values:
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
* "{...}" syntax creates associative arrays
* instead of objects in decode().
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
* Values which can't be encoded (e.g. resources)
* appear as NULL instead of throwing errors.
* By default, a deeply-nested resource will
* bubble up with an error, so all return values
* from encode() should be checked with isError()
*/
function Services_JSON($use = 0)
{
$this->use = $use;
}
/**
* convert a string from one UTF-16 char to one UTF-8 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf16 UTF-16 character
* @return string UTF-8 character
* @access private
*/
function utf162utf8($utf16)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
}
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
switch(true) {
case ((0x7F & $bytes) == $bytes):
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x7F & $bytes);
case (0x07FF & $bytes) == $bytes:
// return a 2-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
case (0xFFFF & $bytes) == $bytes:
// return a 3-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xE0 | (($bytes >> 12) & 0x0F))
. chr(0x80 | (($bytes >> 6) & 0x3F))
. chr(0x80 | ($bytes & 0x3F));
}
// ignoring UTF-32 for now, sorry
return '';
}
/**
* convert a string from one UTF-8 char to one UTF-16 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf8 UTF-8 character
* @return string UTF-16 character
* @access private
*/
function utf82utf16($utf8)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
}
switch(strlen($utf8)) {
case 1:
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return $utf8;
case 2:
// return a UTF-16 character from a 2-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8{0}) >> 2))
. chr((0xC0 & (ord($utf8{0}) << 6))
| (0x3F & ord($utf8{1})));
case 3:
// return a UTF-16 character from a 3-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8{0}) << 4))
| (0x0F & (ord($utf8{1}) >> 2)))
. chr((0xC0 & (ord($utf8{1}) << 6))
| (0x7F & ord($utf8{2})));
}
// ignoring UTF-32 for now, sorry
return '';
}
/**
* encodes an arbitrary variable into JSON format
*
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a strng, note that encode() always expects it
* to be in ASCII or UTF-8 format!
*
* @return mixed JSON string representation of input var or an error if a problem occurs
* @access public
*/
function encode($var)
{
switch (gettype($var)) {
case 'boolean':
return $var ? 'true' : 'false';
case 'NULL':
return 'null';
case 'integer':
return (int) $var;
case 'double':
case 'float':
return (float) $var;
case 'string':
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$ascii = '';
$strlen_var = strlen($var);
/*
* Iterate over every character in the string,
* escaping with a slash or encoding to UTF-8 where necessary
*/
for ($c = 0; $c < $strlen_var; ++$c) {
$ord_var_c = ord($var{$c});
switch (true) {
case $ord_var_c == 0x08:
$ascii .= '\b';
break;
case $ord_var_c == 0x09:
$ascii .= '\t';
break;
case $ord_var_c == 0x0A:
$ascii .= '\n';
break;
case $ord_var_c == 0x0C:
$ascii .= '\f';
break;
case $ord_var_c == 0x0D:
$ascii .= '\r';
break;
case $ord_var_c == 0x22:
case $ord_var_c == 0x2F:
case $ord_var_c == 0x5C:
// double quote, slash, slosh
$ascii .= '\\'.$var{$c};
break;
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII)
$ascii .= $var{$c};
break;
case (($ord_var_c & 0xE0) == 0xC0):
// characters U-00000080 - U-000007FF, mask 110XXXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
$c += 1;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xF0) == 0xE0):
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}));
$c += 2;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xF8) == 0xF0):
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}));
$c += 3;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xFC) == 0xF8):
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}));
$c += 4;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xFE) == 0xFC):
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}),
ord($var{$c + 5}));
$c += 5;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
}
}
return '"'.$ascii.'"';
case 'array':
/*
* As per JSON spec if any array key is not an integer
* we must treat the the whole array as an object. We
* also try to catch a sparsely populated associative
* array with numeric keys here because some JS engines
* will create an array with empty indexes up to
* max_index which can cause memory issues and because
* the keys, which may be relevant, will be remapped
* otherwise.
*
* As per the ECMA and JSON specification an object may
* have any string as a property. Unfortunately due to
* a hole in the ECMA specification if the key is a
* ECMA reserved word or starts with a digit the
* parameter is only accessible using ECMAScript's
* bracket notation.
*/
// treat as a JSON object
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
$properties = array_map(array($this, 'name_value'),
array_keys($var),
array_values($var));
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
return '{' . join(',', $properties) . '}';
}
// treat it like a regular array
$elements = array_map(array($this, 'encode'), $var);
foreach($elements as $element) {
if(Services_JSON::isError($element)) {
return $element;
}
}
return '[' . join(',', $elements) . ']';
case 'object':
$vars = get_object_vars($var);
$properties = array_map(array($this, 'name_value'),
array_keys($vars),
array_values($vars));
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
return '{' . join(',', $properties) . '}';
default:
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
? 'null'
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
}
}
/**
* array-walking function for use in generating JSON-formatted name-value pairs
*
* @param string $name name of key to use
* @param mixed $value reference to an array element to be encoded
*
* @return string JSON-formatted name-value pair, like '"name":value'
* @access private
*/
function name_value($name, $value)
{
$encoded_value = $this->encode($value);
if(Services_JSON::isError($encoded_value)) {
return $encoded_value;
}
return $this->encode(strval($name)) . ':' . $encoded_value;
}
/**
* reduce a string by removing leading and trailing comments and whitespace
*
* @param $str string string value to strip of comments and whitespace
*
* @return string string value stripped of comments and whitespace
* @access private
*/
function reduce_string($str)
{
$str = preg_replace(array(
// eliminate single line comments in '// ...' form
'#^\s*//(.+)$#m',
// eliminate multi-line comments in '/* ... */' form, at start of string
'#^\s*/\*(.+)\*/#Us',
// eliminate multi-line comments in '/* ... */' form, at end of string
'#/\*(.+)\*/\s*$#Us'
), '', $str);
// eliminate extraneous space
return trim($str);
}
/**
* decodes a JSON string into appropriate variable
*
* @param string $str JSON-formatted string
*
* @return mixed number, boolean, string, array, or object
* corresponding to given JSON input string.
* See argument 1 to Services_JSON() above for object-output behavior.
* Note that decode() always returns strings
* in ASCII or UTF-8 format!
* @access public
*/
function decode($str)
{
$str = $this->reduce_string($str);
switch (strtolower($str)) {
case 'true':
return true;
case 'false':
return false;
case 'null':
return null;
default:
$m = array();
if (is_numeric($str)) {
// Lookie-loo, it's a number
// This would work on its own, but I'm trying to be
// good about returning integers where appropriate:
// return (float)$str;
// Return float or int, as appropriate
return ((float)$str == (integer)$str)
? (integer)$str
: (float)$str;
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
// STRINGS RETURNED IN UTF-8 FORMAT
$delim = substr($str, 0, 1);
$chrs = substr($str, 1, -1);
$utf8 = '';
$strlen_chrs = strlen($chrs);
for ($c = 0; $c < $strlen_chrs; ++$c) {
$substr_chrs_c_2 = substr($chrs, $c, 2);
$ord_chrs_c = ord($chrs{$c});
switch (true) {
case $substr_chrs_c_2 == '\b':
$utf8 .= chr(0x08);
++$c;
break;
case $substr_chrs_c_2 == '\t':
$utf8 .= chr(0x09);
++$c;
break;
case $substr_chrs_c_2 == '\n':
$utf8 .= chr(0x0A);
++$c;
break;
case $substr_chrs_c_2 == '\f':
$utf8 .= chr(0x0C);
++$c;
break;
case $substr_chrs_c_2 == '\r':
$utf8 .= chr(0x0D);
++$c;
break;
case $substr_chrs_c_2 == '\\"':
case $substr_chrs_c_2 == '\\\'':
case $substr_chrs_c_2 == '\\\\':
case $substr_chrs_c_2 == '\\/':
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
$utf8 .= $chrs{++$c};
}
break;
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
// single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
. chr(hexdec(substr($chrs, ($c + 4), 2)));
$utf8 .= $this->utf162utf8($utf16);
$c += 5;
break;
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
$utf8 .= $chrs{$c};
break;
case ($ord_chrs_c & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 2);
++$c;
break;
case ($ord_chrs_c & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 3);
$c += 2;
break;
case ($ord_chrs_c & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 4);
$c += 3;
break;
case ($ord_chrs_c & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 5);
$c += 4;
break;
case ($ord_chrs_c & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 6);
$c += 5;
break;
}
}
return $utf8;
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
// array, or object notation
if ($str{0} == '[') {
$stk = array(SERVICES_JSON_IN_ARR);
$arr = array();
} else {
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = array();
} else {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = new stdClass();
}
}
array_push($stk, array('what' => SERVICES_JSON_SLICE,
'where' => 0,
'delim' => false));
$chrs = substr($str, 1, -1);
$chrs = $this->reduce_string($chrs);
if ($chrs == '') {
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
} else {
return $obj;
}
}
//print("\nparsing {$chrs}\n");
$strlen_chrs = strlen($chrs);
for ($c = 0; $c <= $strlen_chrs; ++$c) {
$top = end($stk);
$substr_chrs_c_2 = substr($chrs, $c, 2);
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
// found a comma that is not inside a string, array, etc.,
// OR we've reached the end of the character list
$slice = substr($chrs, $top['where'], ($c - $top['where']));
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
if (reset($stk) == SERVICES_JSON_IN_ARR) {
// we are in an array, so just push an element onto the stack
array_push($arr, $this->decode($slice));
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
// we are in an object, so figure
// out the property name and set an
// element in an associative array,
// for now
$parts = array();
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// "name":value pair
$key = $this->decode($parts[1]);
$val = $this->decode($parts[2]);
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// name:value pair, where name is unquoted
$key = $parts[1];
$val = $this->decode($parts[2]);
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
}
}
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
// found a quote, and we are not inside a string
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
//print("Found start of string at {$c}\n");
} elseif (($chrs{$c} == $top['delim']) &&
($top['what'] == SERVICES_JSON_IN_STR) &&
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
// found a quote, we're in a string, and it's not escaped
// we know that it's not escaped becase there is _not_ an
// odd number of backslashes at the end of the string so far
array_pop($stk);
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
} elseif (($chrs{$c} == '[') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-bracket, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
//print("Found start of array at {$c}\n");
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
// found a right-bracket, and we're in an array
array_pop($stk);
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($chrs{$c} == '{') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-brace, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
//print("Found start of object at {$c}\n");
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
// found a right-brace, and we're in an object
array_pop($stk);
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($substr_chrs_c_2 == '/*') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a comment start, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
$c++;
//print("Found start of comment at {$c}\n");
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
// found a comment end, and we're in one now
array_pop($stk);
$c++;
for ($i = $top['where']; $i <= $c; ++$i)
$chrs = substr_replace($chrs, ' ', $i, 1);
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
}
}
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
return $obj;
}
}
}
}
/**
* @todo Ultimately, this should just call PEAR::isError()
*/
function isError($data, $code = null)
{
if (class_exists('pear')) {
return PEAR::isError($data, $code);
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
is_subclass_of($data, 'services_json_error'))) {
return true;
}
return false;
}
}
if (class_exists('PEAR_Error')) {
class Services_JSON_Error extends PEAR_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
}
}
} else {
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
*/
class Services_JSON_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
}
}
}
}
?>