summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/apidirectmessage.php389
-rw-r--r--actions/apidirectmessagenew.php192
-rw-r--r--actions/apifriendshipscreate.php134
-rw-r--r--actions/apifriendshipsdestroy.php136
-rw-r--r--actions/apifriendshipsexists.php128
-rw-r--r--actions/apifriendshipsshow.php175
-rw-r--r--actions/apistatusesdestroy.php152
-rw-r--r--actions/apistatusesshow.php196
-rw-r--r--actions/apistatusesupdate.php240
-rw-r--r--actions/apisubscriptions.php275
-rw-r--r--actions/apitimelinefriends.php255
-rw-r--r--actions/apitimelinementions.php234
-rw-r--r--actions/apitimelinepublic.php207
-rw-r--r--actions/apitimelineuser.php249
-rw-r--r--actions/apiuserfollowers.php85
-rw-r--r--actions/apiuserfriends.php85
-rw-r--r--actions/apiusershow.php126
-rw-r--r--actions/twitapidirect_messages.php305
-rw-r--r--actions/twitapifriendships.php250
-rw-r--r--actions/twitapistatuses.php606
-rw-r--r--actions/twitapiusers.php80
-rw-r--r--lib/apiauth.php174
-rw-r--r--lib/apibareauth.php79
-rw-r--r--lib/router.php199
-rw-r--r--lib/twitterapi.php239
25 files changed, 3802 insertions, 1388 deletions
diff --git a/actions/apidirectmessage.php b/actions/apidirectmessage.php
new file mode 100644
index 000000000..87ae44d7a
--- /dev/null
+++ b/actions/apidirectmessage.php
@@ -0,0 +1,389 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a the direct messages from or to a user
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Show a list of direct messages from or to the authenticating user
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiDirectMessageAction extends ApiAuthAction
+{
+ var $format = null;
+ var $messages = null;
+ var $page = null;
+ var $count = null;
+ var $max_id = null;
+ var $since_id = null;
+ var $since = null;
+ var $title = null;
+ var $subtitle = null;
+ var $link = null;
+ var $selfuri_base = null;
+ var $id = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->user = $this->auth_user;
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ $this->page = (int)$this->arg('page', 1);
+ $this->count = (int)$this->arg('count', 20);
+ $this->max_id = (int)$this->arg('max_id', 0);
+ $this->since_id = (int)$this->arg('since_id', 0);
+ $this->since = $this->arg('since');
+
+ $server = common_root_url();
+ $taguribase = common_config('integration', 'taguri');
+
+ if ($this->arg('sent')) {
+
+ // Action was called by /api/direct_messages/sent.format
+
+ $this->title = sprintf(
+ _("Direct messages from %s"),
+ $this->user->nickname
+ );
+ $this->subtitle = sprintf(
+ _("All the direct messages sent from %s"),
+ $this->user->nickname
+ );
+ $this->link = $server . $this->user->nickname . '/outbox';
+ $this->selfuri_base = common_root_url() . 'api/direct_messages/sent';
+ $this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id;
+ } else {
+ $this->title = sprintf(
+ _("Direct messages to %s"),
+ $this->user->nickname
+ );
+ $this->subtitle = sprintf(
+ _("All the direct messages sent to %s"),
+ $this->user->nickname
+ );
+ $this->link = $server . $this->user->nickname . '/inbox';
+ $this->selfuri_base = common_root_url() . 'api/direct_messages';
+ $this->id = "tag:$taguribase:DirectMessages:" . $this->user->id;
+ }
+
+ $this->format = $this->arg('format');
+
+ $this->messages = $this->getMessages();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Show the messages
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $this->showMessages();
+ }
+
+ /**
+ * Show the messages
+ *
+ * @return void
+ */
+
+ function showMessages()
+ {
+ switch($this->format) {
+ case 'xml':
+ $this->showXmlDirectMessages();
+ break;
+ case 'rss':
+ $this->showRssDirectMessages();
+ break;
+ case 'atom':
+ $this->showAtomDirectMessages();
+ break;
+ case 'json':
+ $this->showJsonDirectMessages();
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ break;
+ }
+ }
+
+ /**
+ * Get notices
+ *
+ * @return array notices
+ */
+
+ function getMessages()
+ {
+ $message = new Message();
+
+ if ($this->arg('sent')) {
+ $message->from_profile = $this->user->id;
+ } else {
+ $message->to_profile = $this->user->id;
+ }
+
+ if (!empty($this->max_id)) {
+ $message->whereAdd('id <= ' . $this->max_id);
+ }
+
+ if (!empty($this->since_id)) {
+ $message->whereAdd('id > ' . $this->since_id);
+ }
+
+ if (!empty($since)) {
+ $d = date('Y-m-d H:i:s', $this->since);
+ $message->whereAdd("created > '$d'");
+ }
+
+ $message->orderBy('created DESC, id DESC');
+ $message->limit((($this->page - 1) * $this->count), $this->count);
+ $message->find();
+
+ $messages = array();
+
+ while ($message->fetch()) {
+ $messages[] = clone($message);
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this notice last modified?
+ *
+ * @return string datestamp of the latest notice in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->messages)) {
+ return strtotime($this->messages[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * Shows a list of direct messages as Twitter-style XML array
+ *
+ * @return void
+ */
+
+ function showXmlDirectMessages()
+ {
+ $this->init_document('xml');
+ $this->elementStart('direct-messages', array('type' => 'array'));
+
+ foreach ($this->messages as $m) {
+ $dm_array = $this->directMessageArray($m);
+ $this->showXmlDirectMessage($dm_array);
+ }
+
+ $this->elementEnd('direct-messages');
+ $this->end_document('xml');
+ }
+
+ /**
+ * Shows a list of direct messages as a JSON encoded array
+ *
+ * @return void
+ */
+
+ function showJsonDirectMessages()
+ {
+ $this->init_document('json');
+
+ $dmsgs = array();
+
+ foreach ($this->messages as $m) {
+ $dm_array = $this->directMessageArray($m);
+ array_push($dmsgs, $dm_array);
+ }
+
+ $this->show_json_objects($dmsgs);
+ $this->end_document('json');
+ }
+
+ /**
+ * Shows a list of direct messages as RSS items
+ *
+ * @return void
+ */
+
+ function showRssDirectMessages()
+ {
+ $this->init_document('rss');
+
+ $this->element('title', null, $this->title);
+
+ $this->element('link', null, $this->link);
+ $this->element('description', null, $this->subtitle);
+ $this->element('language', null, 'en-us');
+
+ $this->element(
+ 'atom:link',
+ array(
+ 'type' => 'application/rss+xml',
+ 'href' => $this->selfuri_base . '.rss',
+ 'rel' => self
+ ),
+ null
+ );
+ $this->element('ttl', null, '40');
+
+ foreach ($this->messages as $m) {
+ $entry = $this->rssDirectMessageArray($m);
+ $this->show_twitter_rss_item($entry);
+ }
+
+ $this->end_twitter_rss();
+ }
+
+ /**
+ * Shows a list of direct messages as Atom entries
+ *
+ * @return void
+ */
+
+ function showAtomDirectMessages()
+ {
+ $this->init_document('atom');
+
+ $this->element('title', null, $this->title);
+ $this->element('id', null, $this->id);
+
+ $selfuri = common_root_url() . 'api/direct_messages.atom';
+
+ $this->element(
+ 'link', array(
+ 'href' => $this->link,
+ 'rel' => 'alternate',
+ 'type' => 'text/html'),
+ null
+ );
+ $this->element(
+ 'link', array(
+ 'href' => $this->selfuri_base . '.atom', 'rel' => 'self',
+ 'type' => 'application/atom+xml'),
+ null
+ );
+ $this->element('updated', null, common_date_iso8601('now'));
+ $this->element('subtitle', null, $this->subtitle);
+
+ foreach ($this->messages as $m) {
+ $entry = $this->rssDirectMessageArray($m);
+ $this->showTwitterAtomEntry($entry);
+ }
+
+ $this->end_document('atom');
+ }
+
+ /**
+ * An entity tag for this notice
+ *
+ * Returns an Etag based on the action name, language, and
+ * timestamps of the notice
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->messages)) {
+
+ $last = count($this->messages) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ strtotime($this->messages[0]->created),
+ strtotime($this->messages[$last]->created)
+ )
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
diff --git a/actions/apidirectmessagenew.php b/actions/apidirectmessagenew.php
new file mode 100644
index 000000000..d836409ae
--- /dev/null
+++ b/actions/apidirectmessagenew.php
@@ -0,0 +1,192 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Send a direct message via the API
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Creates a new direct message from the authenticating user to
+ * the user specified by id.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiDirectMessageNewAction extends ApiAuthAction
+{
+ var $format = null;
+ var $source = null;
+ var $user = null;
+ var $other = null;
+ var $content = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->user = $this->auth_user;
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ $this->source = $this->trimmed('source'); // Not supported by Twitter.
+
+ $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
+ if (empty($thtis->source) || in_array($this->source, $reserved_sources)) {
+ $source = 'api';
+ }
+
+ $this->content = $this->trimmed('text');
+
+ $this->user = $this->auth_user;
+
+ $user_param = $this->trimmed('user');
+ $user_id = $this->arg('user_id');
+ $screen_name = $this->trimmed('screen_name');
+
+ if (isset($user_param) || isset($user_id) || isset($screen_name)) {
+ $this->other = $this->getTargetUser($user_param);
+ }
+
+ $this->format = $this->arg('format');
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Save the new message
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ $this->clientError(
+ _('This method requires a POST.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->content)) {
+ $this->clientError(
+ _('No message text!'),
+ 406,
+ $this->format
+ );
+ } else {
+ $content_shortened = common_shorten_links($this->content);
+ if (Message::contentTooLong($content_shortened)) {
+ $this->clientError(
+ sprintf(
+ _('That\'s too long. Max message size is %d chars.'),
+ Message::maxContent()
+ ),
+ 406,
+ $this->format
+ );
+ return;
+ }
+ }
+
+ if (empty($this->other)) {
+ $this->clientError(_('Recipient user not found.'), 403, $this->format);
+ return;
+ } else if (!$this->user->mutuallySubscribed($this->other)) {
+ $this->clientError(
+ _('Can\'t send direct messages to users who aren\'t your friend.'),
+ 403,
+ $this->format
+ );
+ return;
+ } else if ($this->user->id == $this->other->id) {
+
+ // Note: sending msgs to yourself is allowed by Twitter
+
+ $errmsg = 'Don\'t send a message to yourself; ' .
+ 'just say it to yourself quietly instead.'
+
+ $this->clientError(_($errmsg), 403, $this->format);
+ return;
+ }
+
+ $message = Message::saveNew(
+ $this->user->id,
+ $this->other->id,
+ html_entity_decode($this->content, ENT_NOQUOTES, 'UTF-8'),
+ $this->source
+ );
+
+ if (is_string($message)) {
+ $this->serverError($message);
+ return;
+ }
+
+ mail_notify_message($message, $this->user, $this->other);
+
+ if ($this->format == 'xml') {
+ $this->showSingleXmlDirectMessage($message);
+ } elseif ($this->format == 'json') {
+ $this->showSingleJsondirectMessage($message);
+ }
+ }
+
+}
+
diff --git a/actions/apifriendshipscreate.php b/actions/apifriendshipscreate.php
new file mode 100644
index 000000000..d691b5b4f
--- /dev/null
+++ b/actions/apifriendshipscreate.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Subscribe to a user via the API
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Allows the authenticating users to follow (subscribe) the user specified in
+ * the ID parameter. Returns the befriended user in the requested format when
+ * successful. Returns a string describing the failure condition when unsuccessful.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiFriendshipsCreateAction extends ApiAuthAction
+{
+
+ var $format = null;
+ var $user = null;
+ var $other = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->format = $this->arg('format');
+ $this->user = $this->auth_user;
+ $this->other = $this->getTargetUser($id);
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Check the format and show the user info
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ $this->clientError(
+ _('This method requires a POST.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->other)) {
+ $this->clientError(
+ _('Could not follow user: User not found.'),
+ 403,
+ $this->format
+ );
+ return;
+ }
+
+ if ($this->user->isSubscribed($this->other)) {
+ $errmsg = sprintf(
+ _('Could not follow user: %s is already on your list.'),
+ $this->other->nickname
+ );
+ $this->clientError($errmsg, 403, $this->format);
+ return;
+ }
+
+ $result = subs_subscribe_to($this->user, $this->other);
+
+ if (is_string($result)) {
+ $this->clientError($result, 403, $this->format);
+ return;
+ }
+
+ $this->init_document($this->format);
+ $this->show_profile($this->other, $this->format);
+ $this->end_document($this->format);
+ }
+
+}
diff --git a/actions/apifriendshipsdestroy.php b/actions/apifriendshipsdestroy.php
new file mode 100644
index 000000000..d97c5aa6d
--- /dev/null
+++ b/actions/apifriendshipsdestroy.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Unsubscribe to a user via API
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Allows the authenticating users to unfollow (unsubscribe) the user specified in
+ * the ID parameter. Returns the unfollowed user in the requested format when
+ * successful. Returns a string describing the failure condition when unsuccessful.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiFriendshipsDestroyAction extends ApiAuthAction
+{
+
+ var $format = null;
+ var $user = null;
+ var $other = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->format = $this->arg('format');
+ $this->user = $this->auth_user;
+ $this->other = $this->getTargetUser($id);
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Check the format and show the user info
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ $this->clientError(
+ _('This method requires a POST.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->other)) {
+ $this->clientError(
+ _('Could not unfollow user: User not found.'),
+ 403,
+ $this->format
+ );
+ return;
+ }
+
+ // Don't allow unsubscribing from yourself!
+
+ if ($this->user->id == $this->other->id) {
+ $this->clientError(
+ _("You cannot unfollow yourself!"),
+ 403,
+ $this->format
+ );
+ return;
+ }
+
+ $result = subs_unsubscribe_user($this->user, $this->other->nickname);
+
+ if (is_string($result)) {
+ $this->clientError($result, 403, $this->format);
+ return;
+ }
+
+ $this->init_document($this->format);
+ $this->show_profile($this->other, $this->format);
+ $this->end_document($this->format);
+ }
+
+}
diff --git a/actions/apifriendshipsexists.php b/actions/apifriendshipsexists.php
new file mode 100644
index 000000000..3d6e7448d
--- /dev/null
+++ b/actions/apifriendshipsexists.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show whether there is a friendship between two users
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/twitterapi.php';
+
+/**
+ * Tests for the existence of friendship between two users. Will return true if
+ * user_a follows user_b, otherwise will return false.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiFriendshipsExistsAction extends TwitterApiAction
+{
+
+ var $format = null;
+ var $user_a = null;
+ var $user_b = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->format = $this->arg('format');
+
+ $user_a_id = $this->trimmed('user_a');
+ $user_b_id = $this->trimmed('user_b');
+
+ common_debug("user_a = " . $user_a_id);
+ common_debug("user_b = " . $user_b_id);
+
+
+ $this->user_a = $this->getTargetUser($user_a_id);
+
+ if (empty($this->user_a)) {
+ common_debug('gargargra');
+ }
+
+ $this->user_b = $this->getTargetUser($user_b_id);
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Check the format and show the user info
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (empty($this->user_a) || empty($this->user_b)) {
+ $this->clientError(
+ _('Two user ids or screen_names must be supplied.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ $result = $this->user_a->isSubscribed($this->user_b);
+
+ switch ($this->format) {
+ case 'xml':
+ $this->init_document('xml');
+ $this->element('friends', null, $result);
+ $this->end_document('xml');
+ break;
+ case 'json':
+ $this->init_document('json');
+ print json_encode($result);
+ $this->end_document('json');
+ break;
+ default:
+ break;
+ }
+ }
+
+}
diff --git a/actions/apifriendshipsshow.php b/actions/apifriendshipsshow.php
new file mode 100644
index 000000000..d35825a43
--- /dev/null
+++ b/actions/apifriendshipsshow.php
@@ -0,0 +1,175 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show information about the relationship between two users
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Outputs detailed information about the relationship between two users
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiFriendshipsShowAction extends ApiBareAuthAction
+{
+
+ var $format = null;
+ var $user = null;
+ var $source = null;
+ var $target = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->format = $this->arg('format');
+
+ $source_id = (int)$this->trimmed('source_id');
+ $source_screen_name = $this->trimmed('source_screen_name');
+ $target_id = (int)$this->trimmed('target_id');
+ $target_screen_name = $this->trimmed('target_screen_name');
+
+ if (!empty($source_id)) {
+ $this->source = User::staticGet($source_id);
+ } elseif (!empty($source_screen_name)) {
+ $this->source = User::staticGet('nickname', $source_screen_name);
+ } else {
+ $this->source = $this->auth_user;
+ }
+
+ if (!empty($target_id)) {
+ $this->target = User::staticGet($target_id);
+ } elseif (!empty($target_screen_name)) {
+ $this->target = User::staticGet('nickname', $target_screen_name);
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Determines whether this API resource requires auth. Overloaded to look
+ * return true in case source_id and source_screen_name are both empty
+ *
+ * @return boolean true or false
+ */
+
+ function requiresAuth()
+ {
+ if (common_config('site', 'private')) {
+ return true;
+ }
+
+ $source_id = $this->trimmed('source_id');
+ $source_screen_name = $this->trimmed('source_screen_name');
+
+ if (empty($source_id) && empty($source_screen_name)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Check the format and show the user info
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(_('API method not found!'), 404);
+ return;
+ }
+
+ if (empty($this->source)) {
+ $this->clientError(
+ _('Could not determine source user.'),
+ 404
+ );
+ return;
+ }
+
+ if (empty($this->target)) {
+ $this->clientError(
+ _('Could not find target user.'),
+ 404
+ );
+ return;
+ }
+
+ $result = $this->twitter_relationship_array($this->source, $this->target);
+
+ switch ($this->format) {
+ case 'xml':
+ $this->init_document('xml');
+ $this->show_twitter_xml_relationship($result[relationship]);
+ $this->end_document('xml');
+ break;
+ case 'json':
+ $this->init_document('json');
+ print json_encode($result);
+ $this->end_document('json');
+ break;
+ default:
+ break;
+ }
+
+ }
+
+}
diff --git a/actions/apistatusesdestroy.php b/actions/apistatusesdestroy.php
new file mode 100644
index 000000000..ae0f4c453
--- /dev/null
+++ b/actions/apistatusesdestroy.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Destroy a notice through the API
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Deletes one of the authenticating user's statuses (notices).
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiStatusesDestroyAction extends ApiAuthAction
+{
+
+ var $user = null;
+ var $status = null;
+ var $format = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return false;
+ }
+ }
+
+ $this->user = $this->auth_user;
+ $this->notice_id = (int)$this->trimmed('id');
+
+ if (empty($notice_id)) {
+ $this->notice_id = (int)$this->arg('id');
+ }
+
+ $this->format = $this->arg('format');
+ $this->notice = Notice::staticGet((int)$this->notice_id);
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Delete the notice and all related replies
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(_('API method not found!'), $code = 404);
+ return;
+ }
+
+ if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
+ $this->clientError(_('This method requires a POST or DELETE.'),
+ 400, $this->format);
+ return;
+ }
+
+ if (empty($this->notice)) {
+ $this->clientError(_('No status found with that ID.'),
+ 404, $this->format);
+ return;
+ }
+
+ if ($this->user->id == $this->notice->profile_id) {
+ $replies = new Reply;
+ $replies->get('notice_id', $this->notice_id);
+ $replies->delete();
+ $this->notice->delete();
+
+ if ($this->format == 'xml') {
+ $this->show_single_xml_status($this->notice);
+ } elseif ($this->format == 'json') {
+ $this->show_single_json_status($this->notice);
+ }
+ } else {
+ $this->clientError(_('You may not delete another user\'s status.'),
+ 403, $this->format);
+ }
+
+ $this->showNotice();
+ }
+
+ /**
+ * Show the deleted notice
+ *
+ * @return void
+ */
+
+ function showNotice()
+ {
+ if (!empty($this->notice)) {
+ if ($this->format == 'xml') {
+ $this->show_single_xml_status($this->notice);
+ } elseif ($this->format == 'json') {
+ $this->show_single_json_status($this->notice);
+ }
+ }
+ }
+
+}
diff --git a/actions/apistatusesshow.php b/actions/apistatusesshow.php
new file mode 100644
index 000000000..55eea2356
--- /dev/null
+++ b/actions/apistatusesshow.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a notice (as a Twitter-style status)
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/twitterapi.php';
+
+/**
+ * Returns the notice specified by id as a Twitter-style status and inline user
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiStatusesShowAction extends TwitterapiAction
+{
+
+ var $notice_id = null;
+ var $notice = null;
+ var $format = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ // 'id' is an undocumented parameter in Twitter's API. Several
+ // clients make use of it, so we support it too.
+
+ // show.json?id=12345 takes precedence over /show/12345.json
+
+ $this->notice_id = (int)$this->trimmed('id');
+
+ if (empty($notice_id)) {
+ $this->notice_id = (int)$this->arg('id');
+ }
+
+ $this->format = $this->arg('format');
+ $this->notice = Notice::staticGet((int)$this->notice_id);
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Check the format and show the notice
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(_('API method not found!'), $code = 404);
+ return;
+ }
+
+ $this->showNotice();
+ }
+
+ /**
+ * Show the notice
+ *
+ * @return void
+ */
+
+ function showNotice()
+ {
+ if (!empty($this->notice)) {
+ if ($this->format == 'xml') {
+ $this->show_single_xml_status($this->notice);
+ } elseif ($this->format == 'json') {
+ $this->show_single_json_status($this->notice);
+ }
+ } else {
+
+ // XXX: Twitter just sets a 404 header and doens't bother
+ // to return an err msg
+
+ $deleted = Deleted_notice::staticGet($this->notice_id);
+
+ if (!empty($deleted)) {
+ $this->clientError(
+ _('Status deleted.'),
+ 410,
+ $this->format
+ );
+ } else {
+ $this->clientError(
+ _('No status with that ID found.'),
+ 404,
+ $this->format
+ );
+ }
+ }
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this notice last modified?
+ *
+ * @return string datestamp of the latest notice in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->notice)) {
+ return strtotime($this->notice->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this notice
+ *
+ * Returns an Etag based on the action name, language, and
+ * timestamps of the notice
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->notice)) {
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ $this->notice->id,
+ strtotime($this->notice->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php
new file mode 100644
index 000000000..fb1278559
--- /dev/null
+++ b/actions/apistatusesupdate.php
@@ -0,0 +1,240 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Post a notice (update your status) through the API
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Updates the authenticating user's status (posts a notice).
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiStatusesUpdateAction extends ApiAuthAction
+{
+
+ var $user = null;
+ var $source = null;
+ var $status = null;
+ var $in_reply_to_status_id = null;
+ var $format = null;
+
+ static $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return false;
+ }
+ }
+
+ $this->user = $this->auth_user;
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return false;
+ }
+
+ $this->status = $this->trimmed('status');
+
+ if (empty($this->status)) {
+ $this->clientError(
+ 'Client must provide a \'status\' parameter with a value.',
+ 400,
+ $this->format
+ );
+
+ return false;
+ }
+
+ $this->source = $this->trimmed('source');
+
+ if (empty($this->source) || in_array($source, $this->reserved_sources)) {
+ $this->source = 'api';
+ }
+
+ $this->format = $this->arg('format');
+
+ $this->in_reply_to_status_id
+ = intval($this->trimmed('in_reply_to_status_id'));
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Make a new notice for the update, save it, and show it
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ $this->clientError(
+ _('This method requires a POST.'),
+ 400, $this->format
+ );
+ return;
+ }
+
+ $status_shortened = common_shorten_links($this->status);
+
+ if (Notice::contentTooLong($status_shortened)) {
+
+ // Note: Twitter truncates anything over 140, flags the status
+ // as "truncated."
+
+ $this->clientError(
+ sprintf(
+ _('That\'s too long. Max notice size is %d chars.'),
+ Notice::maxContent()
+ ),
+ 406,
+ $this->format
+ );
+
+ return;
+ }
+
+ // Check for commands
+
+ $inter = new CommandInterpreter();
+ $cmd = $inter->handle_command($this->user, $status_shortened);
+
+ if ($cmd) {
+
+ if ($this->supported($cmd)) {
+ $cmd->execute(new Channel());
+ }
+
+ // Cmd not supported? Twitter just returns your latest status.
+ // And, it returns your last status whether the cmd was successful
+ // or not!
+
+ $this->notice = $this->user->getCurrentNotice();
+
+ } else {
+
+ $reply_to = null;
+
+ if (!empty($this->in_reply_to_status_id)) {
+
+ // Check whether notice actually exists
+
+ $reply = Notice::staticGet($this->in_reply_to_status_id);
+
+ if ($reply) {
+ $reply_to = $this->in_reply_to_status_id;
+ } else {
+ $this->clientError(
+ _('Not found'),
+ $code = 404,
+ $this->format
+ );
+ return;
+ }
+ }
+
+ $this->notice = Notice::saveNew(
+ $this->user->id,
+ html_entity_decode($this->status, ENT_NOQUOTES, 'UTF-8'),
+ $this->source,
+ 1,
+ $reply_to
+ );
+
+ common_broadcast_notice($this->notice);
+ }
+
+ $this->showNotice();
+ }
+
+ /**
+ * Show the resulting notice
+ *
+ * @return void
+ */
+
+ function showNotice()
+ {
+ if (!empty($this->notice)) {
+ if ($this->format == 'xml') {
+ $this->show_single_xml_status($this->notice);
+ } elseif ($this->format == 'json') {
+ $this->show_single_json_status($this->notice);
+ }
+ }
+ }
+
+ /**
+ * Is this command supported when doing an update from the API?
+ *
+ * @param string $cmd the command to check for
+ *
+ * @return boolean true or false
+ */
+
+ function supported($cmd)
+ {
+ static $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand',
+ 'FavCommand', 'OnCommand', 'OffCommand');
+
+ if (in_array(get_class($cmd), $cmdlist)) {
+ return true;
+ }
+
+ return false;
+ }
+
+}
diff --git a/actions/apisubscriptions.php b/actions/apisubscriptions.php
new file mode 100644
index 000000000..78dcd722d
--- /dev/null
+++ b/actions/apisubscriptions.php
@@ -0,0 +1,275 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base class for showing subscription information in the API
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * This class outputs a list of profiles as Twitter-style user and status objects.
+ * It is used by the API methods /api/statuses/(friends|followers). To support the
+ * social graph methods it also can output a simple list of IDs.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiSubscriptionsAction extends ApiBareAuthAction
+{
+
+ var $page = null;
+ var $count = null;
+ var $user = null;
+ var $profiles = null;
+ var $format = null;
+ var $tag = null;
+ var $lite = null;
+ var $ids_only = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->page = (int)$this->arg('page', 1);
+ $this->tag = $this->arg('tag');
+ $this->format = $this->arg('format');
+
+ // Note: Twitter no longer supports 'lite'
+ $this->lite = $this->arg('lite');
+
+ $this->ids_only = $this->arg('ids_only');
+
+ // If called as a social graph method, show 5000 per page, otherwise 100
+
+ $this->count = isset($this->ids_only) ?
+ 5000 : (int)$this->arg('count', 100);
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return false;
+ }
+ }
+
+ $this->user = $this->getTargetUser($this->arg('id'));
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return false;
+ }
+
+ $this->profiles = $this->getProfiles();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Show the profiles
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(_('API method not found!'), $code = 404);
+ return;
+ }
+
+ $this->init_document($this->format);
+
+ if (isset($this->ids_only)) {
+ $this->showIds();
+ } else {
+ $this->showProfiles(isset($this->lite) ? false : true);
+ }
+
+ $this->end_document($this->format);
+ }
+
+ /**
+ * Get profiles - should get overrrided
+ *
+ * @return array Profiles
+ */
+
+ function getProfiles()
+ {
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this feed last modified?
+ *
+ * @return string datestamp of the latest profile in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->profiles) && (count($this->profiles) > 0)) {
+ return strtotime($this->profiles[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this action
+ *
+ * Returns an Etag based on the action name, language, user ID, and
+ * timestamps of the first and last profiles in the subscriptions list
+ * There's also an indicator to show whether this action is being called
+ * as /api/statuses/(friends|followers) or /api/(friends|followers)/ids
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->profiles) && (count($this->profiles) > 0)) {
+
+ $last = count($this->profiles) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ $this->user->id,
+ isset($this->ids_only) ? 'IDs' : 'Profiles',
+ strtotime($this->profiles[0]->created),
+ strtotime($this->profiles[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+ /**
+ * Show the profiles as Twitter-style useres and statuses
+ *
+ * @param boolean $include_statuses Whether to include the latest status
+ * with each user. Default true.
+ *
+ * @return void
+ */
+
+ function showProfiles($include_statuses = true)
+ {
+ switch ($this->format) {
+ case 'xml':
+ $this->elementStart('users', array('type' => 'array'));
+ foreach ($this->profiles as $profile) {
+ $this->show_profile(
+ $profile,
+ $this->format,
+ null,
+ $include_statuses
+ );
+ }
+ $this->elementEnd('users');
+ break;
+ case 'json':
+ $arrays = array();
+ foreach ($this->profiles as $profile) {
+ $arrays[] = $this->twitter_user_array(
+ $profile,
+ $include_statuses
+ );
+ }
+ print json_encode($arrays);
+ break;
+ default:
+ $this->clientError(_('Unsupported format.'));
+ break;
+ }
+ }
+
+ /**
+ * Show the IDs of the profiles only. 5000 per page. To support
+ * the 'social graph' methods: /api/(friends|followers)/ids
+ *
+ * @return void
+ */
+
+ function showIds()
+ {
+ switch ($this->format) {
+ case 'xml':
+ $this->elementStart('ids');
+ foreach ($this->profiles as $profile) {
+ $this->element('id', null, $profile->id);
+ }
+ $this->elementEnd('ids');
+ break;
+ case 'json':
+ $ids = array();
+ foreach ($this->profiles as $profile) {
+ $ids[] = (int)$profile->id;
+ }
+ print json_encode($ids);
+ break;
+ default:
+ $this->clientError(_('Unsupported format.'));
+ break;
+ }
+ }
+
+}
diff --git a/actions/apitimelinefriends.php b/actions/apitimelinefriends.php
new file mode 100644
index 000000000..65bbb5a74
--- /dev/null
+++ b/actions/apitimelinefriends.php
@@ -0,0 +1,255 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show the friends timeline
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Returns the most recent notices (default 20) posted by the target user.
+ * This is the equivalent of 'You and friends' page accessed via Web.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiTimelineFriendsAction extends ApiBareAuthAction
+{
+
+ var $user = null;
+ var $notices = null;
+ var $count = null;
+ var $max_id = null;
+ var $since_id = null;
+ var $since = null;
+ var $format = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->page = (int)$this->arg('page', 1);
+ $this->count = (int)$this->arg('count', 20);
+ $this->max_id = (int)$this->arg('max_id', 0);
+ $this->since_id = (int)$this->arg('since_id', 0);
+ $this->since = $this->arg('since');
+ $this->format = $this->arg('format');
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->user = $this->getTargetUser($this->arg('id'));
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ $this->notices = $this->getNotices();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Just show the notices
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $this->showTimeline();
+ }
+
+ /**
+ * Show the timeline of notices
+ *
+ * @return void
+ */
+
+ function showTimeline()
+ {
+ $profile = $this->user->getProfile();
+ $sitename = common_config('site', 'name');
+ $title = sprintf(_("%s and friends"), $this->user->nickname);
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:FriendsTimeline:" . $this->user->id;
+ $link = common_local_url(
+ 'all', array('nickname' => $this->user->nickname)
+ );
+ $subtitle = sprintf(
+ _('Updates from %1$s and friends on %2$s!'),
+ $this->user->nickname, $sitename
+ );
+
+ switch($this->format) {
+ case 'xml':
+ $this->show_xml_timeline($this->notices);
+ break;
+ case 'rss':
+ $this->show_rss_timeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+
+ $target_id = $this->arg('id');
+
+ if (isset($target_id)) {
+ $selfuri = common_root_url() .
+ 'api/statuses/friends_timeline/' .
+ $target_id . '.atom';
+ } else {
+ $selfuri = common_root_url() .
+ 'api/statuses/friends_timeline.atom';
+ }
+
+ $this->show_atom_timeline(
+ $this->notices, $title, $id, $link,
+ $subtitle, null, $selfuri
+ );
+ break;
+ case 'json':
+ $this->show_json_timeline($this->notices);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ break;
+ }
+ }
+
+ /**
+ * Get notices
+ *
+ * @return array notices
+ */
+
+ function getNotices()
+ {
+ $notices = array();
+
+ if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) {
+ $notice = $this->user->noticeInbox(
+ ($this->page-1) * $this->count,
+ $this->count, $this->since_id,
+ $this->max_id, $this->since
+ );
+ } else {
+ $notice = $this->user->noticesWithFriends(
+ ($this->page-1) * $this->count,
+ $this->count, $this->since_id,
+ $this->max_id, $this->since
+ );
+ }
+
+ while ($notice->fetch()) {
+ $notices[] = clone($notice);
+ }
+
+ return $notices;
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this feed last modified?
+ *
+ * @return string datestamp of the latest notice in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+ return strtotime($this->notices[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this stream
+ *
+ * Returns an Etag based on the action name, language, user ID, and
+ * timestamps of the first and last notice in the timeline
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+
+ $last = count($this->notices) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ $this->user->id,
+ strtotime($this->notices[0]->created),
+ strtotime($this->notices[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
diff --git a/actions/apitimelinementions.php b/actions/apitimelinementions.php
new file mode 100644
index 000000000..93c6da307
--- /dev/null
+++ b/actions/apitimelinementions.php
@@ -0,0 +1,234 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show notices mentioning a user (@nickname)
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Returns the most recent (default 20) mentions (status containing @nickname)
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiTimelineMentionsAction extends ApiBareAuthAction
+{
+
+ var $user = null;
+ var $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->page = (int)$this->arg('page', 1);
+ $this->count = (int)$this->arg('count', 20);
+ $this->max_id = (int)$this->arg('max_id', 0);
+ $this->since_id = (int)$this->arg('since_id', 0);
+ $this->since = $this->arg('since');
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->user = $this->getTargetUser($this->arg('id'));
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->arg('format'));
+ return;
+ }
+
+ $this->notices = $this->getNotices();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Just show the notices
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $this->showTimeline();
+ }
+
+ /**
+ * Show the timeline of notices
+ *
+ * @return void
+ */
+
+ function showTimeline()
+ {
+ $profile = $this->user->getProfile();
+
+ $sitename = common_config('site', 'name');
+ $title = sprintf(
+ _('%1$s / Updates mentioning %2$s'),
+ $sitename, $this->user->nickname
+ );
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:Mentions:" . $this->user->id;
+ $link = common_local_url(
+ 'replies',
+ array('nickname' => $this->user->nickname)
+ );
+ $subtitle = sprintf(
+ _('%1$s updates that reply to updates from %2$s / %3$s.'),
+ $sitename, $this->user->nickname, $profile->getBestName()
+ );
+
+ switch($this->arg('format')) {
+ case 'xml':
+ $this->show_xml_timeline($this->notices);
+ break;
+ case 'rss':
+ $this->show_rss_timeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() .
+ ltrim($_SERVER['QUERY_STRING'], 'p=');
+ $this->show_atom_timeline(
+ $this->notices, $title, $id, $link, $subtitle,
+ null, $selfuri
+ );
+ break;
+ case 'json':
+ $this->show_json_timeline($this->notices);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ break;
+ }
+ }
+
+ /**
+ * Get notices
+ *
+ * @return array notices
+ */
+
+ function getNotices()
+ {
+ $notices = array();
+
+ $notice = $this->user->getReplies(
+ ($this->page - 1) * $this->count, $this->count,
+ $this->since_id, $this->max_id, $this->since
+ );
+
+ while ($notice->fetch()) {
+ $notices[] = clone($notice);
+ }
+
+ return $notices;
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this feed last modified?
+ *
+ * @return string datestamp of the latest notice in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+ return strtotime($this->notices[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this stream
+ *
+ * Returns an Etag based on the action name, language, user ID, and
+ * timestamps of the first and last notice in the timeline
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+
+ $last = count($this->notices) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ $this->user->id,
+ strtotime($this->notices[0]->created),
+ strtotime($this->notices[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
diff --git a/actions/apitimelinepublic.php b/actions/apitimelinepublic.php
new file mode 100644
index 000000000..10bde6f37
--- /dev/null
+++ b/actions/apitimelinepublic.php
@@ -0,0 +1,207 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show the public timeline
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/twitterapi.php';
+
+/**
+ * Returns the most recent notices (default 20) posted by everybody
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiTimelinePublicAction extends TwitterapiAction
+{
+
+ var $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->page = (int)$this->arg('page', 1);
+ $this->count = (int)$this->arg('count', 20);
+ $this->max_id = (int)$this->arg('max_id', 0);
+ $this->since_id = (int)$this->arg('since_id', 0);
+ $this->since = $this->arg('since');
+
+ $this->notices = $this->getNotices();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Just show the notices
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $this->showTimeline();
+ }
+
+ /**
+ * Show the timeline of notices
+ *
+ * @return void
+ */
+
+ function showTimeline()
+ {
+ $sitename = common_config('site', 'name');
+ $title = sprintf(_("%s public timeline"), $sitename);
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:PublicTimeline";
+ $link = common_root_url();
+ $subtitle = sprintf(_("%s updates from everyone!"), $sitename);
+
+ switch($this->arg('format')) {
+ case 'xml':
+ $this->show_xml_timeline($this->notices);
+ break;
+ case 'rss':
+ $this->show_rss_timeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
+ $this->show_atom_timeline(
+ $this->notices, $title, $id, $link,
+ $subtitle, null, $selfuri
+ );
+ break;
+ case 'json':
+ $this->show_json_timeline($this->notices);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ break;
+ }
+ }
+
+ /**
+ * Get notices
+ *
+ * @return array notices
+ */
+
+ function getNotices()
+ {
+ $notices = array();
+
+ $notice = Notice::publicStream(
+ ($this->page - 1) * $this->count, $this->count, $this->since_id,
+ $this->max_id, $this->since
+ );
+
+ while ($notice->fetch()) {
+ $notices[] = clone($notice);
+ }
+
+ return $notices;
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this feed last modified?
+ *
+ * @return string datestamp of the latest notice in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+ return strtotime($this->notices[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this stream
+ *
+ * Returns an Etag based on the action name, language, and
+ * timestamps of the first and last notice in the timeline
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+
+ $last = count($this->notices) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ strtotime($this->notices[0]->created),
+ strtotime($this->notices[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
diff --git a/actions/apitimelineuser.php b/actions/apitimelineuser.php
new file mode 100644
index 000000000..c4d02bc62
--- /dev/null
+++ b/actions/apitimelineuser.php
@@ -0,0 +1,249 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a user's timeline
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Returns the most recent notices (default 20) posted by the authenticating
+ * user. Another user's timeline can be requested via the id parameter. This
+ * is the API equivalent of the user profile web page.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiTimelineUserAction extends ApiBareAuthAction
+{
+
+ var $user = null;
+ var $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->page = (int)$this->arg('page', 1);
+ $this->count = (int)$this->arg('count', 20);
+ $this->max_id = (int)$this->arg('max_id', 0);
+ $this->since_id = (int)$this->arg('since_id', 0);
+ $this->since = $this->arg('since');
+
+ if ($this->requiresAuth()) {
+ if ($this->checkBasicAuthUser() == false) {
+ return;
+ }
+ }
+
+ $this->user = $this->getTargetUser($this->arg('id'));
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->arg('format'));
+ return;
+ }
+
+ $this->notices = $this->getNotices();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Just show the notices
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $this->showTimeline();
+ }
+
+ /**
+ * Show the timeline of notices
+ *
+ * @return void
+ */
+
+ function showTimeline()
+ {
+ $profile = $this->user->getProfile();
+
+ $sitename = common_config('site', 'name');
+ $title = sprintf(_("%s timeline"), $this->user->nickname);
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:UserTimeline:" . $this->user->id;
+ $link = common_local_url(
+ 'showstream',
+ array('nickname' => $this->user->nickname)
+ );
+ $subtitle = sprintf(
+ _('Updates from %1$s on %2$s!'),
+ $this->user->nickname, $sitename
+ );
+
+ // FriendFeed's SUP protocol
+ // Also added RSS and Atom feeds
+
+ $suplink = common_local_url('sup', null, null, $this->user->id);
+ header('X-SUP-ID: ' . $suplink);
+
+ switch($this->arg('format')) {
+ case 'xml':
+ $this->show_xml_timeline($this->notices);
+ break;
+ case 'rss':
+ $this->show_rss_timeline(
+ $this->notices, $title, $link,
+ $subtitle, $suplink
+ );
+ break;
+ case 'atom':
+ if (isset($apidata['api_arg'])) {
+ $selfuri = common_root_url() .
+ 'api/statuses/user_timeline/' .
+ $apidata['api_arg'] . '.atom';
+ } else {
+ $selfuri = common_root_url() .
+ 'api/statuses/user_timeline.atom';
+ }
+ $this->show_atom_timeline(
+ $this->notices, $title, $id, $link,
+ $subtitle, $suplink, $selfuri
+ );
+ break;
+ case 'json':
+ $this->show_json_timeline($this->notices);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ break;
+ }
+
+ }
+
+ /**
+ * Get notices
+ *
+ * @return array notices
+ */
+
+ function getNotices()
+ {
+ $notices = array();
+
+ $notice = $this->user->getNotices(
+ ($this->page-1) * $this->count, $this->count,
+ $this->since_id, $this->max_id, $this->since
+ );
+
+ while ($notice->fetch()) {
+ $notices[] = clone($notice);
+ }
+
+ return $notices;
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this feed last modified?
+ *
+ * @return string datestamp of the latest notice in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+ return strtotime($this->notices[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this stream
+ *
+ * Returns an Etag based on the action name, language, user ID, and
+ * timestamps of the first and last notice in the timeline
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->notices) && (count($this->notices) > 0)) {
+
+ $last = count($this->notices) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ $this->user->id,
+ strtotime($this->notices[0]->created),
+ strtotime($this->notices[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
diff --git a/actions/apiuserfollowers.php b/actions/apiuserfollowers.php
new file mode 100644
index 000000000..5c0243449
--- /dev/null
+++ b/actions/apiuserfollowers.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a user's followers (subscribers)
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Ouputs the authenticating user's followers (subscribers), each with
+ * current Twitter-style status inline. They are ordered by the order
+ * in which they subscribed to the user, 100 at a time.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiUserFollowersAction extends ApiSubscriptionsAction
+{
+ /**
+ * Get the user's subscribers (followers) as an array of profiles
+ *
+ * @return array Profiles
+ */
+
+ function getProfiles()
+ {
+ $offset = ($this->page - 1) * $this->count;
+ $limit = $this->count + 1;
+
+ $subs = null;
+
+ if (isset($this->tag)) {
+ $subs = $this->user->getTaggedSubscribers(
+ $this->tag, $offset, $limit
+ );
+ } else {
+ $subs = $this->user->getSubscribers(
+ $offset,
+ $limit
+ );
+ }
+
+ $profiles = array();
+
+ if (!empty($subs)) {
+ while ($subs->fetch()) {
+ $profiles[] = clone($subs);
+ }
+ }
+
+ return $profiles;
+ }
+
+}
diff --git a/actions/apiuserfriends.php b/actions/apiuserfriends.php
new file mode 100644
index 000000000..8a42e36b9
--- /dev/null
+++ b/actions/apiuserfriends.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a user's friends (subscriptions)
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Ouputs the authenticating user's friends (subscriptions), each with
+ * current Twitter-style status inline. They are ordered by the date
+ * in which the user subscribed to them, 100 at a time.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiUserFriendsAction extends ApiSubscriptionsAction
+{
+ /**
+ * Get the user's subscriptions (friends) as an array of profiles
+ *
+ * @return array Profiles
+ */
+
+ function getProfiles()
+ {
+ $offset = ($this->page - 1) * $this->count;
+ $limit = $this->count + 1;
+
+ $subs = null;
+
+ if (isset($this->tag)) {
+ $subs = $this->user->getTaggedSubscriptions(
+ $this->tag, $offset, $limit
+ );
+ } else {
+ $subs = $this->user->getSubscriptions(
+ $offset,
+ $limit
+ );
+ }
+
+ $profiles = array();
+
+ if (!empty($subs)) {
+ while ($subs->fetch()) {
+ $profiles[] = clone($subs);
+ }
+ }
+
+ return $profiles;
+ }
+
+}
diff --git a/actions/apiusershow.php b/actions/apiusershow.php
new file mode 100644
index 000000000..2e2ceab41
--- /dev/null
+++ b/actions/apiusershow.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a user's profile information
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/twitterapi.php';
+
+/**
+ * Ouputs information for a user, specified by ID or screen name.
+ * The user's most recent status will be returned inline.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiUserShowAction extends TwitterApiAction
+{
+
+ var $format = null;
+ var $user = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->format = $this->arg('format');
+
+ $email = $this->arg('email');
+
+ // XXX: email field deprecated in Twitter's API
+
+ if (!empty($email)) {
+ $this->user = User::staticGet('email', $email);
+ } else {
+ $this->user = $this->getTargetUser($this->arg('id'));
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Check the format and show the user info
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (empty($this->user)) {
+ $this->clientError(_('Not found.'), 404, $this->format);
+ return;
+ }
+
+ if (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(_('API method not found!'), $code = 404);
+ return;
+ }
+
+ $profile = $this->user->getProfile();
+
+ if (empty($profile)) {
+ $this->clientError(_('User has no profile.'));
+ return;
+ }
+
+ $twitter_user = $this->twitter_user_array($this->user->getProfile(), true);
+
+ if ($this->format == 'xml') {
+ $this->init_document('xml');
+ $this->show_twitter_xml_user($twitter_user);
+ $this->end_document('xml');
+ } elseif ($this->format == 'json') {
+ $this->init_document('json');
+ $this->show_json_objects($twitter_user);
+ $this->end_document('json');
+ }
+
+ }
+
+}
diff --git a/actions/twitapidirect_messages.php b/actions/twitapidirect_messages.php
deleted file mode 100644
index 08b8f4e9c..000000000
--- a/actions/twitapidirect_messages.php
+++ /dev/null
@@ -1,305 +0,0 @@
-<?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);
-}
-
-require_once(INSTALLDIR.'/lib/twitterapi.php');
-
-class Twitapidirect_messagesAction extends TwitterapiAction
-{
-
- function direct_messages($args, $apidata)
- {
- parent::handle($args);
- return $this->show_messages($args, $apidata, 'received');
- }
-
- function sent($args, $apidata)
- {
- parent::handle($args);
- return $this->show_messages($args, $apidata, 'sent');
- }
-
- function show_messages($args, $apidata, $type)
- {
- $user = $apidata['user']; // Always the auth user
-
- $message = new Message();
- $title = null;
- $subtitle = null;
- $link = null;
- $server = common_root_url();
-
- if ($type == 'received') {
- $message->to_profile = $user->id;
- $title = sprintf(_("Direct messages to %s"), $user->nickname);
- $subtitle = sprintf(_("All the direct messages sent to %s"),
- $user->nickname);
- $link = $server . $user->nickname . '/inbox';
- } else {
- $message->from_profile = $user->id;
- $title = _('Direct Messages You\'ve Sent');
- $subtitle = sprintf(_("All the direct messages sent from %s"),
- $user->nickname);
- $link = $server . $user->nickname . '/outbox';
- }
-
- $page = (int)$this->arg('page', 1);
- $count = (int)$this->arg('count', 20);
- $max_id = (int)$this->arg('max_id', 0);
- $since_id = (int)$this->arg('since_id', 0);
- $since = $this->arg('since');
-
- if ($max_id) {
- $message->whereAdd("id <= $max_id");
- }
-
- if ($since_id) {
- $message->whereAdd("id > $since_id");
- }
-
- if ($since) {
- $d = date('Y-m-d H:i:s', $since);
- $message->whereAdd("created > '$d'");
- }
-
- $message->orderBy('created DESC, id DESC');
- $message->limit((($page-1)*$count), $count);
- $message->find();
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_xml_dmsgs($message);
- break;
- case 'rss':
- $this->show_rss_dmsgs($message, $title, $link, $subtitle);
- break;
- case 'atom':
- $selfuri = common_root_url() . 'api/direct_messages';
- $selfuri .= ($type == 'received') ? '.atom' : '/sent.atom';
- $taguribase = common_config('integration', 'taguri');
-
- if ($type == 'sent') {
- $id = "tag:$taguribase:SentDirectMessages:" . $user->id;
- } else {
- $id = "tag:$taguribase:DirectMessages:" . $user->id;
- }
-
- $this->show_atom_dmsgs($message, $title, $link, $subtitle,
- $selfuri, $id);
- break;
- case 'json':
- $this->show_json_dmsgs($message);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
-
- }
-
- // had to change this from "new" to "create" to avoid PHP reserved word
- function create($args, $apidata)
- {
- parent::handle($args);
-
- if ($_SERVER['REQUEST_METHOD'] != 'POST') {
- $this->clientError(_('This method requires a POST.'),
- 400, $apidata['content-type']);
- return;
- }
-
- $user = $apidata['user'];
- $source = $this->trimmed('source'); // Not supported by Twitter.
-
- $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
- if (empty($source) || in_array($source, $reserved_sources)) {
- $source = 'api';
- }
-
- $content = $this->trimmed('text');
-
- if (empty($content)) {
- $this->clientError(_('No message text!'),
- $code = 406, $apidata['content-type']);
- } else {
- $content_shortened = common_shorten_links($content);
- if (Message::contentTooLong($content_shortened)) {
- $this->clientError(sprintf(_('That\'s too long. Max message size is %d chars.'),
- Message::maxContent()),
- $code = 406, $apidata['content-type']);
- return;
- }
- }
-
- $other = $this->get_user($this->trimmed('user'));
-
- if (empty($other)) {
- $this->clientError(_('Recipient user not found.'),
- $code = 403, $apidata['content-type']);
- return;
- } else if (!$user->mutuallySubscribed($other)) {
- $this->clientError(_('Can\'t send direct messages to users who aren\'t your friend.'),
- $code = 403, $apidata['content-type']);
- return;
- } else if ($user->id == $other->id) {
- // Sending msgs to yourself is allowed by Twitter
- $this->clientError(_('Don\'t send a message to yourself; just say it to yourself quietly instead.'),
- $code = 403, $apidata['content-type']);
- return;
- }
-
- $message = Message::saveNew($user->id, $other->id,
- html_entity_decode($content, ENT_NOQUOTES, 'UTF-8'), $source);
-
- if (is_string($message)) {
- $this->serverError($message);
- return;
- }
-
- $this->notify($user, $other, $message);
-
- if ($apidata['content-type'] == 'xml') {
- $this->show_single_xml_dmsg($message);
- } elseif ($apidata['content-type'] == 'json') {
- $this->show_single_json_dmsg($message);
- }
-
- }
-
- function destroy($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), $code=501);
- }
-
- function show_xml_dmsgs($message)
- {
-
- $this->init_document('xml');
- $this->elementStart('direct-messages', array('type' => 'array'));
-
- if (is_array($message)) {
- foreach ($message as $m) {
- $twitter_dm = $this->twitter_dmsg_array($m);
- $this->show_twitter_xml_dmsg($twitter_dm);
- }
- } else {
- while ($message->fetch()) {
- $twitter_dm = $this->twitter_dmsg_array($message);
- $this->show_twitter_xml_dmsg($twitter_dm);
- }
- }
-
- $this->elementEnd('direct-messages');
- $this->end_document('xml');
-
- }
-
- function show_json_dmsgs($message)
- {
-
- $this->init_document('json');
-
- $dmsgs = array();
-
- if (is_array($message)) {
- foreach ($message as $m) {
- $twitter_dm = $this->twitter_dmsg_array($m);
- array_push($dmsgs, $twitter_dm);
- }
- } else {
- while ($message->fetch()) {
- $twitter_dm = $this->twitter_dmsg_array($message);
- array_push($dmsgs, $twitter_dm);
- }
- }
-
- $this->show_json_objects($dmsgs);
- $this->end_document('json');
-
- }
-
- function show_rss_dmsgs($message, $title, $link, $subtitle)
- {
-
- $this->init_document('rss');
-
- $this->elementStart('channel');
- $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($message)) {
- foreach ($message as $m) {
- $entry = $this->twitter_rss_dmsg_array($m);
- $this->show_twitter_rss_item($entry);
- }
- } else {
- while ($message->fetch()) {
- $entry = $this->twitter_rss_dmsg_array($message);
- $this->show_twitter_rss_item($entry);
- }
- }
-
- $this->elementEnd('channel');
- $this->end_twitter_rss();
-
- }
-
- function show_atom_dmsgs($message, $title, $link, $subtitle, $selfuri, $id)
- {
-
- $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);
- $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($message)) {
- foreach ($message as $m) {
- $entry = $this->twitter_rss_dmsg_array($m);
- $this->show_twitter_atom_entry($entry);
- }
- } else {
- while ($message->fetch()) {
- $entry = $this->twitter_rss_dmsg_array($message);
- $this->show_twitter_atom_entry($entry);
- }
- }
-
- $this->end_document('atom');
- }
-
- // swiped from MessageAction. Should it be place in util.php?
- function notify($from, $to, $message)
- {
- mail_notify_message($message, $from, $to);
- # XXX: Jabber, SMS notifications... probably queued
- }
-
-}
diff --git a/actions/twitapifriendships.php b/actions/twitapifriendships.php
deleted file mode 100644
index eea8945c3..000000000
--- a/actions/twitapifriendships.php
+++ /dev/null
@@ -1,250 +0,0 @@
-<?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);
-}
-
-require_once(INSTALLDIR.'/lib/twitterapi.php');
-
-class TwitapifriendshipsAction extends TwitterapiAction
-{
-
- function create($args, $apidata)
- {
- parent::handle($args);
-
- if ($_SERVER['REQUEST_METHOD'] != 'POST') {
- $this->clientError(_('This method requires a POST.'),
- 400, $apidata['content-type']);
- return;
- }
-
- $id = $apidata['api_arg'];
- $other = $this->get_user($id);
-
- if (empty($other)) {
- $this->clientError(_('Could not follow user: User not found.'),
- 403, $apidata['content-type']);
- return;
- }
-
- $user = $apidata['user'];
-
- if ($user->isSubscribed($other)) {
- $errmsg = sprintf(_('Could not follow user: %s is already on your list.'),
- $other->nickname);
- $this->clientError($errmsg, 403, $apidata['content-type']);
- return;
- }
-
- $sub = new Subscription();
-
- $sub->query('BEGIN');
-
- $sub->subscriber = $user->id;
- $sub->subscribed = $other->id;
- $sub->created = DB_DataObject_Cast::dateTime(); # current time
-
- $result = $sub->insert();
-
- if (empty($result)) {
- $errmsg = sprintf(_('Could not follow user: %s is already on your list.'),
- $other->nickname);
- $this->clientError($errmsg, 400, $apidata['content-type']);
- return;
- }
-
- $sub->query('COMMIT');
-
- mail_subscribe_notify($other, $user);
-
- $type = $apidata['content-type'];
- $this->init_document($type);
- $this->show_profile($other, $type);
- $this->end_document($type);
-
- }
-
- function destroy($args, $apidata)
- {
- parent::handle($args);
-
- if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
- $this->clientError(_('This method requires a POST or DELETE.'),
- 400, $apidata['content-type']);
- return;
- }
-
- $id = $apidata['api_arg'];
-
- # We can't subscribe to a remote person, but we can unsub
-
- $other = $this->get_profile($id);
- $user = $apidata['user']; // Alwyas the auth user
-
- if ($user->id == $other->id) {
- $this->clientError(_("You cannot unfollow yourself!"),
- 403, $apidata['content-type']);
- return;
- }
-
- $sub = new Subscription();
- $sub->subscriber = $user->id;
- $sub->subscribed = $other->id;
-
- if ($sub->find(true)) {
- $sub->query('BEGIN');
- $sub->delete();
- $sub->query('COMMIT');
- } else {
- $this->clientError(_('You are not friends with the specified user.'),
- 403, $apidata['content-type']);
- return;
- }
-
- $type = $apidata['content-type'];
- $this->init_document($type);
- $this->show_profile($other, $type);
- $this->end_document($type);
-
- }
-
- function exists($args, $apidata)
- {
- parent::handle($args);
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('API method not found!'), $code = 404);
- return;
- }
-
- $user_a_id = $this->trimmed('user_a');
- $user_b_id = $this->trimmed('user_b');
-
- $user_a = $this->get_user($user_a_id);
- $user_b = $this->get_user($user_b_id);
-
- if (empty($user_a) || empty($user_b)) {
- $this->clientError(_('Two user ids or screen_names must be supplied.'),
- 400, $apidata['content-type']);
- return;
- }
-
- $result = $user_a->isSubscribed($user_b);
-
- switch ($apidata['content-type']) {
- case 'xml':
- $this->init_document('xml');
- $this->element('friends', null, $result);
- $this->end_document('xml');
- break;
- case 'json':
- $this->init_document('json');
- print json_encode($result);
- $this->end_document('json');
- break;
- default:
- break;
- }
-
- }
-
- function show($args, $apidata)
- {
- parent::handle($args);
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('API method not found!'), $code = 404);
- return;
- }
-
- $source_id = (int)$this->trimmed('source_id');
- $source_screen_name = $this->trimmed('source_screen_name');
-
- // If the source is not specified for an unauthenticated request,
- // the method will return an HTTP 403.
-
- if (empty($source_id) && empty($source_screen_name)) {
- if (empty($apidata['user'])) {
- $this->clientError(_('Could not determine source user.'),
- $code = 403);
- return;
- }
- }
-
- $source = null;
-
- if (!empty($source_id)) {
- $source = User::staticGet($source_id);
- } elseif (!empty($source_screen_name)) {
- $source = User::staticGet('nickname', $source_screen_name);
- } else {
- $source = $apidata['user'];
- }
-
- // If a source or target is specified but does not exist,
- // the method will return an HTTP 404.
-
- if (empty($source)) {
- $this->clientError(_('Could not determine source user.'),
- $code = 404);
- return;
- }
-
- $target_id = (int)$this->trimmed('target_id');
- $target_screen_name = $this->trimmed('target_screen_name');
-
- $target = null;
-
- if (!empty($target_id)) {
- $target = User::staticGet($target_id);
- } elseif (!empty($target_screen_name)) {
- $target = User::staticGet('nickname', $target_screen_name);
- } else {
- $this->clientError(_('Target user not specified.'),
- $code = 403);
- return;
- }
-
- if (empty($target)) {
- $this->clientError(_('Could not find target user.'),
- $code = 404);
- return;
- }
-
- $result = $this->twitter_relationship_array($source, $target);
-
- switch ($apidata['content-type']) {
- case 'xml':
- $this->init_document('xml');
- $this->show_twitter_xml_relationship($result[relationship]);
- $this->end_document('xml');
- break;
- case 'json':
- $this->init_document('json');
- print json_encode($result);
- $this->end_document('json');
- break;
- default:
- break;
- }
- }
-
-}
diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php
deleted file mode 100644
index 87043b182..000000000
--- a/actions/twitapistatuses.php
+++ /dev/null
@@ -1,606 +0,0 @@
-<?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);
-}
-
-require_once(INSTALLDIR.'/lib/twitterapi.php');
-
-class TwitapistatusesAction extends TwitterapiAction
-{
-
- function public_timeline($args, $apidata)
- {
- // XXX: To really live up to the spec we need to build a list
- // of notices by users who have custom avatars, so fix this SQL -- Zach
-
- parent::handle($args);
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_("%s public timeline"), $sitename);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:PublicTimeline";
- $link = common_root_url();
- $subtitle = sprintf(_("%s updates from everyone!"), $sitename);
-
- $page = (int)$this->arg('page', 1);
- $count = (int)$this->arg('count', 20);
- $max_id = (int)$this->arg('max_id', 0);
- $since_id = (int)$this->arg('since_id', 0);
- $since = $this->arg('since');
-
- $notice = Notice::publicStream(($page-1)*$count, $count, $since_id,
- $max_id, $since);
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_xml_timeline($notice);
- break;
- case 'rss':
- $this->show_rss_timeline($notice, $title, $link, $subtitle);
- break;
- case 'atom':
- $selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
- $this->show_atom_timeline($notice, $title, $id, $link,
- $subtitle, null, $selfuri);
- break;
- case 'json':
- $this->show_json_timeline($notice);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- break;
- }
-
- }
-
- function friends_timeline($args, $apidata)
- {
- parent::handle($args);
-
- $this->auth_user = $apidata['user'];
- $user = $this->get_user($apidata['api_arg'], $apidata);
-
- if (empty($user)) {
- $this->clientError(_('No such user!'), 404,
- $apidata['content-type']);
- return;
- }
-
- $profile = $user->getProfile();
- $sitename = common_config('site', 'name');
- $title = sprintf(_("%s and friends"), $user->nickname);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:FriendsTimeline:" . $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);
-
- $page = (int)$this->arg('page', 1);
- $count = (int)$this->arg('count', 20);
- $max_id = (int)$this->arg('max_id', 0);
- $since_id = (int)$this->arg('since_id', 0);
- $since = $this->arg('since');
-
- if (!empty($this->auth_user) && $this->auth_user->id == $user->id) {
- $notice = $user->noticeInbox(($page-1)*$count,
- $count, $since_id, $max_id, $since);
- } else {
- $notice = $user->noticesWithFriends(($page-1)*$count,
- $count, $since_id, $max_id, $since);
- }
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_xml_timeline($notice);
- break;
- case 'rss':
- $this->show_rss_timeline($notice, $title, $link, $subtitle);
- break;
- case 'atom':
- if (isset($apidata['api_arg'])) {
- $selfuri = common_root_url() .
- 'api/statuses/friends_timeline/' .
- $apidata['api_arg'] . '.atom';
- } else {
- $selfuri = common_root_url() .
- 'api/statuses/friends_timeline.atom';
- }
- $this->show_atom_timeline($notice, $title, $id, $link,
- $subtitle, null, $selfuri);
- break;
- case 'json':
- $this->show_json_timeline($notice);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
-
- }
-
- function home_timeline($args, $apidata)
- {
- call_user_func(array($this, 'friends_timeline'), $args, $apidata);
- }
-
- function user_timeline($args, $apidata)
- {
- parent::handle($args);
-
- $this->auth_user = $apidata['user'];
- $user = $this->get_user($apidata['api_arg'], $apidata);
-
- if (empty($user)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $profile = $user->getProfile();
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_("%s timeline"), $user->nickname);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:UserTimeline:".$user->id;
- $link = common_local_url('showstream',
- array('nickname' => $user->nickname));
- $subtitle = sprintf(_('Updates from %1$s on %2$s!'),
- $user->nickname, $sitename);
-
- # FriendFeed's SUP protocol
- # Also added RSS and Atom feeds
-
- $suplink = common_local_url('sup', null, null, $user->id);
- header('X-SUP-ID: '.$suplink);
-
- $page = (int)$this->arg('page', 1);
- $count = (int)$this->arg('count', 20);
- $max_id = (int)$this->arg('max_id', 0);
- $since_id = (int)$this->arg('since_id', 0);
- $since = $this->arg('since');
-
- $notice = $user->getNotices(($page-1)*$count,
- $count, $since_id, $max_id, $since);
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_xml_timeline($notice);
- break;
- case 'rss':
- $this->show_rss_timeline($notice, $title, $link,
- $subtitle, $suplink);
- break;
- case 'atom':
- if (isset($apidata['api_arg'])) {
- $selfuri = common_root_url() .
- 'api/statuses/user_timeline/' .
- $apidata['api_arg'] . '.atom';
- } else {
- $selfuri = common_root_url() .
- 'api/statuses/user_timeline.atom';
- }
- $this->show_atom_timeline($notice, $title, $id, $link,
- $subtitle, $suplink, $selfuri);
- break;
- case 'json':
- $this->show_json_timeline($notice);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
-
- }
-
- function update($args, $apidata)
- {
- parent::handle($args);
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('API method not found!'), $code = 404);
- return;
- }
-
- if ($_SERVER['REQUEST_METHOD'] != 'POST') {
- $this->clientError(_('This method requires a POST.'),
- 400, $apidata['content-type']);
- return;
- }
-
- $user = $apidata['user']; // Always the auth user
-
- $status = $this->trimmed('status');
- $source = $this->trimmed('source');
- $in_reply_to_status_id =
- intval($this->trimmed('in_reply_to_status_id'));
- $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
-
- if (empty($source) || in_array($source, $reserved_sources)) {
- $source = 'api';
- }
-
- if (empty($status)) {
-
- // XXX: Note: In this case, Twitter simply returns '200 OK'
- // No error is given, but the status is not posted to the
- // user's timeline. Seems bad. Shouldn't we throw an
- // errror? -- Zach
- return;
-
- } else {
-
- $status_shortened = common_shorten_links($status);
-
- if (Notice::contentTooLong($status_shortened)) {
-
- // XXX: Twitter truncates anything over 140, flags the status
- // as "truncated." Sending this error may screw up some clients
- // that assume Twitter will truncate for them. Should we just
- // truncate too? -- Zach
- $this->clientError(sprintf(_('That\'s too long. Max notice size is %d chars.'),
- Notice::maxContent()),
- $code = 406, $apidata['content-type']);
- return;
- }
- }
-
- // Check for commands
- $inter = new CommandInterpreter();
- $cmd = $inter->handle_command($user, $status_shortened);
-
- if ($cmd) {
-
- if ($this->supported($cmd)) {
- $cmd->execute(new Channel());
- }
-
- // cmd not supported? Twitter just returns your latest status.
- // And, it returns your last status whether the cmd was successful
- // or not!
- $n = $user->getCurrentNotice();
- $apidata['api_arg'] = $n->id;
- } else {
-
- $reply_to = null;
-
- if ($in_reply_to_status_id) {
-
- // check whether notice actually exists
- $reply = Notice::staticGet($in_reply_to_status_id);
-
- if ($reply) {
- $reply_to = $in_reply_to_status_id;
- } else {
- $this->clientError(_('Not found'), $code = 404,
- $apidata['content-type']);
- return;
- }
- }
-
- $notice = Notice::saveNew($user->id,
- html_entity_decode($status, ENT_NOQUOTES, 'UTF-8'),
- $source, 1, $reply_to);
-
- common_broadcast_notice($notice);
- $apidata['api_arg'] = $notice->id;
- }
-
- $this->show($args, $apidata);
- }
-
- function mentions($args, $apidata)
- {
- parent::handle($args);
-
- $user = $this->get_user($apidata['api_arg'], $apidata);
- $this->auth_user = $apidata['user'];
-
- if (empty($user)) {
- $this->clientError(_('No such user!'), 404,
- $apidata['content-type']);
- return;
- }
-
- $profile = $user->getProfile();
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_('%1$s / Updates mentioning %2$s'),
- $sitename, $user->nickname);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:Mentions:".$user->id;
- $link = common_local_url('replies',
- array('nickname' => $user->nickname));
- $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'),
- $sitename, $user->nickname, $profile->getBestName());
-
- $page = (int)$this->arg('page', 1);
- $count = (int)$this->arg('count', 20);
- $max_id = (int)$this->arg('max_id', 0);
- $since_id = (int)$this->arg('since_id', 0);
- $since = $this->arg('since');
-
- $notice = $user->getReplies(($page-1)*$count,
- $count, $since_id, $max_id, $since);
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_xml_timeline($notice);
- break;
- case 'rss':
- $this->show_rss_timeline($notice, $title, $link, $subtitle);
- break;
- case 'atom':
- $selfuri = common_root_url() .
- ltrim($_SERVER['QUERY_STRING'], 'p=');
- $this->show_atom_timeline($notice, $title, $id, $link, $subtitle,
- null, $selfuri);
- break;
- case 'json':
- $this->show_json_timeline($notice);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
-
- }
-
- function replies($args, $apidata)
- {
- call_user_func(array($this, 'mentions'), $args, $apidata);
- }
-
- function show($args, $apidata)
- {
- parent::handle($args);
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('API method not found!'), $code = 404);
- return;
- }
-
- // 'id' is an undocumented parameter in Twitter's API. Several
- // clients make use of it, so we support it too.
-
- // show.json?id=12345 takes precedence over /show/12345.json
-
- $this->auth_user = $apidata['user'];
- $notice_id = $this->trimmed('id');
-
- if (empty($notice_id)) {
- $notice_id = $apidata['api_arg'];
- }
-
- $notice = Notice::staticGet((int)$notice_id);
-
- if ($notice) {
- if ($apidata['content-type'] == 'xml') {
- $this->show_single_xml_status($notice);
- } elseif ($apidata['content-type'] == 'json') {
- $this->show_single_json_status($notice);
- }
- } else {
- // XXX: Twitter just sets a 404 header and doens't bother
- // to return an err msg
- $deleted = Deleted_notice::staticGet($notice_id);
- if (!empty($deleted)) {
- $this->clientError(_('Status deleted.'),
- 410, $apidata['content-type']);
- } else {
- $this->clientError(_('No status with that ID found.'),
- 404, $apidata['content-type']);
- }
- }
- }
-
- function destroy($args, $apidata)
- {
- parent::handle($args);
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('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.
- $this->clientError(_('This method requires a POST or DELETE.'),
- 400, $apidata['content-type']);
- return;
- }
-
- $user = $apidata['user']; // Always the auth user
- $notice_id = $apidata['api_arg'];
- $notice = Notice::staticGet($notice_id);
-
- if (empty($notice)) {
- $this->clientError(_('No status found with that ID.'),
- 404, $apidata['content-type']);
- return;
- }
-
- if ($user->id == $notice->profile_id) {
- $replies = new Reply;
- $replies->get('notice_id', $notice_id);
- $replies->delete();
- $notice->delete();
-
- if ($apidata['content-type'] == 'xml') {
- $this->show_single_xml_status($notice);
- } elseif ($apidata['content-type'] == 'json') {
- $this->show_single_json_status($notice);
- }
- } else {
- $this->clientError(_('You may not delete another user\'s status.'),
- 403, $apidata['content-type']);
- }
-
- }
-
- function friends($args, $apidata)
- {
- parent::handle($args);
- $includeStatuses=! (boolean) $args['lite'];
- return $this->subscriptions($apidata, 'subscribed', 'subscriber', false, $includeStatuses);
- }
-
- function friendsIDs($args, $apidata)
- {
- parent::handle($args);
- return $this->subscriptions($apidata, 'subscribed', 'subscriber', true);
- }
-
- function followers($args, $apidata)
- {
- parent::handle($args);
- $includeStatuses=! (boolean) $args['lite'];
- return $this->subscriptions($apidata, 'subscriber', 'subscribed', false, $includeStatuses);
- }
-
- function followersIDs($args, $apidata)
- {
- parent::handle($args);
- return $this->subscriptions($apidata, 'subscriber', 'subscribed', true);
- }
-
- function subscriptions($apidata, $other_attr, $user_attr, $onlyIDs=false, $includeStatuses=true)
- {
- $this->auth_user = $apidata['user'];
- $user = $this->get_user($apidata['api_arg'], $apidata);
-
- if (empty($user)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $profile = $user->getProfile();
-
- $sub = new Subscription();
- $sub->$user_attr = $profile->id;
-
- $sub->orderBy('created DESC');
-
- // Normally, page 100 friends at a time
-
- if (!$onlyIDs) {
- $page = $this->arg('page', 1);
- $count = $this->arg('count', 100);
- $sub->limit(($page-1)*$count, $count);
- } else {
-
- // If we're just looking at IDs, return
- // ALL of them, unless the user specifies a page,
- // in which case, return 500 per page.
-
- $page = $this->arg('page');
- if (!empty($page)) {
- if ($page < 1) {
- $page = 1;
- }
- $count = 500;
- $sub->limit(($page-1)*$count, $count);
- }
- }
-
- $others = array();
-
- if ($sub->find()) {
- while ($sub->fetch()) {
- $others[] = Profile::staticGet($sub->$other_attr);
- }
- } else {
- // user has no followers
- }
-
- $type = $apidata['content-type'];
-
- $this->init_document($type);
-
- if ($onlyIDs) {
- $this->showIDs($others, $type);
- } else {
- $this->show_profiles($others, $type, $includeStatuses);
- }
-
- $this->end_document($type);
- }
-
- function show_profiles($profiles, $type, $includeStatuses)
- {
- switch ($type) {
- case 'xml':
- $this->elementStart('users', array('type' => 'array'));
- foreach ($profiles as $profile) {
- $this->show_profile($profile,$type,null,$includeStatuses);
- }
- $this->elementEnd('users');
- break;
- case 'json':
- $arrays = array();
- foreach ($profiles as $profile) {
- $arrays[] = $this->twitter_user_array($profile, $includeStatuses);
- }
- print json_encode($arrays);
- break;
- default:
- $this->clientError(_('unsupported file type'));
- }
- }
-
- function showIDs($profiles, $type)
- {
- switch ($type) {
- case 'xml':
- $this->elementStart('ids');
- foreach ($profiles as $profile) {
- $this->element('id', null, $profile->id);
- }
- $this->elementEnd('ids');
- break;
- case 'json':
- $ids = array();
- foreach ($profiles as $profile) {
- $ids[] = (int)$profile->id;
- }
- print json_encode($ids);
- break;
- default:
- $this->clientError(_('unsupported file type'));
- }
- }
-
- function featured($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), $code=501);
- }
-
- function supported($cmd)
- {
- $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand',
- 'FavCommand', 'OnCommand', 'OffCommand');
-
- if (in_array(get_class($cmd), $cmdlist)) {
- return true;
- }
-
- return false;
- }
-
-}
diff --git a/actions/twitapiusers.php b/actions/twitapiusers.php
deleted file mode 100644
index 703fa6754..000000000
--- a/actions/twitapiusers.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?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);
-}
-
-require_once(INSTALLDIR.'/lib/twitterapi.php');
-
-class TwitapiusersAction extends TwitterapiAction
-{
-
- function show($args, $apidata)
- {
- parent::handle($args);
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('API method not found!'), $code = 404);
- return;
- }
-
- $user = null;
- $email = $this->arg('email');
-
- // XXX: email field deprecated in Twitter's API
-
- if ($email) {
- $user = User::staticGet('email', $email);
- } else {
- $user = $this->get_user($apidata['api_arg'], $apidata);
- }
-
- if (empty($user)) {
- $this->clientError(_('Not found.'), 404, $apidata['content-type']);
- return;
- }
-
- $profile = $user->getProfile();
-
- if (!$profile) {
- common_server_error(_('User has no profile.'));
- return;
- }
-
- $twitter_user = $this->twitter_user_array($user->getProfile(), true);
-
- 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 {
-
- // This is in case 'show' was called via /account/verify_credentials
- // without a format (xml or json).
- header('Content-Type: text/html; charset=utf-8');
- print 'Authorized';
- }
-
- }
-}
diff --git a/lib/apiauth.php b/lib/apiauth.php
new file mode 100644
index 000000000..f0b4b6bf7
--- /dev/null
+++ b/lib/apiauth.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base class for API actions that require authentication
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/twitterapi.php';
+
+/**
+ * Actions extending this class will require auth
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiAuthAction extends TwitterapiAction
+{
+
+ var $auth_user = null;
+
+ /**
+ * Does this API resource require authentication?
+ *
+ * @return boolean true
+ */
+
+ function requiresAuth()
+ {
+ return true;
+ }
+
+ /**
+ * Check for a user specified via HTTP basic auth. If there isn't
+ * one, try to get one by outputting the basic auth header.
+ *
+ * @return boolean true or false
+ */
+
+ function checkBasicAuthUser()
+ {
+ $this->basicAuthProcessHeader();
+
+ if (!isset($this->auth_user)) {
+ header('WWW-Authenticate: Basic realm="StatusNet API"');
+
+ // show error if the user clicks 'cancel'
+
+ $this->showBasicAuthError();
+ return false;
+
+ } else {
+ $nickname = $this->auth_user;
+ $password = $this->auth_pw;
+ $this->auth_user = common_check_user($nickname, $password);
+
+ if (empty($this->auth_user)) {
+
+ // basic authentication failed
+
+ list($proxy, $ip) = common_client_ip();
+ common_log(
+ LOG_WARNING,
+ 'Failed API auth attempt, nickname = ' .
+ "$nickname, proxy = $proxy, ip = $ip."
+ );
+ $this->showBasicAuthError();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Read the HTTP headers and set the auth user. Decodes HTTP_AUTHORIZATION
+ * param to support basic auth when PHP is running in CGI mode.
+ *
+ * @return void
+ */
+
+ function basicAuthProcessHeader()
+ {
+ if (isset($_SERVER['AUTHORIZATION'])
+ || isset($_SERVER['HTTP_AUTHORIZATION'])
+ ) {
+ $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])
+ ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION'];
+ }
+
+ if (isset($_SERVER['PHP_AUTH_USER'])) {
+ $this->auth_user = $_SERVER['PHP_AUTH_USER'];
+ $this->auth_pw = $_SERVER['PHP_AUTH_PW'];
+ } elseif (isset($authorization_header)
+ && strstr(substr($authorization_header, 0, 5), 'Basic')) {
+
+ // decode the HTTP_AUTHORIZATION header on php-cgi server self
+ // on fcgid server the header name is AUTHORIZATION
+
+ $auth_hash = base64_decode(substr($authorization_header, 6));
+ list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash);
+
+ // set all to null on a empty basic auth request
+
+ if ($this->auth_user == "") {
+ $this->auth_user = null;
+ $this->auth_pw = null;
+ }
+ } else {
+ $this->auth_user = null;
+ $this->auth_pw = null;
+ }
+ }
+
+ /**
+ * Output an authentication error message. Use XML or JSON if one
+ * of those formats is specified, otherwise output plain text
+ *
+ * @return void
+ */
+
+ function showBasicAuthError()
+ {
+ header('HTTP/1.1 401 Unauthorized');
+ $msg = 'Could not authenticate you.';
+
+ if ($this->arg('format') == 'xml') {
+ header('Content-Type: application/xml; charset=utf-8');
+ $this->startXML();
+ $this->elementStart('hash');
+ $this->element('error', null, $msg);
+ $this->element('request', null, $_SERVER['REQUEST_URI']);
+ $this->elementEnd('hash');
+ $this->endXML();
+ } elseif ($this->arg('format') == '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";
+ }
+ }
+
+}
diff --git a/lib/apibareauth.php b/lib/apibareauth.php
new file mode 100644
index 000000000..a99d450ec
--- /dev/null
+++ b/lib/apibareauth.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base class for API actions that require "bare auth". Bare auth means
+ * authentication is required only if the action is called without an argument
+ * or query param specifying user id.
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Actions extending this class will require auth unless a target
+ * user ID has been specified
+ *
+ * @category API
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiBareAuthAction extends ApiAuthAction
+{
+ /**
+ * Does this API resource require authentication?
+ *
+ * @return boolean true or false
+ */
+
+ function requiresAuth()
+ {
+ // If the site is "private", all API methods except statusnet/config
+ // need authentication
+
+ if (common_config('site', 'private')) {
+ return true;
+ }
+
+ // check whether a user has been specified somehow
+
+ $id = $this->arg('id');
+ $user_id = $this->arg('user_id');
+ $screen_name = $this->arg('screen_name');
+
+ if (empty($id) && empty($user_id) && empty($screen_name)) {
+ return true;
+ }
+
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/lib/router.php b/lib/router.php
index 91f886bce..5ee939657 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -270,22 +270,100 @@ class Router
// statuses API
- $m->connect('api/statuses/:method',
- array('action' => 'api',
- 'apiaction' => 'statuses'),
- array('method' => '(public_timeline|home_timeline|friends_timeline|user_timeline|update|replies|mentions|show|friends|followers|featured)(\.(atom|rss|xml|json))?'));
-
- $m->connect('api/statuses/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'statuses'),
- array('method' => '(user_timeline|home_timeline|friends_timeline|replies|mentions|show|destroy|friends|followers)'));
+ $m->connect('api/statuses/public_timeline.:format',
+ array('action' => 'ApiTimelinePublic',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/friends_timeline.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/friends_timeline/:id.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json|rss|atom)'));
+ $m->connect('api/statuses/home_timeline.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/home_timeline/:id.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/user_timeline.:format',
+ array('action' => 'ApiTimelineUser',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/user_timeline/:id.:format',
+ array('action' => 'ApiTimelineUser',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/mentions.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/mentions/:id.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/replies.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/replies/:id.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/friends.:format',
+ array('action' => 'ApiUserFriends',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/friends/:id.:format',
+ array('action' => 'ApiUserFriends',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/followers.:format',
+ array('action' => 'ApiUserFollowers',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/followers/:id.:format',
+ array('action' => 'ApiUserFollowers',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/show.:format',
+ array('action' => 'ApiStatusesShow',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/show/:id.:format',
+ array('action' => 'ApiStatusesShow',
+ 'id' => '[0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/update.:format',
+ array('action' => 'ApiStatusesUpdate',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/destroy.:format',
+ array('action' => 'ApiStatusesDestroy',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statuses/destroy/:id.:format',
+ array('action' => 'ApiStatusesDestroy',
+ 'id' => '[0-9]+',
+ 'format' => '(xml|json)'));
// users
- $m->connect('api/users/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'users'),
- array('method' => 'show(\.(xml|json))?'));
+ $m->connect('api/users/show/:id.:format',
+ array('action' => 'ApiUserShow',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
$m->connect('api/users/:method',
array('action' => 'api',
@@ -294,68 +372,65 @@ class Router
// direct messages
- foreach (array('xml', 'json') as $e) {
- $m->connect('api/direct_messages/new.'.$e,
- array('action' => 'api',
- 'apiaction' => 'direct_messages',
- 'method' => 'create.'.$e));
- }
- foreach (array('xml', 'json', 'rss', 'atom') as $e) {
- $m->connect('api/direct_messages.'.$e,
- array('action' => 'api',
- 'apiaction' => 'direct_messages',
- 'method' => 'direct_messages.'.$e));
- }
+ $m->connect('api/direct_messages.:format',
+ array('action' => 'ApiDirectMessage',
+ 'format' => '(xml|json|rss|atom)'));
- foreach (array('xml', 'json', 'rss', 'atom') as $e) {
- $m->connect('api/direct_messages/sent.'.$e,
- array('action' => 'api',
- 'apiaction' => 'direct_messages',
- 'method' => 'sent.'.$e));
- }
+ $m->connect('api/direct_messages/sent.:format',
+ array('action' => 'ApiDirectMessage',
+ 'format' => '(xml|json|rss|atom)',
+ 'sent' => true));
- $m->connect('api/direct_messages/destroy/:argument',
- array('action' => 'api',
- 'apiaction' => 'direct_messages'));
+ $m->connect('api/direct_messages/new.:format',
+ array('action' => 'ApiDirectMessageNew',
+ 'format' => '(xml|json)'));
// friendships
- $m->connect('api/friendships/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'friendships'),
- array('method' => '(create|destroy)'));
+ $m->connect('api/friendships/show.:format',
+ array('action' => 'ApiFriendshipsShow',
+ 'format' => '(xml|json)'));
- $m->connect('api/friendships/:method',
- array('action' => 'api',
- 'apiaction' => 'friendships'),
- array('method' => '(show|exists)(\.(xml|json))'));
+ $m->connect('api/friendships/exists.:format',
+ array('action' => 'ApiFriendshipsExists',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/friendships/create.:format',
+ array('action' => 'ApiFriendshipsCreate',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/friendships/destroy.:format',
+ array('action' => 'ApiFriendshipsDestroy',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/friendships/create/:id.:format',
+ array('action' => 'ApiFriendshipsCreate',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/friendships/destroy/:id.:format',
+ array('action' => 'ApiFriendshipsDestroy',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
// Social graph
- $m->connect('api/friends/ids/:argument',
- array('action' => 'api',
- 'apiaction' => 'statuses',
- 'method' => 'friendsIDs'));
+ $m->connect('api/friends/ids/:id.:format',
+ array('action' => 'apiFriends',
+ 'ids_only' => true));
- foreach (array('xml', 'json') as $e) {
- $m->connect('api/friends/ids.'.$e,
- array('action' => 'api',
- 'apiaction' => 'statuses',
- 'method' => 'friendsIDs.'.$e));
- }
+ $m->connect('api/followers/ids/:id.:format',
+ array('action' => 'apiFollowers',
+ 'ids_only' => true));
- $m->connect('api/followers/ids/:argument',
- array('action' => 'api',
- 'apiaction' => 'statuses',
- 'method' => 'followersIDs'));
+ $m->connect('api/friends/ids.:format',
+ array('action' => 'apiFriends',
+ 'ids_only' => true));
- foreach (array('xml', 'json') as $e) {
- $m->connect('api/followers/ids.'.$e,
- array('action' => 'api',
- 'apiaction' => 'statuses',
- 'method' => 'followersIDs.'.$e));
- }
+ $m->connect('api/followers/ids.:format',
+ array('action' => 'apiFollowers',
+ 'ids_only' => true));
// account
diff --git a/lib/twitterapi.php b/lib/twitterapi.php
index 4a5de6ab3..b2104fddd 100644
--- a/lib/twitterapi.php
+++ b/lib/twitterapi.php
@@ -24,8 +24,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
class TwitterapiAction extends Action
{
- var $auth_user;
-
/**
* Initialization.
*
@@ -324,51 +322,6 @@ class TwitterapiAction extends Action
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)
{
@@ -533,40 +486,6 @@ class TwitterapiAction extends Action
$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)
{
@@ -687,6 +606,121 @@ class TwitterapiAction extends Action
$this->end_twitter_rss();
}
+
+ function showTwitterAtomEntry($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('type' => 'text/html',
+ 'href' => $entry['link'],
+ 'rel' => 'alternate'));
+ $this->element('link', array('type' => $entry['avatar-type'],
+ 'href' => $entry['avatar'],
+ 'rel' => 'image'));
+ $this->elementStart('author');
+
+ $this->element('name', null, $entry['author-name']);
+ $this->element('uri', null, $entry['author-uri']);
+
+ $this->elementEnd('author');
+ $this->elementEnd('entry');
+ }
+
+ function showXmlDirectMessage($dm)
+ {
+ $this->elementStart('direct_message');
+ foreach($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);
+ break;
+ }
+ }
+ $this->elementEnd('direct_message');
+ }
+
+ function directMessageArray($message)
+ {
+ $dmsg = array();
+
+ $from_profile = $message->getFrom();
+ $to_profile = $message->getTo();
+
+ $dmsg['id'] = $message->id;
+ $dmsg['sender_id'] = $message->from_profile;
+ $dmsg['text'] = trim($message->content);
+ $dmsg['recipient_id'] = $message->to_profile;
+ $dmsg['created_at'] = $this->date_twitter($message->created);
+ $dmsg['sender_screen_name'] = $from_profile->nickname;
+ $dmsg['recipient_screen_name'] = $to_profile->nickname;
+ $dmsg['sender'] = $this->twitter_user_array($from_profile, false);
+ $dmsg['recipient'] = $this->twitter_user_array($to_profile, false);
+
+ return $dmsg;
+ }
+
+ function rssDirectMessageArray($message)
+ {
+ $entry = array();
+
+ $from = $message->getFrom();
+
+ $entry['title'] = sprintf('Message from %s to %s',
+ $from->nickname, $message->getTo()->nickname);
+
+ $entry['content'] = common_xml_safe_str($message->rendered);
+ $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-name'] = $from->getBestName();
+ $entry['author-uri'] = $from->homepage;
+
+ $avatar = $from->getAvatar(AVATAR_STREAM_SIZE);
+
+ $entry['avatar'] = (!empty($avatar)) ? $avatar->url : Avatar::defaultImage(AVATAR_STREAM_SIZE);
+ $entry['avatar-type'] = (!empty($avatar)) ? $avatar->mediatype : 'image/png';
+
+ // RSS item specific
+
+ $entry['description'] = $entry['content'];
+ $entry['pubDate'] = common_date_rfc2822($message->created);
+ $entry['guid'] = $entry['link'];
+
+ return $entry;
+ }
+
+ function showSingleXmlDirectMessage($message)
+ {
+ $this->init_document('xml');
+ $dmsg = $this->directMessageArray($message);
+ $this->showXmlDirectMessage($dmsg);
+ $this->end_document('xml');
+ }
+
+ function showSingleJsonDirectMessage($message)
+ {
+ $this->init_document('json');
+ $dmsg = $this->directMessageArray($message);
+ $this->show_json_objects($dmsg);
+ $this->end_document('json');
+ }
+
function show_atom_groups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
{
@@ -934,7 +968,7 @@ class TwitterapiAction extends Action
return;
}
- function clientError($msg, $code = 400, $content_type = 'json')
+ function clientError($msg, $code = 400, $format = 'xml')
{
$action = $this->trimmed('action');
@@ -948,20 +982,23 @@ class TwitterapiAction extends Action
header('HTTP/1.1 '.$code.' '.$status_string);
- if ($content_type == 'xml') {
+ 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');
- } else {
+ } 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')
@@ -1073,6 +1110,38 @@ class TwitterapiAction extends Action
}
}
+ 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)) {