forked from GNUsocial/gnu-social
5bab0288af
* 0.9.x: (39 commits)
Timeout a little incase the notice item from XHR response is
Relocated the button for pop up window for notice stream
Script no longer needed for Realtime plugin
Better check to see if the XML prolog should be outputted for XML
Outputting UTF-8 charset in document header irrespective of mimetype.
Switched Doctype to XHTML 1.0 Strict (which best reflects the current
Twitter API returns server errors in preferred format
move HTTP error code strings to class variables
remove string-checks from code using Notice::saveNew()
change string return from Notice::saveNew to exceptions
stop overwriting created timestamp on group edit
Forgot to add home_timeline to the list of methods that only require
Forgot to add home_timeline to the list of methods that only require
moderator can delete another user's notice
show delete button when user has deleteOthersNotice right
let hooks override standard user rights
user rights
Merge DeleteAction class into DeletenoticeAction
Fix some bugs in the URL linkification, and fixed the unit test.
Fix URL linkification test cases for addition of 'title' attribution with long URL in f3c8fccc
...
1217 lines
38 KiB
PHP
1217 lines
38 KiB
PHP
<?php
|
|
/*
|
|
* StatusNet - the distributed open-source microblogging tool
|
|
* Copyright (C) 2008, 2009, StatusNet, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|
exit(1);
|
|
}
|
|
|
|
class TwitterapiAction extends Action
|
|
{
|
|
|
|
/**
|
|
* 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['screen_name'] = $profile->nickname;
|
|
$twitter_user['location'] = ($profile->location) ? $profile->location : null;
|
|
$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['url'] = ($profile->homepage) ? $profile->homepage : null;
|
|
$twitter_user['protected'] = false; # not supported by StatusNet yet
|
|
$twitter_user['followers_count'] = $profile->subscriberCount();
|
|
|
|
// 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'] = '';
|
|
|
|
$twitter_user['friends_count'] = $profile->subscriptionCount();
|
|
|
|
$twitter_user['created_at'] = $this->date_twitter($profile->created);
|
|
|
|
$twitter_user['favourites_count'] = $profile->faveCount(); // 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;
|
|
|
|
$twitter_user['statuses_count'] = $profile->noticeCount();
|
|
|
|
// 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();
|
|
if ($notice) {
|
|
# don't get user!
|
|
$twitter_user['status'] = $this->twitter_status_array($notice, false);
|
|
}
|
|
}
|
|
|
|
return $twitter_user;
|
|
}
|
|
|
|
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 StatusNet
|
|
$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['source'] = $this->source_link($notice->source);
|
|
$twitter_status['id'] = intval($notice->id);
|
|
|
|
$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);
|
|
} else {
|
|
$twitter_status['favorited'] = false;
|
|
}
|
|
|
|
// Enclosures
|
|
$attachments = $notice->attachments();
|
|
|
|
if (!empty($attachments)) {
|
|
|
|
$twitter_status['attachments'] = array();
|
|
|
|
foreach ($attachments as $attachment) {
|
|
if ($attachment->isEnclosure()) {
|
|
$enclosure = array();
|
|
$enclosure['url'] = $attachment->url;
|
|
$enclosure['mimetype'] = $attachment->mimetype;
|
|
$enclosure['size'] = $attachment->size;
|
|
$twitter_status['attachments'][] = $enclosure;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($include_user) {
|
|
# Don't get notice (recursive!)
|
|
$twitter_user = $this->twitter_user_array($profile, false);
|
|
$twitter_status['user'] = $twitter_user;
|
|
}
|
|
|
|
return $twitter_status;
|
|
}
|
|
|
|
function twitter_group_array($group)
|
|
{
|
|
$twitter_group=array();
|
|
$twitter_group['id']=$group->id;
|
|
$twitter_group['url']=$group->permalink();
|
|
$twitter_group['nickname']=$group->nickname;
|
|
$twitter_group['fullname']=$group->fullname;
|
|
$twitter_group['homepage_url']=$group->homepage_url;
|
|
$twitter_group['original_logo']=$group->original_logo;
|
|
$twitter_group['homepage_logo']=$group->homepage_logo;
|
|
$twitter_group['stream_logo']=$group->stream_logo;
|
|
$twitter_group['mini_logo']=$group->mini_logo;
|
|
$twitter_group['homepage']=$group->homepage;
|
|
$twitter_group['description']=$group->description;
|
|
$twitter_group['location']=$group->location;
|
|
$twitter_group['created']=$this->date_twitter($group->created);
|
|
$twitter_group['modified']=$this->date_twitter($group->modified);
|
|
return $twitter_group;
|
|
}
|
|
|
|
function twitter_rss_group_array($group)
|
|
{
|
|
$entry = array();
|
|
$entry['content']=$group->description;
|
|
$entry['title']=$group->nickname;
|
|
$entry['link']=$group->permalink();
|
|
$entry['published']=common_date_iso8601($group->created);
|
|
$entry['updated']==common_date_iso8601($group->modified);
|
|
$taguribase = common_config('integration', 'groupuri');
|
|
$entry['id'] = "group:$groupuribase:$entry[link]";
|
|
|
|
$entry['description'] = $entry['content'];
|
|
$entry['pubDate'] = common_date_rfc2822($group->created);
|
|
$entry['guid'] = $entry['link'];
|
|
|
|
return $entry;
|
|
}
|
|
|
|
function twitter_rss_entry_array($notice)
|
|
{
|
|
$profile = $notice->getProfile();
|
|
$entry = array();
|
|
|
|
// We trim() to avoid extraneous whitespace in the output
|
|
|
|
$entry['content'] = common_xml_safe_str(trim($notice->rendered));
|
|
$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);
|
|
|
|
$taguribase = common_config('integration', 'taguri');
|
|
$entry['id'] = "tag:$taguribase:$entry[link]";
|
|
|
|
$entry['updated'] = $entry['published'];
|
|
$entry['author'] = $profile->getBestName();
|
|
|
|
// Enclosures
|
|
$attachments = $notice->attachments();
|
|
$enclosures = array();
|
|
|
|
foreach ($attachments as $attachment) {
|
|
$enclosure_o=$attachment->getEnclosure();
|
|
if ($enclosure_o) {
|
|
$enclosure = array();
|
|
$enclosure['url'] = $enclosure_o->url;
|
|
$enclosure['mimetype'] = $enclosure_o->mimetype;
|
|
$enclosure['size'] = $enclosure_o->size;
|
|
$enclosures[] = $enclosure;
|
|
}
|
|
}
|
|
|
|
if (!empty($enclosures)) {
|
|
$entry['enclosures'] = $enclosures;
|
|
}
|
|
|
|
/*
|
|
// Enclosure
|
|
$attachments = $notice->attachments();
|
|
if($attachments){
|
|
$entry['enclosures']=array();
|
|
foreach($attachments as $attachment){
|
|
if ($attachment->isEnclosure()) {
|
|
$enclosure=array();
|
|
$enclosure['url']=$attachment->url;
|
|
$enclosure['mimetype']=$attachment->mimetype;
|
|
$enclosure['size']=$attachment->size;
|
|
$entry['enclosures'][]=$enclosure;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Tags/Categories
|
|
$tag = new Notice_tag();
|
|
$tag->notice_id = $notice->id;
|
|
if ($tag->find()) {
|
|
$entry['tags']=array();
|
|
while ($tag->fetch()) {
|
|
$entry['tags'][]=$tag->tag;
|
|
}
|
|
}
|
|
$tag->free();
|
|
|
|
// RSS Item specific
|
|
$entry['description'] = $entry['content'];
|
|
$entry['pubDate'] = common_date_rfc2822($notice->created);
|
|
$entry['guid'] = $entry['link'];
|
|
|
|
return $entry;
|
|
}
|
|
|
|
function twitter_rss_dmsg_array($message)
|
|
{
|
|
|
|
$entry = array();
|
|
|
|
$entry['title'] = sprintf('Message from %s to %s',
|
|
$message->getFrom()->nickname, $message->getTo()->nickname);
|
|
|
|
$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);
|
|
|
|
$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'];
|
|
$entry['pubDate'] = common_date_rfc2822($message->created);
|
|
$entry['guid'] = $entry['link'];
|
|
|
|
return $entry;
|
|
}
|
|
|
|
function twitter_dmsg_array($message)
|
|
{
|
|
$twitter_dm = array();
|
|
|
|
$from_profile = $message->getFrom();
|
|
$to_profile = $message->getTo();
|
|
|
|
$twitter_dm['id'] = $message->id;
|
|
$twitter_dm['sender_id'] = $message->from_profile;
|
|
$twitter_dm['text'] = trim($message->content);
|
|
$twitter_dm['recipient_id'] = $message->to_profile;
|
|
$twitter_dm['created_at'] = $this->date_twitter($message->created);
|
|
$twitter_dm['sender_screen_name'] = $from_profile->nickname;
|
|
$twitter_dm['recipient_screen_name'] = $to_profile->nickname;
|
|
$twitter_dm['sender'] = $this->twitter_user_array($from_profile, false);
|
|
$twitter_dm['recipient'] = $this->twitter_user_array($to_profile, false);
|
|
|
|
return $twitter_dm;
|
|
}
|
|
|
|
function twitter_relationship_array($source, $target)
|
|
{
|
|
$relationship = array();
|
|
|
|
$relationship['source'] =
|
|
$this->relationship_details_array($source, $target);
|
|
$relationship['target'] =
|
|
$this->relationship_details_array($target, $source);
|
|
|
|
return array('relationship' => $relationship);
|
|
}
|
|
|
|
function relationship_details_array($source, $target)
|
|
{
|
|
$details = array();
|
|
|
|
$details['screen_name'] = $source->nickname;
|
|
$details['followed_by'] = $target->isSubscribed($source);
|
|
$details['following'] = $source->isSubscribed($target);
|
|
|
|
$notifications = false;
|
|
|
|
if ($source->isSubscribed($target)) {
|
|
|
|
$sub = Subscription::pkeyGet(array('subscriber' =>
|
|
$source->id, 'subscribed' => $target->id));
|
|
|
|
if (!empty($sub)) {
|
|
$notifications = ($sub->jabber || $sub->sms);
|
|
}
|
|
}
|
|
|
|
$details['notifications_enabled'] = $notifications;
|
|
$details['blocking'] = $source->hasBlocked($target);
|
|
$details['id'] = $source->id;
|
|
|
|
return $details;
|
|
}
|
|
|
|
function show_twitter_xml_relationship($relationship)
|
|
{
|
|
$this->elementStart('relationship');
|
|
|
|
foreach($relationship as $element => $value) {
|
|
if ($element == 'source' || $element == 'target') {
|
|
$this->elementStart($element);
|
|
$this->show_xml_relationship_details($value);
|
|
$this->elementEnd($element);
|
|
}
|
|
}
|
|
|
|
$this->elementEnd('relationship');
|
|
}
|
|
|
|
function show_xml_relationship_details($details)
|
|
{
|
|
foreach($details as $element => $value) {
|
|
$this->element($element, null, $value);
|
|
}
|
|
}
|
|
|
|
function show_twitter_xml_status($twitter_status)
|
|
{
|
|
$this->elementStart('status');
|
|
foreach($twitter_status as $element => $value) {
|
|
switch ($element) {
|
|
case 'user':
|
|
$this->show_twitter_xml_user($twitter_status['user']);
|
|
break;
|
|
case 'text':
|
|
$this->element($element, null, common_xml_safe_str($value));
|
|
break;
|
|
case 'attachments':
|
|
$this->show_xml_attachments($twitter_status['attachments']);
|
|
break;
|
|
default:
|
|
$this->element($element, null, $value);
|
|
}
|
|
}
|
|
$this->elementEnd('status');
|
|
}
|
|
|
|
function show_twitter_xml_group($twitter_group)
|
|
{
|
|
$this->elementStart('group');
|
|
foreach($twitter_group as $element => $value) {
|
|
$this->element($element, null, $value);
|
|
}
|
|
$this->elementEnd('group');
|
|
}
|
|
|
|
function show_twitter_xml_user($twitter_user, $role='user')
|
|
{
|
|
$this->elementStart($role);
|
|
foreach($twitter_user as $element => $value) {
|
|
if ($element == 'status') {
|
|
$this->show_twitter_xml_status($twitter_user['status']);
|
|
} else {
|
|
$this->element($element, null, $value);
|
|
}
|
|
}
|
|
$this->elementEnd($role);
|
|
}
|
|
|
|
function show_xml_attachments($attachments) {
|
|
if (!empty($attachments)) {
|
|
$this->elementStart('attachments', array('type' => 'array'));
|
|
foreach ($attachments as $attachment) {
|
|
$attrs = array();
|
|
$attrs['url'] = $attachment['url'];
|
|
$attrs['mimetype'] = $attachment['mimetype'];
|
|
$attrs['size'] = $attachment['size'];
|
|
$this->element('enclosure', $attrs, '');
|
|
}
|
|
$this->elementEnd('attachments');
|
|
}
|
|
}
|
|
|
|
function show_twitter_rss_item($entry)
|
|
{
|
|
$this->elementStart('item');
|
|
$this->element('title', null, $entry['title']);
|
|
$this->element('description', null, $entry['description']);
|
|
$this->element('pubDate', null, $entry['pubDate']);
|
|
$this->element('guid', null, $entry['guid']);
|
|
$this->element('link', null, $entry['link']);
|
|
|
|
# RSS only supports 1 enclosure per item
|
|
if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){
|
|
$enclosure = $entry['enclosures'][0];
|
|
$this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
|
|
}
|
|
|
|
if(array_key_exists('tags', $entry)){
|
|
foreach($entry['tags'] as $tag){
|
|
$this->element('category', null,$tag);
|
|
}
|
|
}
|
|
|
|
$this->elementEnd('item');
|
|
}
|
|
|
|
function show_json_objects($objects)
|
|
{
|
|
print(json_encode($objects));
|
|
}
|
|
|
|
function show_single_xml_status($notice)
|
|
{
|
|
$this->init_document('xml');
|
|
$twitter_status = $this->twitter_status_array($notice);
|
|
$this->show_twitter_xml_status($twitter_status);
|
|
$this->end_document('xml');
|
|
}
|
|
|
|
function show_single_json_status($notice)
|
|
{
|
|
$this->init_document('json');
|
|
$status = $this->twitter_status_array($notice);
|
|
$this->show_json_objects($status);
|
|
$this->end_document('json');
|
|
}
|
|
|
|
function show_single_xml_dmsg($message)
|
|
{
|
|
$this->init_document('xml');
|
|
$dmsg = $this->twitter_dmsg_array($message);
|
|
$this->show_twitter_xml_dmsg($dmsg);
|
|
$this->end_document('xml');
|
|
}
|
|
|
|
function show_single_json_dmsg($message)
|
|
{
|
|
$this->init_document('json');
|
|
$dmsg = $this->twitter_dmsg_array($message);
|
|
$this->show_json_objects($dmsg);
|
|
$this->end_document('json');
|
|
}
|
|
|
|
function show_twitter_xml_dmsg($twitter_dm)
|
|
{
|
|
$this->elementStart('direct_message');
|
|
foreach($twitter_dm as $element => $value) {
|
|
switch ($element) {
|
|
case 'sender':
|
|
case 'recipient':
|
|
$this->show_twitter_xml_user($value, $element);
|
|
break;
|
|
case 'text':
|
|
$this->element($element, null, common_xml_safe_str($value));
|
|
break;
|
|
default:
|
|
$this->element($element, null, $value);
|
|
}
|
|
}
|
|
$this->elementEnd('direct_message');
|
|
}
|
|
|
|
function show_xml_timeline($notice)
|
|
{
|
|
|
|
$this->init_document('xml');
|
|
$this->elementStart('statuses', array('type' => 'array'));
|
|
|
|
if (is_array($notice)) {
|
|
foreach ($notice as $n) {
|
|
$twitter_status = $this->twitter_status_array($n);
|
|
$this->show_twitter_xml_status($twitter_status);
|
|
}
|
|
} else {
|
|
while ($notice->fetch()) {
|
|
$twitter_status = $this->twitter_status_array($notice);
|
|
$this->show_twitter_xml_status($twitter_status);
|
|
}
|
|
}
|
|
|
|
$this->elementEnd('statuses');
|
|
$this->end_document('xml');
|
|
}
|
|
|
|
function show_rss_timeline($notice, $title, $link, $subtitle, $suplink=null)
|
|
{
|
|
|
|
$this->init_document('rss');
|
|
|
|
$this->element('title', null, $title);
|
|
$this->element('link', null, $link);
|
|
if (!is_null($suplink)) {
|
|
# For FriendFeed's SUP protocol
|
|
$this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom',
|
|
'rel' => 'http://api.friendfeed.com/2008/03#sup',
|
|
'href' => $suplink,
|
|
'type' => 'application/json'));
|
|
}
|
|
$this->element('description', null, $subtitle);
|
|
$this->element('language', null, 'en-us');
|
|
$this->element('ttl', null, '40');
|
|
|
|
if (is_array($notice)) {
|
|
foreach ($notice as $n) {
|
|
$entry = $this->twitter_rss_entry_array($n);
|
|
$this->show_twitter_rss_item($entry);
|
|
}
|
|
} else {
|
|
while ($notice->fetch()) {
|
|
$entry = $this->twitter_rss_entry_array($notice);
|
|
$this->show_twitter_rss_item($entry);
|
|
}
|
|
}
|
|
|
|
$this->end_twitter_rss();
|
|
}
|
|
|
|
function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null)
|
|
{
|
|
|
|
$this->init_document('atom');
|
|
|
|
$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) {
|
|
$this->raw($n->asAtomEntry());
|
|
}
|
|
} else {
|
|
while ($notice->fetch()) {
|
|
$this->raw($notice->asAtomEntry());
|
|
}
|
|
}
|
|
|
|
$this->end_document('atom');
|
|
|
|
}
|
|
|
|
function show_rss_groups($group, $title, $link, $subtitle)
|
|
{
|
|
|
|
$this->init_document('rss');
|
|
|
|
$this->element('title', null, $title);
|
|
$this->element('link', null, $link);
|
|
$this->element('description', null, $subtitle);
|
|
$this->element('language', null, 'en-us');
|
|
$this->element('ttl', null, '40');
|
|
|
|
if (is_array($group)) {
|
|
foreach ($group as $g) {
|
|
$twitter_group = $this->twitter_rss_group_array($g);
|
|
$this->show_twitter_rss_item($twitter_group);
|
|
}
|
|
} else {
|
|
while ($group->fetch()) {
|
|
$twitter_group = $this->twitter_rss_group_array($group);
|
|
$this->show_twitter_rss_item($twitter_group);
|
|
}
|
|
}
|
|
|
|
$this->end_twitter_rss();
|
|
}
|
|
|
|
function show_atom_groups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
|
|
{
|
|
|
|
$this->init_document('atom');
|
|
|
|
$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($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($group)) {
|
|
foreach ($group as $g) {
|
|
$this->raw($g->asAtomEntry());
|
|
}
|
|
} else {
|
|
while ($group->fetch()) {
|
|
$this->raw($group->asAtomEntry());
|
|
}
|
|
}
|
|
|
|
$this->end_document('atom');
|
|
|
|
}
|
|
|
|
function show_json_timeline($notice)
|
|
{
|
|
|
|
$this->init_document('json');
|
|
|
|
$statuses = array();
|
|
|
|
if (is_array($notice)) {
|
|
foreach ($notice as $n) {
|
|
$twitter_status = $this->twitter_status_array($n);
|
|
array_push($statuses, $twitter_status);
|
|
}
|
|
} else {
|
|
while ($notice->fetch()) {
|
|
$twitter_status = $this->twitter_status_array($notice);
|
|
array_push($statuses, $twitter_status);
|
|
}
|
|
}
|
|
|
|
$this->show_json_objects($statuses);
|
|
|
|
$this->end_document('json');
|
|
}
|
|
|
|
function show_json_groups($group)
|
|
{
|
|
|
|
$this->init_document('json');
|
|
|
|
$groups = array();
|
|
|
|
if (is_array($group)) {
|
|
foreach ($group as $g) {
|
|
$twitter_group = $this->twitter_group_array($g);
|
|
array_push($groups, $twitter_group);
|
|
}
|
|
} else {
|
|
while ($group->fetch()) {
|
|
$twitter_group = $this->twitter_group_array($group);
|
|
array_push($groups, $twitter_group);
|
|
}
|
|
}
|
|
|
|
$this->show_json_objects($groups);
|
|
|
|
$this->end_document('json');
|
|
}
|
|
|
|
function show_xml_groups($group)
|
|
{
|
|
|
|
$this->init_document('xml');
|
|
$this->elementStart('groups', array('type' => 'array'));
|
|
|
|
if (is_array($group)) {
|
|
foreach ($group as $g) {
|
|
$twitter_group = $this->twitter_group_array($g);
|
|
$this->show_twitter_xml_group($twitter_group);
|
|
}
|
|
} else {
|
|
while ($group->fetch()) {
|
|
$twitter_group = $this->twitter_group_array($group);
|
|
$this->show_twitter_xml_group($twitter_group);
|
|
}
|
|
}
|
|
|
|
$this->elementEnd('groups');
|
|
$this->end_document('xml');
|
|
}
|
|
|
|
function show_twitter_xml_users($user)
|
|
{
|
|
|
|
$this->init_document('xml');
|
|
$this->elementStart('users', array('type' => 'array'));
|
|
|
|
if (is_array($user)) {
|
|
foreach ($group as $g) {
|
|
$twitter_user = $this->twitter_user_array($g);
|
|
$this->show_twitter_xml_user($twitter_user,'user');
|
|
}
|
|
} else {
|
|
while ($user->fetch()) {
|
|
$twitter_user = $this->twitter_user_array($user);
|
|
$this->show_twitter_xml_user($twitter_user);
|
|
}
|
|
}
|
|
|
|
$this->elementEnd('users');
|
|
$this->end_document('xml');
|
|
}
|
|
|
|
function show_json_users($user)
|
|
{
|
|
|
|
$this->init_document('json');
|
|
|
|
$users = array();
|
|
|
|
if (is_array($user)) {
|
|
foreach ($user as $u) {
|
|
$twitter_user = $this->twitter_user_array($u);
|
|
array_push($users, $twitter_user);
|
|
}
|
|
} else {
|
|
while ($user->fetch()) {
|
|
$twitter_user = $this->twitter_user_array($user);
|
|
array_push($users, $twitter_user);
|
|
}
|
|
}
|
|
|
|
$this->show_json_objects($users);
|
|
|
|
$this->end_document('json');
|
|
}
|
|
|
|
function show_single_json_group($group)
|
|
{
|
|
$this->init_document('json');
|
|
$twitter_group = $this->twitter_group_array($group);
|
|
$this->show_json_objects($twitter_group);
|
|
$this->end_document('json');
|
|
}
|
|
|
|
function show_single_xml_group($group)
|
|
{
|
|
$this->init_document('xml');
|
|
$twitter_group = $this->twitter_group_array($group);
|
|
$this->show_twitter_xml_group($twitter_group);
|
|
$this->end_document('xml');
|
|
}
|
|
|
|
// Anyone know what date format this is?
|
|
// Twitter's dates look like this: "Mon Jul 14 23:52:38 +0000 2008" -- Zach
|
|
function date_twitter($dt)
|
|
{
|
|
$t = strtotime($dt);
|
|
return date("D M d H:i:s O Y", $t);
|
|
}
|
|
|
|
// XXX: Candidate for a general utility method somewhere?
|
|
function count_subscriptions($profile)
|
|
{
|
|
|
|
$count = 0;
|
|
$sub = new Subscription();
|
|
$sub->subscribed = $profile->id;
|
|
|
|
$count = $sub->find();
|
|
|
|
if ($count > 0) {
|
|
return $count - 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function init_document($type='xml')
|
|
{
|
|
switch ($type) {
|
|
case 'xml':
|
|
header('Content-Type: application/xml; charset=utf-8');
|
|
$this->startXML();
|
|
break;
|
|
case 'json':
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// Check for JSONP callback
|
|
$callback = $this->arg('callback');
|
|
if ($callback) {
|
|
print $callback . '(';
|
|
}
|
|
break;
|
|
case 'rss':
|
|
header("Content-Type: application/rss+xml; charset=utf-8");
|
|
$this->init_twitter_rss();
|
|
break;
|
|
case 'atom':
|
|
header('Content-Type: application/atom+xml; charset=utf-8');
|
|
$this->init_twitter_atom();
|
|
break;
|
|
default:
|
|
$this->clientError(_('Not a supported data format.'));
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
function end_document($type='xml')
|
|
{
|
|
switch ($type) {
|
|
case 'xml':
|
|
$this->endXML();
|
|
break;
|
|
case 'json':
|
|
|
|
// Check for JSONP callback
|
|
$callback = $this->arg('callback');
|
|
if ($callback) {
|
|
print ')';
|
|
}
|
|
break;
|
|
case 'rss':
|
|
$this->end_twitter_rss();
|
|
break;
|
|
case 'atom':
|
|
$this->end_twitter_rss();
|
|
break;
|
|
default:
|
|
$this->clientError(_('Not a supported data format.'));
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
function clientError($msg, $code = 400, $format = 'xml')
|
|
{
|
|
$action = $this->trimmed('action');
|
|
|
|
common_debug("User error '$code' on '$action': $msg", __FILE__);
|
|
|
|
if (!array_key_exists($code, ClientErrorAction::$status)) {
|
|
$code = 400;
|
|
}
|
|
|
|
$status_string = ClientErrorAction::$status[$code];
|
|
|
|
header('HTTP/1.1 '.$code.' '.$status_string);
|
|
|
|
if ($format == 'xml') {
|
|
$this->init_document('xml');
|
|
$this->elementStart('hash');
|
|
$this->element('error', null, $msg);
|
|
$this->element('request', null, $_SERVER['REQUEST_URI']);
|
|
$this->elementEnd('hash');
|
|
$this->end_document('xml');
|
|
} elseif ($format == 'json'){
|
|
$this->init_document('json');
|
|
$error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
|
|
print(json_encode($error_array));
|
|
$this->end_document('json');
|
|
} else {
|
|
|
|
// If user didn't request a useful format, throw a regular client error
|
|
throw new ClientException($msg, $code);
|
|
}
|
|
}
|
|
|
|
function serverError($msg, $code = 500, $content_type = 'json')
|
|
{
|
|
$action = $this->trimmed('action');
|
|
|
|
common_debug("Server error '$code' on '$action': $msg", __FILE__);
|
|
|
|
if (!array_key_exists($code, ServerErrorAction::$status)) {
|
|
$code = 400;
|
|
}
|
|
|
|
$status_string = ServerErrorAction::$status[$code];
|
|
|
|
header('HTTP/1.1 '.$code.' '.$status_string);
|
|
|
|
if ($content_type == 'xml') {
|
|
$this->init_document('xml');
|
|
$this->elementStart('hash');
|
|
$this->element('error', null, $msg);
|
|
$this->element('request', null, $_SERVER['REQUEST_URI']);
|
|
$this->elementEnd('hash');
|
|
$this->end_document('xml');
|
|
} else {
|
|
$this->init_document('json');
|
|
$error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
|
|
print(json_encode($error_array));
|
|
$this->end_document('json');
|
|
}
|
|
}
|
|
|
|
function init_twitter_rss()
|
|
{
|
|
$this->startXML();
|
|
$this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
|
|
$this->elementStart('channel');
|
|
Event::handle('StartApiRss', array($this));
|
|
}
|
|
|
|
function end_twitter_rss()
|
|
{
|
|
$this->elementEnd('channel');
|
|
$this->elementEnd('rss');
|
|
$this->endXML();
|
|
}
|
|
|
|
function init_twitter_atom()
|
|
{
|
|
$this->startXML();
|
|
// 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'));
|
|
Event::handle('StartApiAtom', array($this));
|
|
}
|
|
|
|
function end_twitter_atom()
|
|
{
|
|
$this->elementEnd('feed');
|
|
$this->endXML();
|
|
}
|
|
|
|
function show_profile($profile, $content_type='xml', $notice=null, $includeStatuses=true)
|
|
{
|
|
$profile_array = $this->twitter_user_array($profile, $includeStatuses);
|
|
switch ($content_type) {
|
|
case 'xml':
|
|
$this->show_twitter_xml_user($profile_array);
|
|
break;
|
|
case 'json':
|
|
$this->show_json_objects($profile_array);
|
|
break;
|
|
default:
|
|
$this->clientError(_('Not a supported data format.'));
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
function get_user($id, $apidata=null)
|
|
{
|
|
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 {
|
|
$nickname = common_canonical_nickname($id);
|
|
return User::staticGet('nickname', $nickname);
|
|
}
|
|
}
|
|
|
|
function getTargetUser($id)
|
|
{
|
|
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 $this->auth_user;
|
|
}
|
|
|
|
} else if (is_numeric($id)) {
|
|
return User::staticGet($id);
|
|
} else {
|
|
$nickname = common_canonical_nickname($id);
|
|
return User::staticGet('nickname', $nickname);
|
|
}
|
|
}
|
|
|
|
function get_group($id, $apidata=null)
|
|
{
|
|
if (empty($id)) {
|
|
|
|
if (is_numeric($this->arg('id'))) {
|
|
return User_group::staticGet($this->arg('id'));
|
|
} else if ($this->arg('id')) {
|
|
$nickname = common_canonical_nickname($this->arg('id'));
|
|
return User_group::staticGet('nickname', $nickname);
|
|
} else if ($this->arg('group_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('group_id'))) {
|
|
return User_group::staticGet('id', $this->arg('group_id'));
|
|
}
|
|
} else if ($this->arg('group_name')) {
|
|
$nickname = common_canonical_nickname($this->arg('group_name'));
|
|
return User_group::staticGet('nickname', $nickname);
|
|
}
|
|
|
|
} else if (is_numeric($id)) {
|
|
return User_group::staticGet($id);
|
|
} else {
|
|
$nickname = common_canonical_nickname($id);
|
|
return User_group::staticGet('nickname', $nickname);
|
|
}
|
|
}
|
|
|
|
function get_profile($id)
|
|
{
|
|
if (is_numeric($id)) {
|
|
return Profile::staticGet($id);
|
|
} else {
|
|
$user = User::staticGet('nickname', $id);
|
|
if ($user) {
|
|
return $user->getProfile();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
function source_link($source)
|
|
{
|
|
$source_name = _($source);
|
|
switch ($source) {
|
|
case 'web':
|
|
case 'xmpp':
|
|
case 'mail':
|
|
case 'omb':
|
|
case 'api':
|
|
break;
|
|
default:
|
|
$ns = Notice_source::staticGet($source);
|
|
if ($ns) {
|
|
$source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
|
|
}
|
|
break;
|
|
}
|
|
return $source_name;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
} else {
|
|
return $def;
|
|
}
|
|
}
|
|
|
|
}
|