3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2008-2010, StatusNet, Inc.
6 * Really Simple Discovery (RSD) for API access
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * @author Evan Prodromou <evan@status.net>
26 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
27 * @link http://status.net/
31 if (!defined('STATUSNET')) {
38 * Really Simple Discovery (RSD) is a simple (to a fault, maybe)
39 * discovery tool for blog APIs.
41 * http://tales.phrasewise.com/rfc/rsd
43 * Anil Dash suggested that RSD be used for services that implement
46 * http://dashes.com/anil/2009/12/the-twitter-api-is-finished.html
48 * It's in use now for WordPress.com blogs:
50 * http://matt.wordpress.com/xmlrpc.php?rsd
52 * I (evan@status.net) have tried to stay faithful to the premise of
53 * RSD, while adding information useful to StatusNet client developers.
56 * - There is a link from each user's profile page to their personal
57 * RSD feed. A personal rsd.xml includes a 'blogID' element that is
59 * - There is a link from the public root to '/rsd.xml', a public RSD
60 * feed. It's identical to the personal rsd except it doesn't include
62 * - I've added a setting to the API to indicate that OAuth support is
67 * @author Evan Prodromou <evan@status.net>
68 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
69 * @link http://status.net/
71 class RsdAction extends Action
74 * Optional attribute for the personal rsd.xml file.
79 * Prepare the action for use.
81 * Check for a nickname; redirect if non-canonical; if
82 * not provided, assume public rsd.xml.
84 * @param array $args GET, POST, and URI arguments.
86 * @return boolean success flag
88 function prepare($args)
90 parent::prepare($args);
94 $nickname_arg = $this->arg('nickname');
96 if (empty($nickname_arg)) {
99 $nickname = common_canonical_nickname($nickname_arg);
101 // Permanent redirect on non-canonical nickname
103 if ($nickname_arg != $nickname) {
104 common_redirect(common_local_url('rsd', array('nickname' => $nickname)), 301);
107 $this->user = User::getKV('nickname', $nickname);
109 if (empty($this->user)) {
110 // TRANS: Client error.
111 $this->clientError(_('No such user.'), 404);
121 * Outputs the XML format for an RSD file. May include
122 * personal information if this is a personal file
123 * (based on whether $user attribute is set).
125 * @param array $args array of arguments
129 function handle($args)
131 header('Content-Type: application/rsd+xml');
135 $rsdNS = 'http://archipelago.phrasewise.com/rsd';
136 $this->elementStart('rsd', array('version' => '1.0',
138 $this->elementStart('service');
139 // TRANS: Engine name for RSD.
140 $this->element('engineName', null, _('StatusNet'));
141 $this->element('engineLink', null, 'http://status.net/');
142 $this->elementStart('apis');
143 if (Event::handle('StartRsdListApis', array($this, $this->user))) {
145 $blogID = (empty($this->user)) ? '' : $this->user->nickname;
146 $apiAttrs = array('name' => 'Twitter',
147 'preferred' => 'true',
148 'apiLink' => $this->_apiRoot(),
149 'blogID' => $blogID);
151 $this->elementStart('api', $apiAttrs);
152 $this->elementStart('settings');
153 $this->element('docs', null,
154 'http://status.net/wiki/TwitterCompatibleAPI');
155 $this->element('setting', array('name' => 'OAuth'),
157 $this->elementEnd('settings');
158 $this->elementEnd('api');
162 if (empty($this->user)) {
163 $service = common_local_url('ApiAtomService');
165 $service = common_local_url('ApiAtomService', array('id' => $this->user->nickname));
168 $this->element('api', array('name' => 'Atom',
169 'preferred' => 'false',
170 'apiLink' => $service,
171 'blogID' => $blogID));
173 Event::handle('EndRsdListApis', array($this, $this->user));
175 $this->elementEnd('apis');
176 $this->elementEnd('service');
177 $this->elementEnd('rsd');
185 * Returns last-modified date for use in caching
187 * Per-user rsd.xml is dated to last change of user
188 * (in case of nickname change); public has no date.
190 * @return string date of last change of this page
192 function lastModified()
194 if (!empty($this->user)) {
195 return $this->user->modified;
202 * Flag to indicate if this action is read-only
204 * It is; it doesn't change the DB.
206 * @param array $args ignored
208 * @return boolean true
210 function isReadOnly($args)
216 * Return current site's API root
218 * Varies based on URL parameters, like if fancy URLs are
221 * @return string API root URI for this site
223 private function _apiRoot()
225 if (common_config('site', 'fancy')) {
226 return common_path('api/', true);
228 return common_path('index.php/api/', true);