Merge branch 'testing'

This commit is contained in:
Brion Vibber 2010-03-04 07:00:45 -08:00
commit 62d5f1addb
43 changed files with 1326 additions and 348 deletions

View File

@ -83,6 +83,7 @@ class AllrssAction extends Rss10Action
function getNotices($limit=0)
{
$cur = common_current_user();
$user = $this->user;
if (!empty($cur) && $cur->id == $user->id) {
$notice = $this->user->noticeInbox(0, $limit);
@ -90,7 +91,6 @@ class AllrssAction extends Rss10Action
$notice = $this->user->noticesWithFriends(0, $limit);
}
$user = $this->user;
$notice = $user->noticesWithFriends(0, $limit);
$notices = array();

View File

@ -104,30 +104,21 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
function showTimeline()
{
$sitename = common_config('site', 'name');
$avatar = $this->group->homepage_logo;
$title = sprintf(_("%s timeline"), $this->group->nickname);
$subtitle = sprintf(
_('Updates from %1$s on %2$s!'),
$this->group->nickname,
$sitename
);
$logo = ($avatar) ? $avatar : User_group::defaultLogo(AVATAR_PROFILE_SIZE);
// We'll pull common formatting out of this for other formats
$atom = new AtomGroupNoticeFeed($this->group);
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->showRssTimeline(
$this->notices,
$title,
$atom->title,
$this->group->homeUrl(),
$subtitle,
$atom->subtitle,
null,
$logo
$atom->logo
);
break;
case 'atom':
@ -136,38 +127,22 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
try {
$atom = new AtomGroupNoticeFeed($this->group);
// @todo set all this Atom junk up inside the feed class
#$atom->setId($id);
$atom->setTitle($title);
$atom->setSubtitle($subtitle);
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addAuthorRaw($this->group->asAtomAuthor());
$atom->setActivitySubject($this->group->asActivitySubject());
$atom->addLink($this->group->homeUrl());
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$self = $this->getSelfUri('ApiTimelineGroup', $aargs);
$atom->setId($this->getSelfUri('ApiTimelineGroup', $aargs));
$atom->addLink(
$this->getSelfUri('ApiTimelineGroup', $aargs),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->setId($self);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
//$this->raw($atom->getString());
print $atom->getString(); // temp hack until PuSH feeds are redone cleanly
$this->raw($atom->getString());
} catch (Atom10FeedException $e) {
$this->serverError(

View File

@ -112,19 +112,17 @@ class ApiTimelineUserAction extends ApiBareAuthAction
function showTimeline()
{
$profile = $this->user->getProfile();
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
$sitename = common_config('site', 'name');
$title = sprintf(_("%s timeline"), $this->user->nickname);
// We'll use the shared params from the Atom stub
// for other feed types.
$atom = new AtomUserNoticeFeed($this->user);
$title = $atom->title;
$link = common_local_url(
'showstream',
array('nickname' => $this->user->nickname)
);
$subtitle = sprintf(
_('Updates from %1$s on %2$s!'),
$this->user->nickname, $sitename
);
$logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
$subtitle = $atom->subtitle;
$logo = $atom->logo;
// FriendFeed's SUP protocol
// Also added RSS and Atom feeds
@ -146,47 +144,18 @@ class ApiTimelineUserAction extends ApiBareAuthAction
header('Content-Type: application/atom+xml; charset=utf-8');
// @todo set all this Atom junk up inside the feed class
$atom = new AtomUserNoticeFeed($this->user);
$atom->setTitle($title);
$atom->setSubtitle($subtitle);
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink(
common_local_url(
'showstream',
array('nickname' => $this->user->nickname)
)
);
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$atom->setId($this->getSelfUri('ApiTimelineUser', $aargs));
$atom->addLink(
$this->getSelfUri('ApiTimelineUser', $aargs),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->addLink(
$suplink,
array(
'rel' => 'http://api.friendfeed.com/2008/03#sup',
'type' => 'application/json'
)
);
$self = $this->getSelfUri('ApiTimelineUser', $aargs);
$atom->setId($self);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
#$this->raw($atom->getString());
print $atom->getString(); // temporary for output buffering
$this->raw($atom->getString());
break;
case 'json':

99
actions/grantrole.php Normal file
View File

@ -0,0 +1,99 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action class to sandbox an abusive 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 Action
* @package StatusNet
* @author Evan Prodromou <evan@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);
}
/**
* Sandbox a user.
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class GrantRoleAction extends ProfileFormAction
{
/**
* Check parameters
*
* @param array $args action arguments (URL, GET, POST)
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
return false;
}
$this->role = $this->arg('role');
if (!Profile_role::isValid($this->role)) {
$this->clientError(_("Invalid role."));
return false;
}
if (!Profile_role::isSettable($this->role)) {
$this->clientError(_("This role is reserved and cannot be set."));
return false;
}
$cur = common_current_user();
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::GRANTROLE)) {
$this->clientError(_("You cannot grant user roles on this site."));
return false;
}
assert(!empty($this->profile)); // checked by parent
if ($this->profile->hasRole($this->role)) {
$this->clientError(_("User already has this role."));
return false;
}
return true;
}
/**
* Sandbox a user.
*
* @return void
*/
function handlePost()
{
$this->profile->grantRole($this->role);
}
}

99
actions/revokerole.php Normal file
View File

@ -0,0 +1,99 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action class to sandbox an abusive 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 Action
* @package StatusNet
* @author Evan Prodromou <evan@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);
}
/**
* Sandbox a user.
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class RevokeRoleAction extends ProfileFormAction
{
/**
* Check parameters
*
* @param array $args action arguments (URL, GET, POST)
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
return false;
}
$this->role = $this->arg('role');
if (!Profile_role::isValid($this->role)) {
$this->clientError(_("Invalid role."));
return false;
}
if (!Profile_role::isSettable($this->role)) {
$this->clientError(_("This role is reserved and cannot be set."));
return false;
}
$cur = common_current_user();
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::REVOKEROLE)) {
$this->clientError(_("You cannot revoke user roles on this site."));
return false;
}
assert(!empty($this->profile)); // checked by parent
if (!$this->profile->hasRole($this->role)) {
$this->clientError(_("User doesn't have this role."));
return false;
}
return true;
}
/**
* Sandbox a user.
*
* @return void
*/
function handlePost()
{
$this->profile->revokeRole($this->role);
}
}

View File

@ -66,7 +66,7 @@ class SiteadminpanelAction extends AdminPanelAction
function getInstructions()
{
return _('Basic settings for this StatusNet site.');
return _('Basic settings for this StatusNet site');
}
/**
@ -90,10 +90,11 @@ class SiteadminpanelAction extends AdminPanelAction
function saveSettings()
{
static $settings = array('site' => array('name', 'broughtby', 'broughtbyurl',
'email', 'timezone', 'language',
'site', 'textlimit', 'dupelimit'),
'snapshot' => array('run', 'reporturl', 'frequency'));
static $settings = array(
'site' => array('name', 'broughtby', 'broughtbyurl',
'email', 'timezone', 'language',
'site', 'textlimit', 'dupelimit'),
);
$values = array();
@ -158,25 +159,6 @@ class SiteadminpanelAction extends AdminPanelAction
$this->clientError(sprintf(_('Unknown language "%s".'), $values['site']['language']));
}
// Validate report URL
if (!is_null($values['snapshot']['reporturl']) &&
!Validate::uri($values['snapshot']['reporturl'], array('allowed_schemes' => array('http', 'https')))) {
$this->clientError(_("Invalid snapshot report URL."));
}
// Validate snapshot run value
if (!in_array($values['snapshot']['run'], array('web', 'cron', 'never'))) {
$this->clientError(_("Invalid snapshot run value."));
}
// Validate snapshot run value
if (!Validate::number($values['snapshot']['frequency'])) {
$this->clientError(_("Snapshot frequency must be a number."));
}
// Validate text limit
if (!Validate::number($values['site']['textlimit'], array('min' => 140))) {
@ -285,32 +267,6 @@ class SiteAdminPanelForm extends AdminForm
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_admin_snapshots'));
$this->out->element('legend', null, _('Snapshots'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$snapshot = array('web' => _('Randomly during Web hit'),
'cron' => _('In a scheduled job'),
'never' => _('Never'));
$this->out->dropdown('run', _('Data snapshots'),
$snapshot, _('When to send statistical data to status.net servers'),
false, $this->value('run', 'snapshot'));
$this->unli();
$this->li();
$this->input('frequency', _('Frequency'),
_('Snapshots will be sent once every N web hits'),
'snapshot');
$this->unli();
$this->li();
$this->input('reporturl', _('Report URL'),
_('Snapshots will be sent to this URL'),
'snapshot');
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_admin_limits'));
$this->out->element('legend', null, _('Limits'));
$this->out->elementStart('ul', 'form_data');

View File

@ -99,7 +99,7 @@ class SitenoticeadminpanelAction extends AdminPanelAction
$result = Config::save('site', 'notice', $siteNotice);
if (!result) {
if (!$result) {
$this->ServerError(_("Unable to save site notice."));
}
}

View File

@ -0,0 +1,251 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Snapshots administration panel
*
* 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 Settings
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 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);
}
/**
* Manage snapshots
*
* @category Admin
* @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 SnapshotadminpanelAction extends AdminPanelAction
{
/**
* Returns the page title
*
* @return string page title
*/
function title()
{
return _('Snapshots');
}
/**
* Instructions for using this form.
*
* @return string instructions
*/
function getInstructions()
{
return _('Manage snapshot configuration');
}
/**
* Show the snapshots admin panel form
*
* @return void
*/
function showForm()
{
$form = new SnapshotAdminPanelForm($this);
$form->show();
return;
}
/**
* Save settings from the form
*
* @return void
*/
function saveSettings()
{
static $settings = array(
'snapshot' => array('run', 'reporturl', 'frequency')
);
$values = array();
foreach ($settings as $section => $parts) {
foreach ($parts as $setting) {
$values[$section][$setting] = $this->trimmed($setting);
}
}
// This throws an exception on validation errors
$this->validate($values);
// assert(all values are valid);
$config = new Config();
$config->query('BEGIN');
foreach ($settings as $section => $parts) {
foreach ($parts as $setting) {
Config::save($section, $setting, $values[$section][$setting]);
}
}
$config->query('COMMIT');
return;
}
function validate(&$values)
{
// Validate snapshot run value
if (!in_array($values['snapshot']['run'], array('web', 'cron', 'never'))) {
$this->clientError(_("Invalid snapshot run value."));
}
// Validate snapshot frequency value
if (!Validate::number($values['snapshot']['frequency'])) {
$this->clientError(_("Snapshot frequency must be a number."));
}
// Validate report URL
if (!is_null($values['snapshot']['reporturl'])
&& !Validate::uri(
$values['snapshot']['reporturl'],
array('allowed_schemes' => array('http', 'https')
)
)) {
$this->clientError(_("Invalid snapshot report URL."));
}
}
}
class SnapshotAdminPanelForm extends AdminForm
{
/**
* ID of the form
*
* @return int ID of the form
*/
function id()
{
return 'form_snapshot_admin_panel';
}
/**
* class of the form
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
return common_local_url('snapshotadminpanel');
}
/**
* Data elements of the form
*
* @return void
*/
function formData()
{
$this->out->elementStart(
'fieldset',
array('id' => 'settings_admin_snapshots')
);
$this->out->element('legend', null, _('Snapshots'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$snapshot = array(
'web' => _('Randomly during Web hit'),
'cron' => _('In a scheduled job'),
'never' => _('Never')
);
$this->out->dropdown(
'run',
_('Data snapshots'),
$snapshot,
_('When to send statistical data to status.net servers'),
false,
$this->value('run', 'snapshot')
);
$this->unli();
$this->li();
$this->input(
'frequency',
_('Frequency'),
_('Snapshots will be sent once every N web hits'),
'snapshot'
);
$this->unli();
$this->li();
$this->input(
'reporturl',
_('Report URL'),
_('Snapshots will be sent to this URL'),
'snapshot'
);
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
$this->out->submit(
'submit',
_('Save'),
'submit',
null,
_('Save snapshot settings')
);
}
}

View File

@ -1106,7 +1106,7 @@ class Notice extends Memcached_DataObject
return $groups;
}
function asAtomEntry($namespace=false, $source=false)
function asAtomEntry($namespace=false, $source=false, $author=true)
{
$profile = $this->getProfile();
@ -1151,8 +1151,10 @@ class Notice extends Memcached_DataObject
$xs->element('title', null, $this->content);
$xs->raw($profile->asAtomAuthor());
$xs->raw($profile->asActivityActor());
if ($author) {
$xs->raw($profile->asAtomAuthor());
$xs->raw($profile->asActivityActor());
}
$xs->element('link', array('rel' => 'alternate',
'type' => 'text/html',

View File

@ -743,6 +743,10 @@ class Profile extends Memcached_DataObject
case Right::CONFIGURESITE:
$result = $this->hasRole(Profile_role::ADMINISTRATOR);
break;
case Right::GRANTROLE:
case Right::REVOKEROLE:
$result = $this->hasRole(Profile_role::OWNER);
break;
case Right::NEWNOTICE:
case Right::NEWMESSAGE:
case Right::SUBSCRIBE:

View File

@ -53,4 +53,21 @@ class Profile_role extends Memcached_DataObject
const ADMINISTRATOR = 'administrator';
const SANDBOXED = 'sandboxed';
const SILENCED = 'silenced';
public static function isValid($role)
{
// @fixme could probably pull this from class constants
$known = array(self::OWNER,
self::MODERATOR,
self::ADMINISTRATOR,
self::SANDBOXED,
self::SILENCED);
return in_array($role, $known);
}
public static function isSettable($role)
{
$allowedRoles = array('administrator', 'moderator');
return self::isValid($role) && in_array($role, $allowedRoles);
}
}

View File

@ -31,6 +31,7 @@
* @author Robin Millette <millette@controlyourself.ca>
* @author Sarven Capadisli <csarven@status.net>
* @author Tom Adams <tom@holizz.com>
* @author Zach Copley <zach@status.net>
* @license GNU Affero General Public License http://www.gnu.org/licenses/
* @version 0.9.x
* @link http://status.net
@ -490,15 +491,25 @@ function showForm()
<p class="form_guide">Database name</p>
</li>
<li>
<label for="username">Username</label>
<label for="username">DB username</label>
<input type="text" id="username" name="username" />
<p class="form_guide">Database username</p>
</li>
<li>
<label for="password">Password</label>
<label for="password">DB password</label>
<input type="password" id="password" name="password" />
<p class="form_guide">Database password (optional)</p>
</li>
<li>
<label for="admin_nickname">Administrator nickname</label>
<input type="text" id="admin_nickname" name="admin_nickname" />
<p class="form_guide">Nickname for the initial StatusNet user (administrator)</p>
</li>
<li>
<label for="initial_user_password">Administrator password</label>
<input type="password" id="admin_password" name="admin_password" />
<p class="form_guide">Password for the initial StatusNet user (administrator)</p>
</li>
</ul>
<input type="submit" name="submit" class="submit" value="Submit" />
</fieldset>
@ -521,6 +532,10 @@ function handlePost()
$password = $_POST['password'];
$sitename = $_POST['sitename'];
$fancy = !empty($_POST['fancy']);
$adminNick = $_POST['admin_nickname'];
$adminPass = $_POST['admin_password'];
$server = $_SERVER['HTTP_HOST'];
$path = substr(dirname($_SERVER['PHP_SELF']), 1);
@ -552,6 +567,16 @@ STR;
$fail = true;
}
if (empty($adminNick)) {
updateStatus("No initial StatusNet user nickname specified.", true);
$fail = true;
}
if (empty($adminPass)) {
updateStatus("No initial StatusNet user password specified.", true);
$fail = true;
}
if ($fail) {
showForm();
return;
@ -574,13 +599,29 @@ STR;
return;
}
// Okay, cross fingers and try to register an initial user
if (registerInitialUser($adminNick, $adminPass)) {
updateStatus(
"An initial user with the administrator role has been created."
);
} else {
updateStatus(
"Could not create initial StatusNet user (administrator).",
true
);
showForm();
return;
}
/*
TODO https needs to be considered
*/
$link = "http://".$server.'/'.$path;
updateStatus("StatusNet has been installed at $link");
updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
updateStatus(
"You can visit your <a href='$link'>new StatusNet site</a> (login as '$adminNick')."
);
}
function Pgsql_Db_installer($host, $database, $username, $password)
@ -756,6 +797,33 @@ function runDbScript($filename, $conn, $type = 'mysqli')
return true;
}
function registerInitialUser($nickname, $password)
{
define('STATUSNET', true);
define('LACONICA', true); // compatibility
require_once INSTALLDIR . '/lib/common.php';
$user = User::register(
array('nickname' => $nickname,
'password' => $password,
'fullname' => $nickname
)
);
if (empty($user)) {
return false;
}
// give initial user carte blanche
$user->grantRole('owner');
$user->grantRole('moderator');
$user->grantRole('administrator');
return true;
}
?>
<?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
<!DOCTYPE html
@ -765,10 +833,10 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<head>
<title>Install StatusNet</title>
<link rel="shortcut icon" href="favicon.ico"/>
<link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
<!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
<!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
<!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
<link rel="stylesheet" type="text/css" href="theme/default/css/display.css" media="screen, projection, tv"/>
<!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css" /><![endif]-->
<!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css" /><![endif]-->
<!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css" /><![endif]-->
<script src="js/jquery.min.js"></script>
<script src="js/install.js"></script>
</head>
@ -784,8 +852,10 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
</div>
<div id="core">
<div id="content">
<h1>Install StatusNet</h1>
<div id="content_inner">
<h1>Install StatusNet</h1>
<?php main(); ?>
</div>
</div>
</div>
</div>

View File

@ -1064,6 +1064,18 @@ class Activity
}
$this->entry = $entry;
// @fixme Don't send in a DOMDocument
if ($feed instanceof DOMDocument) {
common_log(
LOG_WARNING,
'Activity::__construct() - '
. 'DOMDocument passed in for feed by mistake. '
. "Expecting a 'feed' DOMElement."
);
$feed = $feed->getElementsByTagName('feed')->item(0);
}
$this->feed = $feed;
$pubEl = $this->_child($entry, self::PUBLISHED, self::ATOM);

View File

@ -381,6 +381,11 @@ class AdminPanelNav extends Widget
_('Edit site notice'), $action_name == 'sitenoticeadminpanel', 'nav_sitenotice_admin_panel');
}
if (AdminPanelAction::canAdmin('snapshot')) {
$this->out->menuItem(common_local_url('snapshotadminpanel'), _('Snapshots'),
_('Snapshots configuration'), $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel');
}
Event::handle('EndAdminPanelNav', array($this));
}
$this->action->elementEnd('ul');

View File

@ -1,105 +0,0 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Class for building / manipulating an Atom entry in memory
*
* 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 Feed
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 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);
}
class Atom10EntryException extends Exception
{
}
/**
* Class for manipulating an Atom entry in memory. Get the entry as an XML
* string with Atom10Entry::getString().
*
* @category Feed
* @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 Atom10Entry extends XMLStringer
{
private $namespaces;
private $categories;
private $content;
private $contributors;
private $id;
private $links;
private $published;
private $rights;
private $source;
private $summary;
private $title;
function __construct($indent = true) {
parent::__construct($indent);
$this->namespaces = array();
}
function addNamespace($namespace, $uri)
{
$ns = array($namespace => $uri);
$this->namespaces = array_merge($this->namespaces, $ns);
}
function initEntry()
{
}
function endEntry()
{
}
/**
* Check that all required elements have been set, etc.
* Throws an Atom10EntryException if something's missing.
*
* @return void
*/
function validate()
{
}
function getString()
{
$this->validate();
$this->initEntry();
$this->renderEntries();
$this->endEntry();
return $this->xw->outputMemory();
}
}

View File

@ -49,6 +49,8 @@ class Atom10FeedException extends Exception
class Atom10Feed extends XMLStringer
{
public $xw;
// @fixme most of these should probably be read-only properties
private $namespaces;
private $authors;
private $subject;
@ -57,10 +59,12 @@ class Atom10Feed extends XMLStringer
private $generator;
private $icon;
private $links;
private $logo;
private $selfLink;
private $selfLinkType;
public $logo;
private $rights;
private $subtitle;
private $title;
public $subtitle;
public $title;
private $published;
private $updated;
private $entries;
@ -172,6 +176,14 @@ class Atom10Feed extends XMLStringer
}
$this->elementStart('feed', $commonAttrs);
$this->element(
'generator', array(
'url' => 'http://status.net',
'version' => STATUSNET_VERSION
),
'StatusNet'
);
$this->element('id', null, $this->id);
$this->element('title', null, $this->title);
$this->element('subtitle', null, $this->subtitle);
@ -184,6 +196,10 @@ class Atom10Feed extends XMLStringer
$this->renderAuthors();
if ($this->selfLink) {
$this->addLink($this->selfLink, array('rel' => 'self',
'type' => $this->selfLinkType));
}
$this->renderLinks();
}
@ -253,6 +269,12 @@ class Atom10Feed extends XMLStringer
$this->id = $id;
}
function setSelfLink($url, $type='application/atom+xml')
{
$this->selfLink = $url;
$this->selfLinkType = $type;
}
function setTitle($title)
{
$this->title = $title;

View File

@ -49,14 +49,42 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed
/**
* Constructor
*
* @param Group $group the group for the feed (optional)
* @param Group $group the group for the feed
* @param boolean $indent flag to turn indenting on or off
*
* @return void
*/
function __construct($group = null, $indent = true) {
function __construct($group, $indent = true) {
parent::__construct($indent);
$this->group = $group;
$title = sprintf(_("%s timeline"), $group->nickname);
$this->setTitle($title);
$sitename = common_config('site', 'name');
$subtitle = sprintf(
_('Updates from %1$s on %2$s!'),
$group->nickname,
$sitename
);
$this->setSubtitle($subtitle);
$avatar = $group->homepage_logo;
$logo = ($avatar) ? $avatar : User_group::defaultLogo(AVATAR_PROFILE_SIZE);
$this->setLogo($logo);
$this->setUpdated('now');
$self = common_local_url('ApiTimelineGroup',
array('id' => $group->id,
'format' => 'atom'));
$this->setId($self);
$this->setSelfLink($self);
$this->addAuthorRaw($group->asAtomAuthor());
$this->setActivitySubject($group->asActivitySubject());
$this->addLink($group->homeUrl());
}
function getGroup()

View File

@ -107,9 +107,19 @@ class AtomNoticeFeed extends Atom10Feed
*/
function addEntryFromNotice($notice)
{
$this->addEntryRaw($notice->asAtomEntry());
$source = $this->showSource();
$author = $this->showAuthor();
$this->addEntryRaw($notice->asAtomEntry(false, $source, $author));
}
function showSource()
{
return true;
}
function showAuthor()
{
return true;
}
}

View File

@ -49,23 +49,71 @@ class AtomUserNoticeFeed extends AtomNoticeFeed
/**
* Constructor
*
* @param User $user the user for the feed (optional)
* @param User $user the user for the feed
* @param boolean $indent flag to turn indenting on or off
*
* @return void
*/
function __construct($user = null, $indent = true) {
function __construct($user, $indent = true) {
parent::__construct($indent);
$this->user = $user;
if (!empty($user)) {
$profile = $user->getProfile();
$this->addAuthor($profile->nickname, $user->uri);
$this->setActivitySubject($profile->asActivityNoun('subject'));
}
$title = sprintf(_("%s timeline"), $user->nickname);
$this->setTitle($title);
$sitename = common_config('site', 'name');
$subtitle = sprintf(
_('Updates from %1$s on %2$s!'),
$user->nickname, $sitename
);
$this->setSubtitle($subtitle);
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
$logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
$this->setLogo($logo);
$this->setUpdated('now');
$this->addLink(
common_local_url(
'showstream',
array('nickname' => $user->nickname)
)
);
$self = common_local_url('ApiTimelineUser',
array('id' => $user->id,
'format' => 'atom'));
$this->setId($self);
$this->setSelfLink($self);
$this->addLink(
common_local_url('sup', null, null, $user->id),
array(
'rel' => 'http://api.friendfeed.com/2008/03#sup',
'type' => 'application/json'
)
);
}
function getUser()
{
return $this->user;
}
function showSource()
{
return false;
}
function showAuthor()
{
return false;
}
}

93
lib/grantroleform.php Normal file
View File

@ -0,0 +1,93 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for granting a role
*
* 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 Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>, Brion Vibber <brion@status.net>
* @copyright 2009-2010 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);
}
/**
* Form for sandboxing a user
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@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 UnSandboxForm
*/
class GrantRoleForm extends ProfileActionForm
{
function __construct($role, $label, $writer, $profile, $r2args)
{
parent::__construct($writer, $profile, $r2args);
$this->role = $role;
$this->label = $label;
}
/**
* Action this form provides
*
* @return string Name of the action, lowercased.
*/
function target()
{
return 'grantrole';
}
/**
* Title of the form
*
* @return string Title of the form, internationalized
*/
function title()
{
return $this->label;
}
function formData()
{
parent::formData();
$this->out->hidden('role', $this->role);
}
/**
* Description of the form
*
* @return string description of the form, internationalized
*/
function description()
{
return sprintf(_('Grant this user the "%s" role'), $this->label);
}
}

93
lib/revokeroleform.php Normal file
View File

@ -0,0 +1,93 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for revoking a role
*
* 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 Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>, Brion Vibber <brion@status.net>
* @copyright 2009-2010 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);
}
/**
* Form for sandboxing a user
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@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 UnSandboxForm
*/
class RevokeRoleForm extends ProfileActionForm
{
function __construct($role, $label, $writer, $profile, $r2args)
{
parent::__construct($writer, $profile, $r2args);
$this->role = $role;
$this->label = $label;
}
/**
* Action this form provides
*
* @return string Name of the action, lowercased.
*/
function target()
{
return 'revokerole';
}
/**
* Title of the form
*
* @return string Title of the form, internationalized
*/
function title()
{
return $this->label;
}
function formData()
{
parent::formData();
$this->out->hidden('role', $this->role);
}
/**
* Description of the form
*
* @return string description of the form, internationalized
*/
function description()
{
return sprintf(_('Revoke the "%s" role from this user'), $this->label);
}
}

View File

@ -58,5 +58,7 @@ class Right
const EMAILONSUBSCRIBE = 'emailonsubscribe';
const EMAILONFAVE = 'emailonfave';
const MAKEGROUPADMIN = 'makegroupadmin';
const GRANTROLE = 'grantrole';
const REVOKEROLE = 'revokerole';
}

View File

@ -98,6 +98,7 @@ class Router
'groupblock', 'groupunblock',
'sandbox', 'unsandbox',
'silence', 'unsilence',
'grantrole', 'revokerole',
'repeat',
'deleteuser',
'geocode',
@ -650,6 +651,7 @@ class Router
$m->connect('admin/paths', array('action' => 'pathsadminpanel'));
$m->connect('admin/sessions', array('action' => 'sessionsadminpanel'));
$m->connect('admin/sitenotice', array('action' => 'sitenoticeadminpanel'));
$m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
$m->connect('getfile/:filename',
array('action' => 'getfile'),

View File

@ -354,10 +354,10 @@ class StatusNet
class NoConfigException extends Exception
{
public $config_files;
public $configFiles;
function __construct($msg, $config_files) {
function __construct($msg, $configFiles) {
parent::__construct($msg);
$this->config_files = $config_files;
$this->configFiles = $configFiles;
}
}

View File

@ -346,6 +346,16 @@ class UserProfile extends Widget
$this->out->elementEnd('ul');
$this->out->elementEnd('li');
}
if ($cur->hasRight(Right::GRANTROLE)) {
$this->out->elementStart('li', 'entity_role');
$this->out->element('p', null, _('User role'));
$this->out->elementStart('ul');
$this->roleButton('administrator', _m('role', 'Administrator'));
$this->roleButton('moderator', _m('role', 'Moderator'));
$this->out->elementEnd('ul');
$this->out->elementEnd('li');
}
}
}
@ -359,6 +369,22 @@ class UserProfile extends Widget
}
}
function roleButton($role, $label)
{
list($action, $r2args) = $this->out->returnToArgs();
$r2args['action'] = $action;
$this->out->elementStart('li', "entity_role_$role");
if ($this->user->hasRole($role)) {
$rf = new RevokeRoleForm($role, $label, $this->out, $this->profile, $r2args);
$rf->show();
} else {
$rf = new GrantRoleForm($role, $label, $this->out, $this->profile, $r2args);
$rf->show();
}
$this->out->elementEnd('li');
}
function showRemoteSubscribeLink()
{
$url = common_local_url('remotesubscribe',

View File

@ -44,7 +44,9 @@ class OStatusPlugin extends Plugin
$m->connect('.well-known/host-meta',
array('action' => 'hostmeta'));
$m->connect('main/xrd',
array('action' => 'xrd'));
array('action' => 'userxrd'));
$m->connect('main/ownerxrd',
array('action' => 'ownerxrd'));
$m->connect('main/ostatus',
array('action' => 'ostatusinit'));
$m->connect('main/ostatus?nickname=:nickname',
@ -111,7 +113,7 @@ class OStatusPlugin extends Plugin
{
if ($action instanceof ShowstreamAction) {
$acct = 'acct:'. $action->profile->nickname .'@'. common_config('site', 'server');
$url = common_local_url('xrd');
$url = common_local_url('userxrd');
$url.= '?uri='. $acct;
header('Link: <'.$url.'>; rel="'. Discovery::LRDD_REL.'"; type="application/xrd+xml"');

View File

@ -32,7 +32,7 @@ class HostMetaAction extends Action
parent::handle();
$domain = common_config('site', 'server');
$url = common_local_url('xrd');
$url = common_local_url('userxrd');
$url.= '?uri={uri}';
$xrd = new XRD();

View File

@ -72,9 +72,9 @@ class OStatusGroupAction extends OStatusSubAction
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->input('profile',
_m('Group profile URL'),
_m('Join group'),
$this->profile_uri,
_m('Enter the profile URL of a group on another StatusNet site'));
_m("OStatus group's address, like http://example.net/group/nickname"));
$this->elementEnd('li');
$this->elementEnd('ul');

View File

@ -62,9 +62,9 @@ class OStatusSubAction extends Action
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->input('profile',
_m('Address or profile URL'),
_m('Subscribe to'),
$this->profile_uri,
_m('Enter the profile URL of a PubSubHubbub-enabled feed'));
_m("OStatus user's address, like nickname@example.com or http://example.net/nickname"));
$this->elementEnd('li');
$this->elementEnd('ul');
@ -244,25 +244,33 @@ class OStatusSubAction extends Action
} else if (Validate::uri($this->profile_uri)) {
$this->oprofile = Ostatus_profile::ensureProfile($this->profile_uri);
} else {
$this->error = _m("Invalid address format.");
$this->error = _m("Sorry, we could not reach that address. Please make sure that the OStatus address is like nickname@example.com or http://example.net/nickname");
common_debug('Invalid address format.', __FILE__);
return false;
}
return true;
} catch (FeedSubBadURLException $e) {
$this->error = _m('Invalid URL or could not reach server.');
$this->error = _m("Sorry, we could not reach that address. Please make sure that the OStatus address is like nickname@example.com or http://example.net/nickname");
common_debug('Invalid URL or could not reach server.', __FILE__);
} catch (FeedSubBadResponseException $e) {
$this->error = _m('Cannot read feed; server returned error.');
$this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later.");
common_debug('Cannot read feed; server returned error.', __FILE__);
} catch (FeedSubEmptyException $e) {
$this->error = _m('Cannot read feed; server returned an empty page.');
$this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later.");
common_debug('Cannot read feed; server returned an empty page.', __FILE__);
} catch (FeedSubBadHTMLException $e) {
$this->error = _m('Bad HTML, could not find feed link.');
$this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later.");
common_debug('Bad HTML, could not find feed link.', __FILE__);
} catch (FeedSubNoFeedException $e) {
$this->error = _m('Could not find a feed linked from this URL.');
$this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later.");
common_debug('Could not find a feed linked from this URL.', __FILE__);
} catch (FeedSubUnrecognizedTypeException $e) {
$this->error = _m('Not a recognized feed type.');
} catch (FeedSubException $e) {
$this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later.");
common_debug('Not a recognized feed type.', __FILE__);
} catch (Exception $e) {
// Any new ones we forgot about
$this->error = sprintf(_m('Bad feed URL: %s %s'), get_class($e), $e->getMessage());
$this->error = _m("Sorry, we could not reach that address. Please make sure that the OStatus address is like nickname@example.com or http://example.net/nickname");
common_debug(sprintf('Bad feed URL: %s %s', get_class($e), $e->getMessage()), __FILE__);
}
return false;
@ -315,7 +323,6 @@ class OStatusSubAction extends Action
if ($this->pullRemoteProfile()) {
$this->validateRemoteProfile();
}
return true;
}
@ -391,7 +398,7 @@ class OStatusSubAction extends Action
function title()
{
// TRANS: Page title for OStatus remote subscription form
return _m('Authorize subscription');
return _m('Confirm');
}
/**

View File

@ -0,0 +1,56 @@
<?php
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, 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/>.
*/
/**
* @package OStatusPlugin
* @maintainer James Walker <james@status.net>
*/
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
class OwnerxrdAction extends XrdAction
{
public $uri;
function prepare($args)
{
$this->user = User::siteOwner();
if (!$this->user) {
$this->clientError(_('No such user.'), 404);
return false;
}
$nick = common_canonical_nickname($this->user->nickname);
$acct = 'acct:' . $nick . '@' . common_config('site', 'server');
$this->xrd = new XRD();
// Check to see if a $config['webfinger']['owner'] has been set
if ($owner = common_config('webfinger', 'owner')) {
$this->xrd->subject = Discovery::normalize($owner);
$this->xrd->alias[] = $acct;
} else {
$this->xrd->subject = $acct;
}
return true;
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, 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/>.
*/
/**
* @package OStatusPlugin
* @maintainer James Walker <james@status.net>
*/
if (!defined('STATUSNET')) { exit(1); }
class UserxrdAction extends XrdAction
{
function prepare($args)
{
parent::prepare($args);
$this->uri = $this->trimmed('uri');
$acct = Discovery::normalize($this->uri);
list($nick, $domain) = explode('@', substr(urldecode($acct), 5));
$nick = common_canonical_nickname($nick);
$this->user = User::staticGet('nickname', $nick);
if (!$this->user) {
$this->clientError(_('No such user.'), 404);
return false;
}
return true;
}
}

View File

@ -1267,6 +1267,11 @@ class Ostatus_profile extends Memcached_DataObject
}
}
/**
* @param string $addr webfinger address
* @return Ostatus_profile
* @throws Exception on error conditions
*/
public static function ensureWebfinger($addr)
{
// First, try the cache
@ -1275,7 +1280,8 @@ class Ostatus_profile extends Memcached_DataObject
if ($uri !== false) {
if (is_null($uri)) {
return null;
// Negative cache entry
throw new Exception('Not a valid webfinger address.');
}
$oprofile = Ostatus_profile::staticGet('uri', $uri);
if (!empty($oprofile)) {
@ -1299,20 +1305,24 @@ class Ostatus_profile extends Memcached_DataObject
try {
$result = $disco->lookup($addr);
} catch (Exception $e) {
// Save negative cache entry so we don't waste time looking it up again.
// @fixme distinguish temporary failures?
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null);
return null;
throw new Exception('Not a valid webfinger address.');
}
$hints = array('webfinger' => $addr);
foreach ($result->links as $link) {
switch ($link['rel']) {
case Discovery::PROFILEPAGE:
$profileUrl = $link['href'];
$hints['profileurl'] = $profileUrl = $link['href'];
break;
case Salmon::NS_REPLIES:
$salmonEndpoint = $link['href'];
$hints['salmon'] = $salmonEndpoint = $link['href'];
break;
case Discovery::UPDATESFROM:
$feedUrl = $link['href'];
$hints['feedurl'] = $feedUrl = $link['href'];
break;
case Discovery::HCARD:
$hcardUrl = $link['href'];
@ -1323,11 +1333,6 @@ class Ostatus_profile extends Memcached_DataObject
}
}
$hints = array('webfinger' => $addr,
'profileurl' => $profileUrl,
'feedurl' => $feedUrl,
'salmon' => $salmonEndpoint);
if (isset($hcardUrl)) {
$hcardHints = self::slurpHcard($hcardUrl);
// Note: Webfinger > hcard
@ -1410,7 +1415,7 @@ class Ostatus_profile extends Memcached_DataObject
return $oprofile;
}
return null;
throw new Exception("Couldn't find a valid profile for '$addr'");
}
function saveHTMLFile($title, $rendered)

View File

@ -156,18 +156,32 @@ class MagicEnvelope
public function verify($env)
{
if ($env['alg'] != 'RSA-SHA256') {
common_log(LOG_DEBUG, "Salmon error: bad algorithm");
return false;
}
if ($env['encoding'] != MagicEnvelope::ENCODING) {
common_log(LOG_DEBUG, "Salmon error: bad encoding");
return false;
}
$text = base64_decode($env['data']);
$signer_uri = $this->getAuthor($text);
$verifier = Magicsig::fromString($this->getKeyPair($signer_uri));
try {
$keypair = $this->getKeyPair($signer_uri);
} catch (Exception $e) {
common_log(LOG_DEBUG, "Salmon error: ".$e->getMessage());
return false;
}
$verifier = Magicsig::fromString($keypair);
if (!$verifier) {
common_log(LOG_DEBUG, "Salmon error: unable to parse keypair");
return false;
}
return $verifier->verify($env['data'], $env['sig']);
}

View File

@ -164,46 +164,21 @@ class OStatusQueueHandler extends QueueHandler
*/
function userFeedForNotice()
{
// @fixme this feels VERY hacky...
// should probably be a cleaner way to do it
$atom = new AtomUserNoticeFeed($this->user);
$atom->addEntryFromNotice($this->notice);
$feed = $atom->getString();
ob_start();
$api = new ApiTimelineUserAction();
$api->prepare(array('id' => $this->notice->profile_id,
'format' => 'atom',
'max_id' => $this->notice->id,
'since_id' => $this->notice->id - 1));
$api->showTimeline();
$feed = ob_get_clean();
// ...and override the content-type back to something normal... eww!
// hope there's no other headers that got set while we weren't looking.
header('Content-Type: text/html; charset=utf-8');
common_log(LOG_DEBUG, $feed);
return $feed;
}
function groupFeedForNotice($group_id)
{
// @fixme this feels VERY hacky...
// should probably be a cleaner way to do it
$group = User_group::staticGet('id', $group_id);
ob_start();
$api = new ApiTimelineGroupAction();
$args = array('id' => $group_id,
'format' => 'atom',
'max_id' => $this->notice->id,
'since_id' => $this->notice->id - 1);
$api->prepare($args);
$api->handle($args);
$feed = ob_get_clean();
// ...and override the content-type back to something normal... eww!
// hope there's no other headers that got set while we weren't looking.
header('Content-Type: text/html; charset=utf-8');
$atom = new AtomGroupNoticeFeed($group);
$atom->addEntryFromNotice($this->notice);
$feed = $atom->getString();
common_log(LOG_DEBUG, $feed);
return $feed;
}

View File

@ -57,6 +57,9 @@ class XRD
throw new Exception("Invalid XML");
}
$xrd_element = $dom->getElementsByTagName('XRD')->item(0);
if (!$xrd_element) {
throw new Exception("Invalid XML, missing XRD root");
}
// Check for host-meta host
$host = $xrd_element->getElementsByTagName('Host')->item(0);

View File

@ -28,32 +28,24 @@ class XrdAction extends Action
{
public $uri;
public $user;
function prepare($args)
{
parent::prepare($args);
$this->uri = $this->trimmed('uri');
return true;
}
public $xrd;
function handle()
{
$acct = Discovery::normalize($this->uri);
$nick = $this->user->nickname;
$xrd = new XRD();
list($nick, $domain) = explode('@', substr(urldecode($acct), 5));
$nick = common_canonical_nickname($nick);
$this->user = User::staticGet('nickname', $nick);
if (!$this->user) {
$this->clientError(_('No such user.'), 404);
return false;
if (empty($this->xrd)) {
$xrd = new XRD();
} else {
$xrd = $this->xrd;
}
$xrd->subject = $this->uri;
if (empty($xrd->subject)) {
$xrd->subject = Discovery::normalize($this->uri);
}
$xrd->alias[] = common_profile_url($nick);
$xrd->links[] = array('rel' => Discovery::PROFILEPAGE,
'type' => 'text/html',

View File

@ -52,7 +52,8 @@ margin-bottom:0;
width:405px;
}
.aside #entity_subscriptions .more {
.aside #entity_subscriptions .more,
.aside #entity_groups .more {
float:left;
}

131
tests/UserFeedParseTest.php Normal file
View File

@ -0,0 +1,131 @@
<?php
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
print "This script must be run from the command line\n";
exit();
}
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
define('STATUSNET', true);
require_once INSTALLDIR . '/lib/common.php';
class UserFeedParseTests extends PHPUnit_Framework_TestCase
{
public function testFeed1()
{
global $_testfeed1;
$dom = DOMDocument::loadXML($_testfeed1);
$this->assertFalse(empty($dom));
$entries = $dom->getElementsByTagName('entry');
$entry1 = $entries->item(0);
$this->assertFalse(empty($entry1));
$feedEl = $dom->getElementsByTagName('feed')->item(0);
$this->assertFalse(empty($feedEl));
// Test actor (from activity:subject)
$act1 = new Activity($entry1, $feedEl);
$this->assertFalse(empty($act1));
$this->assertFalse(empty($act1->actor));
$this->assertEquals($act1->actor->type, ActivityObject::PERSON);
$this->assertEquals($act1->actor->title, 'Zach Copley');
$this->assertEquals($act1->actor->id, 'http://localhost/statusnet/user/1');
$this->assertEquals($act1->actor->link, 'http://localhost/statusnet/zach');
$avatars = $act1->actor->avatarLinks;
$this->assertEquals(
$avatars[0]->url,
'http://localhost/statusnet/theme/default/default-avatar-profile.png'
);
$this->assertEquals(
$avatars[1]->url,
'http://localhost/statusnet/theme/default/default-avatar-stream.png'
);
$this->assertEquals(
$avatars[2]->url,
'http://localhost/statusnet/theme/default/default-avatar-mini.png'
);
$this->assertEquals($act1->actor->displayName, 'Zach Copley');
$poco = $act1->actor->poco;
$this->assertEquals($poco->preferredUsername, 'zach');
$this->assertEquals($poco->address->formatted, 'El Cerrito, CA');
$this->assertEquals($poco->urls[0]->type, 'homepage');
$this->assertEquals($poco->urls[0]->value, 'http://zach.copley.name');
$this->assertEquals($poco->urls[0]->primary, 'true');
$this->assertEquals($poco->note, 'Zach Hack Attack');
// test the post
//var_export($act1);
$this->assertEquals($act1->object->type, 'http://activitystrea.ms/schema/1.0/note');
$this->assertEquals($act1->object->title, 'And now for something completely insane...');
$this->assertEquals($act1->object->content, 'And now for something completely insane...');
$this->assertEquals($act1->object->id, 'http://localhost/statusnet/notice/3');
}
}
$_testfeed1 = <<<TESTFEED1
<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0">
<id>http://localhost/statusnet/api/statuses/user_timeline/1.atom</id>
<title>zach timeline</title>
<subtitle>Updates from zach on Zach Dev!</subtitle>
<logo>http://localhost/statusnet/theme/default/default-avatar-profile.png</logo>
<updated>2010-03-04T01:41:14+00:00</updated>
<author>
<name>zach</name>
<uri>http://localhost/statusnet/user/1</uri>
</author>
<link href="http://localhost/statusnet/zach" rel="alternate" type="text/html"/>
<link href="http://localhost/statusnet/main/sup#1" rel="http://api.friendfeed.com/2008/03#sup" type="application/json"/>
<link href="http://localhost/statusnet/main/push/hub" rel="hub"/>
<link href="http://localhost/statusnet/main/salmon/user/1" rel="http://salmon-protocol.org/ns/salmon-replies"/>
<link href="http://localhost/statusnet/main/salmon/user/1" rel="http://salmon-protocol.org/ns/salmon-mention"/>
<link href="http://localhost/statusnet/api/statuses/user_timeline/1.atom" rel="self" type="application/atom+xml"/>
<activity:subject>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<id>http://localhost/statusnet/user/1</id>
<title>Zach Copley</title>
<link rel="alternate" type="text/html" href="http://localhost/statusnet/zach"/>
<link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://localhost/statusnet/theme/default/default-avatar-profile.png"/>
<link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://localhost/statusnet/theme/default/default-avatar-stream.png"/>
<link rel="avatar" type="image/png" media:width="24" media:height="24" href="http://localhost/statusnet/theme/default/default-avatar-mini.png"/>
<poco:preferredUsername>zach</poco:preferredUsername>
<poco:displayName>Zach Copley</poco:displayName>
<poco:note>Zach Hack Attack</poco:note>
<poco:address>
<poco:formatted>El Cerrito, CA</poco:formatted>
</poco:address>
<poco:urls>
<poco:type>homepage</poco:type>
<poco:value>http://zach.copley.name</poco:value>
<poco:primary>true</poco:primary>
</poco:urls>
</activity:subject>
<entry>
<title>And now for something completely insane...</title>
<link rel="alternate" type="text/html" href="http://localhost/statusnet/notice/3"/>
<id>http://localhost/statusnet/notice/3</id>
<published>2010-03-04T01:41:07+00:00</published>
<updated>2010-03-04T01:41:07+00:00</updated>
<link rel="ostatus:conversation" href="http://localhost/statusnet/conversation/3"/>
<content type="html">And now for something completely insane...</content>
</entry>
</feed>
TESTFEED1;

View File

@ -771,10 +771,12 @@ display:none;
text-align:center;
}
.entity_moderation {
.entity_moderation,
.entity_role {
position:relative;
}
.entity_moderation p {
.entity_moderation p,
.entity_role p {
border-radius:4px;
-moz-border-radius:4px;
-webkit-border-radius:4px;
@ -782,13 +784,14 @@ font-weight:bold;
padding-bottom:2px;
margin-bottom:7px;
}
.entity_moderation ul {
.entity_moderation ul,
.entity_role ul {
display:none;
}
.entity_moderation:hover ul {
.entity_moderation:hover ul,
.entity_role:hover ul {
display:block;
min-width:21%;
width:100%;
width:110%;
padding:11px;
position:absolute;
top:-1px;

View File

@ -45,6 +45,11 @@
White pin with green background
White underscore with green background
White C with green background
White magic wand with green background
Green badge with white background
Green sandbox with white background
Green speech bubble broken with white background
Green person with tie with white background
*/
Created by various authors

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -49,6 +49,7 @@ box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3);
.pagination .nav_next a,
.form_settings fieldset fieldset,
.entity_moderation:hover ul,
.entity_role:hover ul,
.dialogbox {
border-color:#DDDDDD;
}
@ -67,6 +68,7 @@ input.submit,
.entity_actions a,
.entity_actions input,
.entity_moderation p,
.entity_role p,
button {
box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
@ -127,7 +129,8 @@ a,
.notice-options input,
.entity_actions a,
.entity_actions input,
.entity_moderation p {
.entity_moderation p,
.entity_role p {
color:#002FA7;
}
@ -190,6 +193,9 @@ button.close,
.entity_sandbox input.submit,
.entity_silence input.submit,
.entity_delete input.submit,
.entity_role p,
.entity_role_administrator input.submit,
.entity_role_moderator input.submit,
.notice-options .repeated,
.form_notice label[for=notice_data-geo],
button.minimize,
@ -229,6 +235,7 @@ border-color:transparent;
#site_nav_local_views .current a,
.entity_send-a-message .form_notice,
.entity_moderation:hover ul,
.entity_role:hover ul,
.dialogbox {
background-color:#FFFFFF;
}
@ -319,6 +326,7 @@ background-position: 5px -852px;
}
.entity_send-a-message .form_notice,
.entity_moderation:hover ul,
.entity_role:hover ul,
.dialogbox {
box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7);
-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7);
@ -350,6 +358,27 @@ background-position: 5px -1445px;
.entity_delete input.submit {
background-position: 5px -1511px;
}
.entity_sandbox .form_user_unsandbox input.submit {
background-position: 5px -2568px;
}
.entity_silence .form_user_unsilence input.submit {
background-position: 5px -2633px;
}
.entity_role p {
background-position: 5px -2436px;
}
.entity_role_administrator .form_user_grantrole input.submit {
background-position: 5px -983px;
}
.entity_role_moderator .form_user_grantrole input.submit {
background-position: 5px -1313px;
}
.entity_role_administrator .form_user_revokerole input.submit {
background-position: 5px -2699px;
}
.entity_role_moderator .form_user_revokerole input.submit {
background-position: 5px -2501px;
}
.form_reset_key input.submit {
background-position: 5px -1973px;
}

View File

@ -49,6 +49,7 @@ box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3);
.pagination .nav_next a,
.form_settings fieldset fieldset,
.entity_moderation:hover ul,
.entity_role:hover ul,
.dialogbox {
border-color:#DDDDDD;
}
@ -67,6 +68,7 @@ input.submit,
.entity_actions a,
.entity_actions input,
.entity_moderation p,
.entity_role p,
button {
box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
@ -128,7 +130,8 @@ a,
.notice-options input,
.entity_actions a,
.entity_actions input,
.entity_moderation p {
.entity_moderation p,
.entity_role p {
color:#002FA7;
}
@ -191,6 +194,9 @@ button.close,
.entity_sandbox input.submit,
.entity_silence input.submit,
.entity_delete input.submit,
.entity_role p,
.entity_role_administrator input.submit,
.entity_role_moderator input.submit,
.notice-options .repeated,
.form_notice label[for=notice_data-geo],
button.minimize,
@ -230,6 +236,7 @@ border-color:transparent;
#site_nav_local_views .current a,
.entity_send-a-message .form_notice,
.entity_moderation:hover ul,
.entity_role:hover ul,
.dialogbox {
background-color:#FFFFFF;
}
@ -319,6 +326,7 @@ background-position: 5px -852px;
}
.entity_send-a-message .form_notice,
.entity_moderation:hover ul,
.entity_role:hover ul,
.dialogbox {
box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7);
-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7);
@ -350,6 +358,27 @@ background-position: 5px -1445px;
.entity_delete input.submit {
background-position: 5px -1511px;
}
.entity_sandbox .form_user_unsandbox input.submit {
background-position: 5px -2568px;
}
.entity_silence .form_user_unsilence input.submit {
background-position: 5px -2633px;
}
.entity_role p {
background-position: 5px -2436px;
}
.entity_role_administrator .form_user_grantrole input.submit {
background-position: 5px -983px;
}
.entity_role_moderator .form_user_grantrole input.submit {
background-position: 5px -1313px;
}
.entity_role_administrator .form_user_revokerole input.submit {
background-position: 5px -2699px;
}
.entity_role_moderator .form_user_revokerole input.submit {
background-position: 5px -2501px;
}
.form_reset_key input.submit {
background-position: 5px -1973px;
}