X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Ftwitterapi.php;h=f538a0298148e2c49659dab968e5cb7c5c89a381;hb=0461aafeef55782056b02cd0d8702ae0a0e3812a;hp=a4d183fcd05e3c3739185755fc7e4be40a5d49ea;hpb=be6ce275387971692944c3677ea5e03f6fd038f8;p=quix0rs-gnu-social.git diff --git a/lib/twitterapi.php b/lib/twitterapi.php index a4d183fcd0..f538a02981 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -1,7 +1,7 @@ . */ -if (!defined('LACONICA')) { exit(1); } +if (!defined('LACONICA')) { + exit(1); +} class TwitterapiAction extends Action { var $auth_user; + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + + function prepare($args) + { + parent::prepare($args); + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + function handle($args) { parent::handle($args); } - + + /** + * Overrides XMLOutputter::element to write booleans as strings (true|false). + * See that method's documentation for more info. + * + * @param string $tag Element type or tagname + * @param array $attrs Array of element attributes, as + * key-value pairs + * @param string $content string content of the element + * + * @return void + */ + function element($tag, $attrs=null, $content=null) + { + if (is_bool($content)) { + $content = ($content ? 'true' : 'false'); + } + + return parent::element($tag, $attrs, $content); + } + function twitter_user_array($profile, $get_notice=false) { - $twitter_user = array(); + $twitter_user['id'] = intval($profile->id); $twitter_user['name'] = $profile->getBestName(); - $twitter_user['followers_count'] = $this->count_subscriptions($profile); $twitter_user['screen_name'] = $profile->nickname; - $twitter_user['description'] = ($profile->bio) ? $profile->bio : null; $twitter_user['location'] = ($profile->location) ? $profile->location : null; - $twitter_user['id'] = intval($profile->id); + $twitter_user['description'] = ($profile->bio) ? $profile->bio : null; $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() : + Avatar::defaultImage(AVATAR_STREAM_SIZE); - $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE); - $twitter_user['protected'] = 'false'; # not supported by Laconica yet $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null; + $twitter_user['protected'] = false; # not supported by Laconica yet + $twitter_user['followers_count'] = $this->count_subscriptions($profile); + + // To be supported soon... + $twitter_user['profile_background_color'] = ''; + $twitter_user['profile_text_color'] = ''; + $twitter_user['profile_link_color'] = ''; + $twitter_user['profile_sidebar_fill_color'] = ''; + $twitter_user['profile_sidebar_border_color'] = ''; + + $subbed = DB_DataObject::factory('subscription'); + $subbed->subscriber = $profile->id; + $subbed_count = (int) $subbed->count() - 1; + $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0; + + $twitter_user['created_at'] = $this->date_twitter($profile->created); + + $faves = DB_DataObject::factory('fave'); + $faves->user_id = $user->id; + $faves_count = (int) $faves->count(); + $twitter_user['favourites_count'] = $faves_count; // British spelling! + + // Need to pull up the user for some of this + $user = User::staticGet($profile->id); + + $timezone = 'UTC'; + + if ($user->timezone) { + $timezone = $user->timezone; + } + + $t = new DateTime; + $t->setTimezone(new DateTimeZone($timezone)); + + $twitter_user['utc_offset'] = $t->format('Z'); + $twitter_user['time_zone'] = $timezone; + + // To be supported some day, perhaps + $twitter_user['profile_background_image_url'] = ''; + $twitter_user['profile_background_tile'] = false; + + $notices = DB_DataObject::factory('notice'); + $notices->profile_id = $profile->id; + $notice_count = (int) $notices->count(); + + $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0; + + // Is the requesting user following this user? + $twitter_user['following'] = false; + $twitter_user['notifications'] = false; + + if (isset($apidata['user'])) { + + $twitter_user['following'] = $apidata['user']->isSubscribed($profile); + + // Notifications on? + $sub = Subscription::pkeyGet(array('subscriber' => + $apidata['user']->id, 'subscribed' => $profile->id)); + + if ($sub) { + $twitter_user['notifications'] = ($sub->jabber || $sub->sms); + } + } if ($get_notice) { $notice = $profile->getCurrentNotice(); @@ -60,22 +165,35 @@ class TwitterapiAction extends Action function twitter_status_array($notice, $include_user=true) { - $profile = $notice->getProfile(); $twitter_status = array(); $twitter_status['text'] = $notice->content; - $twitter_status['truncated'] = 'false'; # Not possible on Laconica + $twitter_status['truncated'] = false; # Not possible on Laconica $twitter_status['created_at'] = $this->date_twitter($notice->created); - $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? intval($notice->reply_to) : null; + $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? + intval($notice->reply_to) : null; $twitter_status['source'] = $this->source_link($notice->source); $twitter_status['id'] = intval($notice->id); - $twitter_status['in_reply_to_user_id'] = ($notice->reply_to) ? $this->replier_by_reply(intval($notice->reply_to)) : null; + + $replier_profile = null; + + if ($notice->reply_to) { + $reply = Notice::staticGet(intval($notice->reply_to)); + if ($reply) { + $replier_profile = $reply->getProfile(); + } + } + + $twitter_status['in_reply_to_user_id'] = + ($replier_profile) ? intval($replier_profile->id) : null; + $twitter_status['in_reply_to_screen_name'] = + ($replier_profile) ? $replier_profile->nickname : null; if (isset($this->auth_user)) { - $twitter_status['favorited'] = ($this->auth_user->hasFave($notice)) ? 'true' : 'false'; + $twitter_status['favorited'] = $this->auth_user->hasFave($notice); } else { - $twitter_status['favorited'] = 'false'; + $twitter_status['favorited'] = false; } if ($include_user) { @@ -91,8 +209,6 @@ class TwitterapiAction extends Action { $profile = $notice->getProfile(); - - $server = common_config('site', 'server'); $entry = array(); # We trim() to avoid extraneous whitespace in the output @@ -101,8 +217,12 @@ class TwitterapiAction extends Action $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id)); $entry['published'] = common_date_iso8601($notice->created); - $entry['id'] = "tag:$server,2008:$entry[link]"; + + $taguribase = common_config('integration', 'taguri'); + $entry['id'] = "tag:$taguribase:$entry[link]"; + $entry['updated'] = $entry['published']; + $entry['author'] = $profile->getBestName(); # RSS Item specific $entry['description'] = $entry['content']; @@ -115,7 +235,6 @@ class TwitterapiAction extends Action function twitter_rss_dmsg_array($message) { - $server = common_config('site', 'server'); $entry = array(); $entry['title'] = sprintf('Message from %s to %s', @@ -124,8 +243,12 @@ class TwitterapiAction extends Action $entry['content'] = common_xml_safe_str(trim($message->content)); $entry['link'] = common_local_url('showmessage', array('message' => $message->id)); $entry['published'] = common_date_iso8601($message->created); - $entry['id'] = "tag:$server,2008:$entry[link]"; + + $taguribase = common_config('integration', 'taguri'); + + $entry['id'] = "tag:$taguribase,:$entry[link]"; $entry['updated'] = $entry['published']; + $entry['author'] = $message->getFrom()->getBestName(); # RSS Item specific $entry['description'] = $entry['content']; @@ -137,7 +260,6 @@ class TwitterapiAction extends Action function twitter_dmsg_array($message) { - $twitter_dm = array(); $from_profile = $message->getFrom(); @@ -198,18 +320,6 @@ class TwitterapiAction extends Action $this->elementEnd('item'); } - function show_twitter_atom_entry($entry) - { - $this->elementStart('entry'); - $this->element('title', null, $entry['title']); - $this->element('content', array('type' => 'html'), $entry['content']); - $this->element('id', null, $entry['id']); - $this->element('published', null, $entry['published']); - $this->element('updated', null, $entry['updated']); - $this->element('link', array('href' => $entry['link'], 'rel' => 'alternate', 'type' => 'text/html'), null); - $this->elementEnd('entry'); - } - function show_json_objects($objects) { print(json_encode($objects)); @@ -323,7 +433,7 @@ class TwitterapiAction extends Action $this->end_twitter_rss(); } - function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null) + function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null) { $this->init_document('atom'); @@ -331,23 +441,29 @@ class TwitterapiAction extends Action $this->element('title', null, $title); $this->element('id', null, $id); $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); + if (!is_null($suplink)) { # For FriendFeed's SUP protocol $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup', 'href' => $suplink, 'type' => 'application/json')); } + + if (!is_null($selfuri)) { + $this->element('link', array('href' => $selfuri, + 'rel' => 'self', 'type' => 'application/atom+xml'), null); + } + + $this->element('updated', null, common_date_iso8601('now')); $this->element('subtitle', null, $subtitle); if (is_array($notice)) { foreach ($notice as $n) { - $entry = $this->twitter_rss_entry_array($n); - $this->show_twitter_atom_entry($entry); + $this->raw($n->asAtomEntry()); } } else { while ($notice->fetch()) { - $entry = $this->twitter_rss_entry_array($notice); - $this->show_twitter_atom_entry($entry); + $this->raw($notice->asAtomEntry()); } } @@ -384,23 +500,7 @@ class TwitterapiAction extends Action function date_twitter($dt) { $t = strtotime($dt); - return date("D M d G:i:s O Y", $t); - } - - function replier_by_reply($reply_id) - { - $notice = Notice::staticGet($reply_id); - if ($notice) { - $profile = $notice->getProfile(); - if ($profile) { - return intval($profile->id); - } else { - common_debug('Can\'t find a profile for notice: ' . $notice->id, __FILE__); - } - } else { - common_debug("Can't get notice: $reply_id", __FILE__); - } - return null; + return date("D M d H:i:s O Y", $t); } // XXX: Candidate for a general utility method somewhere? @@ -423,11 +523,11 @@ class TwitterapiAction extends Action function init_document($type='xml') { switch ($type) { - case 'xml': + case 'xml': header('Content-Type: application/xml; charset=utf-8'); $this->startXML(); break; - case 'json': + case 'json': header('Content-Type: application/json; charset=utf-8'); // Check for JSONP callback @@ -436,16 +536,16 @@ class TwitterapiAction extends Action print $callback . '('; } break; - case 'rss': + case 'rss': header("Content-Type: application/rss+xml; charset=utf-8"); $this->init_twitter_rss(); break; - case 'atom': + case 'atom': header('Content-Type: application/atom+xml; charset=utf-8'); $this->init_twitter_atom(); break; - default: - $this->client_error(_('Not a supported data format.')); + default: + $this->clientError(_('Not a supported data format.')); break; } @@ -455,10 +555,10 @@ class TwitterapiAction extends Action function end_document($type='xml') { switch ($type) { - case 'xml': + case 'xml': $this->endXML(); break; - case 'json': + case 'json': // Check for JSONP callback $callback = $this->arg('callback'); @@ -466,20 +566,20 @@ class TwitterapiAction extends Action print ')'; } break; - case 'rss': + case 'rss': $this->end_twitter_rss(); break; - case 'atom': + case 'atom': $this->end_twitter_rss(); break; - default: - $this->client_error(_('Not a supported data format.')); + default: + $this->clientError(_('Not a supported data format.')); break; } return; } - function client_error($msg, $code = 400, $content_type = 'json') + function clientError($msg, $code = 400, $content_type = 'json') { static $status = array(400 => 'Bad Request', @@ -543,27 +643,30 @@ class TwitterapiAction extends Action function init_twitter_atom() { $this->startXML(); - $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom', 'xml:lang' => 'en-US')); + // FIXME: don't hardcode the language here! + $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom', + 'xml:lang' => 'en-US', + 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0')); } function end_twitter_atom() { - $this->endXML(); $this->elementEnd('feed'); + $this->endXML(); } function show_profile($profile, $content_type='xml', $notice=null) { $profile_array = $this->twitter_user_array($profile, true); switch ($content_type) { - case 'xml': + case 'xml': $this->show_twitter_xml_user($profile_array); break; - case 'json': + case 'json': $this->show_json_objects($profile_array); break; - default: - $this->client_error(_('Not a supported data format.')); + default: + $this->clientError(_('Not a supported data format.')); return; } return; @@ -571,8 +674,28 @@ class TwitterapiAction extends Action function get_user($id, $apidata=null) { - if (!$id) { - return $apidata['user']; + if (empty($id)) { + + // Twitter supports these other ways of passing the user ID + if (is_numeric($this->arg('id'))) { + return User::staticGet($this->arg('id')); + } else if ($this->arg('id')) { + $nickname = common_canonical_nickname($this->arg('id')); + return User::staticGet('nickname', $nickname); + } else if ($this->arg('user_id')) { + // This is to ensure that a non-numeric user_id still + // overrides screen_name even if it doesn't get used + if (is_numeric($this->arg('user_id'))) { + return User::staticGet('id', $this->arg('user_id')); + } + } else if ($this->arg('screen_name')) { + $nickname = common_canonical_nickname($this->arg('screen_name')); + return User::staticGet('nickname', $nickname); + } else { + // Fall back to trying the currently authenticated user + return $apidata['user']; + } + } else if (is_numeric($id)) { return User::staticGet($id); } else { @@ -599,13 +722,13 @@ class TwitterapiAction extends Action { $source_name = _($source); switch ($source) { - case 'web': - case 'xmpp': - case 'mail': - case 'omb': - case 'api': + case 'web': + case 'xmpp': + case 'mail': + case 'omb': + case 'api': break; - default: + default: $ns = Notice_source::staticGet($source); if ($ns) { $source_name = '' . $ns->name . ''; @@ -615,79 +738,49 @@ class TwitterapiAction extends Action return $source_name; } - function show_extended_profile($user, $apidata) - { - - $this->auth_user = $apidata['user']; - - $profile = $user->getProfile(); - - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } - - $twitter_user = $this->twitter_user_array($profile, true); - - // Add in extended user fields offered up by this method - $twitter_user['created_at'] = $this->date_twitter($profile->created); - - $subbed = DB_DataObject::factory('subscription'); - $subbed->subscriber = $profile->id; - $subbed_count = (int) $subbed->count() - 1; - - $notices = DB_DataObject::factory('notice'); - $notices->profile_id = $profile->id; - $notice_count = (int) $notices->count(); - - $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0; - $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0; - - // Other fields Twitter sends... - $twitter_user['profile_background_color'] = ''; - $twitter_user['profile_text_color'] = ''; - $twitter_user['profile_link_color'] = ''; - $twitter_user['profile_sidebar_fill_color'] = ''; - - $faves = DB_DataObject::factory('fave'); - $faves->user_id = $user->id; - $faves_count = (int) $faves->count(); - $twitter_user['favourites_count'] = $faves_count; - - $timezone = 'UTC'; - - if ($user->timezone) { - $timezone = $user->timezone; - } - - $t = new DateTime; - $t->setTimezone(new DateTimeZone($timezone)); - $twitter_user['utc_offset'] = $t->format('Z'); - $twitter_user['time_zone'] = $timezone; - - $following = 'false'; - - if (isset($this->auth_user)) { - if ($this->auth_user->isSubscribed($profile)) { - $following = 'true'; + /** + * Returns query argument or default value if not found. Certain + * parameters used throughout the API are lightly scrubbed and + * bounds checked. This overrides Action::arg(). + * + * @param string $key requested argument + * @param string $def default value to return if $key is not provided + * + * @return var $var + */ + function arg($key, $def=null) + { + + // XXX: Do even more input validation/scrubbing? + + if (array_key_exists($key, $this->args)) { + switch($key) { + case 'page': + $page = (int)$this->args['page']; + return ($page < 1) ? 1 : $page; + case 'count': + $count = (int)$this->args['count']; + if ($count < 1) { + return 20; + } elseif ($count > 200) { + return 200; + } else { + return $count; + } + case 'since_id': + $since_id = (int)$this->args['since_id']; + return ($since_id < 1) ? 0 : $since_id; + case 'max_id': + $max_id = (int)$this->args['max_id']; + return ($max_id < 1) ? 0 : $max_id; + case 'since': + return strtotime($this->args['since']); + default: + return parent::arg($key, $def); } - - // Not implemented yet - $twitter_user['notifications'] = 'false'; - } - - $twitter_user['following'] = $following; - - if ($apidata['content-type'] == 'xml') { - $this->init_document('xml'); - $this->show_twitter_xml_user($twitter_user); - $this->end_document('xml'); - } elseif ($apidata['content-type'] == 'json') { - $this->init_document('json'); - $this->show_json_objects($twitter_user); - $this->end_document('json'); + } else { + return $def; } - } }