2010-02-01 05:16:59 +09:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* StatusNet - the distributed open-source microblogging tool
|
|
|
|
* Copyright (C) 2008-2010, StatusNet, Inc.
|
|
|
|
*
|
|
|
|
* Really Simple Discovery (RSD) for API access
|
|
|
|
*
|
|
|
|
* PHP version 5
|
|
|
|
*
|
|
|
|
* 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>
|
|
|
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
|
|
|
* @link http://status.net/
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!defined('STATUSNET')) {
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* RSD action class
|
|
|
|
*
|
|
|
|
* Really Simple Discovery (RSD) is a simple (to a fault, maybe)
|
|
|
|
* discovery tool for blog APIs.
|
|
|
|
*
|
|
|
|
* http://tales.phrasewise.com/rfc/rsd
|
|
|
|
*
|
|
|
|
* Anil Dash suggested that RSD be used for services that implement
|
|
|
|
* the Twitter API:
|
|
|
|
*
|
|
|
|
* http://dashes.com/anil/2009/12/the-twitter-api-is-finished.html
|
|
|
|
*
|
|
|
|
* It's in use now for WordPress.com blogs:
|
|
|
|
*
|
|
|
|
* http://matt.wordpress.com/xmlrpc.php?rsd
|
|
|
|
*
|
|
|
|
* I (evan@status.net) have tried to stay faithful to the premise of
|
|
|
|
* RSD, while adding information useful to StatusNet client developers.
|
|
|
|
* In particular:
|
|
|
|
*
|
|
|
|
* - There is a link from each user's profile page to their personal
|
|
|
|
* RSD feed. A personal rsd.xml includes a 'blogID' element that is
|
|
|
|
* their username.
|
|
|
|
* - There is a link from the public root to '/rsd.xml', a public RSD
|
|
|
|
* feed. It's identical to the personal rsd except it doesn't include
|
|
|
|
* a blogId.
|
|
|
|
* - I've added a setting to the API to indicate that OAuth support is
|
|
|
|
* available.
|
|
|
|
*
|
|
|
|
* @category API
|
|
|
|
* @package StatusNet
|
|
|
|
* @author Evan Prodromou <evan@status.net>
|
|
|
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
|
|
|
* @link http://status.net/
|
|
|
|
*/
|
|
|
|
|
|
|
|
class RsdAction extends Action
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Optional attribute for the personal rsd.xml file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var $user = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare the action for use.
|
|
|
|
*
|
|
|
|
* Check for a nickname; redirect if non-canonical; if
|
|
|
|
* not provided, assume public rsd.xml.
|
|
|
|
*
|
|
|
|
* @param array $args GET, POST, and URI arguments.
|
|
|
|
*
|
|
|
|
* @return boolean success flag
|
|
|
|
*/
|
|
|
|
|
|
|
|
function prepare($args)
|
|
|
|
{
|
|
|
|
parent::prepare($args);
|
|
|
|
|
|
|
|
// optional argument
|
|
|
|
|
|
|
|
$nickname_arg = $this->arg('nickname');
|
|
|
|
|
|
|
|
if (empty($nickname_arg)) {
|
|
|
|
$this->user = null;
|
|
|
|
} else {
|
|
|
|
$nickname = common_canonical_nickname($nickname_arg);
|
|
|
|
|
|
|
|
// Permanent redirect on non-canonical nickname
|
|
|
|
|
|
|
|
if ($nickname_arg != $nickname) {
|
|
|
|
common_redirect(common_local_url('rsd',
|
|
|
|
array('nickname' => $nickname)),
|
|
|
|
301);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->user = User::staticGet('nickname', $nickname);
|
|
|
|
|
|
|
|
if (empty($this->user)) {
|
|
|
|
$this->clientError(_('No such user.'), 404);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Action handler.
|
|
|
|
*
|
|
|
|
* Outputs the XML format for an RSD file. May include
|
|
|
|
* personal information if this is a personal file
|
|
|
|
* (based on whether $user attribute is set).
|
|
|
|
*
|
|
|
|
* @param array $args array of arguments
|
|
|
|
*
|
|
|
|
* @return nothing
|
|
|
|
*/
|
|
|
|
|
|
|
|
function handle($args)
|
|
|
|
{
|
|
|
|
header('Content-Type: application/rsd+xml');
|
|
|
|
|
|
|
|
$this->startXML();
|
|
|
|
|
|
|
|
$rsdNS = 'http://archipelago.phrasewise.com/rsd';
|
|
|
|
$this->elementStart('rsd', array('version' => '1.0',
|
|
|
|
'xmlns' => $rsdNS));
|
|
|
|
$this->elementStart('service');
|
|
|
|
$this->element('engineName', null, _('StatusNet'));
|
|
|
|
$this->element('engineLink', null, 'http://status.net/');
|
|
|
|
$this->elementStart('apis');
|
|
|
|
if (Event::handle('StartRsdListApis', array($this, $this->user))) {
|
|
|
|
|
|
|
|
$blogID = (empty($this->user)) ? '' : $this->user->nickname;
|
|
|
|
$apiAttrs = array('name' => 'Twitter',
|
|
|
|
'preferred' => 'true',
|
|
|
|
'apiLink' => $this->_apiRoot(),
|
|
|
|
'blogID' => $blogID);
|
|
|
|
|
|
|
|
$this->elementStart('api', $apiAttrs);
|
|
|
|
$this->elementStart('settings');
|
|
|
|
$this->element('docs', null,
|
|
|
|
'http://status.net/wiki/TwitterCompatibleAPI');
|
|
|
|
$this->element('setting', array('name' => 'OAuth'),
|
|
|
|
'true');
|
|
|
|
$this->elementEnd('settings');
|
|
|
|
$this->elementEnd('api');
|
2010-10-25 12:43:26 +09:00
|
|
|
|
|
|
|
// Atom API
|
|
|
|
|
|
|
|
if (empty($this->user)) {
|
|
|
|
$service = common_local_url('ApiAtomService');
|
|
|
|
} else {
|
|
|
|
$service = common_local_url('ApiAtomService', array('id' => $this->user->nickname));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->element('api', array('name' => 'Atom',
|
|
|
|
'preferred' => 'false',
|
|
|
|
'apiLink' => $service,
|
|
|
|
'blogID' => $blogID));
|
|
|
|
|
2010-02-01 05:16:59 +09:00
|
|
|
Event::handle('EndRsdListApis', array($this, $this->user));
|
|
|
|
}
|
|
|
|
$this->elementEnd('apis');
|
|
|
|
$this->elementEnd('service');
|
|
|
|
$this->elementEnd('rsd');
|
|
|
|
|
|
|
|
$this->endXML();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns last-modified date for use in caching
|
|
|
|
*
|
|
|
|
* Per-user rsd.xml is dated to last change of user
|
|
|
|
* (in case of nickname change); public has no date.
|
|
|
|
*
|
|
|
|
* @return string date of last change of this page
|
|
|
|
*/
|
|
|
|
|
|
|
|
function lastModified()
|
|
|
|
{
|
|
|
|
if (!empty($this->user)) {
|
|
|
|
return $this->user->modified;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag to indicate if this action is read-only
|
|
|
|
*
|
|
|
|
* It is; it doesn't change the DB.
|
|
|
|
*
|
|
|
|
* @param array $args ignored
|
|
|
|
*
|
|
|
|
* @return boolean true
|
|
|
|
*/
|
|
|
|
|
|
|
|
function isReadOnly($args)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return current site's API root
|
|
|
|
*
|
|
|
|
* Varies based on URL parameters, like if fancy URLs are
|
|
|
|
* turned on.
|
|
|
|
*
|
|
|
|
* @return string API root URI for this site
|
|
|
|
*/
|
|
|
|
|
|
|
|
private function _apiRoot()
|
|
|
|
{
|
|
|
|
if (common_config('site', 'fancy')) {
|
|
|
|
return common_path('api/', true);
|
|
|
|
} else {
|
|
|
|
return common_path('index.php/api/', true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|