+++ /dev/null
-<?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/>.
- *
- * @category Actions
- * @package Actions
- * @author Evan Prodromou <evan@status.net>
- * @author Brenda Wallace <shiny@cpan.org>
- * @author Jeffery To <jeffery.to@gmail.com>
- * @author Robin Millette <millette@controlyourself.ca>
- * @author Tom Adams <tom@holizz.com>
- * @author Christopher Vollick <psycotica0@gmail.com>
- * @author CiaranG <ciaran@ciarang.com>
- * @author Craig Andrews <candrews@integralblue.com>
- * @author Gina Haeussge <osd@foosel.net>
- * @author Mike Cochrane <mikec@mikenz.geek.nz>
- * @author Sarven Capadisli <csarven@status.net>
- * @license GNU Affero General Public License http://www.gnu.org/licenses/
- * @link http://status.net
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-class ApiAction extends Action
-{
-
- var $user;
- var $content_type;
- var $api_arg;
- var $api_method;
- var $api_action;
- var $auth_user;
- var $auth_pw;
-
- function handle($args)
- {
- parent::handle($args);
-
- $this->api_action = $this->arg('apiaction');
- $method = $this->arg('method');
- $argument = $this->arg('argument');
- $this->basic_auth_process_header();
-
- if (isset($argument)) {
- $cmdext = explode('.', $argument);
- $this->api_arg = $cmdext[0];
- $this->api_method = $method;
- $this->content_type = strtolower($cmdext[1]);
- } else {
-
- //Requested format / content-type will be an extension on the method
- $cmdext = explode('.', $method);
- $this->api_method = $cmdext[0];
- $this->content_type = strtolower($cmdext[1]);
- }
-
- if ($this->requires_auth()) {
- if (!isset($this->auth_user)) {
-
- //This header makes basic auth go
- header('WWW-Authenticate: Basic realm="StatusNet API"');
-
- //If the user hits cancel -- bam!
- $this->show_basic_auth_error();
- } else {
- $nickname = $this->auth_user;
- $password = $this->auth_pw;
- $user = common_check_user($nickname, $password);
-
- if ($user) {
- $this->user = $user;
- $this->process_command();
- } else {
- //basic authentication failed
- list($proxy, $ip) = common_client_ip();
-
- common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
- $this->show_basic_auth_error();
- }
- }
- } else {
-
- // Caller might give us a username even if not required
- if (isset($this->auth_user)) {
- $user = User::staticGet('nickname', $this->auth_user);
- if ($user) {
- $this->user = $user;
- }
- //Twitter doesn't throw an error if the user isn't found
- }
-
- $this->process_command();
- }
- }
-
- function process_command()
- {
- $action = "twitapi$this->api_action";
- $actionfile = INSTALLDIR."/actions/$action.php";
-
- if (file_exists($actionfile)) {
- include_once $actionfile;
- $action_class = ucfirst($action)."Action";
- $action_obj = new $action_class();
-
- if (!$action_obj->prepare($this->args)) {
- return;
- }
-
- if (method_exists($action_obj, $this->api_method)) {
- $apidata = array( 'content-type' => $this->content_type,
- 'api_method' => $this->api_method,
- 'api_arg' => $this->api_arg,
- 'user' => $this->user);
-
- call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata);
- } else {
- $this->clientError("API method not found!", $code = 404);
- }
- } else {
- $this->clientError("API method not found!", $code = 404);
- }
- }
-
- // Whitelist of API methods that don't need authentication
- function requires_auth()
- {
- static $noauth = array( 'statuses/public_timeline',
- 'statuses/show',
- 'users/show',
- 'help/test',
- 'help/downtime_schedule',
- 'statusnet/version',
- 'statusnet/config',
- 'statusnet/wadl',
- 'tags/timeline',
- 'oembed/oembed',
- 'groups/show',
- 'groups/timeline',
- 'groups/list_all',
- 'groups/membership',
- 'groups/is_member',
- 'groups/timeline');
-
- static $bareauth = array('statuses/user_timeline',
- 'statuses/friends_timeline',
- 'statuses/home_timeline',
- 'statuses/friends',
- 'statuses/replies',
- 'statuses/mentions',
- 'statuses/followers',
- 'favorites/favorites',
- 'friendships/show',
- 'groups/list_groups');
-
- $fullname = "$this->api_action/$this->api_method";
-
- // If the site is "private", all API methods except statusnet/config
- // need authentication
-
- if (common_config('site', 'private')) {
- return $fullname != 'statusnet/config' || false;
- }
-
- // bareauth: only needs auth if without an argument or query param specifying user
-
- if (in_array($fullname, $bareauth)) {
-
- // Special case: friendships/show only needs auth if source_id or
- // source_screen_name is not specified as a param
-
- if ($fullname == 'friendships/show') {
-
- $source_id = $this->arg('source_id');
- $source_screen_name = $this->arg('source_screen_name');
-
- if (empty($source_id) && empty($source_screen_name)) {
- return true;
- }
-
- return false;
- }
-
- // if all of these are empty, auth is required
-
- $id = $this->arg('id');
- $user_id = $this->arg('user_id');
- $screen_name = $this->arg('screen_name');
-
- if (empty($this->api_arg)
- && empty($id)
- && empty($user_id)
- && empty($screen_name)
- ) {
- return true;
- } else {
- return false;
- }
-
- } else if (in_array($fullname, $noauth)) {
-
- // noauth: never needs auth
-
- return false;
- } else {
-
- // everybody else needs auth
-
- return true;
- }
- }
-
- function basic_auth_process_header()
- {
- 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;
- }
- }
-
- function show_basic_auth_error()
- {
- header('HTTP/1.1 401 Unauthorized');
- $msg = 'Could not authenticate you.';
-
- if ($this->content_type == '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();
- } else if ($this->content_type == 'json') {
- header('Content-Type: application/json; charset=utf-8');
- $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
- print(json_encode($error_array));
- } else {
- header('Content-type: text/plain');
- print "$msg\n";
- }
- }
-
- function isReadOnly($args)
- {
- $apiaction = $args['apiaction'];
- $method = $args['method'];
-
- list($cmdtext, $fmt) = explode('.', $method);
-
- static $write_methods = array(
- 'account' => array('update_location', 'update_delivery_device', 'end_session'),
- 'blocks' => array('create', 'destroy'),
- 'direct_messages' => array('create', 'destroy'),
- 'favorites' => array('create', 'destroy'),
- 'friendships' => array('create', 'destroy'),
- 'help' => array(),
- 'notifications' => array('follow', 'leave'),
- 'statuses' => array('update', 'destroy'),
- 'users' => array()
- );
-
- if (array_key_exists($apiaction, $write_methods)) {
- if (!in_array($cmdtext, $write_methods[$apiaction])) {
- return true;
- }
- }
-
- return false;
- }
-}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Dummy action that emulates Twitter's rate limit status API resource
+ *
+ * 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 Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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';
+
+/**
+ * We don't have a rate limit, but some clients check this method.
+ * It always returns the same thing: 150 hits left.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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 ApiAccountRateLimitStatusAction extends ApiBareAuthAction
+{
+
+ /**
+ * Handle the request
+ *
+ * Return some Twitter-ish data about API limits
+ *
+ * @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,
+ $this->format
+ );
+ return;
+ }
+
+ $reset = new DateTime();
+ $reset->modify('+1 hour');
+
+ $this->initDocument($this->format);
+
+ if ($this->format == 'xml') {
+ $this->elementStart('hash');
+ $this->element('remaining-hits', array('type' => 'integer'), 150);
+ $this->element('hourly-limit', array('type' => 'integer'), 150);
+ $this->element(
+ 'reset-time', array('type' => 'datetime'),
+ common_date_iso8601($reset->format('r'))
+ );
+ $this->element(
+ 'reset_time_in_seconds',
+ array('type' => 'integer'),
+ strtotime('+1 hour')
+ );
+ $this->elementEnd('hash');
+ } elseif ($this->format == 'json') {
+ $out = array(
+ 'reset_time_in_seconds' => strtotime('+1 hour'),
+ 'remaining_hits' => 150,
+ 'hourly_limit' => 150,
+ 'reset_time' => common_date_rfc2822(
+ $reset->format('r')
+ )
+ );
+ print json_encode($out);
+ }
+
+ $this->endDocument($this->format);
+ }
+
+}
+
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Test if supplied user credentials are valid.
+ *
+ * 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 Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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';
+
+/**
+ * Check a user's credentials. Returns an HTTP 200 OK response code and a
+ * representation of the requesting user if authentication was successful;
+ * returns a 401 status code and an error message if not.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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 ApiAccountVerifyCredentialsAction extends ApiAuthAction
+{
+
+ /**
+ * Handle the request
+ *
+ * Check whether the credentials are valid and output the result
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ switch ($this->format) {
+ case 'xml':
+ case 'json':
+ $args['id'] = $this->auth_user->id;
+ $action_obj = new ApiUserShowAction();
+ if ($action_obj->prepare($args)) {
+ $action_obj->handle($args);
+ }
+ break;
+ default:
+ header('Content-Type: text/html; charset=utf-8');
+ print 'Authorized';
+ }
+
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Block 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 Evan Prodromou <evan@status.net>
+ * @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';
+
+/**
+ * Blocks the user specified in the ID parameter as the authenticating user.
+ * Destroys a friendship to the blocked user if it exists. Returns the
+ * blocked user in the requested format when successful.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiBlockCreateAction extends ApiAuthAction
+{
+ var $other = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+ $this->other = $this->getTargetUser($this->arg('id'));
+
+ 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->user) || empty($this->other)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ if ($this->user->hasBlocked($this->other)
+ || $this->user->block($this->other)
+ ) {
+ $this->initDocument($this->format);
+ $this->showProfile($this->other, $this->format);
+ $this->endDocument($this->format);
+ } else {
+ $this->serverError(_('Block user failed.'), 500, $this->format);
+ }
+
+ }
+
+}
+
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Un-block 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 Evan Prodromou <evan@status.net>
+ * @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';
+
+/**
+ * Un-blocks the user specified in the ID parameter for the authenticating user.
+ * Returns the un-blocked user in the requested format when successful.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiBlockDestroyAction extends ApiAuthAction
+{
+ var $other = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+ $this->other = $this->getTargetUser($this->arg('id'));
+
+ 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->user) || empty($this->other)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ if (!$this->user->hasBlocked($this->other)
+ || $this->user->unblock($this->other)
+ ) {
+ $this->initDocument($this->format);
+ $this->showProfile($this->other, $this->format);
+ $this->endDocument($this->format);
+ } else {
+ $this->serverError(_('Unblock user failed.'));
+ }
+
+ }
+
+}
+
--- /dev/null
+<?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 Adrian Lang <mail@adrianlang.de>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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 Adrian Lang <mail@adrianlang.de>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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 $messages = 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);
+
+ $this->user = $this->auth_user;
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ $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->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->initDocument('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->endDocument('xml');
+ }
+
+ /**
+ * Shows a list of direct messages as a JSON encoded array
+ *
+ * @return void
+ */
+
+ function showJsonDirectMessages()
+ {
+ $this->initDocument('json');
+
+ $dmsgs = array();
+
+ foreach ($this->messages as $m) {
+ $dm_array = $this->directMessageArray($m);
+ array_push($dmsgs, $dm_array);
+ }
+
+ $this->showJsonObjects($dmsgs);
+ $this->endDocument('json');
+ }
+
+ /**
+ * Shows a list of direct messages as RSS items
+ *
+ * @return void
+ */
+
+ function showRssDirectMessages()
+ {
+ $this->initDocument('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->showTwitterRssItem($entry);
+ }
+
+ $this->endTwitterRss();
+ }
+
+ /**
+ * Shows a list of direct messages as Atom entries
+ *
+ * @return void
+ */
+
+ function showAtomDirectMessages()
+ {
+ $this->initDocument('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->endDocument('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;
+ }
+
+}
--- /dev/null
+<?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 Adrian Lang <mail@adrianlang.de>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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 Adrian Lang <mail@adrianlang.de>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <robin@millette.info>
+ * @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 $source = 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);
+
+ $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);
+ }
+
+ 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);
+ }
+ }
+
+}
+
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Add a notice to a user's list of favorite notices 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @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';
+
+/**
+ * Favorites the status specified in the ID parameter as the authenticating user.
+ * Returns the favorite status when successful.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiFavoriteCreateAction extends ApiAuthAction
+{
+ var $notice = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+ $this->notice = Notice::staticGet($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 ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ $this->clientError(
+ _('This method requires a POST.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->notice)) {
+ $this->clientError(
+ _('No status found with that ID.'),
+ 404,
+ $this->format
+ );
+ return;
+ }
+
+ // Note: Twitter lets you fave things repeatedly via API.
+
+ if ($this->user->hasFave($this->notice)) {
+ $this->clientError(
+ _('This status is already a favorite!'),
+ 403,
+ $this->format
+ );
+ return;
+ }
+
+ $fave = Fave::addNew($this->user, $this->notice);
+
+ if (empty($fave)) {
+ $this->clientError(
+ _('Could not create favorite.')
+ 403,
+ $this->format
+ );
+ return;
+ }
+
+ $this->notify($fave, $this->notice, $this->user);
+ $this->user->blowFavesCache();
+
+ if ($this->format == 'xml') {
+ $this->showSingleXmlStatus($this->notice);
+ } elseif ($this->format == 'json') {
+ $this->show_single_json_status($this->notice);
+ }
+ }
+
+ /**
+ * Notify the author of the favorite that the user likes their notice
+ *
+ * @param Favorite $fave the favorite in question
+ * @param Notice $notice the notice that's been faved
+ * @param User $user the user doing the favoriting
+ *
+ * @return void
+ */
+ function notify($fave, $notice, $user)
+ {
+ $other = User::staticGet('id', $notice->profile_id);
+ if ($other && $other->id != $user->id) {
+ if ($other->email && $other->emailnotifyfav) {
+ mail_notify_fave($other, $user, $notice);
+ }
+ // XXX: notify by IM
+ // XXX: notify by SMS
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Remote a notice from a user's list of favorite notices 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @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';
+
+/**
+ * Un-favorites the status specified in the ID parameter as the authenticating user.
+ * Returns the un-favorited status in the requested format when successful.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiFavoriteDestroyAction extends ApiAuthAction
+{
+
+ var $notice = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+ $this->notice = Notice::staticGet($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 ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ $this->clientError(
+ _('This method requires a POST.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->notice)) {
+ $this->clientError(
+ _('No status found with that ID.'),
+ 404,
+ $this->format
+ );
+ return;
+ }
+
+ $fave = new Fave();
+ $fave->user_id = $this->user->id;
+ $fave->notice_id = $this->notice->id;
+
+ if (!$fave->find(true)) {
+ $this->clientError(
+ _('That status is not a favorite!'),
+ 403,
+ $this->favorite
+ );
+ return;
+ }
+
+ $result = $fave->delete();
+
+ if (!$result) {
+ common_log_db_error($fave, 'DELETE', __FILE__);
+ $this->clientError(
+ _('Could not delete favorite.'),
+ 404,
+ $this->format
+ );
+ return;
+ }
+
+ $this->user->blowFavesCache();
+
+ if ($this->format == 'xml') {
+ $this->showSingleXmlStatus($this->notice);
+ } elseif ($this->format == 'json') {
+ $this->show_single_json_status($this->notice);
+ }
+ }
+
+}
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 $other = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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 (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $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->initDocument($this->format);
+ $this->showProfile($this->other, $this->format);
+ $this->endDocument($this->format);
+ }
+
+}
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 $other = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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 (!in_array($this->format, array('xml', 'json'))) {
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $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->initDocument($this->format);
+ $this->showProfile($this->other, $this->format);
+ $this->endDocument($this->format);
+ }
+
+}
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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/api.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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiAction
+{
+ 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);
+
+ $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->initDocument('xml');
+ $this->element('friends', null, $result);
+ $this->endDocument('xml');
+ break;
+ case 'json':
+ $this->initDocument('json');
+ print json_encode($result);
+ $this->endDocument('json');
+ break;
+ default:
+ break;
+ }
+ }
+
+}
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 $source = null;
+ var $target = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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->twitterRelationshipArray($this->source, $this->target);
+
+ switch ($this->format) {
+ case 'xml':
+ $this->initDocument('xml');
+ $this->showTwitterXmlRelationship($result[relationship]);
+ $this->endDocument('xml');
+ break;
+ case 'json':
+ $this->initDocument('json');
+ print json_encode($result);
+ $this->endDocument('json');
+ break;
+ default:
+ break;
+ }
+
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Create a group 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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';
+
+/**
+ * Make a new group. Sets the authenticated user as the administrator of the group.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupCreateAction extends ApiAuthAction
+{
+ var $group = null;
+ var $nickname = null;
+ var $fullname = null;
+ var $homepage = null;
+ var $description = null;
+ var $location = null;
+ var $aliasstring = null;
+ var $aliases = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+
+ $this->nickname = $this->arg('nickname');
+ $this->fullname = $this->arg('full_name');
+ $this->homepage = $this->arg('homepage');
+ $this->description = $this->arg('description');
+ $this->location = $this->arg('location');
+ $this->aliasstring = $this->arg('aliases');
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Save the new group
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (!common_config('inboxes', 'enabled')) {
+ $this->serverError(
+ _('Inboxes must be enabled for groups to work'),
+ 400,
+ $this->format
+ );
+ return false;
+ }
+
+ if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ $this->clientError(
+ _('This method requires a POST.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ if ($this->validateParams() == false) {
+ return;
+ }
+
+ $group = new User_group();
+
+ $group->query('BEGIN');
+
+ $group->nickname = $this->nickname;
+ $group->fullname = $this->fullname;
+ $group->homepage = $this->homepage;
+ $group->description = $this->description;
+ $group->location = $this->location;
+ $group->created = common_sql_now();
+
+ $result = $group->insert();
+
+ if (!$result) {
+ common_log_db_error($group, 'INSERT', __FILE__);
+ $this->serverError(
+ _('Could not create group.'),
+ 500,
+ $this->format
+ );
+ return;
+ }
+
+ $result = $group->setAliases($this->aliases);
+
+ if (!$result) {
+ $this->serverError(
+ _('Could not create aliases.'),
+ 500,
+ $this->format
+ );
+ return;
+ }
+
+ $member = new Group_member();
+
+ $member->group_id = $group->id;
+ $member->profile_id = $this->user->id;
+ $member->is_admin = 1;
+ $member->created = $group->created;
+
+ $result = $member->insert();
+
+ if (!$result) {
+ common_log_db_error($member, 'INSERT', __FILE__);
+ $this->serverError(
+ _('Could not set group membership.'),
+ 500,
+ $this->format
+ );
+ return;
+ }
+
+ $group->query('COMMIT');
+
+ switch($this->format) {
+ case 'xml':
+ $this->showSingleXmlGroup($group);
+ break;
+ case 'json':
+ $this->showSingleJsonGroup($group);
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+
+ }
+
+ /**
+ * Validate params for the new group
+ *
+ * @return void
+ */
+
+ function validateParams()
+ {
+ $valid = Validate::string(
+ $this->nickname, array(
+ 'min_length' => 1,
+ 'max_length' => 64,
+ 'format' => NICKNAME_FMT
+ )
+ );
+
+ if (!$valid) {
+ $this->clientError(
+ _(
+ 'Nickname must have only lowercase letters ' .
+ 'and numbers and no spaces.'
+ ),
+ 403,
+ $this->format
+ );
+ return false;
+ } elseif ($this->groupNicknameExists($this->nickname)) {
+ $this->clientError(
+ _('Nickname already in use. Try another one.'),
+ 403,
+ $this->format
+ );
+ return false;
+ } else if (!User_group::allowedNickname($this->nickname)) {
+ $this->clientError(
+ _('Not a valid nickname.'),
+ 403,
+ $this->format
+ );
+ return false;
+
+ } elseif (
+ !is_null($this->homepage)
+ && strlen($this->homepage) > 0
+ && !Validate::uri(
+ $this->homepage, array(
+ 'allowed_schemes' =>
+ array('http', 'https')
+ )
+ )) {
+ $this->clientError(
+ _('Homepage is not a valid URL.'),
+ 403,
+ $this->format
+ );
+ return false;
+ } elseif (
+ !is_null($this->fullname)
+ && mb_strlen($this->fullname) > 255) {
+ $this->clientError(
+ _('Full name is too long (max 255 chars).'),
+ 403,
+ $this->format
+ );
+ return false;
+ } elseif (User_group::descriptionTooLong($this->description)) {
+ $this->clientError(
+ sprintf(
+ _('Description is too long (max %d chars).'),
+ User_group::maxDescription()
+ ),
+ 403,
+ $this->format
+ );
+ return false;
+ } elseif (
+ !is_null($this->location)
+ && mb_strlen($this->location) > 255) {
+ $this->clientError(
+ _('Location is too long (max 255 chars).'),
+ 403,
+ $this->format
+ );
+ return false;
+ }
+
+ if (!empty($this->aliasstring)) {
+ $this->aliases = array_map(
+ 'common_canonical_nickname',
+ array_unique(preg_split('/[\s,]+/', $this->aliasstring))
+ );
+ } else {
+ $this->aliases = array();
+ }
+
+ if (count($this->aliases) > common_config('group', 'maxaliases')) {
+ $this->clientError(
+ sprintf(
+ _('Too many aliases! Maximum %d.'),
+ common_config('group', 'maxaliases')
+ ),
+ 403,
+ $this->format
+ );
+ return false;
+ }
+
+ foreach ($this->aliases as $alias) {
+
+ $valid = Validate::string(
+ $alias, array(
+ 'min_length' => 1,
+ 'max_length' => 64,
+ 'format' => NICKNAME_FMT
+ )
+ );
+
+ if (!$valid) {
+ $this->clientError(
+ sprintf(_('Invalid alias: "%s"'), $alias),
+ 403,
+ $this->format
+ );
+ return false;
+ }
+ if ($this->groupNicknameExists($alias)) {
+ $this->clientError(
+ sprintf(
+ _('Alias "%s" already in use. Try another one.'),
+ $alias
+ ),
+ 403,
+ $this->format
+ );
+ return false;
+ }
+
+ // XXX assumes alphanum nicknames
+
+ if (strcmp($alias, $this->nickname) == 0) {
+ $this->clientError(
+ _('Alias can\'t be the same as nickname.'),
+ 403,
+ $this->format
+ );
+ return false;
+ }
+ }
+
+ // Evarything looks OK
+
+ return true;
+ }
+
+ /**
+ * Check to see whether a nickname is already in use by a group
+ *
+ * @param String $nickname The nickname in question
+ *
+ * @return boolean true or false
+ */
+
+ function groupNicknameExists($nickname)
+ {
+ $group = User_group::staticGet('nickname', $nickname);
+
+ if (!empty($group)) {
+ return true;
+ }
+
+ $alias = Group_alias::staticGet('alias', $nickname);
+
+ if (!empty($alias)) {
+ return true;
+ }
+
+ return false;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Check to see whether a user a member of a group
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 whether a user is a member of a specified group.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupIsMemberAction extends ApiBareAuthAction
+{
+ var $group = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->getTargetUser(null);
+ $this->group = $this->getTargetGroup(null);
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Save the new message
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ if (empty($this->group)) {
+ $this->clientError('Group not found!', 404, $this->format);
+ return false;
+ }
+
+ $is_member = $this->user->isMember($this->group);
+
+ switch($this->format) {
+ case 'xml':
+ $this->initDocument('xml');
+ $this->element('is_member', null, $is_member);
+ $this->endDocument('xml');
+ break;
+ case 'json':
+ $this->initDocument('json');
+ $this->showJsonObjects(array('is_member' => $is_member));
+ $this->endDocument('json');
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 400,
+ $this->format
+ );
+ break;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Join a group 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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';
+
+/**
+ * Joins the authenticated user to the group speicified by ID
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupJoinAction extends ApiAuthAction
+{
+ var $group = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+ $this->group = $this->getTargetGroup($this->arg('id'));
+
+ 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->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ if (empty($this->group)) {
+ $this->clientError('Group not found!', 404, $this->format);
+ return false;
+ }
+
+ if ($this->user->isMember($this->group)) {
+ $this->clientError(
+ _('You are already a member of that group.'),
+ 403,
+ $this->format
+ );
+ return;
+ }
+
+ if (Group_block::isBlocked($this->group, $this->user->getProfile())) {
+ $this->clientError(
+ _('You have been blocked from that group by the admin.'),
+ 403,
+ $this->format
+ );
+ return;
+ }
+
+ $member = new Group_member();
+
+ $member->group_id = $this->group->id;
+ $member->profile_id = $this->user->id;
+ $member->created = common_sql_now();
+
+ $result = $member->insert();
+
+ if (!$result) {
+ common_log_db_error($member, 'INSERT', __FILE__);
+ $this->serverError(
+ sprintf(
+ _('Could not join user %s to group %s.'),
+ $this->user->nickname,
+ $this->group->nickname
+ )
+ );
+ return;
+ }
+
+ switch($this->format) {
+ case 'xml':
+ $this->show_single_xml_group($this->group);
+ break;
+ case 'json':
+ $this->showSingleJsonGroup($this->group);
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Leave a group 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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';
+
+/**
+ * Removes the authenticated user from the group specified by ID
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupLeaveAction extends ApiAuthAction
+{
+ var $group = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+ $this->group = $this->getTargetGroup($this->arg('id'));
+
+ 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->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ if (empty($this->group)) {
+ $this->clientError('Group not found!', 404, $this->format);
+ return false;
+ }
+
+ $member = new Group_member();
+
+ $member->group_id = $this->group->id;
+ $member->profile_id = $this->auth->id;
+
+ if (!$member->find(true)) {
+ $this->serverError(_('You are not a member of this group.'));
+ return;
+ }
+
+ $result = $member->delete();
+
+ if (!$result) {
+ common_log_db_error($member, 'INSERT', __FILE__);
+ $this->serverError(
+ sprintf(
+ _('Could not remove user %s to group %s.'),
+ $this->user->nickname,
+ $this->$group->nickname
+ )
+ );
+ return;
+ }
+
+ switch($this->format) {
+ case 'xml':
+ $this->show_single_xml_group($this->group);
+ break;
+ case 'json':
+ $this->showSingleJsonGroup($this->group);
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Check to see whether a user a member of a group
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 whether a user is a member of a specified group.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupListAction extends ApiBareAuthAction
+{
+ var $groups = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->getTargetUser($id);
+ $this->groups = $this->getGroups();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Show the user's groups
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ $sitename = common_config('site', 'name');
+ $title = sprintf(_("%s's groups"), $this->user->nickname);
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:Groups";
+ $link = common_local_url(
+ 'usergroups',
+ array('nickname' => $this->user->nickname)
+ );
+ $subtitle = sprintf(
+ _("Groups %s is a member of on %s."),
+ $this->user->nickname,
+ $sitename
+ );
+
+ switch($this->format) {
+ case 'xml':
+ $this->showXmlGroups($this->groups);
+ break;
+ case 'rss':
+ $this->showRssGroups($this->groups, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() . 'api/statusnet/groups/list/' .
+ $this->user->id . '.atom';
+ $this->showAtomGroups(
+ $this->groups,
+ $title,
+ $id,
+ $link,
+ $subtitle,
+ $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonGroups($this->groups);
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+
+ }
+
+ /**
+ * Get groups
+ *
+ * @return array groups
+ */
+
+ function getGroups()
+ {
+ $groups = array();
+
+ $group = $this->user->getGroups(
+ ($this->page - 1) * $this->count,
+ $this->count,
+ $this->since_id,
+ $this->max_id,
+ $this->since
+ );
+
+ while ($group->fetch()) {
+ $groups[] = clone($group);
+ }
+
+ return $groups;
+ }
+
+ /**
+ * 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 group the user has joined
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->groups) && (count($this->groups) > 0)) {
+ return strtotime($this->groups[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this list of groups
+ *
+ * Returns an Etag based on the action name, language, user ID and
+ * timestamps of the first and last group the user has joined
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->groups) && (count($this->groups) > 0)) {
+
+ $last = count($this->groups) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ $this->user->id,
+ strtotime($this->groups[0]->created),
+ strtotime($this->groups[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show the newest groups
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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/api.php';
+
+/**
+ * Returns of the lastest 20 groups for the site
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupListAllAction extends ApiAction
+{
+ var $groups = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->getTargetUser($id);
+ $this->groups = $this->getGroups();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Show the user's groups
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ $sitename = common_config('site', 'name');
+ $title = sprintf(_("%s groups"), $sitename);
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:Groups";
+ $link = common_local_url('groups');
+ $subtitle = sprintf(_("groups on %s"), $sitename);
+
+ switch($this->format) {
+ case 'xml':
+ $this->showXmlGroups($this->groups);
+ break;
+ case 'rss':
+ $this->showRssGroups($this->groups, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() .
+ 'api/statusnet/groups/list_all.atom';
+ $this->showAtomGroups(
+ $this->groups,
+ $title,
+ $id,
+ $link,
+ $subtitle,
+ $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonGroups($this->groups);
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+
+ }
+
+ /**
+ * Get groups
+ *
+ * @return array groups
+ */
+
+ function getGroups()
+ {
+ $groups = array();
+
+ // XXX: Use the $page, $count, $max_id, $since_id, and $since parameters
+
+ $group = new User_group();
+ $group->orderBy('created DESC');
+ $group->find();
+
+ while ($group->fetch()) {
+ $groups[] = clone($group);
+ }
+
+ return $groups;
+ }
+
+ /**
+ * 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 site's latest group
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->groups) && (count($this->groups) > 0)) {
+ return strtotime($this->groups[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this list of groups
+ *
+ * Returns an Etag based on the action name, language, and
+ * timestamps of the first and last group the user has joined
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->groups) && (count($this->groups) > 0)) {
+
+ $last = count($this->groups) - 1;
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ strtotime($this->groups[0]->created),
+ strtotime($this->groups[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List a group's members
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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/api.php';
+
+/**
+ * List 20 newest members of the group specified by name or ID.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupMembershipAction extends ApiAction
+{
+ var $group = null;
+ var $profiles = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->group = $this->getTargetGroup($this->arg('id'));
+ $this->profiles = $this->getProfiles();
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * Show the members of the group
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ // XXX: RSS and Atom
+
+ switch($this->format) {
+ case 'xml':
+ $this->showTwitterXmlUsers($this->profiles);
+ break;
+ case 'json':
+ $this->showJsonUsers($this->profiles);
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+ }
+
+ /**
+ * Fetch the members of a group
+ *
+ * @return array $profiles list of profiles
+ */
+
+ function getProfiles()
+ {
+ $profiles = array();
+
+ $profile = $this->group->getMembers(
+ ($this->page - 1) * $this->count,
+ $this->count,
+ $this->since_id,
+ $this->max_id,
+ $this->since
+ );
+
+ while ($profile->fetch()) {
+ $profiles[] = clone($profile);
+ }
+
+ return $profiles;
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * When was this list of profiles last modified?
+ *
+ * @return string datestamp of the lastest profile in the group
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->profiles) && (count($this->profiles) > 0)) {
+ return strtotime($this->profiles[0]->created);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this list of groups
+ *
+ * Returns an Etag based on the action name, language
+ * the group id, and timestamps of the first and last
+ * user who has joined the group
+ *
+ * @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->group->id,
+ strtotime($this->profiles[0]->created),
+ strtotime($this->profiles[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show information about a group
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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/api.php';
+
+/**
+ * Outputs detailed information about the group specified by ID
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiGroupShowAction extends ApiAction
+{
+ var $group = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->group = $this->getTargetGroup($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->group)) {
+ $this->clientError(
+ 'Group not found!',
+ 404,
+ $this->format
+ );
+ return;
+ }
+
+ switch($this->format) {
+ case 'xml':
+ $this->show_single_xml_group($this->group);
+ break;
+ case 'json':
+ $this->showSingleJsonGroup($this->group);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), 404, $this->format);
+ break;
+ }
+
+ }
+
+ /**
+ * When was this group last modified?
+ *
+ * @return string datestamp of the latest notice in the stream
+ */
+
+ function lastModified()
+ {
+ if (!empty($this->group)) {
+ return strtotime($this->group->modified);
+ }
+
+ return null;
+ }
+
+ /**
+ * An entity tag for this group
+ *
+ * Returns an Etag based on the action name, language, and
+ * timestamps of the notice
+ *
+ * @return string etag
+ */
+
+ function etag()
+ {
+ if (!empty($this->group)) {
+
+ return '"' . implode(
+ ':',
+ array($this->arg('action'),
+ common_language(),
+ $this->group->id,
+ strtotime($this->group->modified))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Test that you can connect to 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 Evan Prodromou <evan@status.net>
+ * @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/api.php';
+
+/**
+ * Returns the string "ok" in the requested format with a 200 OK HTTP status code.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiHelpTestAction extends ApiAction
+{
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if ($this->format == 'xml') {
+ $this->initDocument('xml');
+ $this->element('ok', null, 'true');
+ $this->endDocument('xml');
+ } elseif ($this->format == 'json') {
+ $this->initDocument('json');
+ print '"ok"';
+ $this->endDocument('json');
+ } else {
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ }
+ }
+
+}
+
--- /dev/null
+<?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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Tom Blankenship <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Tom Blankenship <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 $status = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->user = $this->auth_user;
+ $this->notice_id = (int)$this->trimmed('id');
+
+ if (empty($notice_id)) {
+ $this->notice_id = (int)$this->arg('id');
+ }
+
+ $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->showSingleXmlStatus($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->showSingleXmlStatus($this->notice);
+ } elseif ($this->format == 'json') {
+ $this->show_single_json_status($this->notice);
+ }
+ }
+ }
+
+}
--- /dev/null
+<?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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Tom Blankenship <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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/api.php';
+
+/**
+ * Returns the notice specified by id as a Twitter-style status and inline user
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Tom Blankenship <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 ApiAction
+{
+
+ var $notice_id = null;
+ var $notice = 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->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->showSingleXmlStatus($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;
+ }
+
+}
--- /dev/null
+<?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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Tom Blankenship <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Tom Blankenship <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 $source = null;
+ var $status = null;
+ var $in_reply_to_status_id = 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);
+
+ $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->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->showSingleXmlStatus($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;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Dump of configuration variables
+ *
+ * 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 Evan Prodromou <evan@status.net>
+ * @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/api.php';
+
+/**
+ * Gives a full dump of configuration variables for this instance
+ * of StatusNet, minus variables that may be security-sensitive (like
+ * passwords).
+ * URL: http://identi.ca/api/statusnet/config.(xml|json)
+ * Formats: xml, json
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiStatusnetConfigAction extends ApiAction
+{
+ var $keys = array(
+ 'site' => array('name', 'server', 'theme', 'path', 'fancy', 'language',
+ 'email', 'broughtby', 'broughtbyurl', 'closed',
+ 'inviteonly', 'private'),
+ 'license' => array('url', 'title', 'image'),
+ 'nickname' => array('featured'),
+ 'throttle' => array('enabled', 'count', 'timespan'),
+ 'xmpp' => array('enabled', 'server', 'user')
+ );
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ switch ($this->format) {
+ case 'xml':
+ $this->initDocument('xml');
+ $this->elementStart('config');
+
+ // XXX: check that all sections and settings are legal XML elements
+
+ common_debug(var_export($this->keys, true));
+
+ foreach ($this->keys as $section => $settings) {
+ $this->elementStart($section);
+ foreach ($settings as $setting) {
+ $value = common_config($section, $setting);
+ if (is_array($value)) {
+ $value = implode(',', $value);
+ } else if ($value === false) {
+ $value = 'false';
+ } else if ($value === true) {
+ $value = 'true';
+ }
+ $this->element($setting, null, $value);
+ }
+ $this->elementEnd($section);
+ }
+ $this->elementEnd('config');
+ $this->endDocument('xml');
+ break;
+ case 'json':
+ $result = array();
+ foreach ($this->keys as $section => $settings) {
+ $result[$section] = array();
+ foreach ($settings as $setting) {
+ $result[$section][$setting]
+ = common_config($section, $setting);
+ }
+ }
+ $this->initDocument('json');
+ $this->showJsonObjects($result);
+ $this->endDocument('json');
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+ }
+
+}
+
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * A version stamp for 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 Evan Prodromou <evan@status.net>
+ * @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/api.php';
+
+/**
+ * Returns a version number for this version of StatusNet, which
+ * should make things a bit easier for upgrades.
+ * URL: http://identi.ca/api/statusnet/version.(xml|json)
+ * Formats: xml, js
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiStatusnetVersionAction extends ApiAction
+{
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * @param array $args $_REQUEST data (unused)
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ switch ($this->format) {
+ case 'xml':
+ $this->initDocument('xml');
+ $this->element('version', null, STATUSNET_VERSION);
+ $this->endDocument('xml');
+ break;
+ case 'json':
+ $this->initDocument('json');
+ print '"'.STATUSNET_VERSION.'"';
+ $this->endDocument('json');
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+ }
+
+}
+
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 $profiles = 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->tag = $this->arg('tag');
+
+ // 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);
+
+ $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->initDocument($this->format);
+
+ if (isset($this->ids_only)) {
+ $this->showIds();
+ } else {
+ $this->showProfiles(isset($this->lite) ? false : true);
+ }
+
+ $this->endDocument($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->showProfile(
+ $profile,
+ $this->format,
+ null,
+ $include_statuses
+ );
+ }
+ $this->elementEnd('users');
+ break;
+ case 'json':
+ $arrays = array();
+ foreach ($this->profiles as $profile) {
+ $arrays[] = $this->twitterUserArray(
+ $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;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a user's favorite notices
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 20 most recent favorite notices for the authenticating user or user
+ * specified by the ID parameter in the requested format.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 ApiTimelineFavoritesAction extends ApiBareAuthAction
+{
+ var $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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 / Favorites from %s'),
+ $sitename,
+ $this->user->nickname
+ );
+
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:Favorites:" . $this->user->id;
+ $link = common_local_url(
+ 'favorites',
+ array('nickname' => $this->user->nickname)
+ );
+ $subtitle = sprintf(
+ _('%s updates favorited by %s / %s.'),
+ $sitename,
+ $profile->getBestName(),
+ $this->user->nickname
+ );
+
+ switch($this->format) {
+ case 'xml':
+ $this->showXmlTimeline($this->notices);
+ break;
+ case 'rss':
+ $this->showRssTimeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() .
+ ltrim($_SERVER['QUERY_STRING'], 'p=');
+ $this->showAtomTimeline(
+ $this->notices, $title, $id, $link, $subtitle,
+ null, $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonTimeline($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->favoriteNotices(
+ ($this->page-1) * $this->count,
+ $this->count,
+ true
+ );
+ } else {
+ $notice = $this->user->favoriteNotices(
+ ($this->page-1) * $this->count,
+ $this->count,
+ false
+ );
+ }
+
+ 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;
+ }
+
+}
--- /dev/null
+<?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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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->showXmlTimeline($this->notices);
+ break;
+ case 'rss':
+ $this->showRssTimeline($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->showAtomTimeline(
+ $this->notices, $title, $id, $link,
+ $subtitle, null, $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonTimeline($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;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a group's notices
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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/api.php';
+
+/**
+ * Returns the most recent notices (default 20) posted to the group specified by ID
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiTimelineGroupAction extends ApiAction
+{
+
+ var $group = null;
+ var $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->group = $this->getTargetGroup($this->arg('id'));
+ $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 timeline"), $this->group->nickname);
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:GroupTimeline:" . $this->group->id;
+ $link = common_local_url(
+ 'showgroup',
+ array('nickname' => $this->group->nickname)
+ );
+ $subtitle = sprintf(
+ _('Updates from %1$s on %2$s!'),
+ $this->group->nickname,
+ $sitename
+ );
+
+ switch($this->format) {
+ case 'xml':
+ $this->showXmlTimeline($this->notices);
+ break;
+ case 'rss':
+ $this->showRssTimeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() .
+ 'api/statusnet/groups/timeline/' .
+ $this->group->nickname . '.atom';
+ $this->showAtomTimeline(
+ $this->notices,
+ $title,
+ $id,
+ $link,
+ $subtitle,
+ null,
+ $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonTimeline($this->notices);
+ break;
+ default:
+ $this->clientError(
+ _('API method not found!'),
+ 404,
+ $this->format
+ );
+ break;
+ }
+ }
+
+ /**
+ * Get notices
+ *
+ * @return array notices
+ */
+
+ function getNotices()
+ {
+ $notices = array();
+
+ $notice = $this->group->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, group 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->group->id,
+ strtotime($this->notices[0]->created),
+ strtotime($this->notices[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+<?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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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(
+ _('%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->format) {
+ case 'xml':
+ $this->showXmlTimeline($this->notices);
+ break;
+ case 'rss':
+ $this->showRssTimeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() .
+ ltrim($_SERVER['QUERY_STRING'], 'p=');
+ $this->showAtomTimeline(
+ $this->notices, $title, $id, $link, $subtitle,
+ null, $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonTimeline($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;
+ }
+
+}
--- /dev/null
+<?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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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/api.php';
+
+/**
+ * Returns the most recent notices (default 20) posted by everybody
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 ApiAction
+{
+
+ var $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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->format) {
+ case 'xml':
+ $this->showXmlTimeline($this->notices);
+ break;
+ case 'rss':
+ $this->showRssTimeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
+ $this->showAtomTimeline(
+ $this->notices, $title, $id, $link,
+ $subtitle, null, $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonTimeline($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;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show the latest notices for a given tag
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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/api.php';
+
+/**
+ * Returns the 20 most recent notices tagged by a given tag
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @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 ApiTimelineTagAction extends ApiAction
+{
+
+ var $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->tag = $this->arg('tag');
+ $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(_("Notices tagged with %s"), $this->tag);
+ $link = common_local_url(
+ 'tag',
+ array('tag' => $this->tag)
+ );
+ $subtitle = sprintf(
+ _('Updates tagged with %1$s on %2$s!'),
+ $this->tag,
+ $sitename
+ );
+ $taguribase = common_config('integration', 'taguri');
+ $id = "tag:$taguribase:TagTimeline:".$tag;
+
+ switch($this->format) {
+ case 'xml':
+ $this->showXmlTimeline($this->notices);
+ break;
+ case 'rss':
+ $this->showRssTimeline($this->notices, $title, $link, $subtitle);
+ break;
+ case 'atom':
+ $selfuri = common_root_url() .
+ 'api/statusnet/tags/timeline/' .
+ $this->tag . '.atom';
+ $this->showAtomTimeline(
+ $this->notices,
+ $title,
+ $id,
+ $link,
+ $subtitle,
+ null,
+ $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonTimeline($this->notices);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ break;
+ }
+ }
+
+ /**
+ * Get notices
+ *
+ * @return array notices
+ */
+
+ function getNotices()
+ {
+ $notices = array();
+
+ $notice = Notice_tag::getStream(
+ $this->tag,
+ ($this->page - 1) * $this->count,
+ $this->count + 1
+ );
+
+ 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(),
+ $this->tag,
+ strtotime($this->notices[0]->created),
+ strtotime($this->notices[$last]->created))
+ )
+ . '"';
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+<?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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author mac65 <mac65@mac65.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <robin@millette.info>
+ * @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 $notices = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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 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->format) {
+ case 'xml':
+ $this->showXmlTimeline($this->notices);
+ break;
+ case 'rss':
+ $this->showRssTimeline(
+ $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->showAtomTimeline(
+ $this->notices, $title, $id, $link,
+ $subtitle, $suplink, $selfuri
+ );
+ break;
+ case 'json':
+ $this->showJsonTimeline($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;
+ }
+
+}
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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;
+ }
+
+}
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @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;
+ }
+
+}
--- /dev/null
+<?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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mac65 <mac65@mac65.com>
+ * @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/api.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 Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mac65 <mac65@mac65.com>
+ * @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 ApiAction
+{
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $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->twitterUserArray($this->user->getProfile(), true);
+
+ if ($this->format == 'xml') {
+ $this->initDocument('xml');
+ $this->showTwitterXmlUser($twitter_user);
+ $this->endDocument('xml');
+ } elseif ($this->format == 'json') {
+ $this->initDocument('json');
+ $this->showJsonObjects($twitter_user);
+ $this->endDocument('json');
+ }
+
+ }
+
+}
+++ /dev/null
-<?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 TwitapiaccountAction extends TwitterapiAction
-{
- function verify_credentials($args, $apidata)
- {
- parent::handle($args);
-
- switch ($apidata['content-type']) {
- case 'xml':
- case 'json':
- $action_obj = new TwitapiusersAction();
- $action_obj->prepare($args);
- call_user_func(array($action_obj, 'show'), $args, $apidata);
- break;
- default:
- header('Content-Type: text/html; charset=utf-8');
- print 'Authorized';
- }
- }
-
- function end_session($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), $code=501);
- }
-
- function update_location($args, $apidata)
- {
- parent::handle($args);
-
- if ($_SERVER['REQUEST_METHOD'] != 'POST') {
- $this->clientError(_('This method requires a POST.'),
- 400, $apidata['content-type']);
- return;
- }
-
- $location = trim($this->arg('location'));
-
- if (!is_null($location) && mb_strlen($location) > 255) {
-
- // XXX: But Twitter just truncates and runs with it. -- Zach
- $this->clientError(_('That\'s too long. Max notice size is 255 chars.'),
- 406, $apidate['content-type']);
- return;
- }
-
- $user = $apidata['user']; // Always the auth user
- $profile = $user->getProfile();
-
- $orig_profile = clone($profile);
- $profile->location = $location;
-
- $result = $profile->update($orig_profile);
-
- if (empty($result)) {
- common_log_db_error($profile, 'UPDATE', __FILE__);
- $this->serverError(_('Couldn\'t save profile.'));
- return;
- }
-
- common_broadcast_profile($profile);
- $type = $apidata['content-type'];
-
- $this->init_document($type);
- $this->show_profile($profile, $type);
- $this->end_document($type);
- }
-
-
- function update_delivery_device($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), $code=501);
- }
-
- // We don't have a rate limit, but some clients check this method.
- // It always returns the same thing: 100 hit left.
- function rate_limit_status($args, $apidata)
- {
- parent::handle($args);
-
- $type = $apidata['content-type'];
- $this->init_document($type);
-
- if ($apidata['content-type'] == 'xml') {
- $this->elementStart('hash');
- $this->element('remaining-hits', array('type' => 'integer'), 100);
- $this->element('hourly-limit', array('type' => 'integer'), 100);
- $this->element('reset-time', array('type' => 'datetime'), null);
- $this->element('reset_time_in_seconds', array('type' => 'integer'), 0);
- $this->elementEnd('hash');
- } elseif ($apidata['content-type'] == 'json') {
-
- $out = array('reset_time_in_seconds' => 0,
- 'remaining_hits' => 100,
- 'hourly_limit' => 100,
- 'reset_time' => '');
- print json_encode($out);
- }
-
- $this->end_document($type);
- }
-}
+++ /dev/null
-<?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 TwitapiblocksAction extends TwitterapiAction
-{
-
- function create($args, $apidata)
- {
-
- parent::handle($args);
-
- $blockee = $this->get_user($apidata['api_arg'], $apidata);
-
- if (empty($blockee)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $user = $apidata['user']; // Always the auth user
-
- if ($user->hasBlocked($blockee) || $user->block($blockee)) {
- $type = $apidata['content-type'];
- $this->init_document($type);
- $this->show_profile($blockee, $type);
- $this->end_document($type);
- } else {
- $this->serverError(_('Block user failed.'));
- }
- }
-
- function destroy($args, $apidata)
- {
- parent::handle($args);
- $blockee = $this->get_user($apidata['api_arg'], $apidata);
-
- if (empty($blockee)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $user = $apidata['user'];
-
- if (!$user->hasBlocked($blockee) || $user->unblock($blockee)) {
- $type = $apidata['content-type'];
- $this->init_document($type);
- $this->show_profile($blockee, $type);
- $this->end_document($type);
- } else {
- $this->serverError(_('Unblock user failed.'));
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<?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
- }
-
-}
+++ /dev/null
-<?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 TwitapifavoritesAction extends TwitterapiAction
-{
-
- function favorites($args, $apidata)
- {
- parent::handle($args);
-
- $this->auth_user = $apidata['user'];
- $user = $this->get_user($apidata['api_arg'], $apidata);
-
- if (empty($user)) {
- if ($apidata['content-type'] == 'xml') {
- $this->show_single_xml_status($notice);
- } elseif ($apidata['content-type'] == 'json') {
- $this->show_single_json_status($notice);
- }
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $profile = $user->getProfile();
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_('%s / Favorites from %s'), $sitename,
- $user->nickname);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:Favorites:".$user->id;
- $link = common_local_url('favorites',
- array('nickname' => $user->nickname));
- $subtitle = sprintf(_('%s updates favorited by %s / %s.'), $sitename,
- $profile->getBestName(), $user->nickname);
-
- $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->favoriteNotices(($page-1)*$count, $count, true);
- } else {
- $notice = $user->favoriteNotices(($page-1)*$count, $count, false);
- }
-
- 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 = $selfuri = common_root_url() .
- 'api/favorites/' . $apidata['api_arg'] . '.atom';
- } else {
- $selfuri = $selfuri = common_root_url() .
- 'api/favorites.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 create($args, $apidata)
- {
- parent::handle($args);
-
- // Check for RESTfulness
- if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
- $this->clientError(_('This method requires a POST or DELETE.'),
- 400, $apidata['content-type']);
- return;
- }
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('API method not found!'), $code = 404);
- 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;
- }
-
- // XXX: Twitter lets you fave things repeatedly via api.
- if ($user->hasFave($notice)) {
- $this->clientError(_('This status is already a favorite!'),
- 403, $apidata['content-type']);
- return;
- }
-
- $fave = Fave::addNew($user, $notice);
-
- if (empty($fave)) {
- $this->clientError(_('Could not create favorite.'));
- return;
- }
-
- $this->notify($fave, $notice, $user);
- $user->blowFavesCache();
-
- if ($apidata['content-type'] == 'xml') {
- $this->show_single_xml_status($notice);
- } elseif ($apidata['content-type'] == 'json') {
- $this->show_single_json_status($notice);
- }
-
- }
-
- function destroy($args, $apidata)
- {
- parent::handle($args);
-
- // Check for RESTfulness
- if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
- $this->clientError(_('This method requires a POST or DELETE.'),
- 400, $apidata['content-type']);
- return;
- }
-
- if (!in_array($apidata['content-type'], array('xml', 'json'))) {
- $this->clientError(_('API method not found!'), $code = 404);
- 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;
- }
-
- $fave = new Fave();
- $fave->user_id = $this->id;
- $fave->notice_id = $notice->id;
-
- if (!$fave->find(true)) {
- $this->clientError(_('That status is not a favorite!'),
- 403, $apidata['content-type']);
- return;
- }
-
- $result = $fave->delete();
-
- if (!$result) {
- common_log_db_error($fave, 'DELETE', __FILE__);
- $this->clientError(_('Could not delete favorite.'), 404);
- return;
- }
-
- $user->blowFavesCache();
-
- if ($apidata['content-type'] == 'xml') {
- $this->show_single_xml_status($notice);
- } elseif ($apidata['content-type'] == 'json') {
- $this->show_single_json_status($notice);
- }
-
- }
-
- // XXX: these two funcs swiped from faves.
- // Maybe put in util.php, or some common base class?
-
- function notify($fave, $notice, $user)
- {
- $other = User::staticGet('id', $notice->profile_id);
- if ($other && $other->id != $user->id) {
- if ($other->email && $other->emailnotifyfav) {
- mail_notify_fave($other, $user, $notice);
- }
- # XXX: notify by IM
- # XXX: notify by SMS
- }
- }
-}
+++ /dev/null
-<?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;
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * StatusNet extensions to the Twitter-like API for groups
- *
- * 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 Twitter
- * @package StatusNet
- * @author Craig Andrews <candrews@integralblue.com>
- * @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') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/twitterapi.php';
-
-/**
- * Group-specific API methods
- *
- * This class handles StatusNet group API methods.
- *
- * @category Twitter
- * @package StatusNet
- * @author Craig Andrews <candrews@integralblue.com>
- * @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/
- */
-
- class TwitapigroupsAction extends TwitterapiAction
- {
-
- function list_groups($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $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;
- }
-
- $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');
- $group = $user->getGroups(($page-1)*$count,
- $count, $since_id, $max_id, $since);
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_("%s's groups"), $user->nickname);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:Groups";
- $link = common_root_url();
- $subtitle = sprintf(_("groups %s is a member of on %s"), $user->nickname, $sitename);
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_xml_groups($group);
- break;
- case 'rss':
- $this->show_rss_groups($group, $title, $link, $subtitle);
- break;
- case 'atom':
- $selfuri = common_root_url() . 'api/statusnet/groups/list/' . $user->id . '.atom';
- $this->show_atom_groups($group, $title, $id, $link,
- $subtitle, $selfuri);
- break;
- case 'json':
- $this->show_json_groups($group);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- break;
- }
- }
-
- function list_all($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $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');
-
- /* TODO:
- Use the $page, $count, $max_id, $since_id, and $since parameters
- */
- $group = new User_group();
- $group->orderBy('created DESC');
- $group->find();
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_("%s groups"), $sitename);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:Groups";
- $link = common_root_url();
- $subtitle = sprintf(_("groups on %s"), $sitename);
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_xml_groups($group);
- break;
- case 'rss':
- $this->show_rss_groups($group, $title, $link, $subtitle);
- break;
- case 'atom':
- $selfuri = common_root_url() . 'api/statusnet/groups/list_all.atom';
- $this->show_atom_groups($group, $title, $id, $link,
- $subtitle, $selfuri);
- break;
- case 'json':
- $this->show_json_groups($group);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- break;
- }
- }
-
- function show($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $this->auth_user = $apidata['user'];
- $group = $this->get_group($apidata['api_arg'], $apidata);
-
- if (empty($group)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_single_xml_group($group);
- break;
- case 'json':
- $this->show_single_json_group($group);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
- }
-
- function timeline($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $this->auth_user = $apidata['user'];
- $group = $this->get_group($apidata['api_arg'], $apidata);
-
- if (empty($group)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_("%s timeline"), $group->nickname);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:GroupTimeline:".$group->id;
- $link = common_local_url('showgroup',
- array('nickname' => $group->nickname));
- $subtitle = sprintf(_('Updates from %1$s on %2$s!'),
- $group->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');
-
- $notice = $group->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);
- break;
- case 'atom':
- if (isset($apidata['api_arg'])) {
- $selfuri = common_root_url() .
- 'api/statusnet/groups/timeline/' .
- $apidata['api_arg'] . '.atom';
- } else {
- $selfuri = common_root_url() .
- 'api/statusnet/groups/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 membership($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $this->auth_user = $apidata['user'];
- $group = $this->get_group($apidata['api_arg'], $apidata);
-
- if (empty($group)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_("Members of %s group"), $group->nickname);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:GroupMembership:".$group->id;
- $link = common_local_url('showgroup',
- array('nickname' => $group->nickname));
- $subtitle = sprintf(_('Members of %1$s on %2$s'),
- $group->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');
-
- $member = $group->getMembers(($page-1)*$count,
- $count, $since_id, $max_id, $since);
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_twitter_xml_users($member);
- break;
- //TODO implement the RSS and ATOM content types
- /*case 'rss':
- $this->show_rss_users($member, $title, $link, $subtitle);
- break;*/
- /*case 'atom':
- if (isset($apidata['api_arg'])) {
- $selfuri = common_root_url() .
- 'api/statusnet/groups/membership/' .
- $apidata['api_arg'] . '.atom';
- } else {
- $selfuri = common_root_url() .
- 'api/statusnet/groups/membership.atom';
- }
- $this->show_atom_users($member, $title, $id, $link,
- $subtitle, null, $selfuri);
- break;*/
- case 'json':
- $this->show_json_users($member);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
- }
-
- function join($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $this->auth_user = $apidata['user'];
- $group = $this->get_group($apidata['api_arg'], $apidata);
-
- if (empty($group)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return false;
- }
-
- if($this->auth_user->isMember($group)){
- $this->clientError(_('You are already a member of that group'), $code = 403);
- return false;
- }
-
- if (Group_block::isBlocked($group, $this->auth_user->getProfile())) {
- $this->clientError(_('You have been blocked from that group by the admin.'), 403);
- return false;
- }
-
- $member = new Group_member();
-
- $member->group_id = $group->id;
- $member->profile_id = $this->auth_user->id;
- $member->created = common_sql_now();
-
- $result = $member->insert();
-
- if (!$result) {
- common_log_db_error($member, 'INSERT', __FILE__);
- $this->serverError(sprintf(_('Could not join user %s to group %s'),
- $this->auth_user->nickname, $group->nickname));
- }
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_single_xml_group($group);
- break;
- case 'json':
- $this->show_single_json_group($group);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
- }
-
- function leave($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $this->auth_user = $apidata['user'];
- $group = $this->get_group($apidata['api_arg'], $apidata);
-
- if (empty($group)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return false;
- }
-
- if(! $this->auth_user->isMember($group)){
- $this->clientError(_('You are not a member of that group'), $code = 403);
- return false;
- }
-
- $member = new Group_member();
-
- $member->group_id = $group->id;
- $member->profile_id = $this->auth_user->id;
-
- if (!$member->find(true)) {
- $this->serverError(_('Could not find membership record.'));
- return;
- }
-
- $result = $member->delete();
-
- if (!$result) {
- common_log_db_error($member, 'INSERT', __FILE__);
- $this->serverError(sprintf(_('Could not remove user %s to group %s'),
- $this->auth_user->nickname, $group->nickname));
- }
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_single_xml_group($group);
- break;
- case 'json':
- $this->show_single_json_group($group);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
- }
-
- function is_member($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
-
- $this->auth_user = $apidata['user'];
- $group = User_group::staticGet($args['group_id']);
- if(! $group){
- $this->clientError(_('Group not found'), $code = 500);
- }
- $user = User::staticGet('id', $args['user_id']);
- if(! $user){
- $this->clientError(_('User not found'), $code = 500);
- }
-
- $is_member=$user->isMember($group);
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->init_document('xml');
- $this->element('is_member', null, $is_member);
- $this->end_document('xml');
- break;
- case 'json':
- $this->init_document('json');
- $this->show_json_objects(array('is_member'=>$is_member));
- $this->end_document('json');
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
- }
-
- function create($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in groups api action");
- if (!common_config('inboxes','enabled')) {
- $this->serverError(_('Inboxes must be enabled for groups to work'));
- return false;
- }
-
- $this->auth_user = $apidata['user'];
-
- $nickname = $args['nickname'];
- $fullname = $args['full_name'];
- $homepage = $args['homepage'];
- $description = $args['description'];
- $location = $args['location'];
- $aliasstring = $args['aliases'];
-
- if (!Validate::string($nickname, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- $this->clientError(_('Nickname must have only lowercase letters '.
- 'and numbers and no spaces.'), $code=403);
- return;
- } else if ($this->groupNicknameExists($nickname)) {
- $this->clientError(_('Nickname already in use. Try another one.'), $code=403);
- return;
- } else if (!User_group::allowedNickname($nickname)) {
- $this->clientError(_('Not a valid nickname.'), $code=403);
- return;
- } else if (!is_null($homepage) && (strlen($homepage) > 0) &&
- !Validate::uri($homepage,
- array('allowed_schemes' =>
- array('http', 'https')))) {
- $this->clientError(_('Homepage is not a valid URL.'), $code=403);
- return;
- } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
- $this->clientError(_('Full name is too long (max 255 chars).'), $code=403);
- return;
- } else if (User_group::descriptionTooLong($description)) {
- $this->clientError(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription()), $code=403);
- return;
- } else if (!is_null($location) && mb_strlen($location) > 255) {
- $this->clientError(_('Location is too long (max 255 chars).'), $code=403);
- return;
- }
-
- if (!empty($aliasstring)) {
- $aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
- } else {
- $aliases = array();
- }
-
- if (count($aliases) > common_config('group', 'maxaliases')) {
- $this->clientError(sprintf(_('Too many aliases! Maximum %d.'),
- common_config('group', 'maxaliases')), $code=403);
- return;
- }
-
- foreach ($aliases as $alias) {
- if (!Validate::string($alias, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- $this->clientError(sprintf(_('Invalid alias: "%s"'), $alias), $code=403);
- return;
- }
- if ($this->groupNicknameExists($alias)) {
- $this->clientError(sprintf(_('Alias "%s" already in use. Try another one.'),
- $alias), $code=403);
- return;
- }
- // XXX assumes alphanum nicknames
- if (strcmp($alias, $nickname) == 0) {
- $this->clientError(_('Alias can\'t be the same as nickname.'), $code=403);
- return;
- }
- }
-
- $group = new User_group();
-
- $group->query('BEGIN');
-
- $group->nickname = $nickname;
- $group->fullname = $fullname;
- $group->homepage = $homepage;
- $group->description = $description;
- $group->location = $location;
- $group->created = common_sql_now();
-
- $result = $group->insert();
-
- if (!$result) {
- common_log_db_error($group, 'INSERT', __FILE__);
- $this->serverError(_('Could not create group.'));
- }
-
- $result = $group->setAliases($aliases);
-
- if (!$result) {
- $this->serverError(_('Could not create aliases.'));
- }
-
- $member = new Group_member();
-
- $member->group_id = $group->id;
- $member->profile_id = $this->auth_user->id;
- $member->is_admin = 1;
- $member->created = $group->created;
-
- $result = $member->insert();
-
- if (!$result) {
- common_log_db_error($member, 'INSERT', __FILE__);
- $this->serverError(_('Could not set group membership.'));
- }
-
- $group->query('COMMIT');
-
- switch($apidata['content-type']) {
- case 'xml':
- $this->show_single_xml_group($group);
- break;
- case 'json':
- $this->show_single_json_group($group);
- break;
- default:
- $this->clientError(_('API method not found!'), $code = 404);
- }
- }
-
- function update($args, $apidata)
- {
- die("todo");
- }
-
- function update_group_logo($args, $apidata)
- {
- die("todo");
- }
-
- function destroy($args, $apidata)
- {
- die("todo");
- }
-
- function tag($args, $apidata)
- {
- die("todo");
- }
-
- function groupNicknameExists($nickname)
- {
- $group = User_group::staticGet('nickname', $nickname);
-
- if (!empty($group)) {
- return true;
- }
-
- $alias = Group_alias::staticGet('alias', $nickname);
-
- if (!empty($alias)) {
- return true;
- }
-
- return false;
- }
-}
+++ /dev/null
-<?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 TwitapihelpAction extends TwitterapiAction
-{
-
- /* Returns the string "ok" in the requested format with a 200 OK HTTP status code.
- * URL:http://identi.ca/api/help/test.format
- * Formats: xml, json
- */
- function test($args, $apidata)
- {
- parent::handle($args);
-
- if ($apidata['content-type'] == 'xml') {
- $this->init_document('xml');
- $this->element('ok', null, 'true');
- $this->end_document('xml');
- } elseif ($apidata['content-type'] == 'json') {
- $this->init_document('json');
- print '"ok"';
- $this->end_document('json');
- } else {
- $this->clientError(_('API method not found!'), $code=404);
- }
-
- }
-
- function downtime_schedule($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), $code=501);
- }
-
-}
\ No newline at end of file
+++ /dev/null
-<?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');
-
-# This naming convention looks real sick
-class TwitapinotificationsAction extends TwitterapiAction
-{
-
- function follow($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), $code=501);
- }
-
- function leave($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), $code=501);
- }
-
-}
\ No newline at end of file
exit(1);
}
-require_once INSTALLDIR.'/lib/twitterapi.php';
+require_once INSTALLDIR.'/lib/api.php';
/**
* Action for outputting search results in Twitter compatible Atom
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
- * @see TwitterapiAction
+ * @see ApiAction
*/
-class TwitapisearchatomAction extends TwitterapiAction
+class TwitapisearchatomAction extends ApiAction
{
var $cnt;
// TODO: Here is where we'd put in a link to an atom feed for threads
$this->element("twitter:source", null,
- htmlentities($this->source_link($notice->source)));
+ htmlentities($this->sourceLink($notice->source)));
$this->elementStart('author');
exit(1);
}
-require_once INSTALLDIR.'/lib/twitterapi.php';
+require_once INSTALLDIR.'/lib/api.php';
require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
/**
* @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/
- * @see TwitterapiAction
+ * @see ApiAction
*/
-class TwitapisearchjsonAction extends TwitterapiAction
+class TwitapisearchjsonAction extends ApiAction
{
var $query;
var $lang;
$results = new JSONSearchResultsList($notice, $q, $this->rpp, $this->page);
- $this->init_document('json');
+ $this->initDocument('json');
$results->show();
- $this->end_document('json');
+ $this->endDocument('json');
}
/**
+++ /dev/null
-<?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= !(array_key_exists('lite', $args) and $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= !(array_key_exists('lite', $args) and $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;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * StatusNet-only extensions to the Twitter-like 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 Twitter
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @copyright 2008 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') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/twitterapi.php';
-
-/**
- * StatusNet-specific API methods
- *
- * This class handles all /statusnet/ API methods.
- *
- * @category Twitter
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @copyright 2008 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/
- */
-
-class TwitapistatusnetAction extends TwitterapiAction
-{
- /**
- * A version stamp for the API
- *
- * Returns a version number for this version of StatusNet, which
- * should make things a bit easier for upgrades.
- * URL: http://identi.ca/api/statusnet/version.(xml|json)
- * Formats: xml, json
- *
- * @param array $args Web arguments
- * @param array $apidata Twitter API data
- *
- * @return void
- *
- * @see ApiAction::process_command()
- */
-
- function version($args, $apidata)
- {
- parent::handle($args);
- switch ($apidata['content-type']) {
- case 'xml':
- $this->init_document('xml');
- $this->element('version', null, STATUSNET_VERSION);
- $this->end_document('xml');
- break;
- case 'json':
- $this->init_document('json');
- print '"'.STATUSNET_VERSION.'"';
- $this->end_document('json');
- break;
- default:
- $this->clientError(_('API method not found!'), $code=404);
- }
- }
-
- /**
- * Dump of configuration variables
- *
- * Gives a full dump of configuration variables for this instance
- * of StatusNet, minus variables that may be security-sensitive (like
- * passwords).
- * URL: http://identi.ca/api/statusnet/config.(xml|json)
- * Formats: xml, json
- *
- * @param array $args Web arguments
- * @param array $apidata Twitter API data
- *
- * @return void
- *
- * @see ApiAction::process_command()
- */
-
- function config($args, $apidata)
- {
- static $keys = array('site' => array('name', 'server', 'theme', 'path', 'fancy', 'language',
- 'email', 'broughtby', 'broughtbyurl', 'closed',
- 'inviteonly', 'private'),
- 'license' => array('url', 'title', 'image'),
- 'nickname' => array('featured'),
- 'throttle' => array('enabled', 'count', 'timespan'),
- 'xmpp' => array('enabled', 'server', 'user'));
-
- parent::handle($args);
-
- switch ($apidata['content-type']) {
- case 'xml':
- $this->init_document('xml');
- $this->elementStart('config');
- // XXX: check that all sections and settings are legal XML elements
- foreach ($keys as $section => $settings) {
- $this->elementStart($section);
- foreach ($settings as $setting) {
- $value = common_config($section, $setting);
- if (is_array($value)) {
- $value = implode(',', $value);
- } else if ($value === false) {
- $value = 'false';
- } else if ($value === true) {
- $value = 'true';
- }
- $this->element($setting, null, $value);
- }
- $this->elementEnd($section);
- }
- $this->elementEnd('config');
- $this->end_document('xml');
- break;
- case 'json':
- $result = array();
- foreach ($keys as $section => $settings) {
- $result[$section] = array();
- foreach ($settings as $setting) {
- $result[$section][$setting] = common_config($section, $setting);
- }
- }
- $this->init_document('json');
- $this->show_json_objects($result);
- $this->end_document('json');
- break;
- default:
- $this->clientError(_('API method not found!'), $code=404);
- }
- }
-
- /**
- * WADL description of the API
- *
- * Gives a WADL description of the API provided by this version of the
- * software.
- *
- * @param array $args Web arguments
- * @param array $apidata Twitter API data
- *
- * @return void
- *
- * @see ApiAction::process_command()
- */
-
- function wadl($args, $apidata)
- {
- parent::handle($args);
- $this->serverError(_('API method under construction.'), 501);
- }
-
-}
+++ /dev/null
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * StatusNet extensions to the Twitter-like API for groups
- *
- * 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 Twitter
- * @package StatusNet
- * @author Craig Andrews <candrews@integralblue.com>
- * @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') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/twitterapi.php';
-
-/**
- * Group-specific API methods
- *
- * This class handles StatusNet group API methods.
- *
- * @category Twitter
- * @package StatusNet
- * @author Craig Andrews <candrews@integralblue.com>
- * @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/
- */
-
- class TwitapitagsAction extends TwitterapiAction
- {
-
- function timeline($args, $apidata)
- {
- parent::handle($args);
-
- common_debug("in tags api action");
-
- $this->auth_user = $apidata['user'];
- $tag = $apidata['api_arg'];
-
- if (empty($tag)) {
- $this->clientError('Not Found', 404, $apidata['content-type']);
- return;
- }
-
- $sitename = common_config('site', 'name');
- $title = sprintf(_("Notices tagged with %s"), $tag);
- $taguribase = common_config('integration', 'taguri');
- $id = "tag:$taguribase:TagTimeline:".$tag;
- $link = common_local_url('tag',
- array('tag' => $tag));
- $subtitle = sprintf(_('Updates tagged with %1$s on %2$s!'),
- $tag, $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');
-
- # XXX: support max_id, since_id, and since arguments
- $notice = Notice_tag::getStream($tag, ($page-1)*$count, $count + 1);
-
- 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/statusnet/tags/timeline/' .
- $apidata['api_arg'] . '.atom';
- } else {
- $selfuri = common_root_url() .
- 'api/statusnet/tags/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);
- }
- }
-
-}
exit(1);
}
-require_once INSTALLDIR.'/lib/twitterapi.php';
+require_once INSTALLDIR.'/lib/api.php';
/**
* Returns the top ten queries that are currently trending
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
- * @see TwitterapiAction
+ * @see ApiAction
*/
-class TwitapitrendsAction extends TwitterapiAction
+class TwitapitrendsAction extends ApiAction
{
var $callback;
+++ /dev/null
-<?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';
- }
-
- }
-}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base API action
+ *
+ * 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 Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Toby Inkster <mail@tobyinkster.co.uk>
+ * @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);
+}
+
+/**
+ * Contains most of the Twitter-compatible API output functions.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Toby Inkster <mail@tobyinkster.co.uk>
+ * @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 ApiAction extends Action
+{
+ var $format = null;
+ var $user = null;
+ var $page = null;
+ var $count = null;
+ var $max_id = null;
+ var $since_id = null;
+ var $since = null;
+
+ /**
+ * Initialization.
+ *
+ * @param array $args Web and URL arguments
+ *
+ * @return boolean false if user doesn't exist
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->format = $this->arg('format');
+ $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');
+
+ return true;
+ }
+
+ /**
+ * Handle a request
+ *
+ * @param array $args Arguments from $_REQUEST
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ }
+
+ /**
+ * Overrides XMLOutputter::element to write booleans as strings (true|false).
+ * See that method's documentation for more info.
+ *
+ * @param string $tag Element type or tagname
+ * @param array $attrs Array of element attributes, as
+ * key-value pairs
+ * @param string $content string content of the element
+ *
+ * @return void
+ */
+ function element($tag, $attrs=null, $content=null)
+ {
+ if (is_bool($content)) {
+ $content = ($content ? 'true' : 'false');
+ }
+
+ return parent::element($tag, $attrs, $content);
+ }
+
+ function twitterUserArray($profile, $get_notice=false)
+ {
+ $twitter_user = array();
+
+ $twitter_user['id'] = intval($profile->id);
+ $twitter_user['name'] = $profile->getBestName();
+ $twitter_user['screen_name'] = $profile->nickname;
+ $twitter_user['location'] = ($profile->location) ? $profile->location : null;
+ $twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
+
+ $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+ $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() :
+ Avatar::defaultImage(AVATAR_STREAM_SIZE);
+
+ $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null;
+ $twitter_user['protected'] = false; # not supported by StatusNet yet
+ $twitter_user['followers_count'] = $profile->subscriberCount();
+
+ // To be supported soon...
+ $twitter_user['profile_background_color'] = '';
+ $twitter_user['profile_text_color'] = '';
+ $twitter_user['profile_link_color'] = '';
+ $twitter_user['profile_sidebar_fill_color'] = '';
+ $twitter_user['profile_sidebar_border_color'] = '';
+
+ $twitter_user['friends_count'] = $profile->subscriptionCount();
+
+ $twitter_user['created_at'] = $this->dateTwitter($profile->created);
+
+ $twitter_user['favourites_count'] = $profile->faveCount(); // British spelling!
+
+ // Need to pull up the user for some of this
+ $user = User::staticGet($profile->id);
+
+ $timezone = 'UTC';
+
+ if ($user->timezone) {
+ $timezone = $user->timezone;
+ }
+
+ $t = new DateTime;
+ $t->setTimezone(new DateTimeZone($timezone));
+
+ $twitter_user['utc_offset'] = $t->format('Z');
+ $twitter_user['time_zone'] = $timezone;
+
+ // To be supported some day, perhaps
+ $twitter_user['profile_background_image_url'] = '';
+ $twitter_user['profile_background_tile'] = false;
+
+ $twitter_user['statuses_count'] = $profile->noticeCount();
+
+ // Is the requesting user following this user?
+ $twitter_user['following'] = false;
+ $twitter_user['notifications'] = false;
+
+ if (isset($apidata['user'])) {
+
+ $twitter_user['following'] = $apidata['user']->isSubscribed($profile);
+
+ // Notifications on?
+ $sub = Subscription::pkeyGet(array('subscriber' =>
+ $apidata['user']->id, 'subscribed' => $profile->id));
+
+ if ($sub) {
+ $twitter_user['notifications'] = ($sub->jabber || $sub->sms);
+ }
+ }
+
+ if ($get_notice) {
+ $notice = $profile->getCurrentNotice();
+ if ($notice) {
+ # don't get user!
+ $twitter_user['status'] = $this->twitterStatusArray($notice, false);
+ }
+ }
+
+ return $twitter_user;
+ }
+
+ function twitterStatusArray($notice, $include_user=true)
+ {
+ $profile = $notice->getProfile();
+
+ $twitter_status = array();
+ $twitter_status['text'] = $notice->content;
+ $twitter_status['truncated'] = false; # Not possible on StatusNet
+ $twitter_status['created_at'] = $this->dateTwitter($notice->created);
+ $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ?
+ intval($notice->reply_to) : null;
+ $twitter_status['source'] = $this->sourceLink($notice->source);
+ $twitter_status['id'] = intval($notice->id);
+
+ $replier_profile = null;
+
+ if ($notice->reply_to) {
+ $reply = Notice::staticGet(intval($notice->reply_to));
+ if ($reply) {
+ $replier_profile = $reply->getProfile();
+ }
+ }
+
+ $twitter_status['in_reply_to_user_id'] =
+ ($replier_profile) ? intval($replier_profile->id) : null;
+ $twitter_status['in_reply_to_screen_name'] =
+ ($replier_profile) ? $replier_profile->nickname : null;
+
+ if (isset($this->auth_user)) {
+ $twitter_status['favorited'] = $this->auth_user->hasFave($notice);
+ } else {
+ $twitter_status['favorited'] = false;
+ }
+
+ // Enclosures
+ $attachments = $notice->attachments();
+
+ if (!empty($attachments)) {
+
+ $twitter_status['attachments'] = array();
+
+ foreach ($attachments as $attachment) {
+ if ($attachment->isEnclosure()) {
+ $enclosure = array();
+ $enclosure['url'] = $attachment->url;
+ $enclosure['mimetype'] = $attachment->mimetype;
+ $enclosure['size'] = $attachment->size;
+ $twitter_status['attachments'][] = $enclosure;
+ }
+ }
+ }
+
+ if ($include_user) {
+ # Don't get notice (recursive!)
+ $twitter_user = $this->twitterUserArray($profile, false);
+ $twitter_status['user'] = $twitter_user;
+ }
+
+ return $twitter_status;
+ }
+
+ function twitterGroupArray($group)
+ {
+ $twitter_group=array();
+ $twitter_group['id']=$group->id;
+ $twitter_group['url']=$group->permalink();
+ $twitter_group['nickname']=$group->nickname;
+ $twitter_group['fullname']=$group->fullname;
+ $twitter_group['homepage_url']=$group->homepage_url;
+ $twitter_group['original_logo']=$group->original_logo;
+ $twitter_group['homepage_logo']=$group->homepage_logo;
+ $twitter_group['stream_logo']=$group->stream_logo;
+ $twitter_group['mini_logo']=$group->mini_logo;
+ $twitter_group['homepage']=$group->homepage;
+ $twitter_group['description']=$group->description;
+ $twitter_group['location']=$group->location;
+ $twitter_group['created']=$this->dateTwitter($group->created);
+ $twitter_group['modified']=$this->dateTwitter($group->modified);
+ return $twitter_group;
+ }
+
+ function twitterRssGroupArray($group)
+ {
+ $entry = array();
+ $entry['content']=$group->description;
+ $entry['title']=$group->nickname;
+ $entry['link']=$group->permalink();
+ $entry['published']=common_date_iso8601($group->created);
+ $entry['updated']==common_date_iso8601($group->modified);
+ $taguribase = common_config('integration', 'groupuri');
+ $entry['id'] = "group:$groupuribase:$entry[link]";
+
+ $entry['description'] = $entry['content'];
+ $entry['pubDate'] = common_date_rfc2822($group->created);
+ $entry['guid'] = $entry['link'];
+
+ return $entry;
+ }
+
+ function twitterRssEntryArray($notice)
+ {
+ $profile = $notice->getProfile();
+ $entry = array();
+
+ // We trim() to avoid extraneous whitespace in the output
+
+ $entry['content'] = common_xml_safe_str(trim($notice->rendered));
+ $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
+ $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
+ $entry['published'] = common_date_iso8601($notice->created);
+
+ $taguribase = common_config('integration', 'taguri');
+ $entry['id'] = "tag:$taguribase:$entry[link]";
+
+ $entry['updated'] = $entry['published'];
+ $entry['author'] = $profile->getBestName();
+
+ // Enclosures
+ $attachments = $notice->attachments();
+ $enclosures = array();
+
+ foreach ($attachments as $attachment) {
+ $enclosure_o=$attachment->getEnclosure();
+ if ($enclosure_o) {
+ $enclosure = array();
+ $enclosure['url'] = $enclosure_o->url;
+ $enclosure['mimetype'] = $enclosure_o->mimetype;
+ $enclosure['size'] = $enclosure_o->size;
+ $enclosures[] = $enclosure;
+ }
+ }
+
+ if (!empty($enclosures)) {
+ $entry['enclosures'] = $enclosures;
+ }
+
+ // Tags/Categories
+ $tag = new Notice_tag();
+ $tag->notice_id = $notice->id;
+ if ($tag->find()) {
+ $entry['tags']=array();
+ while ($tag->fetch()) {
+ $entry['tags'][]=$tag->tag;
+ }
+ }
+ $tag->free();
+
+ // RSS Item specific
+ $entry['description'] = $entry['content'];
+ $entry['pubDate'] = common_date_rfc2822($notice->created);
+ $entry['guid'] = $entry['link'];
+
+ return $entry;
+ }
+
+
+ function twitterRelationshipArray($source, $target)
+ {
+ $relationship = array();
+
+ $relationship['source'] =
+ $this->relationshipDetailsArray($source, $target);
+ $relationship['target'] =
+ $this->relationshipDetailsArray($target, $source);
+
+ return array('relationship' => $relationship);
+ }
+
+ function relationshipDetailsArray($source, $target)
+ {
+ $details = array();
+
+ $details['screen_name'] = $source->nickname;
+ $details['followed_by'] = $target->isSubscribed($source);
+ $details['following'] = $source->isSubscribed($target);
+
+ $notifications = false;
+
+ if ($source->isSubscribed($target)) {
+
+ $sub = Subscription::pkeyGet(array('subscriber' =>
+ $source->id, 'subscribed' => $target->id));
+
+ if (!empty($sub)) {
+ $notifications = ($sub->jabber || $sub->sms);
+ }
+ }
+
+ $details['notifications_enabled'] = $notifications;
+ $details['blocking'] = $source->hasBlocked($target);
+ $details['id'] = $source->id;
+
+ return $details;
+ }
+
+ function showTwitterXmlRelationship($relationship)
+ {
+ $this->elementStart('relationship');
+
+ foreach($relationship as $element => $value) {
+ if ($element == 'source' || $element == 'target') {
+ $this->elementStart($element);
+ $this->showXmlRelationshipDetails($value);
+ $this->elementEnd($element);
+ }
+ }
+
+ $this->elementEnd('relationship');
+ }
+
+ function showXmlRelationshipDetails($details)
+ {
+ foreach($details as $element => $value) {
+ $this->element($element, null, $value);
+ }
+ }
+
+ function showTwitterXmlStatus($twitter_status)
+ {
+ $this->elementStart('status');
+ foreach($twitter_status as $element => $value) {
+ switch ($element) {
+ case 'user':
+ $this->showTwitterXmlUser($twitter_status['user']);
+ break;
+ case 'text':
+ $this->element($element, null, common_xml_safe_str($value));
+ break;
+ case 'attachments':
+ $this->showXmlAttachments($twitter_status['attachments']);
+ break;
+ default:
+ $this->element($element, null, $value);
+ }
+ }
+ $this->elementEnd('status');
+ }
+
+ function showTwitterXmlGroup($twitter_group)
+ {
+ $this->elementStart('group');
+ foreach($twitter_group as $element => $value) {
+ $this->element($element, null, $value);
+ }
+ $this->elementEnd('group');
+ }
+
+ function showTwitterXmlUser($twitter_user, $role='user')
+ {
+ $this->elementStart($role);
+ foreach($twitter_user as $element => $value) {
+ if ($element == 'status') {
+ $this->showTwitterXmlStatus($twitter_user['status']);
+ } else {
+ $this->element($element, null, $value);
+ }
+ }
+ $this->elementEnd($role);
+ }
+
+ function showXmlAttachments($attachments) {
+ if (!empty($attachments)) {
+ $this->elementStart('attachments', array('type' => 'array'));
+ foreach ($attachments as $attachment) {
+ $attrs = array();
+ $attrs['url'] = $attachment['url'];
+ $attrs['mimetype'] = $attachment['mimetype'];
+ $attrs['size'] = $attachment['size'];
+ $this->element('enclosure', $attrs, '');
+ }
+ $this->elementEnd('attachments');
+ }
+ }
+
+ function showTwitterRssItem($entry)
+ {
+ $this->elementStart('item');
+ $this->element('title', null, $entry['title']);
+ $this->element('description', null, $entry['description']);
+ $this->element('pubDate', null, $entry['pubDate']);
+ $this->element('guid', null, $entry['guid']);
+ $this->element('link', null, $entry['link']);
+
+ # RSS only supports 1 enclosure per item
+ if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){
+ $enclosure = $entry['enclosures'][0];
+ $this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
+ }
+
+ if(array_key_exists('tags', $entry)){
+ foreach($entry['tags'] as $tag){
+ $this->element('category', null,$tag);
+ }
+ }
+
+ $this->elementEnd('item');
+ }
+
+ function showJsonObjects($objects)
+ {
+ print(json_encode($objects));
+ }
+
+ function showSingleXmlStatus($notice)
+ {
+ $this->initDocument('xml');
+ $twitter_status = $this->twitterStatusArray($notice);
+ $this->showTwitterXmlStatus($twitter_status);
+ $this->endDocument('xml');
+ }
+
+ function show_single_json_status($notice)
+ {
+ $this->initDocument('json');
+ $status = $this->twitterStatusArray($notice);
+ $this->showJsonObjects($status);
+ $this->endDocument('json');
+ }
+
+
+ function showXmlTimeline($notice)
+ {
+
+ $this->initDocument('xml');
+ $this->elementStart('statuses', array('type' => 'array'));
+
+ if (is_array($notice)) {
+ foreach ($notice as $n) {
+ $twitter_status = $this->twitterStatusArray($n);
+ $this->showTwitterXmlStatus($twitter_status);
+ }
+ } else {
+ while ($notice->fetch()) {
+ $twitter_status = $this->twitterStatusArray($notice);
+ $this->showTwitterXmlStatus($twitter_status);
+ }
+ }
+
+ $this->elementEnd('statuses');
+ $this->endDocument('xml');
+ }
+
+ function showRssTimeline($notice, $title, $link, $subtitle, $suplink=null)
+ {
+
+ $this->initDocument('rss');
+
+ $this->element('title', null, $title);
+ $this->element('link', null, $link);
+ if (!is_null($suplink)) {
+ // For FriendFeed's SUP protocol
+ $this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom',
+ 'rel' => 'http://api.friendfeed.com/2008/03#sup',
+ 'href' => $suplink,
+ 'type' => 'application/json'));
+ }
+ $this->element('description', null, $subtitle);
+ $this->element('language', null, 'en-us');
+ $this->element('ttl', null, '40');
+
+ if (is_array($notice)) {
+ foreach ($notice as $n) {
+ $entry = $this->twitterRssEntryArray($n);
+ $this->showTwitterRssItem($entry);
+ }
+ } else {
+ while ($notice->fetch()) {
+ $entry = $this->twitterRssEntryArray($notice);
+ $this->showTwitterRssItem($entry);
+ }
+ }
+
+ $this->endTwitterRss();
+ }
+
+ function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null)
+ {
+
+ $this->initDocument('atom');
+
+ $this->element('title', null, $title);
+ $this->element('id', null, $id);
+ $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
+
+ if (!is_null($suplink)) {
+ # For FriendFeed's SUP protocol
+ $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
+ 'href' => $suplink,
+ 'type' => 'application/json'));
+ }
+
+ if (!is_null($selfuri)) {
+ $this->element('link', array('href' => $selfuri,
+ 'rel' => 'self', 'type' => 'application/atom+xml'), null);
+ }
+
+ $this->element('updated', null, common_date_iso8601('now'));
+ $this->element('subtitle', null, $subtitle);
+
+ if (is_array($notice)) {
+ foreach ($notice as $n) {
+ $this->raw($n->asAtomEntry());
+ }
+ } else {
+ while ($notice->fetch()) {
+ $this->raw($notice->asAtomEntry());
+ }
+ }
+
+ $this->endDocument('atom');
+
+ }
+
+ function showRssGroups($group, $title, $link, $subtitle)
+ {
+
+ $this->initDocument('rss');
+
+ $this->element('title', null, $title);
+ $this->element('link', null, $link);
+ $this->element('description', null, $subtitle);
+ $this->element('language', null, 'en-us');
+ $this->element('ttl', null, '40');
+
+ if (is_array($group)) {
+ foreach ($group as $g) {
+ $twitter_group = $this->twitterRssGroupArray($g);
+ $this->showTwitterRssItem($twitter_group);
+ }
+ } else {
+ while ($group->fetch()) {
+ $twitter_group = $this->twitterRssGroupArray($group);
+ $this->showTwitterRssItem($twitter_group);
+ }
+ }
+
+ $this->endTwitterRss();
+ }
+
+
+ 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->showTwitterXmlUser($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->dateTwitter($message->created);
+ $dmsg['sender_screen_name'] = $from_profile->nickname;
+ $dmsg['recipient_screen_name'] = $to_profile->nickname;
+ $dmsg['sender'] = $this->twitterUserArray($from_profile, false);
+ $dmsg['recipient'] = $this->twitterUserArray($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->initDocument('xml');
+ $dmsg = $this->directMessageArray($message);
+ $this->showXmlDirectMessage($dmsg);
+ $this->endDocument('xml');
+ }
+
+ function showSingleJsonDirectMessage($message)
+ {
+ $this->initDocument('json');
+ $dmsg = $this->directMessageArray($message);
+ $this->showJsonObjects($dmsg);
+ $this->endDocument('json');
+ }
+
+ function showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
+ {
+
+ $this->initDocument('atom');
+
+ $this->element('title', null, $title);
+ $this->element('id', null, $id);
+ $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
+
+ if (!is_null($selfuri)) {
+ $this->element('link', array('href' => $selfuri,
+ 'rel' => 'self', 'type' => 'application/atom+xml'), null);
+ }
+
+ $this->element('updated', null, common_date_iso8601('now'));
+ $this->element('subtitle', null, $subtitle);
+
+ if (is_array($group)) {
+ foreach ($group as $g) {
+ $this->raw($g->asAtomEntry());
+ }
+ } else {
+ while ($group->fetch()) {
+ $this->raw($group->asAtomEntry());
+ }
+ }
+
+ $this->endDocument('atom');
+
+ }
+
+ function showJsonTimeline($notice)
+ {
+
+ $this->initDocument('json');
+
+ $statuses = array();
+
+ if (is_array($notice)) {
+ foreach ($notice as $n) {
+ $twitter_status = $this->twitterStatusArray($n);
+ array_push($statuses, $twitter_status);
+ }
+ } else {
+ while ($notice->fetch()) {
+ $twitter_status = $this->twitterStatusArray($notice);
+ array_push($statuses, $twitter_status);
+ }
+ }
+
+ $this->showJsonObjects($statuses);
+
+ $this->endDocument('json');
+ }
+
+ function showJsonGroups($group)
+ {
+
+ $this->initDocument('json');
+
+ $groups = array();
+
+ if (is_array($group)) {
+ foreach ($group as $g) {
+ $twitter_group = $this->twitterGroupArray($g);
+ array_push($groups, $twitter_group);
+ }
+ } else {
+ while ($group->fetch()) {
+ $twitter_group = $this->twitterGroupArray($group);
+ array_push($groups, $twitter_group);
+ }
+ }
+
+ $this->showJsonObjects($groups);
+
+ $this->endDocument('json');
+ }
+
+ function showXmlGroups($group)
+ {
+
+ $this->initDocument('xml');
+ $this->elementStart('groups', array('type' => 'array'));
+
+ if (is_array($group)) {
+ foreach ($group as $g) {
+ $twitter_group = $this->twitterGroupArray($g);
+ $this->showTwitterXmlGroup($twitter_group);
+ }
+ } else {
+ while ($group->fetch()) {
+ $twitter_group = $this->twitterGroupArray($group);
+ $this->showTwitterXmlGroup($twitter_group);
+ }
+ }
+
+ $this->elementEnd('groups');
+ $this->endDocument('xml');
+ }
+
+ function showTwitterXmlUsers($user)
+ {
+
+ $this->initDocument('xml');
+ $this->elementStart('users', array('type' => 'array'));
+
+ if (is_array($user)) {
+ foreach ($user as $u) {
+ $twitter_user = $this->twitterUserArray($u);
+ $this->showTwitterXmlUser($twitter_user);
+ }
+ } else {
+ while ($user->fetch()) {
+ $twitter_user = $this->twitterUserArray($user);
+ $this->showTwitterXmlUser($twitter_user);
+ }
+ }
+
+ $this->elementEnd('users');
+ $this->endDocument('xml');
+ }
+
+ function showJsonUsers($user)
+ {
+
+ $this->initDocument('json');
+
+ $users = array();
+
+ if (is_array($user)) {
+ foreach ($user as $u) {
+ $twitter_user = $this->twitterUserArray($u);
+ array_push($users, $twitter_user);
+ }
+ } else {
+ while ($user->fetch()) {
+ $twitter_user = $this->twitterUserArray($user);
+ array_push($users, $twitter_user);
+ }
+ }
+
+ $this->showJsonObjects($users);
+
+ $this->endDocument('json');
+ }
+
+ function showSingleJsonGroup($group)
+ {
+ $this->initDocument('json');
+ $twitter_group = $this->twitterGroupArray($group);
+ $this->showJsonObjects($twitter_group);
+ $this->endDocument('json');
+ }
+
+ function showSingleXmlGroup($group)
+ {
+ $this->initDocument('xml');
+ $twitter_group = $this->twitterGroupArray($group);
+ $this->showTwitterXmlGroup($twitter_group);
+ $this->endDocument('xml');
+ }
+
+ function dateTwitter($dt)
+ {
+ $dateStr = date('d F Y H:i:s', strtotime($dt));
+ $d = new DateTime($dateStr, new DateTimeZone('UTC'));
+ $d->setTimezone(new DateTimeZone(common_timezone()));
+ return $d->format('D M d H:i:s O Y');
+ }
+
+ function initDocument($type='xml')
+ {
+ switch ($type) {
+ case 'xml':
+ header('Content-Type: application/xml; charset=utf-8');
+ $this->startXML();
+ break;
+ case 'json':
+ header('Content-Type: application/json; charset=utf-8');
+
+ // Check for JSONP callback
+ $callback = $this->arg('callback');
+ if ($callback) {
+ print $callback . '(';
+ }
+ break;
+ case 'rss':
+ header("Content-Type: application/rss+xml; charset=utf-8");
+ $this->initTwitterRss();
+ break;
+ case 'atom':
+ header('Content-Type: application/atom+xml; charset=utf-8');
+ $this->initTwitterAtom();
+ break;
+ default:
+ $this->clientError(_('Not a supported data format.'));
+ break;
+ }
+
+ return;
+ }
+
+ function endDocument($type='xml')
+ {
+ switch ($type) {
+ case 'xml':
+ $this->endXML();
+ break;
+ case 'json':
+
+ // Check for JSONP callback
+ $callback = $this->arg('callback');
+ if ($callback) {
+ print ')';
+ }
+ break;
+ case 'rss':
+ $this->endTwitterRss();
+ break;
+ case 'atom':
+ $this->endTwitterRss();
+ break;
+ default:
+ $this->clientError(_('Not a supported data format.'));
+ break;
+ }
+ return;
+ }
+
+ function clientError($msg, $code = 400, $format = 'xml')
+ {
+ $action = $this->trimmed('action');
+
+ common_debug("User error '$code' on '$action': $msg", __FILE__);
+
+ if (!array_key_exists($code, ClientErrorAction::$status)) {
+ $code = 400;
+ }
+
+ $status_string = ClientErrorAction::$status[$code];
+
+ header('HTTP/1.1 '.$code.' '.$status_string);
+
+ if ($format == 'xml') {
+ $this->initDocument('xml');
+ $this->elementStart('hash');
+ $this->element('error', null, $msg);
+ $this->element('request', null, $_SERVER['REQUEST_URI']);
+ $this->elementEnd('hash');
+ $this->endDocument('xml');
+ } elseif ($format == 'json'){
+ $this->initDocument('json');
+ $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
+ print(json_encode($error_array));
+ $this->endDocument('json');
+ } else {
+
+ // If user didn't request a useful format, throw a regular client error
+ throw new ClientException($msg, $code);
+ }
+ }
+
+ function serverError($msg, $code = 500, $content_type = 'json')
+ {
+ $action = $this->trimmed('action');
+
+ common_debug("Server error '$code' on '$action': $msg", __FILE__);
+
+ if (!array_key_exists($code, ServerErrorAction::$status)) {
+ $code = 400;
+ }
+
+ $status_string = ServerErrorAction::$status[$code];
+
+ header('HTTP/1.1 '.$code.' '.$status_string);
+
+ if ($content_type == 'xml') {
+ $this->initDocument('xml');
+ $this->elementStart('hash');
+ $this->element('error', null, $msg);
+ $this->element('request', null, $_SERVER['REQUEST_URI']);
+ $this->elementEnd('hash');
+ $this->endDocument('xml');
+ } else {
+ $this->initDocument('json');
+ $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
+ print(json_encode($error_array));
+ $this->endDocument('json');
+ }
+ }
+
+ function initTwitterRss()
+ {
+ $this->startXML();
+ $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
+ $this->elementStart('channel');
+ Event::handle('StartApiRss', array($this));
+ }
+
+ function endTwitterRss()
+ {
+ $this->elementEnd('channel');
+ $this->elementEnd('rss');
+ $this->endXML();
+ }
+
+ function initTwitterAtom()
+ {
+ $this->startXML();
+ // FIXME: don't hardcode the language here!
+ $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
+ 'xml:lang' => 'en-US',
+ 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
+ Event::handle('StartApiAtom', array($this));
+ }
+
+ function endTwitterAtom()
+ {
+ $this->elementEnd('feed');
+ $this->endXML();
+ }
+
+ function showProfile($profile, $content_type='xml', $notice=null, $includeStatuses=true)
+ {
+ $profile_array = $this->twitterUserArray($profile, $includeStatuses);
+ switch ($content_type) {
+ case 'xml':
+ $this->showTwitterXmlUser($profile_array);
+ break;
+ case 'json':
+ $this->showJsonObjects($profile_array);
+ break;
+ default:
+ $this->clientError(_('Not a supported data format.'));
+ return;
+ }
+ return;
+ }
+
+ 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 getTargetGroup($id)
+ {
+ if (empty($id)) {
+ if (is_numeric($this->arg('id'))) {
+ return User_group::staticGet($this->arg('id'));
+ } else if ($this->arg('id')) {
+ $nickname = common_canonical_nickname($this->arg('id'));
+ return User_group::staticGet('nickname', $nickname);
+ } else if ($this->arg('group_id')) {
+ // This is to ensure that a non-numeric user_id still
+ // overrides screen_name even if it doesn't get used
+ if (is_numeric($this->arg('group_id'))) {
+ return User_group::staticGet('id', $this->arg('group_id'));
+ }
+ } else if ($this->arg('group_name')) {
+ $nickname = common_canonical_nickname($this->arg('group_name'));
+ return User_group::staticGet('nickname', $nickname);
+ }
+
+ } else if (is_numeric($id)) {
+ return User_group::staticGet($id);
+ } else {
+ $nickname = common_canonical_nickname($id);
+ return User_group::staticGet('nickname', $nickname);
+ }
+ }
+
+ function sourceLink($source)
+ {
+ $source_name = _($source);
+ switch ($source) {
+ case 'web':
+ case 'xmpp':
+ case 'mail':
+ case 'omb':
+ case 'api':
+ break;
+ default:
+ $ns = Notice_source::staticGet($source);
+ if ($ns) {
+ $source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
+ }
+ break;
+ }
+ return $source_name;
+ }
+
+ /**
+ * Returns query argument or default value if not found. Certain
+ * parameters used throughout the API are lightly scrubbed and
+ * bounds checked. This overrides Action::arg().
+ *
+ * @param string $key requested argument
+ * @param string $def default value to return if $key is not provided
+ *
+ * @return var $var
+ */
+ function arg($key, $def=null)
+ {
+
+ // XXX: Do even more input validation/scrubbing?
+
+ if (array_key_exists($key, $this->args)) {
+ switch($key) {
+ case 'page':
+ $page = (int)$this->args['page'];
+ return ($page < 1) ? 1 : $page;
+ case 'count':
+ $count = (int)$this->args['count'];
+ if ($count < 1) {
+ return 20;
+ } elseif ($count > 200) {
+ return 200;
+ } else {
+ return $count;
+ }
+ case 'since_id':
+ $since_id = (int)$this->args['since_id'];
+ return ($since_id < 1) ? 0 : $since_id;
+ case 'max_id':
+ $max_id = (int)$this->args['max_id'];
+ return ($max_id < 1) ? 0 : $max_id;
+ case 'since':
+ return strtotime($this->args['since']);
+ default:
+ return parent::arg($key, $def);
+ }
+ } else {
+ return $def;
+ }
+ }
+
+}
--- /dev/null
+<?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 Adrian Lang <mail@adrianlang.de>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mEDI <medi@milaro.net>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @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/api.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 ApiAction
+{
+
+ var $auth_user = null;
+
+ /**
+ * Take arguments for running, and output basic auth header if needed
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if ($this->requiresAuth()) {
+ $this->checkBasicAuthUser();
+ }
+
+ return true;
+ }
+
+ /**
+ * 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();
+
+ $realm = common_config('site', 'name') . ' API';
+
+ if (!isset($this->auth_user)) {
+ header('WWW-Authenticate: Basic realm="' . $realm . '"');
+
+ // show error if the user clicks 'cancel'
+
+ $this->showBasicAuthError();
+ exit;
+
+ } 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();
+ exit;
+ }
+ }
+ 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->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->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";
+ }
+ }
+
+}
--- /dev/null
+<?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 Adrian Lang <mail@adrianlang.de>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mEDI <medi@milaro.net>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @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 Adrian Lang <mail@adrianlang.de>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mEDI <medi@milaro.net>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @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
+{
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ return true;
+ }
+
+ /**
+ * 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
// 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',
// 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'));
-
- foreach (array('xml', 'json') as $e) {
- $m->connect('api/friends/ids.'.$e,
- array('action' => 'api',
- 'apiaction' => 'statuses',
- 'method' => 'friendsIDs.'.$e));
- }
+ $m->connect('api/friends/ids/:id.:format',
+ array('action' => 'apiFriends',
+ 'ids_only' => true));
- $m->connect('api/followers/ids/:argument',
- array('action' => 'api',
- 'apiaction' => 'statuses',
- 'method' => 'followersIDs'));
-
- 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/:id.:format',
+ array('action' => 'apiFollowers',
+ 'ids_only' => true));
+
+ $m->connect('api/friends/ids.:format',
+ array('action' => 'apiFriends',
+ 'ids_only' => true));
+
+ $m->connect('api/followers/ids.:format',
+ array('action' => 'apiFollowers',
+ 'ids_only' => true));
// account
- $m->connect('api/account/:method',
- array('action' => 'api',
- 'apiaction' => 'account'));
+ $m->connect('api/account/verify_credentials.:format',
+ array('action' => 'ApiAccountVerifyCredentials'));
+
+ // special case where verify_credentials is called w/out a format
+
+ $m->connect('api/account/verify_credentials',
+ array('action' => 'ApiAccountVerifyCredentials'));
+
+ $m->connect('api/account/rate_limit_status.:format',
+ array('action' => 'ApiAccountRateLimitStatus'));
// favorites
- $m->connect('api/favorites/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'favorites',
- array('method' => '(create|destroy)')));
+ $m->connect('api/favorites.:format',
+ array('action' => 'ApiTimelineFavorites',
+ 'format' => '(xml|json|rss|atom)'));
- $m->connect('api/favorites/:argument',
- array('action' => 'api',
- 'apiaction' => 'favorites',
- 'method' => 'favorites'));
-
- foreach (array('xml', 'json', 'rss', 'atom') as $e) {
- $m->connect('api/favorites.'.$e,
- array('action' => 'api',
- 'apiaction' => 'favorites',
- 'method' => 'favorites.'.$e));
- }
+ $m->connect('api/favorites/:id.:format',
+ array('action' => 'ApiTimelineFavorites',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xmljson|rss|atom)'));
+
+ $m->connect('api/favorites/create/:id.:format',
+ array('action' => 'ApiFavoriteCreate',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/favorites/destroy/:id.:format',
+ array('action' => 'ApiFavoriteDestroy',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
// notifications
// blocks
- $m->connect('api/blocks/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'blocks'));
+ $m->connect('api/blocks/create/:id.:format',
+ array('action' => 'ApiBlockCreate',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+ $m->connect('api/blocks/destroy/:id.:format',
+ array('action' => 'ApiBlockDestroy',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
// help
- $m->connect('api/help/:method',
- array('action' => 'api',
- 'apiaction' => 'help'));
+ $m->connect('api/help/test.:format',
+ array('action' => 'ApiHelpTest',
+ 'format' => '(xml|json)'));
// statusnet
- $m->connect('api/statusnet/:method',
- array('action' => 'api',
- 'apiaction' => 'statusnet'));
+ $m->connect('api/statusnet/version.:format',
+ array('action' => 'ApiStatusnetVersion',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/config.:format',
+ array('action' => 'ApiStatusnetConfig',
+ 'format' => '(xml|json)'));
// For older methods, we provide "laconica" base action
- $m->connect('api/laconica/:method',
- array('action' => 'api',
- 'apiaction' => 'statusnet'));
+ $m->connect('api/laconica/version.:format',
+ array('action' => 'ApiStatusnetVersion',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/laconica/config.:format',
+ array('action' => 'ApiStatusnetConfig',
+ 'format' => '(xml|json)'));
// Groups and tags are newer than 0.8.1 so no backward-compatibility
// necessary
// Groups
//'list' has to be handled differently, as php will not allow a method to be named 'list'
- $m->connect('api/statusnet/groups/list/:argument',
- array('action' => 'api',
- 'method' => 'list_groups',
- 'apiaction' => 'groups'));
-
- foreach (array('xml', 'json', 'rss', 'atom') as $e) {
- $m->connect('api/statusnet/groups/list.' . $e,
- array('action' => 'api',
- 'method' => 'list_groups.' . $e,
- 'apiaction' => 'groups'));
- }
-
- $m->connect('api/statusnet/groups/:method',
- array('action' => 'api',
- 'apiaction' => 'statuses'),
- array('method' => '(list_all|)(\.(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/statusnet/groups/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'groups'));
-
- $m->connect('api/statusnet/groups/:method',
- array('action' => 'api',
- 'apiaction' => 'groups'));
+ $m->connect('api/statusnet/groups/timeline/:id.:format',
+ array('action' => 'ApiTimelineGroup',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xmljson|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/show.:format',
+ array('action' => 'ApiGroupShow',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/show/:id.:format',
+ array('action' => 'ApiGroupShow',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/join.:format',
+ array('action' => 'ApiGroupJoin',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/join/:id.:format',
+ array('action' => 'ApiGroupJoin',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/leave.:format',
+ array('action' => 'ApiGroupLeave',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/leave/:id.:format',
+ array('action' => 'ApiGroupLeave',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/is_member.:format',
+ array('action' => 'ApiGroupIsMember',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/list.:format',
+ array('action' => 'ApiGroupList',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/list/:id.:format',
+ array('action' => 'ApiGroupList',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/list_all.:format',
+ array('action' => 'ApiGroupListAll',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/membership.:format',
+ array('action' => 'ApiGroupMembership',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/membership/:id.:format',
+ array('action' => 'ApiGroupMembership',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/create.:format',
+ array('action' => 'ApiGroupCreate',
+ 'format' => '(xml|json)'));
// Tags
- $m->connect('api/statusnet/tags/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'tags'));
-
- $m->connect('api/statusnet/tags/:method',
- array('action' => 'api',
- 'apiaction' => 'tags'));
+ $m->connect('api/statusnet/tags/timeline/:tag.:format',
+ array('action' => 'ApiTimelineTag',
+ 'format' => '(xmljson|rss|atom)'));
// search
$m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1
-function update_twitter_user($twitter_id, $screen_name)
+function updateTwitter_user($twitter_id, $screen_name)
{
$uri = 'http://twitter.com/' . $screen_name;
$fuser = new Foreign_user();
// Only update if Twitter screen name has changed
if ($fuser->nickname != $screen_name) {
- $result = update_twitter_user($twitter_id, $screen_name);
+ $result = updateTwitter_user($twitter_id, $screen_name);
common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' .
"$fuser->id to $screen_name, was $fuser->nickname");
+++ /dev/null
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-class TwitterapiAction extends Action
-{
-
- var $auth_user;
-
- /**
- * Initialization.
- *
- * @param array $args Web and URL arguments
- *
- * @return boolean false if user doesn't exist
- */
-
- function prepare($args)
- {
- parent::prepare($args);
- return true;
- }
-
- /**
- * Handle a request
- *
- * @param array $args Arguments from $_REQUEST
- *
- * @return void
- */
-
- function handle($args)
- {
- parent::handle($args);
- }
-
- /**
- * Overrides XMLOutputter::element to write booleans as strings (true|false).
- * See that method's documentation for more info.
- *
- * @param string $tag Element type or tagname
- * @param array $attrs Array of element attributes, as
- * key-value pairs
- * @param string $content string content of the element
- *
- * @return void
- */
- function element($tag, $attrs=null, $content=null)
- {
- if (is_bool($content)) {
- $content = ($content ? 'true' : 'false');
- }
-
- return parent::element($tag, $attrs, $content);
- }
-
- function twitter_user_array($profile, $get_notice=false)
- {
- $twitter_user = array();
-
- $twitter_user['id'] = intval($profile->id);
- $twitter_user['name'] = $profile->getBestName();
- $twitter_user['screen_name'] = $profile->nickname;
- $twitter_user['location'] = ($profile->location) ? $profile->location : null;
- $twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
-
- $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
- $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() :
- Avatar::defaultImage(AVATAR_STREAM_SIZE);
-
- $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null;
- $twitter_user['protected'] = false; # not supported by StatusNet yet
- $twitter_user['followers_count'] = $profile->subscriberCount();
-
- // To be supported soon...
- $twitter_user['profile_background_color'] = '';
- $twitter_user['profile_text_color'] = '';
- $twitter_user['profile_link_color'] = '';
- $twitter_user['profile_sidebar_fill_color'] = '';
- $twitter_user['profile_sidebar_border_color'] = '';
-
- $twitter_user['friends_count'] = $profile->subscriptionCount();
-
- $twitter_user['created_at'] = $this->date_twitter($profile->created);
-
- $twitter_user['favourites_count'] = $profile->faveCount(); // British spelling!
-
- // Need to pull up the user for some of this
- $user = User::staticGet($profile->id);
-
- $timezone = 'UTC';
-
- if ($user->timezone) {
- $timezone = $user->timezone;
- }
-
- $t = new DateTime;
- $t->setTimezone(new DateTimeZone($timezone));
-
- $twitter_user['utc_offset'] = $t->format('Z');
- $twitter_user['time_zone'] = $timezone;
-
- // To be supported some day, perhaps
- $twitter_user['profile_background_image_url'] = '';
- $twitter_user['profile_background_tile'] = false;
-
- $twitter_user['statuses_count'] = $profile->noticeCount();
-
- // Is the requesting user following this user?
- $twitter_user['following'] = false;
- $twitter_user['notifications'] = false;
-
- if (isset($apidata['user'])) {
-
- $twitter_user['following'] = $apidata['user']->isSubscribed($profile);
-
- // Notifications on?
- $sub = Subscription::pkeyGet(array('subscriber' =>
- $apidata['user']->id, 'subscribed' => $profile->id));
-
- if ($sub) {
- $twitter_user['notifications'] = ($sub->jabber || $sub->sms);
- }
- }
-
- if ($get_notice) {
- $notice = $profile->getCurrentNotice();
- if ($notice) {
- # don't get user!
- $twitter_user['status'] = $this->twitter_status_array($notice, false);
- }
- }
-
- return $twitter_user;
- }
-
- function twitter_status_array($notice, $include_user=true)
- {
- $profile = $notice->getProfile();
-
- $twitter_status = array();
- $twitter_status['text'] = $notice->content;
- $twitter_status['truncated'] = false; # Not possible on StatusNet
- $twitter_status['created_at'] = $this->date_twitter($notice->created);
- $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ?
- intval($notice->reply_to) : null;
- $twitter_status['source'] = $this->source_link($notice->source);
- $twitter_status['id'] = intval($notice->id);
-
- $replier_profile = null;
-
- if ($notice->reply_to) {
- $reply = Notice::staticGet(intval($notice->reply_to));
- if ($reply) {
- $replier_profile = $reply->getProfile();
- }
- }
-
- $twitter_status['in_reply_to_user_id'] =
- ($replier_profile) ? intval($replier_profile->id) : null;
- $twitter_status['in_reply_to_screen_name'] =
- ($replier_profile) ? $replier_profile->nickname : null;
-
- if (isset($this->auth_user)) {
- $twitter_status['favorited'] = $this->auth_user->hasFave($notice);
- } else {
- $twitter_status['favorited'] = false;
- }
-
- // Enclosures
- $attachments = $notice->attachments();
-
- if (!empty($attachments)) {
-
- $twitter_status['attachments'] = array();
-
- foreach ($attachments as $attachment) {
- if ($attachment->isEnclosure()) {
- $enclosure = array();
- $enclosure['url'] = $attachment->url;
- $enclosure['mimetype'] = $attachment->mimetype;
- $enclosure['size'] = $attachment->size;
- $twitter_status['attachments'][] = $enclosure;
- }
- }
- }
-
- if ($include_user) {
- # Don't get notice (recursive!)
- $twitter_user = $this->twitter_user_array($profile, false);
- $twitter_status['user'] = $twitter_user;
- }
-
- return $twitter_status;
- }
-
- function twitter_group_array($group)
- {
- $twitter_group=array();
- $twitter_group['id']=$group->id;
- $twitter_group['url']=$group->permalink();
- $twitter_group['nickname']=$group->nickname;
- $twitter_group['fullname']=$group->fullname;
- $twitter_group['homepage_url']=$group->homepage_url;
- $twitter_group['original_logo']=$group->original_logo;
- $twitter_group['homepage_logo']=$group->homepage_logo;
- $twitter_group['stream_logo']=$group->stream_logo;
- $twitter_group['mini_logo']=$group->mini_logo;
- $twitter_group['homepage']=$group->homepage;
- $twitter_group['description']=$group->description;
- $twitter_group['location']=$group->location;
- $twitter_group['created']=$this->date_twitter($group->created);
- $twitter_group['modified']=$this->date_twitter($group->modified);
- return $twitter_group;
- }
-
- function twitter_rss_group_array($group)
- {
- $entry = array();
- $entry['content']=$group->description;
- $entry['title']=$group->nickname;
- $entry['link']=$group->permalink();
- $entry['published']=common_date_iso8601($group->created);
- $entry['updated']==common_date_iso8601($group->modified);
- $taguribase = common_config('integration', 'groupuri');
- $entry['id'] = "group:$groupuribase:$entry[link]";
-
- $entry['description'] = $entry['content'];
- $entry['pubDate'] = common_date_rfc2822($group->created);
- $entry['guid'] = $entry['link'];
-
- return $entry;
- }
-
- function twitter_rss_entry_array($notice)
- {
- $profile = $notice->getProfile();
- $entry = array();
-
- // We trim() to avoid extraneous whitespace in the output
-
- $entry['content'] = common_xml_safe_str(trim($notice->rendered));
- $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
- $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
- $entry['published'] = common_date_iso8601($notice->created);
-
- $taguribase = common_config('integration', 'taguri');
- $entry['id'] = "tag:$taguribase:$entry[link]";
-
- $entry['updated'] = $entry['published'];
- $entry['author'] = $profile->getBestName();
-
- // Enclosures
- $attachments = $notice->attachments();
- $enclosures = array();
-
- foreach ($attachments as $attachment) {
- $enclosure_o=$attachment->getEnclosure();
- if ($enclosure_o) {
- $enclosure = array();
- $enclosure['url'] = $enclosure_o->url;
- $enclosure['mimetype'] = $enclosure_o->mimetype;
- $enclosure['size'] = $enclosure_o->size;
- $enclosures[] = $enclosure;
- }
- }
-
- if (!empty($enclosures)) {
- $entry['enclosures'] = $enclosures;
- }
-
-/*
- // Enclosure
- $attachments = $notice->attachments();
- if($attachments){
- $entry['enclosures']=array();
- foreach($attachments as $attachment){
- if ($attachment->isEnclosure()) {
- $enclosure=array();
- $enclosure['url']=$attachment->url;
- $enclosure['mimetype']=$attachment->mimetype;
- $enclosure['size']=$attachment->size;
- $entry['enclosures'][]=$enclosure;
- }
- }
- }
-*/
-
- // Tags/Categories
- $tag = new Notice_tag();
- $tag->notice_id = $notice->id;
- if ($tag->find()) {
- $entry['tags']=array();
- while ($tag->fetch()) {
- $entry['tags'][]=$tag->tag;
- }
- }
- $tag->free();
-
- // RSS Item specific
- $entry['description'] = $entry['content'];
- $entry['pubDate'] = common_date_rfc2822($notice->created);
- $entry['guid'] = $entry['link'];
-
- return $entry;
- }
-
- function twitter_rss_dmsg_array($message)
- {
-
- $entry = array();
-
- $entry['title'] = sprintf('Message from %s to %s',
- $message->getFrom()->nickname, $message->getTo()->nickname);
-
- $entry['content'] = common_xml_safe_str(trim($message->content));
- $entry['link'] = common_local_url('showmessage', array('message' => $message->id));
- $entry['published'] = common_date_iso8601($message->created);
-
- $taguribase = common_config('integration', 'taguri');
-
- $entry['id'] = "tag:$taguribase,:$entry[link]";
- $entry['updated'] = $entry['published'];
- $entry['author'] = $message->getFrom()->getBestName();
-
- # RSS Item specific
- $entry['description'] = $entry['content'];
- $entry['pubDate'] = common_date_rfc2822($message->created);
- $entry['guid'] = $entry['link'];
-
- return $entry;
- }
-
- function twitter_dmsg_array($message)
- {
- $twitter_dm = array();
-
- $from_profile = $message->getFrom();
- $to_profile = $message->getTo();
-
- $twitter_dm['id'] = $message->id;
- $twitter_dm['sender_id'] = $message->from_profile;
- $twitter_dm['text'] = trim($message->content);
- $twitter_dm['recipient_id'] = $message->to_profile;
- $twitter_dm['created_at'] = $this->date_twitter($message->created);
- $twitter_dm['sender_screen_name'] = $from_profile->nickname;
- $twitter_dm['recipient_screen_name'] = $to_profile->nickname;
- $twitter_dm['sender'] = $this->twitter_user_array($from_profile, false);
- $twitter_dm['recipient'] = $this->twitter_user_array($to_profile, false);
-
- return $twitter_dm;
- }
-
- function twitter_relationship_array($source, $target)
- {
- $relationship = array();
-
- $relationship['source'] =
- $this->relationship_details_array($source, $target);
- $relationship['target'] =
- $this->relationship_details_array($target, $source);
-
- return array('relationship' => $relationship);
- }
-
- function relationship_details_array($source, $target)
- {
- $details = array();
-
- $details['screen_name'] = $source->nickname;
- $details['followed_by'] = $target->isSubscribed($source);
- $details['following'] = $source->isSubscribed($target);
-
- $notifications = false;
-
- if ($source->isSubscribed($target)) {
-
- $sub = Subscription::pkeyGet(array('subscriber' =>
- $source->id, 'subscribed' => $target->id));
-
- if (!empty($sub)) {
- $notifications = ($sub->jabber || $sub->sms);
- }
- }
-
- $details['notifications_enabled'] = $notifications;
- $details['blocking'] = $source->hasBlocked($target);
- $details['id'] = $source->id;
-
- return $details;
- }
-
- function show_twitter_xml_relationship($relationship)
- {
- $this->elementStart('relationship');
-
- foreach($relationship as $element => $value) {
- if ($element == 'source' || $element == 'target') {
- $this->elementStart($element);
- $this->show_xml_relationship_details($value);
- $this->elementEnd($element);
- }
- }
-
- $this->elementEnd('relationship');
- }
-
- function show_xml_relationship_details($details)
- {
- foreach($details as $element => $value) {
- $this->element($element, null, $value);
- }
- }
-
- function show_twitter_xml_status($twitter_status)
- {
- $this->elementStart('status');
- foreach($twitter_status as $element => $value) {
- switch ($element) {
- case 'user':
- $this->show_twitter_xml_user($twitter_status['user']);
- break;
- case 'text':
- $this->element($element, null, common_xml_safe_str($value));
- break;
- case 'attachments':
- $this->show_xml_attachments($twitter_status['attachments']);
- break;
- default:
- $this->element($element, null, $value);
- }
- }
- $this->elementEnd('status');
- }
-
- function show_twitter_xml_group($twitter_group)
- {
- $this->elementStart('group');
- foreach($twitter_group as $element => $value) {
- $this->element($element, null, $value);
- }
- $this->elementEnd('group');
- }
-
- function show_twitter_xml_user($twitter_user, $role='user')
- {
- $this->elementStart($role);
- foreach($twitter_user as $element => $value) {
- if ($element == 'status') {
- $this->show_twitter_xml_status($twitter_user['status']);
- } else {
- $this->element($element, null, $value);
- }
- }
- $this->elementEnd($role);
- }
-
- function show_xml_attachments($attachments) {
- if (!empty($attachments)) {
- $this->elementStart('attachments', array('type' => 'array'));
- foreach ($attachments as $attachment) {
- $attrs = array();
- $attrs['url'] = $attachment['url'];
- $attrs['mimetype'] = $attachment['mimetype'];
- $attrs['size'] = $attachment['size'];
- $this->element('enclosure', $attrs, '');
- }
- $this->elementEnd('attachments');
- }
- }
-
- function show_twitter_rss_item($entry)
- {
- $this->elementStart('item');
- $this->element('title', null, $entry['title']);
- $this->element('description', null, $entry['description']);
- $this->element('pubDate', null, $entry['pubDate']);
- $this->element('guid', null, $entry['guid']);
- $this->element('link', null, $entry['link']);
-
- # RSS only supports 1 enclosure per item
- if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){
- $enclosure = $entry['enclosures'][0];
- $this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
- }
-
- if(array_key_exists('tags', $entry)){
- foreach($entry['tags'] as $tag){
- $this->element('category', null,$tag);
- }
- }
-
- $this->elementEnd('item');
- }
-
- function show_json_objects($objects)
- {
- print(json_encode($objects));
- }
-
- function show_single_xml_status($notice)
- {
- $this->init_document('xml');
- $twitter_status = $this->twitter_status_array($notice);
- $this->show_twitter_xml_status($twitter_status);
- $this->end_document('xml');
- }
-
- function show_single_json_status($notice)
- {
- $this->init_document('json');
- $status = $this->twitter_status_array($notice);
- $this->show_json_objects($status);
- $this->end_document('json');
- }
-
- function show_single_xml_dmsg($message)
- {
- $this->init_document('xml');
- $dmsg = $this->twitter_dmsg_array($message);
- $this->show_twitter_xml_dmsg($dmsg);
- $this->end_document('xml');
- }
-
- function show_single_json_dmsg($message)
- {
- $this->init_document('json');
- $dmsg = $this->twitter_dmsg_array($message);
- $this->show_json_objects($dmsg);
- $this->end_document('json');
- }
-
- function show_twitter_xml_dmsg($twitter_dm)
- {
- $this->elementStart('direct_message');
- foreach($twitter_dm as $element => $value) {
- switch ($element) {
- case 'sender':
- case 'recipient':
- $this->show_twitter_xml_user($value, $element);
- break;
- case 'text':
- $this->element($element, null, common_xml_safe_str($value));
- break;
- default:
- $this->element($element, null, $value);
- }
- }
- $this->elementEnd('direct_message');
- }
-
- function show_xml_timeline($notice)
- {
-
- $this->init_document('xml');
- $this->elementStart('statuses', array('type' => 'array'));
-
- if (is_array($notice)) {
- foreach ($notice as $n) {
- $twitter_status = $this->twitter_status_array($n);
- $this->show_twitter_xml_status($twitter_status);
- }
- } else {
- while ($notice->fetch()) {
- $twitter_status = $this->twitter_status_array($notice);
- $this->show_twitter_xml_status($twitter_status);
- }
- }
-
- $this->elementEnd('statuses');
- $this->end_document('xml');
- }
-
- function show_rss_timeline($notice, $title, $link, $subtitle, $suplink=null)
- {
-
- $this->init_document('rss');
-
- $this->element('title', null, $title);
- $this->element('link', null, $link);
- if (!is_null($suplink)) {
- # For FriendFeed's SUP protocol
- $this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom',
- 'rel' => 'http://api.friendfeed.com/2008/03#sup',
- 'href' => $suplink,
- 'type' => 'application/json'));
- }
- $this->element('description', null, $subtitle);
- $this->element('language', null, 'en-us');
- $this->element('ttl', null, '40');
-
- if (is_array($notice)) {
- foreach ($notice as $n) {
- $entry = $this->twitter_rss_entry_array($n);
- $this->show_twitter_rss_item($entry);
- }
- } else {
- while ($notice->fetch()) {
- $entry = $this->twitter_rss_entry_array($notice);
- $this->show_twitter_rss_item($entry);
- }
- }
-
- $this->end_twitter_rss();
- }
-
- function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null)
- {
-
- $this->init_document('atom');
-
- $this->element('title', null, $title);
- $this->element('id', null, $id);
- $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
-
- if (!is_null($suplink)) {
- # For FriendFeed's SUP protocol
- $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
- 'href' => $suplink,
- 'type' => 'application/json'));
- }
-
- if (!is_null($selfuri)) {
- $this->element('link', array('href' => $selfuri,
- 'rel' => 'self', 'type' => 'application/atom+xml'), null);
- }
-
- $this->element('updated', null, common_date_iso8601('now'));
- $this->element('subtitle', null, $subtitle);
-
- if (is_array($notice)) {
- foreach ($notice as $n) {
- $this->raw($n->asAtomEntry());
- }
- } else {
- while ($notice->fetch()) {
- $this->raw($notice->asAtomEntry());
- }
- }
-
- $this->end_document('atom');
-
- }
-
- function show_rss_groups($group, $title, $link, $subtitle)
- {
-
- $this->init_document('rss');
-
- $this->element('title', null, $title);
- $this->element('link', null, $link);
- $this->element('description', null, $subtitle);
- $this->element('language', null, 'en-us');
- $this->element('ttl', null, '40');
-
- if (is_array($group)) {
- foreach ($group as $g) {
- $twitter_group = $this->twitter_rss_group_array($g);
- $this->show_twitter_rss_item($twitter_group);
- }
- } else {
- while ($group->fetch()) {
- $twitter_group = $this->twitter_rss_group_array($group);
- $this->show_twitter_rss_item($twitter_group);
- }
- }
-
- $this->end_twitter_rss();
- }
-
- function show_atom_groups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
- {
-
- $this->init_document('atom');
-
- $this->element('title', null, $title);
- $this->element('id', null, $id);
- $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
-
- if (!is_null($selfuri)) {
- $this->element('link', array('href' => $selfuri,
- 'rel' => 'self', 'type' => 'application/atom+xml'), null);
- }
-
- $this->element('updated', null, common_date_iso8601('now'));
- $this->element('subtitle', null, $subtitle);
-
- if (is_array($group)) {
- foreach ($group as $g) {
- $this->raw($g->asAtomEntry());
- }
- } else {
- while ($group->fetch()) {
- $this->raw($group->asAtomEntry());
- }
- }
-
- $this->end_document('atom');
-
- }
-
- function show_json_timeline($notice)
- {
-
- $this->init_document('json');
-
- $statuses = array();
-
- if (is_array($notice)) {
- foreach ($notice as $n) {
- $twitter_status = $this->twitter_status_array($n);
- array_push($statuses, $twitter_status);
- }
- } else {
- while ($notice->fetch()) {
- $twitter_status = $this->twitter_status_array($notice);
- array_push($statuses, $twitter_status);
- }
- }
-
- $this->show_json_objects($statuses);
-
- $this->end_document('json');
- }
-
- function show_json_groups($group)
- {
-
- $this->init_document('json');
-
- $groups = array();
-
- if (is_array($group)) {
- foreach ($group as $g) {
- $twitter_group = $this->twitter_group_array($g);
- array_push($groups, $twitter_group);
- }
- } else {
- while ($group->fetch()) {
- $twitter_group = $this->twitter_group_array($group);
- array_push($groups, $twitter_group);
- }
- }
-
- $this->show_json_objects($groups);
-
- $this->end_document('json');
- }
-
- function show_xml_groups($group)
- {
-
- $this->init_document('xml');
- $this->elementStart('groups', array('type' => 'array'));
-
- if (is_array($group)) {
- foreach ($group as $g) {
- $twitter_group = $this->twitter_group_array($g);
- $this->show_twitter_xml_group($twitter_group);
- }
- } else {
- while ($group->fetch()) {
- $twitter_group = $this->twitter_group_array($group);
- $this->show_twitter_xml_group($twitter_group);
- }
- }
-
- $this->elementEnd('groups');
- $this->end_document('xml');
- }
-
- function show_twitter_xml_users($user)
- {
-
- $this->init_document('xml');
- $this->elementStart('users', array('type' => 'array'));
-
- if (is_array($user)) {
- foreach ($group as $g) {
- $twitter_user = $this->twitter_user_array($g);
- $this->show_twitter_xml_user($twitter_user,'user');
- }
- } else {
- while ($user->fetch()) {
- $twitter_user = $this->twitter_user_array($user);
- $this->show_twitter_xml_user($twitter_user);
- }
- }
-
- $this->elementEnd('users');
- $this->end_document('xml');
- }
-
- function show_json_users($user)
- {
-
- $this->init_document('json');
-
- $users = array();
-
- if (is_array($user)) {
- foreach ($user as $u) {
- $twitter_user = $this->twitter_user_array($u);
- array_push($users, $twitter_user);
- }
- } else {
- while ($user->fetch()) {
- $twitter_user = $this->twitter_user_array($user);
- array_push($users, $twitter_user);
- }
- }
-
- $this->show_json_objects($users);
-
- $this->end_document('json');
- }
-
- function show_single_json_group($group)
- {
- $this->init_document('json');
- $twitter_group = $this->twitter_group_array($group);
- $this->show_json_objects($twitter_group);
- $this->end_document('json');
- }
-
- function show_single_xml_group($group)
- {
- $this->init_document('xml');
- $twitter_group = $this->twitter_group_array($group);
- $this->show_twitter_xml_group($twitter_group);
- $this->end_document('xml');
- }
-
- // Anyone know what date format this is?
- // Twitter's dates look like this: "Mon Jul 14 23:52:38 +0000 2008" -- Zach
- function date_twitter($dt)
- {
- $t = strtotime($dt);
- return date("D M d H:i:s O Y", $t);
- }
-
- // XXX: Candidate for a general utility method somewhere?
- function count_subscriptions($profile)
- {
-
- $count = 0;
- $sub = new Subscription();
- $sub->subscribed = $profile->id;
-
- $count = $sub->find();
-
- if ($count > 0) {
- return $count - 1;
- } else {
- return 0;
- }
- }
-
- function init_document($type='xml')
- {
- switch ($type) {
- case 'xml':
- header('Content-Type: application/xml; charset=utf-8');
- $this->startXML();
- break;
- case 'json':
- header('Content-Type: application/json; charset=utf-8');
-
- // Check for JSONP callback
- $callback = $this->arg('callback');
- if ($callback) {
- print $callback . '(';
- }
- break;
- case 'rss':
- header("Content-Type: application/rss+xml; charset=utf-8");
- $this->init_twitter_rss();
- break;
- case 'atom':
- header('Content-Type: application/atom+xml; charset=utf-8');
- $this->init_twitter_atom();
- break;
- default:
- $this->clientError(_('Not a supported data format.'));
- break;
- }
-
- return;
- }
-
- function end_document($type='xml')
- {
- switch ($type) {
- case 'xml':
- $this->endXML();
- break;
- case 'json':
-
- // Check for JSONP callback
- $callback = $this->arg('callback');
- if ($callback) {
- print ')';
- }
- break;
- case 'rss':
- $this->end_twitter_rss();
- break;
- case 'atom':
- $this->end_twitter_rss();
- break;
- default:
- $this->clientError(_('Not a supported data format.'));
- break;
- }
- return;
- }
-
- function clientError($msg, $code = 400, $content_type = 'json')
- {
- $action = $this->trimmed('action');
-
- common_debug("User error '$code' on '$action': $msg", __FILE__);
-
- if (!array_key_exists($code, ClientErrorAction::$status)) {
- $code = 400;
- }
-
- $status_string = ClientErrorAction::$status[$code];
-
- header('HTTP/1.1 '.$code.' '.$status_string);
-
- if ($content_type == 'xml') {
- $this->init_document('xml');
- $this->elementStart('hash');
- $this->element('error', null, $msg);
- $this->element('request', null, $_SERVER['REQUEST_URI']);
- $this->elementEnd('hash');
- $this->end_document('xml');
- } else {
- $this->init_document('json');
- $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
- print(json_encode($error_array));
- $this->end_document('json');
- }
-
- }
-
- function serverError($msg, $code = 500, $content_type = 'json')
- {
- $action = $this->trimmed('action');
-
- common_debug("Server error '$code' on '$action': $msg", __FILE__);
-
- if (!array_key_exists($code, ServerErrorAction::$status)) {
- $code = 400;
- }
-
- $status_string = ServerErrorAction::$status[$code];
-
- header('HTTP/1.1 '.$code.' '.$status_string);
-
- if ($content_type == 'xml') {
- $this->init_document('xml');
- $this->elementStart('hash');
- $this->element('error', null, $msg);
- $this->element('request', null, $_SERVER['REQUEST_URI']);
- $this->elementEnd('hash');
- $this->end_document('xml');
- } else {
- $this->init_document('json');
- $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
- print(json_encode($error_array));
- $this->end_document('json');
- }
- }
-
- function init_twitter_rss()
- {
- $this->startXML();
- $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
- $this->elementStart('channel');
- Event::handle('StartApiRss', array($this));
- }
-
- function end_twitter_rss()
- {
- $this->elementEnd('channel');
- $this->elementEnd('rss');
- $this->endXML();
- }
-
- function init_twitter_atom()
- {
- $this->startXML();
- // FIXME: don't hardcode the language here!
- $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
- 'xml:lang' => 'en-US',
- 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
- Event::handle('StartApiAtom', array($this));
- }
-
- function end_twitter_atom()
- {
- $this->elementEnd('feed');
- $this->endXML();
- }
-
- function show_profile($profile, $content_type='xml', $notice=null, $includeStatuses=true)
- {
- $profile_array = $this->twitter_user_array($profile, $includeStatuses);
- switch ($content_type) {
- case 'xml':
- $this->show_twitter_xml_user($profile_array);
- break;
- case 'json':
- $this->show_json_objects($profile_array);
- break;
- default:
- $this->clientError(_('Not a supported data format.'));
- return;
- }
- return;
- }
-
- function get_user($id, $apidata=null)
- {
- if (empty($id)) {
-
- // Twitter supports these other ways of passing the user ID
- if (is_numeric($this->arg('id'))) {
- return User::staticGet($this->arg('id'));
- } else if ($this->arg('id')) {
- $nickname = common_canonical_nickname($this->arg('id'));
- return User::staticGet('nickname', $nickname);
- } else if ($this->arg('user_id')) {
- // This is to ensure that a non-numeric user_id still
- // overrides screen_name even if it doesn't get used
- if (is_numeric($this->arg('user_id'))) {
- return User::staticGet('id', $this->arg('user_id'));
- }
- } else if ($this->arg('screen_name')) {
- $nickname = common_canonical_nickname($this->arg('screen_name'));
- return User::staticGet('nickname', $nickname);
- } else {
- // Fall back to trying the currently authenticated user
- return $apidata['user'];
- }
-
- } else if (is_numeric($id)) {
- return User::staticGet($id);
- } else {
- $nickname = common_canonical_nickname($id);
- return User::staticGet('nickname', $nickname);
- }
- }
-
- function get_group($id, $apidata=null)
- {
- if (empty($id)) {
-
- if (is_numeric($this->arg('id'))) {
- return User_group::staticGet($this->arg('id'));
- } else if ($this->arg('id')) {
- $nickname = common_canonical_nickname($this->arg('id'));
- return User_group::staticGet('nickname', $nickname);
- } else if ($this->arg('group_id')) {
- // This is to ensure that a non-numeric user_id still
- // overrides screen_name even if it doesn't get used
- if (is_numeric($this->arg('group_id'))) {
- return User_group::staticGet('id', $this->arg('group_id'));
- }
- } else if ($this->arg('group_name')) {
- $nickname = common_canonical_nickname($this->arg('group_name'));
- return User_group::staticGet('nickname', $nickname);
- }
-
- } else if (is_numeric($id)) {
- return User_group::staticGet($id);
- } else {
- $nickname = common_canonical_nickname($id);
- return User_group::staticGet('nickname', $nickname);
- }
- }
-
- function get_profile($id)
- {
- if (is_numeric($id)) {
- return Profile::staticGet($id);
- } else {
- $user = User::staticGet('nickname', $id);
- if ($user) {
- return $user->getProfile();
- } else {
- return null;
- }
- }
- }
-
- function source_link($source)
- {
- $source_name = _($source);
- switch ($source) {
- case 'web':
- case 'xmpp':
- case 'mail':
- case 'omb':
- case 'api':
- break;
- default:
- $ns = Notice_source::staticGet($source);
- if ($ns) {
- $source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
- }
- break;
- }
- return $source_name;
- }
-
- /**
- * Returns query argument or default value if not found. Certain
- * parameters used throughout the API are lightly scrubbed and
- * bounds checked. This overrides Action::arg().
- *
- * @param string $key requested argument
- * @param string $def default value to return if $key is not provided
- *
- * @return var $var
- */
- function arg($key, $def=null)
- {
-
- // XXX: Do even more input validation/scrubbing?
-
- if (array_key_exists($key, $this->args)) {
- switch($key) {
- case 'page':
- $page = (int)$this->args['page'];
- return ($page < 1) ? 1 : $page;
- case 'count':
- $count = (int)$this->args['count'];
- if ($count < 1) {
- return 20;
- } elseif ($count > 200) {
- return 200;
- } else {
- return $count;
- }
- case 'since_id':
- $since_id = (int)$this->args['since_id'];
- return ($since_id < 1) ? 0 : $since_id;
- case 'max_id':
- $max_id = (int)$this->args['max_id'];
- return ($max_id < 1) ? 0 : $max_id;
- case 'since':
- return strtotime($this->args['since']);
- default:
- return parent::arg($key, $def);
- }
- } else {
- return $def;
- }
- }
-
-}
*
* @category Integration
* @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
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mEDI <medi@milaro.net>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @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/
*
*/
// FIXME: this code should be abstracted to a neutral third
// party, like Notice::asJson(). I'm not sure of the ethics
// of refactoring from within a plugin, so I'm just abusing
- // the TwitterApiAction method. Don't do this unless you're me!
+ // the ApiAction method. Don't do this unless you're me!
- require_once(INSTALLDIR.'/lib/twitterapi.php');
+ require_once(INSTALLDIR.'/lib/api.php');
- $act = new TwitterApiAction('/dev/null');
+ $act = new ApiAction('/dev/null');
- $arr = $act->twitter_status_array($notice, true);
+ $arr = $act->twitterStatusArray($notice, true);
$arr['url'] = $notice->bestUrl();
$arr['html'] = htmlspecialchars($notice->rendered);
$arr['source'] = htmlspecialchars($arr['source']);
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin that requires the user to have a validated email address before they can post notices
+ *
+ * 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 Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @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') && !defined('LACONICA')) {
+ exit(1);
+}
+
+class RequireValidatedEmailPlugin extends Plugin
+{
+ function __construct()
+ {
+ parent::__construct();
+ }
+
+ function onStartNoticeSave($notice)
+ {
+ $user = User::staticGet('id', $notice->profile_id);
+ if (!empty($user)) { // it's a remote notice
+ if (empty($user->email)) {
+ throw new ClientException(_("You must validate your email address before posting."));
+ }
+ }
+ return true;
+ }
+}
+
color:#000000;
}
#form_notice label[for=notice_data-attach] {
-background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+background:transparent url(../../base/images/icons/icons-01.png) no-repeat 0 -328px;
}
#form_notice #notice_data-attach {
opacity:0;
#export_data li a {
background-repeat:no-repeat;
-background-position:0 45%;
}
#export_data li a.rss {
-background-image:url(../../base/images/icons/icon_rss.png);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position:0 -130px;
}
#export_data li a.atom {
-background-image:url(../../base/images/icons/icon_atom.png);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position:0 -64px;
}
#export_data li a.foaf {
-background-image:url(../../base/images/icons/icon_foaf.gif);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position:0 1px;
}
.entity_edit a,
.form_group_unblock input.submit,
.entity_nudge p,
.form_make_admin input.submit {
-background-position: 0 40%;
background-repeat: no-repeat;
background-color:transparent;
}
}
.entity_edit a {
-background-image:url(../../base/images/icons/twotone/green/edit.gif);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position: 0 -718px;
}
.entity_send-a-message a {
-background-image:url(../../base/images/icons/twotone/green/quote.gif);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position: 0 -849px;
}
.entity_nudge p,
.form_user_nudge input.submit {
-background-image:url(../../base/images/icons/twotone/green/mail.gif);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position: 0 -785px;
}
.form_user_block input.submit,
.form_user_unblock input.submit,
.form_group_block input.submit,
.form_group_unblock input.submit {
-background-image:url(../../base/images/icons/twotone/green/shield.gif);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position: 0 -918px;
}
.form_make_admin input.submit {
-background-image:url(../../base/images/icons/twotone/green/admin.gif);
+background-image:url(../../base/images/icons/icons-01.png);
+background-position: 0 -983px;
}
/* NOTICES */
.notice .attachment {
-background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%;
+background:transparent url(../../base/images/icons/icons-01.png) no-repeat 0 -394px;
}
#attachments .attachment {
background:none;
}
.notice-options .notice_reply {
-background:transparent url(../../base/images/icons/twotone/green/reply.gif) no-repeat 0 45%;
+background:transparent url(../../base/images/icons/icons-01.png) no-repeat 0 -589px;
}
.notice-options form.form_favor input.submit {
-background:transparent url(../../base/images/icons/twotone/green/favourite.gif) no-repeat 0 45%;
+background:transparent url(../../base/images/icons/icons-01.png) no-repeat 0 -457px;
}
.notice-options form.form_disfavor input.submit {
-background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%;
+background:transparent url(../../base/images/icons/icons-01.png) no-repeat 0 -523px;
}
.notice-options .notice_delete {
-background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%;
+background:transparent url(../../base/images/icons/icons-01.png) no-repeat 0 -655px;
}
.notices div.entry-content,
/*END: NOTICES */
#new_group a {
-background:transparent url(../../base/images/icons/twotone/green/news.gif) no-repeat 0 45%;
+background:transparent url(../../base/images/icons/icons-01.png) no-repeat 0 -1054px;
}
.pagination .nav_prev a,
border-color:#C8D1D5;
}
.pagination .nav_prev a {
-background-image:url(../../base/images/icons/twotone/green/arrow-left.gif);
-background-position:10% 45%;
+background-image:url(../../base/images/icons/icons-01.png);
+background-position:10% -187px;
}
.pagination .nav_next a {
-background-image:url(../../base/images/icons/twotone/green/arrow-right.gif);
-background-position:90% 45%;
+background-image:url(../../base/images/icons/icons-01.png);
+background-position:105% -252px;
}