$this->api_method = $method;
$this->content_type = strtolower($cmdext[1]);
} else {
- #content type will be an extension on the method
+
+ # Requested format / content-type will be an extension on the method
$cmdext = explode('.', $method);
$this->api_method = $cmdext[0];
$this->content_type = strtolower($cmdext[1]);
}
- # XXX Maybe check to see if the command actually exists first?
if($this->requires_auth()) {
if (!isset($_SERVER['PHP_AUTH_USER'])) {
# This header makes basic auth go
header('WWW-Authenticate: Basic realm="Laconica API"');
- # if the user hits cancel -- bam!
+ # If the user hits cancel -- bam!
$this->show_basic_auth_error();
} else {
$nickname = $_SERVER['PHP_AUTH_USER'];
}
}
} else {
+
+ # Caller might give us a username even if not required
+ if (isset($_SERVER['PHP_AUTH_USER'])) {
+ $user = User::staticGet('nickname', $_SERVER['PHP_AUTH_USER']);
+ if ($user) {
+ $this->user = $user;
+ }
+ # Twitter doesn't throw an error if the user isn't found
+ }
+
$this->process_command();
}
}
function process_command() {
$action = "twitapi$this->api_action";
$actionfile = INSTALLDIR."/actions/$action.php";
+
if (file_exists($actionfile)) {
require_once($actionfile);
$action_class = ucfirst($action)."Action";
$action_obj = new $action_class();
if (method_exists($action_obj, $this->api_method)) {
-
$apidata = array( 'content-type' => $this->content_type,
'api_method' => $this->api_method,
'api_arg' => $this->api_arg,
'user' => $this->user);
call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata);
+ } else {
+ common_user_error("API method not found!", $code=404);
}
} else {
common_user_error("API method not found!", $code=404);
}
}
- function show_basic_auth_error() {
+ function show_basic_auth_error() {
header('HTTP/1.1 401 Unauthorized');
- header('Content-type: text/plain');
- print("Could not authenticate you."); # exactly what Twitter says - no \n
+ $msg = 'Could not authenticate you.';
+
+ if ($this->content_type == 'xml') {
+ header('Content-Type: application/xml; charset=utf-8');
+ common_start_xml();
+ common_element_start('hash');
+ common_element('error', NULL, $msg);
+ common_element('request', NULL, $_SERVER['REQUEST_URI']);
+ common_element_end('hash');
+ common_end_xml();
+ } else if ($this->content_type == 'json') {
+ header('Content-Type: application/json; charset=utf-8');
+ $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
+ print(json_encode($error_array));
+ } else {
+ header('Content-type: text/plain');
+ print "$msg\n";
+ }
}
}
function favorites($args, $apidata) {
parent::handle($args);
- $user = null;
-
- // function was called with an argument /favorites/api_arg.format
- if (isset($apidata['api_arg'])) {
-
- if (is_numeric($apidata['api_arg'])) {
- $user = User::staticGet($apidata['api_arg']);
- } else {
- $nickname = common_canonical_nickname($apidata['api_arg']);
- $user = User::staticGet('nickname', $nickname);
- }
- } else {
-
- // if no user was specified, then we'll use the authenticated user
- $user = $apidata['user'];
- }
+ $this->auth_user = $apidata['user'];
+ $user = $this->get_user($apidata['api_arg'], $apidata);
if (!$user) {
- // Set the user to be the auth user if asked-for can't be found
- // honestly! This is what Twitter does, I swear --Zach
- $user = $apidata['user'];
+ $this->client_error('Not Found', 404, $apidata['content-type']);
+ return;
}
$profile = $user->getProfile();
function create($args, $apidata) {
parent::handle($args);
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- common_user_error(_('API method not found!'), $code = 404);
- return;
- }
-
// Check for RESTfulness
if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
// XXX: Twitter just prints the err msg, no XML / JSON.
return;
}
- $user = $apidata['user'];
+ if (!in_array($apidata['content-type'], array('xml', 'json'))) {
+ common_user_error(_('API method not found!'), $code = 404);
+ return;
+ }
+
+ $this->auth_user = $apidata['user'];
+ $user = $this->auth_user;
$notice_id = $apidata['api_arg'];
$notice = Notice::staticGet($notice_id);
}
- //destroy
- //
- //Discontinues friendship with the user specified in the ID parameter as the authenticating user. Returns the un-friended user in the requested format when successful. Returns a string describing the failure condition when unsuccessful.
- //
- //URL: http://twitter.com/friendships/destroy/id.format
- //
- //Formats: xml, json
- //
- //Parameters:
- //
- //* id. Required. The ID or screen name of the user with whom to discontinue friendship. Ex: http://twitter.com/friendships/destroy/12345.json or http://twitter.com/friendships/destroy/bob.xml
-
function destroy($args, $apidata) {
parent::handle($args);
}
- // Tests if a friendship exists between two users.
- //
- //
- // URL: http://twitter.com/friendships/exists.format
- //
- // Formats: xml, json, none
- //
- // Parameters:
- //
- // * user_a. Required. The ID or screen_name of the first user to test friendship for.
- // * user_b. Required. The ID or screen_name of the second user to test friendship for.
- // * Ex: http://twitter.com/friendships/exists.xml?user_a=alice&user_b=bob
-
function exists($args, $apidata) {
parent::handle($args);
require_once(INSTALLDIR.'/lib/twitterapi.php');
-/* XXX: Please don't freak out about all the ugly comments in this file.
- * They are mostly in here for reference while I work on the
- * API. I'll fix things up later to make them look better later. -- Zach
- */
class TwitapistatusesAction extends TwitterapiAction {
function is_readonly() {
}
- /*
- Returns the 20 most recent statuses posted by the authenticating user and that user's friends.
- This is the equivalent of /home on the Web.
-
- URL: http://server/api/statuses/friends_timeline.format
-
- Parameters:
-
- * since. Optional. Narrows the returned results to just those statuses created after the specified
- HTTP-formatted date. The same behavior is available by setting an If-Modified-Since header in
- your HTTP request.
- Ex: http://server/api/statuses/friends_timeline.rss?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
- * since_id. Optional. Returns only statuses with an ID greater than (that is, more recent than)
- the specified ID. Ex: http://server/api/statuses/friends_timeline.xml?since_id=12345
- * count. Optional. Specifies the number of statuses to retrieve. May not be greater than 200.
- Ex: http://server/api/statuses/friends_timeline.xml?count=5
- * page. Optional. Ex: http://server/api/statuses/friends_timeline.rss?page=3
-
- Formats: xml, json, rss, atom
- */
function friends_timeline($args, $apidata) {
parent::handle($args);
$since_id = 0;
}
- // NOTE: before_id is an extensions to Twitter API -- TB
+ // NOTE: before_id is an extension to Twitter API -- TB
if (!$before_id) {
$before_id = 0;
}
$user = $this->get_user($id, $apidata);
+ $this->auth_user = $user;
+
$profile = $user->getProfile();
$sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_("%s and friends"), $user->nickname);
- $id = "tag:$siteserver:friends:".$user->id;
+ $id = "tag:$siteserver:friends:" . $user->id;
$link = common_local_url('all', array('nickname' => $user->nickname));
$subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
}
- /*
- Returns the 20 most recent statuses posted from the authenticating user. It's also possible to
- request another user's timeline via the id parameter below. This is the equivalent of the Web
- /archive page for your own user, or the profile page for a third party.
-
- URL: http://server/api/statuses/user_timeline.format
-
- Formats: xml, json, rss, atom
-
- Parameters:
-
- * id. Optional. Specifies the ID or screen name of the user for whom to return the
- friends_timeline. Ex: http://server/api/statuses/user_timeline/12345.xml or
- http://server/api/statuses/user_timeline/bob.json.
- * count. Optional. Specifies the number of
- statuses to retrieve. May not be greater than 200. Ex:
- http://server/api/statuses/user_timeline.xml?count=5
- * since. Optional. Narrows the returned
- results to just those statuses created after the specified HTTP-formatted date. The same
- behavior is available by setting an If-Modified-Since header in your HTTP request. Ex:
- http://server/api/statuses/user_timeline.rss?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
- * since_id. Optional. Returns only statuses with an ID greater than (that is, more recent than)
- the specified ID. Ex: http://server/api/statuses/user_timeline.xml?since_id=12345 * page.
- Optional. Ex: http://server/api/statuses/friends_timeline.rss?page=3
- */
function user_timeline($args, $apidata) {
parent::handle($args);
- $user = null;
-
- // function was called with an argument /statuses/user_timeline/api_arg.format
- if (isset($apidata['api_arg'])) {
-
- if (is_numeric($apidata['api_arg'])) {
- $user = User::staticGet($apidata['api_arg']);
- } else {
- $nickname = common_canonical_nickname($apidata['api_arg']);
- $user = User::staticGet('nickname', $nickname);
- }
- } else {
-
- // if no user was specified, then we'll use the authenticated user
- $user = $apidata['user'];
- }
+ $this->auth_user = $apidata['user'];
+ $user = $this->get_user($apidata['api_arg'], $apidata);
if (!$user) {
- // Set the user to be the auth user if asked-for can't be found
- // honestly! This is what Twitter does, I swear --Zach
- $user = $apidata['user'];
+ $this->client_error('Not Found', 404, $apidata['content-type']);
+ return;
}
$profile = $user->getProfile();
return;
}
- $user = $apidata['user'];
+ $this->auth_user = $apidata['user'];
+ $user = $this->auth_user;
$status = $this->trimmed('status');
$source = $this->trimmed('source');
$in_reply_to_status_id = intval($this->trimmed('in_reply_to_status_id'));
$this->show($args, $apidata);
}
- /*
- Returns the 20 most recent @replies (status updates prefixed with @username) for the authenticating user.
- URL: http://server/api/statuses/replies.format
-
- Formats: xml, json, rss, atom
-
- Parameters:
-
- * page. Optional. Retrieves the 20 next most recent replies. Ex: http://server/api/statuses/replies.xml?page=3
- * since. Optional. Narrows the returned results to just those replies created after the specified HTTP-formatted date. The
- same behavior is available by setting an If-Modified-Since header in your HTTP request. Ex:
- http://server/api/statuses/replies.xml?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
- * since_id. Optional. Returns only statuses with an ID greater than (that is, more recent than) the specified
- ID. Ex: http://server/api/statuses/replies.xml?since_id=12345
- */
function replies($args, $apidata) {
parent::handle($args);
$since = $this->arg('since');
-
$count = $this->arg('count');
$page = $this->arg('page');
$since_id = $this->arg('since_id');
$before_id = $this->arg('before_id');
- $user = $apidata['user'];
+ $this->auth_user = $apidata['user'];
+ $user = $this->auth_user;
$profile = $user->getProfile();
$sitename = common_config('site', 'name');
$since_id = 0;
}
- // NOTE: before_id is an extensions to Twitter API -- TB
+ // NOTE: before_id is an extension to Twitter API -- TB
if (!$before_id) {
$before_id = 0;
}
return;
}
+ $this->auth_user = $apidata['user'];
$notice_id = $apidata['api_arg'];
$notice = Notice::staticGet($notice_id);
}
-
- /*
- Destroys the status specified by the required ID parameter. The authenticating user must be
- the author of the specified status.
-
- URL: http://server/api/statuses/destroy/id.format
-
- Formats: xml, json
-
- Parameters:
-
- * id. Required. The ID of the status to destroy. Ex:
- http://server/api/statuses/destroy/12345.json or
- http://server/api/statuses/destroy/23456.xml
-
- */
function destroy($args, $apidata) {
parent::handle($args);
return;
}
- $user = $apidata['user'];
+ $this->auth_user = $apidata['user'];
+ $user = $this->auth_user;
$notice_id = $apidata['api_arg'];
$notice = Notice::staticGet($notice_id);
}
- # User Methods
-
- /*
- Returns up to 100 of the authenticating user's friends who have most recently updated, each with current status inline.
- It's also possible to request another user's recent friends list via the id parameter below.
-
- URL: http://server/api/statuses/friends.format
-
- Formats: xml, json
-
- Parameters:
-
- * id. Optional. The ID or screen name of the user for whom to request a list of friends. Ex:
- http://server/api/statuses/friends/12345.json
- or
- http://server/api/statuses/friends/bob.xml
- * page. Optional. Retrieves the next 100 friends. Ex: http://server/api/statuses/friends.xml?page=2
- * lite. Optional. Prevents the inline inclusion of current status. Must be set to a value of true. Ex:
- http://server/api/statuses/friends.xml?lite=true
- * since. Optional. Narrows the returned results to just those friendships created after the specified
- HTTP-formatted date. The same behavior is available by setting an If-Modified-Since header in your HTTP
- request. Ex: http://server/api/statuses/friends.xml?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
- */
function friends($args, $apidata) {
parent::handle($args);
return $this->subscriptions($apidata, 'subscribed', 'subscriber');
}
- /*
- Returns the authenticating user's followers, each with current status inline. They are ordered by the
- order in which they joined Twitter (this is going to be changed).
-
- URL: http://server/api/statuses/followers.format
- Formats: xml, json
-
- Parameters:
-
- * id. Optional. The ID or screen name of the user for whom to request a list of followers. Ex:
- http://server/api/statuses/followers/12345.json
- or
- http://server/api/statuses/followers/bob.xml
- * page. Optional. Retrieves the next 100 followers. Ex: http://server/api/statuses/followers.xml?page=2
- * lite. Optional. Prevents the inline inclusion of current status. Must be set to a value of true.
- Ex: http://server/api/statuses/followers.xml?lite=true
- */
function followers($args, $apidata) {
parent::handle($args);
function subscriptions($apidata, $other_attr, $user_attr) {
- $user = $this->get_subs_user($apidata);
-
- # XXX: id
# XXX: lite
+ $this->auth_user = $apidate['user'];
+ $user = $this->get_user($apidata['api_arg'], $apidata);
+
+ if (!$user) {
+ $this->client_error('Not Found', 404, $apidata['content-type']);
+ return;
+ }
+
$page = $this->trimmed('page');
if (!$page || !is_numeric($page)) {
$this->end_document($type);
}
- function get_subs_user($apidata) {
-
- // function was called with an argument /statuses/user_timeline/api_arg.format
- if (isset($apidata['api_arg'])) {
-
- if (is_numeric($apidata['api_arg'])) {
- $user = User::staticGet($apidata['api_arg']);
- } else {
- $nickname = common_canonical_nickname($apidata['api_arg']);
- $user = User::staticGet('nickname', $nickname);
- }
- } else {
-
- // if no user was specified, then we'll use the authenticated user
- $user = $apidata['user'];
- }
-
- if (!$user) {
- // Set the user to be the auth user if asked-for can't be found
- // honestly! This is what Twitter does, I swear --Zach
- $user = $apidata['user'];
- }
-
- return $user;
- }
-
function show_profiles($profiles, $type) {
switch ($type) {
case 'xml':
}
}
- /*
- Returns a list of the users currently featured on the site with their current statuses inline.
- URL: http://server/api/statuses/featured.format
-
- Formats: xml, json
- */
function featured($args, $apidata) {
parent::handle($args);
common_server_error(_('API method under construction.'), $code=501);
}
- function get_user($id, $apidata) {
- if (!$id) {
- return $apidata['user'];
- } else if (is_numeric($id)) {
- return User::staticGet($id);
- } else {
- return User::staticGet('nickname', $id);
- }
- }
-
function supported($cmd) {
$cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand', 'FavCommand', 'OnCommand', 'OffCommand');
}
}
-
return true;
}
-/*
- Returns extended information of a given user, specified by ID or
- screen name as per the required id parameter below. This information
- includes design settings, so third party developers can theme their
- widgets according to a given user's preferences. You must be properly
- authenticated to request the page of a protected user.
-
- URL: http://twitter.com/users/show/id.format
-
- Formats: xml, json
-
- Parameters:
-
- * id. Required. The ID or screen name of a user.
- Ex: http://twitter.com/users/show/12345.json or
- http://twitter.com/users/show/bob.xml
-
- * email. Optional. The email address of a user. Ex:
- http://twitter.com/users/show.xml?email=test@example.com
-
-*/
function show($args, $apidata) {
parent::handle($args);
return;
}
+ $this->auth_user = $apidata['user'];
$user = null;
$email = $this->arg('email');
- if (isset($apidata['api_arg'])) {
- if (is_numeric($apidata['api_arg'])) {
- // by user id
- $user = User::staticGet($apidata['api_arg']);
- } else {
- // by nickname
- $nickname = common_canonical_nickname($apidata['api_arg']);
- $user = User::staticGet('nickname', $nickname);
- }
- } elseif ($email) {
- // or, find user by email address
- // XXX: The Twitter API spec say an id is *required*, but you can actually
- // pull up a user with just an email address. -- Zach
+ if ($email) {
$user = User::staticGet('email', $email);
+ } elseif (isset($apidata['api_arg'])) {
+ $user = $this->get_user($apidata['api_arg']);
}
if (!$user) {
// XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach
- $this->client_error(_('User not found.'), 404, $apidata['content-type']);
+ $this->client_error(_('Not found.'), 404, $apidata['content-type']);
return;
}
$twitter_user['profile_text_color'] = '';
$twitter_user['profile_link_color'] = '';
$twitter_user['profile_sidebar_fill_color'] = '';
- $twitter_user['favourites_count'] = 0;
- $twitter_user['utc_offset'] = '';
- $twitter_user['time_zone'] = '';
- $twitter_user['following'] = '';
- $twitter_user['notifications'] = '';
+
+ $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;
+
+ if (isset($this->auth_user)) {
+
+ if ($this->auth_user->isSubscribed($profile)) {
+ $twitter_user['following'] = 'true';
+ } else {
+ $twitter_user['following'] = 'false';
+ }
+
+ // Not implemented yet
+ $twitter_user['notifications'] = 'false';
+ }
if ($apidata['content-type'] == 'xml') {
$this->init_document('xml');
class TwitterapiAction extends Action {
+ var $auth_user;
+
function handle($args) {
parent::handle($args);
}
return $twitter_user;
}
- function twitter_status_array($notice, $get_user=true) {
+ function twitter_status_array($notice, $include_user=true) {
- $twitter_status = array();
+ $profile = $notice->getProfile();
+ $twitter_status = array();
$twitter_status['text'] = $notice->content;
$twitter_status['truncated'] = 'false'; # Not possible on Laconica
$twitter_status['created_at'] = $this->date_twitter($notice->created);
$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;
- $twitter_status['favorited'] = NULL; # XXX: Not implemented on Laconica yet.
+
+ if (isset($this->auth_user)) {
+ common_debug("auth user set: " . $this->auth_user->nickname);
+ $twitter_status['favorited'] = ($this->auth_user->hasFave($notice)) ? 'true' : 'false';
+ } else {
+ common_debug("no auth user set");
+ $twitter_status['favorited'] = 'false';
+ }
- if ($get_user) {
- $profile = $notice->getProfile();
+ if ($include_user) {
# Don't get notice (recursive!)
- $twitter_user = $this->twitter_user_array($profile, false);
+ $twitter_user = $this->twitter_user_array($profile, false, $user);
$twitter_status['user'] = $twitter_user;
}
}
}
- function init_document($type='xml') {
+ function init_document($type='xml') {
switch ($type) {
case 'xml':
header('Content-Type: application/xml; charset=utf-8');
return;
}
- function get_user($id) {
- if (is_numeric($id)) {
+ function get_user($id, $apidata=NULL) {
+ if (!$id) {
+ return $apidata['user'];
+ } else if (is_numeric($id)) {
return User::staticGet($id);
} else {
- return User::staticGet('nickname', $id);
+ $nickname = common_canonical_nickname($id);
+ return User::staticGet('nickname', $nickname);
}
}