Merge branch 'testing' of git@gitorious.org:statusnet/mainline into testing

This commit is contained in:
Evan Prodromou 2010-03-13 06:36:25 -06:00
commit c1e96cbdef
166 changed files with 25381 additions and 13913 deletions

2
README
View File

@ -690,7 +690,7 @@ instructions; read to the end first before trying them.
9. Copy htaccess.sample to .htaccess in the new directory. Change the
RewriteBase to use the correct path.
10. Rebuild the database. (You can safely skip this step and go to #12
if you're upgrading from another 0.8.x version).
if you're upgrading from another 0.9.x version).
NOTE: this step is destructive and cannot be
reversed. YOU CAN EASILY DESTROY YOUR SITE WITH THIS STEP. Don't

View File

@ -51,6 +51,7 @@ class AccessadminpanelAction extends AdminPanelAction
function title()
{
// TRANS: Page title
return _('Access');
}
@ -62,6 +63,7 @@ class AccessadminpanelAction extends AdminPanelAction
function getInstructions()
{
// TRANS: Page notice
return _('Site access settings');
}
@ -155,24 +157,34 @@ class AccessAdminPanelForm extends AdminForm
function formData()
{
$this->out->elementStart('fieldset', array('id' => 'settings_admin_access'));
// TRANS: Form legend for registration form.
$this->out->element('legend', null, _('Registration'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->out->checkbox('private', _('Private'),
// TRANS: Checkbox instructions for admin setting "Private"
$instructions = _('Prohibit anonymous users (not logged in) from viewing site?');
// TRANS: Checkbox label for prohibiting anonymous users from viewing site.
$this->out->checkbox('private', _m('LABEL', 'Private'),
(bool) $this->value('private'),
_('Prohibit anonymous users (not logged in) from viewing site?'));
$instructions);
$this->unli();
$this->li();
// TRANS: Checkbox instructions for admin setting "Invite only"
$instructions = _('Make registration invitation only.');
// TRANS: Checkbox label for configuring site as invite only.
$this->out->checkbox('inviteonly', _('Invite only'),
(bool) $this->value('inviteonly'),
_('Make registration invitation only.'));
$instructions);
$this->unli();
$this->li();
// TRANS: Checkbox instructions for admin setting "Closed" (no new registrations)
$instructions = _('Disable new registrations.');
// TRANS: Checkbox label for disabling new user registrations.
$this->out->checkbox('closed', _('Closed'),
(bool) $this->value('closed'),
_('Disable new registrations.'));
$instructions);
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
@ -186,7 +198,9 @@ class AccessAdminPanelForm extends AdminForm
function formActions()
{
$this->out->submit('submit', _('Save'), 'submit', null, _('Save access settings'));
// TRANS: Title / tooltip for button to save access settings in site admin panel
$title = _('Save access settings');
$this->out->submit('submit', _m('BUTTON', 'Save'), 'submit', null, $title);
}
}

View File

@ -60,6 +60,7 @@ class AllAction extends ProfileAction
}
if ($this->page > 1 && $this->notice->N == 0) {
// TRANS: Server error when page not found (404)
$this->serverError(_('No such page'), $code = 404);
}
@ -81,8 +82,10 @@ class AllAction extends ProfileAction
function title()
{
if ($this->page > 1) {
// TRANS: Page title. %1$s is user nickname, %2$d is page number
return sprintf(_('%1$s and friends, page %2$d'), $this->user->nickname, $this->page);
} else {
// TRANS: Page title. %1$s is user nickname
return sprintf(_("%s and friends"), $this->user->nickname);
}
}
@ -96,6 +99,7 @@ class AllAction extends ProfileAction
'nickname' =>
$this->user->nickname)
),
// TRANS: %1$s is user nickname
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
new Feed(Feed::RSS2,
common_local_url(
@ -104,6 +108,7 @@ class AllAction extends ProfileAction
'id' => $this->user->nickname
)
),
// TRANS: %1$s is user nickname
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
new Feed(Feed::ATOM,
common_local_url(
@ -112,6 +117,7 @@ class AllAction extends ProfileAction
'id' => $this->user->nickname
)
),
// TRANS: %1$s is user nickname
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
);
}
@ -124,6 +130,7 @@ class AllAction extends ProfileAction
function showEmptyListMessage()
{
// TRANS: %1$s is user nickname
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->user->nickname) . ' ';
if (common_logged_in()) {
@ -131,6 +138,7 @@ class AllAction extends ProfileAction
if ($this->user->id === $current_user->id) {
$message .= _('Try subscribing to more people, [join a group](%%action.groups%%) or post something yourself.');
} else {
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
}
} else {
@ -166,8 +174,10 @@ class AllAction extends ProfileAction
{
$user = common_current_user();
if ($user && ($user->id == $this->user->id)) {
// TRANS: H1 text
$this->element('h1', null, _("You and friends"));
} else {
// TRANS: H1 text. %1$s is user nickname
$this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
}
}

View File

@ -115,11 +115,11 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$original = clone($profile);
if (empty($this->name)) {
if (!empty($this->name)) {
$profile->fullname = $this->name;
}
if (empty($this->url)) {
if (!empty($this->url)) {
$profile->homepage = $this->url;
}

View File

@ -45,6 +45,7 @@ require_once INSTALLDIR . '/lib/apiprivateauth.php';
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @author Michele <macno@macno.org>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@ -68,6 +69,24 @@ class ApiGroupShowAction extends ApiPrivateAuthAction
$this->group = $this->getTargetGroup($this->arg('id'));
if (empty($this->group)) {
$alias = Group_alias::staticGet(
'alias',
common_canonical_nickname($this->arg('id'))
);
if (!empty($alias)) {
$args = array('id' => $alias->group_id, 'format' => $this->format);
common_redirect(common_local_url('ApiGroupShow', $args), 301);
} else {
$this->clientError(
_('Group not found!'),
404,
$this->format
);
}
return;
}
return true;
}
@ -85,15 +104,6 @@ class ApiGroupShowAction extends ApiPrivateAuthAction
{
parent::handle($args);
if (empty($this->group)) {
$this->clientError(
_('Group not found!'),
404,
$this->format
);
return;
}
switch($this->format) {
case 'xml':
$this->showSingleXmlGroup($this->group);
@ -105,7 +115,6 @@ class ApiGroupShowAction extends ApiPrivateAuthAction
$this->clientError(_('API method not found.'), 404, $this->format);
break;
}
}
/**

View File

@ -156,7 +156,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!$result) {
common_log_db_error($appUser, 'DELETE', __FILE__);
throw new ServerException(_('DB error deleting OAuth app user.'));
throw new ServerException(_('Database error deleting OAuth application user.'));
return;
}
}
@ -182,7 +182,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
throw new ServerException(_('DB error inserting OAuth app user.'));
throw new ServerException(_('Database error inserting OAuth application user.'));
return;
}

View File

@ -50,13 +50,17 @@ if (!defined('STATUSNET')) {
class ApiStatusnetConfigAction extends ApiAction
{
var $keys = array(
'site' => array('name', 'server', 'theme', 'path', 'fancy', 'language',
'email', 'broughtby', 'broughtbyurl', 'closed',
'inviteonly', 'private','textlimit'),
'license' => array('url', 'title', 'image'),
'site' => array('name', 'server', 'theme', 'path', 'logo', 'fancy', 'language',
'email', 'broughtby', 'broughtbyurl', 'timezone', 'closed',
'inviteonly', 'private', 'textlimit', 'ssl', 'sslserver', 'shorturllength'),
'license' => array('type', 'owner', 'url', 'title', 'image'),
'nickname' => array('featured'),
'profile' => array('biolimit'),
'group' => array('desclimit'),
'notice' => array('contentlimit'),
'throttle' => array('enabled', 'count', 'timespan'),
'xmpp' => array('enabled', 'server', 'user')
'xmpp' => array('enabled', 'server', 'port', 'user'),
'integration' => array('source')
);
/**

View File

@ -23,7 +23,8 @@
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc.
* @author Zach Copley <zach@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/
*/
@ -123,22 +124,26 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
? $avatar->displayUrl()
: Avatar::defaultImage(AVATAR_PROFILE_SIZE);
$link = common_local_url(
'showfavorites',
array('nickname' => $this->user->nickname)
);
$self = $this->getSelfUri();
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$link = common_local_url(
'showfavorites',
array('nickname' => $this->user->nickname)
);
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$logo
$logo,
$self
);
break;
case 'atom':
@ -153,23 +158,8 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink(
common_local_url(
'showfavorites',
array('nickname' => $this->user->nickname)
)
);
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$atom->addLink(
$this->getSelfUri('ApiTimelineFavorites', $aargs),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);

View File

@ -117,9 +117,17 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
$subtitle = sprintf(
_('Updates from %1$s and friends on %2$s!'),
$this->user->nickname, $sitename
$this->user->nickname,
$sitename
);
$link = common_local_url(
'all',
array('nickname' => $this->user->nickname)
);
$self = $this->getSelfUri();
$logo = (!empty($avatar))
? $avatar->displayUrl()
: Avatar::defaultImage(AVATAR_PROFILE_SIZE);
@ -130,19 +138,14 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
break;
case 'rss':
$link = common_local_url(
'all', array(
'nickname' => $this->user->nickname
)
);
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$logo
$logo,
$self
);
break;
case 'atom':
@ -156,24 +159,8 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
$atom->setSubtitle($subtitle);
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink(
common_local_url(
'all',
array('nickname' => $this->user->nickname)
)
);
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$atom->addLink(
$this->getSelfUri('ApiTimelineFriends', $aargs),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);

View File

@ -107,6 +107,8 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
// We'll pull common formatting out of this for other formats
$atom = new AtomGroupNoticeFeed($this->group);
$self = $this->getSelfUri();
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
@ -118,7 +120,8 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
$this->group->homeUrl(),
$atom->subtitle,
null,
$atom->logo
$atom->logo,
$self
);
break;
case 'atom':
@ -126,24 +129,12 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
header('Content-Type: application/atom+xml; charset=utf-8');
try {
$atom->addAuthorRaw($this->group->asAtomAuthor());
$atom->setActivitySubject($this->group->asActivitySubject());
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$self = $this->getSelfUri('ApiTimelineGroup', $aargs);
$atom->setId($self);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
} catch (Atom10FeedException $e) {
$this->serverError(
'Could not generate feed for group - ' . $e->getMessage()

View File

@ -72,7 +72,7 @@ class ApiTimelineHomeAction extends ApiBareAuthAction
function prepare($args)
{
parent::prepare($args);
common_debug("api home_timeline");
$this->user = $this->getTargetUser($this->arg('id'));
if (empty($this->user)) {
@ -121,8 +121,15 @@ class ApiTimelineHomeAction extends ApiBareAuthAction
$this->user->nickname, $sitename
);
$logo = (!empty($avatar))
? $avatar->displayUrl()
$link = common_local_url(
'all',
array('nickname' => $this->user->nickname)
);
$self = $this->getSelfUri();
$logo = (!empty($avatar))
? $avatar->displayUrl()
: Avatar::defaultImage(AVATAR_PROFILE_SIZE);
switch($this->format) {
@ -130,17 +137,14 @@ class ApiTimelineHomeAction extends ApiBareAuthAction
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$link = common_local_url(
'all',
array('nickname' => $this->user->nickname)
);
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$logo
$logo,
$self
);
break;
case 'atom':
@ -155,23 +159,8 @@ class ApiTimelineHomeAction extends ApiBareAuthAction
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink(
common_local_url(
'all',
array('nickname' => $this->user->nickname)
)
);
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$atom->addLink(
$this->getSelfUri('ApiTimelineHome', $aargs),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());

View File

@ -123,6 +123,9 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
'replies',
array('nickname' => $this->user->nickname)
);
$self = $this->getSelfUri();
$subtitle = sprintf(
_('%1$s updates that reply to updates from %2$s / %3$s.'),
$sitename, $this->user->nickname, $profile->getBestName()
@ -134,10 +137,20 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo);
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed();
$atom->setId($id);
@ -146,23 +159,8 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink(
common_local_url(
'replies',
array('nickname' => $this->user->nickname)
)
);
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$atom->addLink(
$this->getSelfUri('ApiTimelineMentions', $aargs),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());

View File

@ -107,7 +107,8 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction
$title = sprintf(_("%s public timeline"), $sitename);
$taguribase = TagURI::base();
$id = "tag:$taguribase:PublicTimeline";
$link = common_root_url();
$link = common_local_url('public');
$self = $this->getSelfUri();
$subtitle = sprintf(_("%s updates from everyone!"), $sitename);
switch($this->format) {
@ -115,10 +116,20 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $sitelogo);
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$sitelogo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed();
$atom->setId($id);
@ -126,16 +137,8 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction
$atom->setSubtitle($subtitle);
$atom->setLogo($sitelogo);
$atom->setUpdated('now');
$atom->addLink(common_local_url('public'));
$atom->addLink(
$this->getSelfUri(
'ApiTimelinePublic', array('format' => 'atom')
),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());

View File

@ -25,7 +25,7 @@
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @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/
*/
@ -67,6 +67,8 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
{
parent::prepare($args);
common_debug("apitimelinetag prepare()");
$this->tag = $this->arg('tag');
$this->notices = $this->getNotices();
@ -108,22 +110,28 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
$taguribase = TagURI::base();
$id = "tag:$taguribase:TagTimeline:".$tag;
$link = common_local_url(
'tag',
array('tag' => $this->tag)
);
$self = $this->getSelfUri();
common_debug("self link is: $self");
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$link = common_local_url(
'tag',
array('tag' => $this->tag)
);
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$sitelogo
$sitelogo,
$self
);
break;
case 'atom':
@ -138,22 +146,8 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink(
common_local_url(
'tag',
array('tag' => $this->tag)
)
);
$aargs = array('format' => 'atom');
if (!empty($this->tag)) {
$aargs['tag'] = $this->tag;
}
$atom->addLink(
$this->getSelfUri('ApiTimelineTag', $aargs),
array('rel' => 'self', 'type' => 'application/atom+xml')
);
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());

View File

@ -116,13 +116,13 @@ class ApiTimelineUserAction extends ApiBareAuthAction
// 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(
$link = common_local_url(
'showstream',
array('nickname' => $this->user->nickname)
);
$subtitle = $atom->subtitle;
$logo = $atom->logo;
$self = $this->getSelfUri();
// FriendFeed's SUP protocol
// Also added RSS and Atom feeds
@ -136,25 +136,22 @@ class ApiTimelineUserAction extends ApiBareAuthAction
break;
case 'rss':
$this->showRssTimeline(
$this->notices, $title, $link,
$subtitle, $suplink, $logo
$this->notices,
$atom->title,
$link,
$atom->subtitle,
$suplink,
$atom->logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$id = $this->arg('id');
$aargs = array('format' => 'atom');
if (!empty($id)) {
$aargs['id'] = $id;
}
$self = $this->getSelfUri('ApiTimelineUser', $aargs);
$atom->setId($self);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;

View File

@ -141,7 +141,7 @@ class ConfirmaddressAction extends Action
function title()
{
return _('Confirm Address');
return _('Confirm address');
}
/**

View File

@ -13,7 +13,7 @@
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
* Copyright (C) 2008-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
@ -168,14 +168,28 @@ class DocAction extends Action
function getFilename()
{
if (file_exists(INSTALLDIR.'/local/doc-src/'.$this->title)) {
$localDef = INSTALLDIR.'/local/doc-src/'.$this->title;
}
$localDef = null;
$local = null;
$local = glob(INSTALLDIR.'/local/doc-src/'.$this->title.'.*');
if ($local === false) {
// Some systems return false, others array(), if dir didn't exist.
$local = array();
$site = StatusNet::currentSite();
if (!empty($site) && file_exists(INSTALLDIR.'/local/doc-src/'.$site.'/'.$this->title)) {
$localDef = INSTALLDIR.'/local/doc-src/'.$site.'/'.$this->title;
$local = glob(INSTALLDIR.'/local/doc-src/'.$site.'/'.$this->title.'.*');
if ($local === false) {
// Some systems return false, others array(), if dir didn't exist.
$local = array();
}
} else {
if (file_exists(INSTALLDIR.'/local/doc-src/'.$this->title)) {
$localDef = INSTALLDIR.'/local/doc-src/'.$this->title;
}
$local = glob(INSTALLDIR.'/local/doc-src/'.$this->title.'.*');
if ($local === false) {
$local = array();
}
}
if (count($local) || isset($localDef)) {

View File

@ -251,7 +251,7 @@ class FoafAction extends Action
}
// Their account
$this->elementStart('holdsAccount');
$this->elementStart('account');
$this->elementStart('OnlineAccount', $attr);
if ($service) {
$this->element('accountServiceHomepage', array('rdf:resource' =>
@ -306,7 +306,7 @@ class FoafAction extends Action
}
$this->elementEnd('OnlineAccount');
$this->elementEnd('holdsAccount');
$this->elementEnd('account');
return $person;
}

View File

@ -146,7 +146,7 @@ class FoafGroupAction extends Action
{
$this->elementStart('Agent', array('rdf:about' => $uri));
$this->element('nick', null, $details['nickname']);
$this->elementStart('holdsAccount');
$this->elementStart('account');
$this->elementStart('sioc:User', array('rdf:about'=>$uri.'#acct'));
$this->elementStart('sioc:has_function');
$this->elementStart('statusnet:GroupAdminRole');
@ -154,7 +154,7 @@ class FoafGroupAction extends Action
$this->elementEnd('statusnet:GroupAdminRole');
$this->elementEnd('sioc:has_function');
$this->elementEnd('sioc:User');
$this->elementEnd('holdsAccount');
$this->elementEnd('account');
$this->elementEnd('Agent');
}
else
@ -177,4 +177,4 @@ class FoafGroupAction extends Action
$this->elementEnd('Document');
}
}
}

View File

@ -194,7 +194,8 @@ class InviteAction extends CurrentUserDesignAction
_('Optionally add a personal message to the invitation.'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->submit('send', _('Send'));
// TRANS: Send button for inviting friends
$this->submit('send', _m('BUTTON', 'Send'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}

View File

@ -57,7 +57,7 @@ class OthersettingsAction extends AccountSettingsAction
function title()
{
return _('Other Settings');
return _('Other settings');
}
/**

View File

@ -94,6 +94,7 @@ class PublicAction extends Action
}
if($this->page > 1 && $this->notice->N == 0){
// TRANS: Server error when page not found (404)
$this->serverError(_('No such page'),$code=404);
}

View File

@ -21,7 +21,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
# You have 24 hours to claim your password
define(MAX_RECOVERY_TIME, 24 * 60 * 60);
define('MAX_RECOVERY_TIME', 24 * 60 * 60);
class RecoverpasswordAction extends Action
{
@ -262,10 +262,20 @@ class RecoverpasswordAction extends Action
# See if it's an unconfirmed email address
if (!$user) {
$confirm_email = Confirm_address::staticGet('address', common_canonical_email($nore));
if ($confirm_email && $confirm_email->address_type == 'email') {
// Warning: it may actually be legit to have multiple folks
// who have claimed, but not yet confirmed, the same address.
// We'll only send to the first one that comes up.
$confirm_email = new Confirm_address();
$confirm_email->address = common_canonical_email($nore);
$confirm_email->address_type = 'email';
$confirm_email->find();
if ($confirm_email->fetch()) {
$user = User::staticGet($confirm_email->user_id);
} else {
$confirm_email = null;
}
} else {
$confirm_email = null;
}
if (!$user) {
@ -276,9 +286,11 @@ class RecoverpasswordAction extends Action
# Try to get an unconfirmed email address if they used a user name
if (!$user->email && !$confirm_email) {
$confirm_email = Confirm_address::staticGet('user_id', $user->id);
if ($confirm_email && $confirm_email->address_type != 'email') {
# Skip non-email confirmations
$confirm_email = new Confirm_address();
$confirm_email->user_id = $user->id;
$confirm_email->address_type = 'email';
$confirm_email->find();
if (!$confirm_email->fetch()) {
$confirm_email = null;
}
}
@ -294,7 +306,7 @@ class RecoverpasswordAction extends Action
$confirm->code = common_confirmation_code(128);
$confirm->address_type = 'recover';
$confirm->user_id = $user->id;
$confirm->address = (isset($user->email)) ? $user->email : $confirm_email->address;
$confirm->address = (!empty($user->email)) ? $user->email : $confirm_email->address;
if (!$confirm->insert()) {
common_log_db_error($confirm, 'INSERT', __FILE__);
@ -319,7 +331,8 @@ class RecoverpasswordAction extends Action
$body .= common_config('site', 'name');
$body .= "\n";
mail_to_user($user, _('Password recovery requested'), $body, $confirm->address);
$headers = _mail_prepare_headers('recoverpassword', $user->nickname, $user->nickname);
mail_to_user($user, _('Password recovery requested'), $body, $headers, $confirm->address);
$this->mode = 'sent';
$this->msg = _('Instructions for recovering your password ' .

View File

@ -89,6 +89,7 @@ class RepliesAction extends OwnerDesignAction
NOTICES_PER_PAGE + 1);
if($this->page > 1 && $this->notice->N == 0){
// TRANS: Server error when page not found (404)
$this->serverError(_('No such page'),$code=404);
}

View File

@ -134,6 +134,7 @@ class ShowfavoritesAction extends OwnerDesignAction
}
if($this->page > 1 && $this->notice->N == 0){
// TRANS: Server error when page not found (404)
$this->serverError(_('No such page'),$code=404);
}

View File

@ -48,6 +48,7 @@ class TagAction extends Action
$this->notice = Notice_tag::getStream($this->tag, (($this->page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1);
if($this->page > 1 && $this->notice->N == 0){
// TRANS: Server error when page not found (404)
$this->serverError(_('No such page'),$code=404);
}

View File

@ -55,7 +55,8 @@ class UseradminpanelAction extends AdminPanelAction
function title()
{
return _('User');
// TRANS: User admin panel title
return _m('TITLE', 'User');
}
/**

View File

@ -266,5 +266,6 @@ class VersionAction extends Action
'Craig Andrews',
'mEDI',
'Brett Taylor',
'Brigitte Schuster');
'Brigitte Schuster',
'Brion Vibber');
}

View File

@ -67,7 +67,14 @@ class File extends Memcached_DataObject
return $att;
}
function saveNew($redir_data, $given_url) {
/**
* Save a new file record.
*
* @param array $redir_data lookup data eg from File_redirection::where()
* @param string $given_url
* @return File
*/
function saveNew(array $redir_data, $given_url) {
$x = new File;
$x->url = $given_url;
if (!empty($redir_data['protected'])) $x->protected = $redir_data['protected'];
@ -77,19 +84,36 @@ class File extends Memcached_DataObject
if (isset($redir_data['time']) && $redir_data['time'] > 0) $x->date = intval($redir_data['time']);
$file_id = $x->insert();
$x->saveOembed($redir_data, $given_url);
return $x;
}
/**
* Save embedding information for this file, if applicable.
*
* Normally this won't need to be called manually, as File::saveNew()
* takes care of it.
*
* @param array $redir_data lookup data eg from File_redirection::where()
* @param string $given_url
* @return boolean success
*/
public function saveOembed($redir_data, $given_url)
{
if (isset($redir_data['type'])
&& (('text/html' === substr($redir_data['type'], 0, 9) || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21)))
&& ($oembed_data = File_oembed::_getOembed($given_url))) {
$fo = File_oembed::staticGet('file_id', $file_id);
$fo = File_oembed::staticGet('file_id', $this->id);
if (empty($fo)) {
File_oembed::saveNew($oembed_data, $file_id);
File_oembed::saveNew($oembed_data, $this->id);
return true;
} else {
common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file $file_id", __FILE__);
}
}
return $x;
return false;
}
function processNew($given_url, $notice_id=null) {
@ -105,6 +129,7 @@ class File extends Memcached_DataObject
$redir_url = $redir_data['url'];
} elseif (is_string($redir_data)) {
$redir_url = $redir_data;
$redir_data = array();
} else {
throw new ServerException("Can't process url '$given_url'");
}
@ -169,7 +194,11 @@ class File extends Memcached_DataObject
{
require_once 'MIME/Type/Extension.php';
$mte = new MIME_Type_Extension();
$ext = $mte->getExtension($mimetype);
try {
$ext = $mte->getExtension($mimetype);
} catch ( Exception $e) {
$ext = strtolower(preg_replace('/\W/', '', $mimetype));
}
$nickname = $profile->nickname;
$datestamp = strftime('%Y%m%dT%H%M%S', time());
$random = strtolower(common_confirmation_code(32));
@ -256,7 +285,7 @@ class File extends Memcached_DataObject
$enclosure->mimetype=$this->mimetype;
if(! isset($this->filename)){
$notEnclosureMimeTypes = array('text/html','application/xhtml+xml');
$notEnclosureMimeTypes = array(null,'text/html','application/xhtml+xml');
$mimetype = strtolower($this->mimetype);
$semicolon = strpos($mimetype,';');
if($semicolon){

View File

@ -81,6 +81,12 @@ class File_oembed extends Memcached_DataObject
}
}
/**
* Save embedding info for a new file.
*
* @param object $data Services_oEmbed_Object_*
* @param int $file_id
*/
function saveNew($data, $file_id) {
$file_oembed = new File_oembed;
$file_oembed->file_id = $file_id;

View File

@ -58,24 +58,30 @@ class File_redirection extends Memcached_DataObject
return $request;
}
function _redirectWhere_imp($short_url, $redirs = 10, $protected = false) {
/**
* Check if this URL is a redirect and return redir info.
*
* Most code should call File_redirection::where instead, to check if we
* already know that redirection and avoid extra hits to the web.
*
* The URL is hit and any redirects are followed, up to 10 levels or until
* a protected URL is reached.
*
* @param string $in_url
* @return mixed one of:
* string - target URL, if this is a direct link or can't be followed
* array - redirect info if this is an *unknown* redirect:
* associative array with the following elements:
* code: HTTP status code
* redirects: count of redirects followed
* url: URL string of final target
* type (optional): MIME type from Content-Type header
* size (optional): byte size from Content-Length header
* time (optional): timestamp from Last-Modified header
*/
public function lookupWhere($short_url, $redirs = 10, $protected = false) {
if ($redirs < 0) return false;
// let's see if we know this...
$a = File::staticGet('url', $short_url);
if (!empty($a)) {
// this is a direct link to $a->url
return $a->url;
} else {
$b = File_redirection::staticGet('url', $short_url);
if (!empty($b)) {
// this is a redirect to $b->file_id
$a = File::staticGet('id', $b->file_id);
return $a->url;
}
}
if(strpos($short_url,'://') === false){
return $short_url;
}
@ -93,12 +99,13 @@ class File_redirection extends Memcached_DataObject
}
} catch (Exception $e) {
// Invalid URL or failure to reach server
common_log(LOG_ERR, "Error while following redirects for $short_url: " . $e->getMessage());
return $short_url;
}
if ($response->getRedirectCount() && File::isProtected($response->getUrl())) {
// Bump back up the redirect chain until we find a non-protected URL
return self::_redirectWhere_imp($short_url, $response->getRedirectCount() - 1, true);
return self::lookupWhere($short_url, $response->getRedirectCount() - 1, true);
}
$ret = array('code' => $response->getStatus()
@ -115,11 +122,60 @@ class File_redirection extends Memcached_DataObject
return $ret;
}
function where($in_url) {
$ret = File_redirection::_redirectWhere_imp($in_url);
/**
* Check if this URL is a redirect and return redir info.
* If a File record is present for this URL, it is not considered a redirect.
* If a File_redirection record is present for this URL, the recorded target is returned.
*
* If no File or File_redirect record is present, the URL is hit and any
* redirects are followed, up to 10 levels or until a protected URL is
* reached.
*
* @param string $in_url
* @return mixed one of:
* string - target URL, if this is a direct link or a known redirect
* array - redirect info if this is an *unknown* redirect:
* associative array with the following elements:
* code: HTTP status code
* redirects: count of redirects followed
* url: URL string of final target
* type (optional): MIME type from Content-Type header
* size (optional): byte size from Content-Length header
* time (optional): timestamp from Last-Modified header
*/
public function where($in_url) {
// let's see if we know this...
$a = File::staticGet('url', $in_url);
if (!empty($a)) {
// this is a direct link to $a->url
return $a->url;
} else {
$b = File_redirection::staticGet('url', $in_url);
if (!empty($b)) {
// this is a redirect to $b->file_id
$a = File::staticGet('id', $b->file_id);
return $a->url;
}
}
$ret = File_redirection::lookupWhere($in_url);
return $ret;
}
/**
* Shorten a URL with the current user's configured shortening
* options, if applicable.
*
* If it cannot be shortened or the "short" URL is longer than the
* original, the original is returned.
*
* If the referenced item has not been seen before, embedding data
* may be saved.
*
* @param string $long_url
* @return string
*/
function makeShort($long_url) {
$canon = File_redirection::_canonUrl($long_url);
@ -141,11 +197,20 @@ class File_redirection extends Memcached_DataObject
// store it
$file = File::staticGet('url', $long_url);
if (empty($file)) {
// Check if the target URL is itself a redirect...
$redir_data = File_redirection::where($long_url);
$file = File::saveNew($redir_data, $long_url);
$file_id = $file->id;
if (!empty($redir_data['oembed']['json'])) {
File_oembed::saveNew($redir_data['oembed']['json'], $file_id);
if (is_array($redir_data)) {
// We haven't seen the target URL before.
// Save file and embedding data about it!
$file = File::saveNew($redir_data, $long_url);
$file_id = $file->id;
if (!empty($redir_data['oembed']['json'])) {
File_oembed::saveNew($redir_data['oembed']['json'], $file_id);
}
} else if (is_string($redir_data)) {
// The file is a known redirect target.
$file = File::staticGet('url', $redir_data);
$file_id = $file->id;
}
} else {
$file_id = $file->id;

View File

@ -34,7 +34,7 @@ class Group_alias extends Memcached_DataObject
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/* Static get */
function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Group_alias',$k,$v); }
function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Group_alias',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE

View File

@ -1128,6 +1128,7 @@ class Notice extends Memcached_DataObject
if ($source) {
$xs->elementStart('source');
$xs->element('id', null, $profile->profileurl);
$xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
$xs->element('link', array('href' => $profile->profileurl));
$user = User::staticGet('id', $profile->id);
@ -1143,13 +1144,14 @@ class Notice extends Memcached_DataObject
}
$xs->element('icon', null, $profile->avatarUrl(AVATAR_PROFILE_SIZE));
$xs->element('updated', null, common_date_w3dtf($this->created));
}
if ($source) {
$xs->elementEnd('source');
}
$xs->element('title', null, $this->content);
$xs->element('title', null, common_xml_safe_str($this->content));
if ($author) {
$xs->raw($profile->asAtomAuthor());
@ -1225,7 +1227,11 @@ class Notice extends Memcached_DataObject
}
}
$xs->element('content', array('type' => 'html'), $this->rendered);
$xs->element(
'content',
array('type' => 'html'),
common_xml_safe_str($this->rendered)
);
$tag = new Notice_tag();
$tag->notice_id = $this->id;

View File

@ -147,14 +147,16 @@ class Profile extends Memcached_DataObject
return ($this->fullname) ? $this->fullname : $this->nickname;
}
# Get latest notice on or before date; default now
function getCurrentNotice($dt=null)
/**
* Get the most recent notice posted by this user, if any.
*
* @return mixed Notice or null
*/
function getCurrentNotice()
{
$notice = new Notice();
$notice->profile_id = $this->id;
if ($dt) {
$notice->whereAdd('created < "' . $dt . '"');
}
// @fixme change this to sort on notice.id only when indexes are updated
$notice->orderBy('created DESC, notice.id DESC');
$notice->limit(1);
if ($notice->find(true)) {

View File

@ -132,13 +132,18 @@ class User extends Memcached_DataObject
return !in_array($nickname, $blacklist);
}
function getCurrentNotice($dt=null)
/**
* Get the most recent notice posted by this user, if any.
*
* @return mixed Notice or null
*/
function getCurrentNotice()
{
$profile = $this->getProfile();
if (!$profile) {
return null;
}
return $profile->getCurrentNotice($dt);
return $profile->getCurrentNotice();
}
function getCarrier()
@ -206,6 +211,7 @@ class User extends Memcached_DataObject
if(! User::allowed_nickname($nickname)){
common_log(LOG_WARNING, sprintf("Attempted to register a nickname that is not allowed: %s", $profile->nickname),
__FILE__);
return false;
}
$profile->profileurl = common_profile_url($nickname);

View File

@ -295,7 +295,7 @@ class User_group extends Memcached_DataObject
}
// If not, check local groups.
$group = Local_group::staticGet('nickname', $nickname);
if (!empty($group)) {
return User_group::staticGet('id', $group->group_id);
@ -371,16 +371,15 @@ class User_group extends Memcached_DataObject
if ($source) {
$xs->elementStart('source');
$xs->element('id', null, $this->permalink());
$xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
$xs->element('link', array('href' => $this->permalink()));
}
if ($source) {
$xs->element('updated', null, $this->modified);
$xs->elementEnd('source');
}
$xs->element('title', null, $this->nickname);
$xs->element('summary', null, $this->description);
$xs->element('summary', null, common_xml_safe_str($this->description));
$xs->element('link', array('rel' => 'alternate',
'href' => $this->permalink()));
@ -390,7 +389,11 @@ class User_group extends Memcached_DataObject
$xs->element('published', null, common_date_w3dtf($this->created));
$xs->element('updated', null, common_date_w3dtf($this->modified));
$xs->element('content', array('type' => 'html'), $this->description);
$xs->element(
'content',
array('type' => 'html'),
common_xml_safe_str($this->description)
);
$xs->elementEnd('entry');
@ -455,7 +458,7 @@ class User_group extends Memcached_DataObject
$group = new User_group();
$group->query('BEGIN');
if (empty($uri)) {
// fill in later...
$uri = null;
@ -483,7 +486,7 @@ class User_group extends Memcached_DataObject
$result = $group->update($orig);
if (!$result) {
common_log_db_error($group, 'UPDATE', __FILE__);
throw new ServerException(_('Could not set group uri.'));
throw new ServerException(_('Could not set group URI.'));
}
}

View File

@ -55,7 +55,7 @@ class User_username extends Memcached_DataObject
// now define the keys.
function keys() {
return array('provider_name', 'username');
return array('provider_name' => 'K', 'username' => 'K');
}
}

View File

@ -197,7 +197,7 @@ $config['sphinx']['port'] = 3312;
//
// $config['twitterimport']['enabled'] = true;
// Twitter OAuth settings
// Twitter OAuth settings. Documentation is at http://apiwiki.twitter.com/OAuth-FAQ
// $config['twitter']['consumer_key'] = 'YOURKEY';
// $config['twitter']['consumer_secret'] = 'YOURSECRET';
@ -268,6 +268,8 @@ $config['sphinx']['port'] = 3312;
// Support for file uploads (attachments),
// select supported mimetypes and quotas (in bytes)
// $config['attachments']['supported'] = array('image/png', 'application/ogg');
// $config['attachments']['supported'] = true; //allow all file types to be uploaded
// $config['attachments']['file_quota'] = 5000000;
// $config['attachments']['user_quota'] = 50000000;
// $config['attachments']['monthly_quota'] = 15000000;

103
index.php
View File

@ -37,8 +37,6 @@ define('INSTALLDIR', dirname(__FILE__));
define('STATUSNET', true);
define('LACONICA', true); // compatibility
require_once INSTALLDIR . '/lib/common.php';
$user = null;
$action = null;
@ -68,52 +66,69 @@ function getPath($req)
*/
function handleError($error)
{
if ($error->getCode() == DB_DATAOBJECT_ERROR_NODATA) {
return;
}
try {
$logmsg = "PEAR error: " . $error->getMessage();
if (common_config('site', 'logdebug')) {
$logmsg .= " : ". $error->getDebugInfo();
}
// DB queries often end up with a lot of newlines; merge to a single line
// for easier grepability...
$logmsg = str_replace("\n", " ", $logmsg);
common_log(LOG_ERR, $logmsg);
// @fixme backtrace output should be consistent with exception handling
if (common_config('site', 'logdebug')) {
$bt = $error->getBacktrace();
foreach ($bt as $n => $line) {
common_log(LOG_ERR, formatBacktraceLine($n, $line));
if ($error->getCode() == DB_DATAOBJECT_ERROR_NODATA) {
return;
}
}
if ($error instanceof DB_DataObject_Error
|| $error instanceof DB_Error
) {
$msg = sprintf(
_(
'The database for %s isn\'t responding correctly, '.
'so the site won\'t work properly. '.
'The site admins probably know about the problem, '.
'but you can contact them at %s to make sure. '.
'Otherwise, wait a few minutes and try again.'
),
common_config('site', 'name'),
common_config('site', 'email')
);
} else {
$msg = _(
'An important error occured, probably related to email setup. '.
'Check logfiles for more info..'
);
}
$dac = new DBErrorAction($msg, 500);
$dac->showPage();
$logmsg = "PEAR error: " . $error->getMessage();
if ($error instanceof PEAR_Exception && common_config('site', 'logdebug')) {
$logmsg .= " : ". $error->toText();
}
// DB queries often end up with a lot of newlines; merge to a single line
// for easier grepability...
$logmsg = str_replace("\n", " ", $logmsg);
common_log(LOG_ERR, $logmsg);
// @fixme backtrace output should be consistent with exception handling
if (common_config('site', 'logdebug')) {
$bt = $error->getTrace();
foreach ($bt as $n => $line) {
common_log(LOG_ERR, formatBacktraceLine($n, $line));
}
}
if ($error instanceof DB_DataObject_Error
|| $error instanceof DB_Error
|| ($error instanceof PEAR_Exception && $error->getCode() == -24)
) {
//If we run into a DB error, assume we can't connect to the DB at all
//so set the current user to null, so we don't try to access the DB
//while rendering the error page.
global $_cur;
$_cur = null;
$msg = sprintf(
_(
'The database for %s isn\'t responding correctly, '.
'so the site won\'t work properly. '.
'The site admins probably know about the problem, '.
'but you can contact them at %s to make sure. '.
'Otherwise, wait a few minutes and try again.'
),
common_config('site', 'name'),
common_config('site', 'email')
);
} else {
$msg = _(
'An important error occured, probably related to email setup. '.
'Check logfiles for more info..'
);
}
$dac = new DBErrorAction($msg, 500);
$dac->showPage();
} catch (Exception $e) {
echo _('An error occurred.');
}
exit(-1);
}
set_exception_handler('handleError');
require_once INSTALLDIR . '/lib/common.php';
/**
* Format a backtrace line for debug output roughly like debug_print_backtrace() does.
* Exceptions already have this built in, but PEAR error objects just give us the array.
@ -238,10 +253,6 @@ function main()
return;
}
// For database errors
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');
// Make sure RW database is setup
setupRW();

View File

@ -426,39 +426,69 @@ class Action extends HTMLOutputter // lawsuit
$this->elementStart('ul', array('class' => 'nav'));
if (Event::handle('StartPrimaryNav', array($this))) {
if ($user) {
// TRANS: Tooltip for main menu option "Personal"
$tooltip = _m('TOOLTIP', 'Personal profile and friends timeline');
// TRANS: Main menu option when logged in for access to personal profile and friends timeline
$this->menuItem(common_local_url('all', array('nickname' => $user->nickname)),
_('Home'), _('Personal profile and friends timeline'), false, 'nav_home');
_m('MENU', 'Personal'), $tooltip, false, 'nav_home');
// TRANS: Tooltip for main menu option "Account"
$tooltip = _m('TOOLTIP', 'Change your email, avatar, password, profile');
// TRANS: Main menu option when logged in for access to user settings
$this->menuItem(common_local_url('profilesettings'),
_('Account'), _('Change your email, avatar, password, profile'), false, 'nav_account');
_('Account'), $tooltip, false, 'nav_account');
// TRANS: Tooltip for main menu option "Services"
$tooltip = _m('TOOLTIP', 'Connect to services');
// TRANS: Main menu option when logged in and connection are possible for access to options to connect to other services
$this->menuItem(common_local_url('oauthconnectionssettings'),
_('Connect'), _('Connect to services'), false, 'nav_connect');
_('Connect'), $tooltip, false, 'nav_connect');
if ($user->hasRight(Right::CONFIGURESITE)) {
// TRANS: Tooltip for menu option "Admin"
$tooltip = _m('TOOLTIP', 'Change site configuration');
// TRANS: Main menu option when logged in and site admin for access to site configuration
$this->menuItem(common_local_url('siteadminpanel'),
_('Admin'), _('Change site configuration'), false, 'nav_admin');
_m('MENU', 'Admin'), $tooltip, false, 'nav_admin');
}
if (common_config('invite', 'enabled')) {
// TRANS: Tooltip for main menu option "Invite"
$tooltip = _m('TOOLTIP', 'Invite friends and colleagues to join you on %s');
// TRANS: Main menu option when logged in and invitations are allowed for inviting new users
$this->menuItem(common_local_url('invite'),
_('Invite'),
sprintf(_('Invite friends and colleagues to join you on %s'),
_m('MENU', 'Invite'),
sprintf($tooltip,
common_config('site', 'name')),
false, 'nav_invitecontact');
}
// TRANS: Tooltip for main menu option "Logout"
$tooltip = _m('TOOLTIP', 'Logout from the site');
// TRANS: Main menu option when logged in to log out the current user
$this->menuItem(common_local_url('logout'),
_('Logout'), _('Logout from the site'), false, 'nav_logout');
_m('MENU', 'Logout'), $tooltip, false, 'nav_logout');
}
else {
if (!common_config('site', 'closed')) {
// TRANS: Tooltip for main menu option "Register"
$tooltip = _m('TOOLTIP', 'Create an account');
// TRANS: Main menu option when not logged in to register a new account
$this->menuItem(common_local_url('register'),
_('Register'), _('Create an account'), false, 'nav_register');
_m('MENU', 'Register'), $tooltip, false, 'nav_register');
}
// TRANS: Tooltip for main menu option "Login"
$tooltip = _m('TOOLTIP', 'Login to the site');
// TRANS: Main menu option when not logged in to log in
$this->menuItem(common_local_url('login'),
_('Login'), _('Login to the site'), false, 'nav_login');
_m('MENU', 'Login'), $tooltip, false, 'nav_login');
}
// TRANS: Tooltip for main menu option "Help"
$tooltip = _m('TOOLTIP', 'Help me!');
// TRANS: Main menu option for help on the StatusNet site
$this->menuItem(common_local_url('doc', array('title' => 'help')),
_('Help'), _('Help me!'), false, 'nav_help');
_m('MENU', 'Help'), $tooltip, false, 'nav_help');
if ($user || !common_config('site', 'private')) {
// TRANS: Tooltip for main menu option "Search"
$tooltip = _m('TOOLTIP', 'Search for people or text');
// TRANS: Main menu option when logged in or when the StatusNet instance is not private
$this->menuItem(common_local_url('peoplesearch'),
_('Search'), _('Search for people or text'), false, 'nav_search');
_m('MENU', 'Search'), $tooltip, false, 'nav_search');
}
Event::handle('EndPrimaryNav', array($this));
}
@ -479,6 +509,7 @@ class Action extends HTMLOutputter // lawsuit
if ($text) {
$this->elementStart('dl', array('id' => 'site_notice',
'class' => 'system_notice'));
// TRANS: DT element for site notice. String is hidden in default CSS.
$this->element('dt', null, _('Site notice'));
$this->elementStart('dd', null);
$this->raw($text);

View File

@ -78,7 +78,7 @@ class PoCoAddress
if (!empty($this->formatted)) {
$xs = new XMLStringer(true);
$xs->elementStart('poco:address');
$xs->element('poco:formatted', null, $this->formatted);
$xs->element('poco:formatted', null, common_xml_safe_str($this->formatted));
$xs->elementEnd('poco:address');
return $xs->getString();
}
@ -279,7 +279,7 @@ class PoCo
);
if (!empty($this->note)) {
$xs->element('poco:note', null, $this->note);
$xs->element('poco:note', null, common_xml_safe_str($this->note));
}
if (!empty($this->address)) {
@ -805,7 +805,6 @@ class ActivityObject
return $object;
}
function asString($tag='activity:object')
{
$xs = new XMLStringer(true);
@ -817,16 +816,28 @@ class ActivityObject
$xs->element(self::ID, null, $this->id);
if (!empty($this->title)) {
$xs->element(self::TITLE, null, $this->title);
$xs->element(
self::TITLE,
null,
common_xml_safe_str($this->title)
);
}
if (!empty($this->summary)) {
$xs->element(self::SUMMARY, null, $this->summary);
$xs->element(
self::SUMMARY,
null,
common_xml_safe_str($this->summary)
);
}
if (!empty($this->content)) {
// XXX: assuming HTML content here
$xs->element(ActivityUtils::CONTENT, array('type' => 'html'), $this->content);
$xs->element(
ActivityUtils::CONTENT,
array('type' => 'html'),
common_xml_safe_str($this->content)
);
}
if (!empty($this->link)) {

View File

@ -69,6 +69,7 @@ class AdminPanelAction extends Action
// User must be logged in.
if (!common_logged_in()) {
// TRANS: Client error message
$this->clientError(_('Not logged in.'));
return false;
}
@ -93,6 +94,7 @@ class AdminPanelAction extends Action
// User must have the right to change admin settings
if (!$user->hasRight(Right::CONFIGURESITE)) {
// TRANS: Client error message
$this->clientError(_('You cannot make changes to this site.'));
return false;
}
@ -104,6 +106,7 @@ class AdminPanelAction extends Action
$name = mb_substr($name, 0, -10);
if (!self::canAdmin($name)) {
// TRANS: Client error message
$this->clientError(_('Changes to that panel are not allowed.'), 403);
return false;
}
@ -134,6 +137,7 @@ class AdminPanelAction extends Action
Config::loadSettings();
$this->success = true;
// TRANS: Message after successful saving of administrative settings.
$this->msg = _('Settings saved.');
} catch (Exception $e) {
$this->success = false;
@ -221,6 +225,7 @@ class AdminPanelAction extends Action
function showForm()
{
// TRANS: Client error message
$this->clientError(_('showForm() not implemented.'));
return;
}
@ -250,6 +255,7 @@ class AdminPanelAction extends Action
function saveSettings()
{
// TRANS: Client error message
$this->clientError(_('saveSettings() not implemented.'));
return;
}
@ -273,6 +279,7 @@ class AdminPanelAction extends Action
$result = $config->delete();
if (!$result) {
common_log_db_error($config, 'DELETE', __FILE__);
// TRANS: Client error message
$this->clientError(_("Unable to delete design setting."));
return null;
}
@ -337,43 +344,67 @@ class AdminPanelNav extends Widget
if (Event::handle('StartAdminPanelNav', array($this))) {
if (AdminPanelAction::canAdmin('site')) {
$this->out->menuItem(common_local_url('siteadminpanel'), _('Site'),
_('Basic site configuration'), $action_name == 'siteadminpanel', 'nav_site_admin_panel');
// TRANS: Menu item title/tooltip
$menu_title = _('Basic site configuration');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('siteadminpanel'), _m('MENU', 'Site'),
$menu_title, $action_name == 'siteadminpanel', 'nav_site_admin_panel');
}
if (AdminPanelAction::canAdmin('design')) {
$this->out->menuItem(common_local_url('designadminpanel'), _('Design'),
_('Design configuration'), $action_name == 'designadminpanel', 'nav_design_admin_panel');
// TRANS: Menu item title/tooltip
$menu_title = _('Design configuration');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('designadminpanel'), _m('MENU', 'Design'),
$menu_title, $action_name == 'designadminpanel', 'nav_design_admin_panel');
}
if (AdminPanelAction::canAdmin('user')) {
// TRANS: Menu item title/tooltip
$menu_title = _('User configuration');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('useradminpanel'), _('User'),
_('User configuration'), $action_name == 'useradminpanel', 'nav_user_admin_panel');
$menu_title, $action_name == 'useradminpanel', 'nav_user_admin_panel');
}
if (AdminPanelAction::canAdmin('access')) {
// TRANS: Menu item title/tooltip
$menu_title = _('Access configuration');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('accessadminpanel'), _('Access'),
_('Access configuration'), $action_name == 'accessadminpanel', 'nav_access_admin_panel');
$menu_title, $action_name == 'accessadminpanel', 'nav_access_admin_panel');
}
if (AdminPanelAction::canAdmin('paths')) {
// TRANS: Menu item title/tooltip
$menu_title = _('Paths configuration');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
_('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_paths_admin_panel');
$menu_title, $action_name == 'pathsadminpanel', 'nav_paths_admin_panel');
}
if (AdminPanelAction::canAdmin('sessions')) {
// TRANS: Menu item title/tooltip
$menu_title = _('Sessions configuration');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('sessionsadminpanel'), _('Sessions'),
_('Sessions configuration'), $action_name == 'sessionsadminpanel', 'nav_sessions_admin_panel');
$menu_title, $action_name == 'sessionsadminpanel', 'nav_sessions_admin_panel');
}
if (AdminPanelAction::canAdmin('sitenotice')) {
// TRANS: Menu item title/tooltip
$menu_title = _('Edit site notice');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('sitenoticeadminpanel'), _('Site notice'),
_('Edit site notice'), $action_name == 'sitenoticeadminpanel', 'nav_sitenotice_admin_panel');
$menu_title, $action_name == 'sitenoticeadminpanel', 'nav_sitenotice_admin_panel');
}
if (AdminPanelAction::canAdmin('snapshot')) {
// TRANS: Menu item title/tooltip
$menu_title = _('Snapshots configuration');
// TRANS: Menu item for site administration
$this->out->menuItem(common_local_url('snapshotadminpanel'), _('Snapshots'),
_('Snapshots configuration'), $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel');
$menu_title, $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel');
}
Event::handle('EndAdminPanelNav', array($this));

View File

@ -491,7 +491,7 @@ class ApiAction extends Action
$this->showXmlAttachments($twitter_status['attachments']);
break;
case 'geo':
$this->showGeoRSS($value);
$this->showGeoXML($value);
break;
case 'retweeted_status':
$this->showTwitterXmlStatus($value, 'retweeted_status');
@ -539,7 +539,7 @@ class ApiAction extends Action
}
}
function showGeoRSS($geo)
function showGeoXML($geo)
{
if (empty($geo)) {
// empty geo element
@ -551,6 +551,17 @@ class ApiAction extends Action
}
}
function showGeoRSS($geo)
{
if (!empty($geo)) {
$this->element(
'georss:point',
null,
$geo['coordinates'][0] . ' ' . $geo['coordinates'][1]
);
}
}
function showTwitterRssItem($entry)
{
$this->elementStart('item');
@ -619,13 +630,25 @@ class ApiAction extends Action
$this->endDocument('xml');
}
function showRssTimeline($notice, $title, $link, $subtitle, $suplink=null, $logo=null)
function showRssTimeline($notice, $title, $link, $subtitle, $suplink = null, $logo = null, $self = null)
{
$this->initDocument('rss');
$this->element('title', null, $title);
$this->element('link', null, $link);
if (!is_null($self)) {
$this->element(
'atom:link',
array(
'type' => 'application/rss+xml',
'href' => $self,
'rel' => 'self'
)
);
}
if (!is_null($suplink)) {
// For FriendFeed's SUP protocol
$this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom',
@ -732,8 +755,12 @@ class ApiAction extends Action
function showTwitterAtomEntry($entry)
{
$this->elementStart('entry');
$this->element('title', null, $entry['title']);
$this->element('content', array('type' => 'html'), $entry['content']);
$this->element('title', null, common_xml_safe_str($entry['title']));
$this->element(
'content',
array('type' => 'html'),
common_xml_safe_str($entry['content'])
);
$this->element('id', null, $entry['id']);
$this->element('published', null, $entry['published']);
$this->element('updated', null, $entry['updated']);
@ -848,7 +875,7 @@ class ApiAction extends Action
$this->initDocument('atom');
$this->element('title', null, $title);
$this->element('title', null, common_xml_safe_str($title));
$this->element('id', null, $id);
$this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
@ -858,7 +885,7 @@ class ApiAction extends Action
}
$this->element('updated', null, common_date_iso8601('now'));
$this->element('subtitle', null, $subtitle);
$this->element('subtitle', null, common_xml_safe_str($subtitle));
if (is_array($group)) {
foreach ($group as $g) {
@ -1138,7 +1165,14 @@ class ApiAction extends Action
function initTwitterRss()
{
$this->startXML();
$this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
$this->elementStart(
'rss',
array(
'version' => '2.0',
'xmlns:atom' => 'http://www.w3.org/2005/Atom',
'xmlns:georss' => 'http://www.georss.org/georss'
)
);
$this->elementStart('channel');
Event::handle('StartApiRss', array($this));
}
@ -1336,8 +1370,27 @@ class ApiAction extends Action
}
}
function getSelfUri($action, $aargs)
/**
* Calculate the complete URI that called up this action. Used for
* Atom rel="self" links. Warning: this is funky.
*
* @return string URL a URL suitable for rel="self" Atom links
*/
function getSelfUri()
{
$action = mb_substr(get_class($this), 0, -6); // remove 'Action'
$id = $this->arg('id');
$aargs = array('format' => $this->format);
if (!empty($id)) {
$aargs['id'] = $id;
}
$tag = $this->arg('tag');
if (!empty($tag)) {
$aargs['tag'] = $tag;
}
parse_str($_SERVER['QUERY_STRING'], $params);
$pstring = '';
if (!empty($params)) {

View File

@ -178,7 +178,7 @@ class Atom10Feed extends XMLStringer
$this->element(
'generator', array(
'url' => 'http://status.net',
'uri' => 'http://status.net',
'version' => STATUSNET_VERSION
),
'StatusNet'

View File

@ -69,13 +69,17 @@ abstract class AuthenticationPlugin extends Plugin
/**
* Automatically register a user when they attempt to login with valid credentials.
* User::register($data) is a very useful method for this implementation
* @param username
* @param username username (that is used to login and find the user in the authentication provider) of the user to be registered
* @param nickname nickname of the user in the SN system. If nickname is null, then set nickname = username
* @return mixed instance of User, or false (if user couldn't be created)
*/
function autoRegister($username)
function autoRegister($username, $nickname = null)
{
if(is_null($nickname)){
$nickname = $username;
}
$registration_data = array();
$registration_data['nickname'] = $username ;
$registration_data['nickname'] = $nickname;
return User::register($registration_data);
}
@ -92,6 +96,21 @@ abstract class AuthenticationPlugin extends Plugin
return false;
}
/**
* Given a username, suggest what the nickname should be
* Used during autoregistration
* Useful if your usernames are ugly, and you want to suggest
* nice looking nicknames when users initially sign on
* All nicknames returned by this function should be valid
* implementations may want to use common_nicknamize() to ensure validity
* @param username
* @return string nickname
*/
function suggestNicknameForUsername($username)
{
return common_nicknamize($username);
}
//------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\
function onInitializePlugin(){
if(!isset($this->provider_name)){
@ -108,10 +127,22 @@ abstract class AuthenticationPlugin extends Plugin
function onAutoRegister($nickname, $provider_name, &$user)
{
if($provider_name == $this->provider_name && $this->autoregistration){
$user = $this->autoregister($nickname);
if($user){
User_username::register($user,$nickname,$this->provider_name);
return false;
$suggested_nickname = $this->suggestNicknameForUsername($nickname);
$test_user = User::staticGet('nickname', $suggested_nickname);
if($test_user) {
//someone already exists with the suggested nickname, so used the passed nickname
$suggested_nickname = common_nicknamize($nickname);
}
$test_user = User::staticGet('nickname', $suggested_nickname);
if($test_user) {
//someone already exists with the suggested nickname
//not much else we can do
}else{
$user = $this->autoRegister($nickname, $suggested_nickname);
if($user){
User_username::register($user,$nickname,$this->provider_name);
return false;
}
}
}
}
@ -122,23 +153,30 @@ abstract class AuthenticationPlugin extends Plugin
$user_username->username=$nickname;
$user_username->provider_name=$this->provider_name;
if($user_username->find() && $user_username->fetch()){
$username = $user_username->username;
$authenticated = $this->checkPassword($username, $password);
$authenticated = $this->checkPassword($user_username->username, $password);
if($authenticated){
$authenticatedUser = User::staticGet('id', $user_username->user_id);
return false;
}
}else{
$user = User::staticGet('nickname', $nickname);
//$nickname is the username used to login
//$suggested_nickname is the nickname the auth provider suggests for that username
$suggested_nickname = $this->suggestNicknameForUsername($nickname);
$user = User::staticGet('nickname', $suggested_nickname);
if($user){
//make sure a different provider isn't handling this nickname
//make sure this user isn't claimed
$user_username = new User_username();
$user_username->username=$nickname;
if(!$user_username->find()){
//no other provider claims this username, so it's safe for us to handle it
$user_username->user_id=$user->id;
$we_can_handle = false;
if($user_username->find()){
//either this provider, or another one, has already claimed this user
//so we cannot. Let another plugin try.
return;
}else{
//no other provider claims this user, so it's safe for us to handle it
$authenticated = $this->checkPassword($nickname, $password);
if($authenticated){
$authenticatedUser = User::staticGet('nickname', $nickname);
$authenticatedUser = $user;
User_username::register($authenticatedUser,$nickname,$this->provider_name);
return false;
}

View File

@ -85,7 +85,7 @@ abstract class AuthorizationPlugin extends Plugin
}
function onStartSetApiUser(&$user) {
return $this->onStartSetUser(&$user);
return $this->onStartSetUser($user);
}
function onStartHasRole($profile, $name, &$has_role) {

View File

@ -711,6 +711,34 @@ class LoginCommand extends Command
}
}
class LoseCommand extends Command
{
var $other = null;
function __construct($user, $other)
{
parent::__construct($user);
$this->other = $other;
}
function execute($channel)
{
if(!$this->other) {
$channel->error($this->user, _('Specify the name of the user to unsubscribe from'));
return;
}
$result = Subscription::cancel($this->getProfile($this->other), $this->user->getProfile());
if ($result) {
$channel->output($this->user, sprintf(_('Unsubscribed %s'), $this->other));
} else {
$channel->error($this->user, $result);
}
}
}
class SubscriptionsCommand extends Command
{
function handle($channel)
@ -793,6 +821,7 @@ class HelpCommand extends Command
"d <nickname> <text> - direct message to user\n".
"get <nickname> - get last notice from user\n".
"whois <nickname> - get profile info on user\n".
"lose <nickname> - force user to stop following you\n".
"fav <nickname> - add user's last notice as a 'fave'\n".
"fav #<notice_id> - add notice with the given id as a 'fave'\n".
"repeat #<notice_id> - repeat a notice with a given id\n".

View File

@ -47,6 +47,17 @@ class CommandInterpreter
} else {
return new LoginCommand($user);
}
case 'lose':
if ($arg) {
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new LoseCommand($user, $other);
}
} else {
return null;
}
case 'subscribers':
if ($arg) {
return null;

View File

@ -71,6 +71,7 @@ if (!function_exists('dl')) {
# global configuration object
require_once('PEAR.php');
require_once('PEAR/Exception.php');
require_once('DB/DataObject.php');
require_once('DB/DataObject/Cast.php'); # for dates
@ -128,6 +129,17 @@ require_once INSTALLDIR.'/lib/activity.php';
require_once INSTALLDIR.'/lib/clientexception.php';
require_once INSTALLDIR.'/lib/serverexception.php';
//set PEAR error handling to use regular PHP exceptions
function PEAR_ErrorToPEAR_Exception($err)
{
if ($err->getCode()) {
throw new PEAR_Exception($err->getMessage(), $err->getCode());
}
throw new PEAR_Exception($err->getMessage());
}
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'PEAR_ErrorToPEAR_Exception');
try {
StatusNet::init(@$server, @$path, @$conffile);
} catch (NoConfigException $e) {

View File

@ -282,6 +282,7 @@ $default =
'Mapstraction' => null,
'OStatus' => null,
'WikiHashtags' => null,
'RSSCloud' => null,
'OpenID' => null),
),
'admin' =>

View File

@ -356,40 +356,47 @@ class HTMLOutputter extends XMLOutputter
if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment']))
{
$path = common_config('javascript', 'path');
if (strpos($src, 'plugins/') === 0 || strpos($src, 'local/') === 0) {
if (empty($path)) {
$path = common_config('site', 'path') . '/js/';
}
$src = common_path($src) . '?version=' . STATUSNET_VERSION;
if ($path[strlen($path)-1] != '/') {
$path .= '/';
}
}else{
if ($path[0] != '/') {
$path = '/'.$path;
}
$path = common_config('javascript', 'path');
$server = common_config('javascript', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('javascript', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('javascript', 'server')) {
$ssl = true;
} else {
$ssl = false;
if (empty($path)) {
$path = common_config('site', 'path') . '/js/';
}
if ($path[strlen($path)-1] != '/') {
$path .= '/';
}
if ($path[0] != '/') {
$path = '/'.$path;
}
$server = common_config('javascript', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('javascript', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('javascript', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
$src = $protocol.'://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
}
$protocol = ($ssl) ? 'https' : 'http';
$src = $protocol.'://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
}
$this->element('script', array('type' => $type,

View File

@ -120,6 +120,16 @@ class HTTPClient extends HTTP_Request2
{
$this->config['max_redirs'] = 10;
$this->config['follow_redirects'] = true;
// We've had some issues with keepalive breaking with
// HEAD requests, such as to youtube which seems to be
// emitting chunked encoding info for an empty body
// instead of not emitting anything. This may be a
// bug on YouTube's end, but the upstream libray
// ought to be investigated to see if we can handle
// it gracefully in that case as well.
$this->config['protocol_version'] = '1.0';
parent::__construct($url, $method, $config);
$this->setHeader('User-Agent', $this->userAgent());
}

View File

@ -60,6 +60,21 @@ class ImageFile
$this->filepath = $filepath;
$info = @getimagesize($this->filepath);
if (!(
($info[2] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) ||
($info[2] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) ||
$info[2] == IMAGETYPE_BMP ||
($info[2] == IMAGETYPE_WBMP && function_exists('imagecreatefromwbmp')) ||
($info[2] == IMAGETYPE_XBM && function_exists('imagecreatefromxbm')) ||
($info[2] == IMAGETYPE_XPM && function_exists('imagecreatefromxpm')) ||
($info[2] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')))) {
@unlink($_FILES[$param]['tmp_name']);
throw new Exception(_('Unsupported image file format.'));
return;
}
$this->type = ($info) ? $info[2]:$type;
$this->width = ($info) ? $info[0]:$width;
$this->height = ($info) ? $info[1]:$height;
@ -97,15 +112,6 @@ class ImageFile
return;
}
if ($info[2] !== IMAGETYPE_GIF &&
$info[2] !== IMAGETYPE_JPEG &&
$info[2] !== IMAGETYPE_PNG) {
@unlink($_FILES[$param]['tmp_name']);
throw new Exception(_('Unsupported image file format.'));
return;
}
return new ImageFile(null, $_FILES[$param]['tmp_name']);
}
@ -146,6 +152,18 @@ class ImageFile
case IMAGETYPE_PNG:
$image_src = imagecreatefrompng($this->filepath);
break;
case IMAGETYPE_BMP:
$image_src = imagecreatefrombmp($this->filepath);
break;
case IMAGETYPE_WBMP:
$image_src = imagecreatefromwbmp($this->filepath);
break;
case IMAGETYPE_XBM:
$image_src = imagecreatefromxbm($this->filepath);
break;
case IMAGETYPE_XPM:
$image_src = imagecreatefromxpm($this->filepath);
break;
default:
throw new Exception(_('Unknown file type'));
return;
@ -153,7 +171,7 @@ class ImageFile
$image_dest = imagecreatetruecolor($size, $size);
if ($this->type == IMAGETYPE_GIF || $this->type == IMAGETYPE_PNG) {
if ($this->type == IMAGETYPE_GIF || $this->type == IMAGETYPE_PNG || $this->type == IMAGETYPE_BMP) {
$transparent_idx = imagecolortransparent($image_src);
@ -176,6 +194,24 @@ class ImageFile
imagecopyresampled($image_dest, $image_src, 0, 0, $x, $y, $size, $size, $w, $h);
if($this->type == IMAGETYPE_BMP) {
//we don't want to save BMP... it's an inefficient, rare, antiquated format
//save png instead
$this->type = IMAGETYPE_PNG;
} else if($this->type == IMAGETYPE_WBMP) {
//we don't want to save WBMP... it's a rare format that we can't guarantee clients will support
//save png instead
$this->type = IMAGETYPE_PNG;
} else if($this->type == IMAGETYPE_XBM) {
//we don't want to save XBM... it's a rare format that we can't guarantee clients will support
//save png instead
$this->type = IMAGETYPE_PNG;
} else if($this->type == IMAGETYPE_XPM) {
//we don't want to save XPM... it's a rare format that we can't guarantee clients will support
//save png instead
$this->type = IMAGETYPE_PNG;
}
$outname = Avatar::filename($this->id,
image_type_to_extension($this->type),
$size,
@ -245,4 +281,101 @@ class ImageFile
return $num;
}
}
}
//PHP doesn't (as of 2/24/2010) have an imagecreatefrombmp so conditionally define one
if(!function_exists('imagecreatefrombmp')){
//taken shamelessly from http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
function imagecreatefrombmp($p_sFile)
{
// Load the image into a string
$file = fopen($p_sFile,"rb");
$read = fread($file,10);
while(!feof($file)&&($read<>""))
$read .= fread($file,1024);
$temp = unpack("H*",$read);
$hex = $temp[1];
$header = substr($hex,0,108);
// Process the header
// Structure: http://www.fastgraph.com/help/bmp_header_format.html
if (substr($header,0,4)=="424d")
{
// Cut it in parts of 2 bytes
$header_parts = str_split($header,2);
// Get the width 4 bytes
$width = hexdec($header_parts[19].$header_parts[18]);
// Get the height 4 bytes
$height = hexdec($header_parts[23].$header_parts[22]);
// Unset the header params
unset($header_parts);
}
// Define starting X and Y
$x = 0;
$y = 1;
// Create newimage
$image = imagecreatetruecolor($width,$height);
// Grab the body from the image
$body = substr($hex,108);
// Calculate if padding at the end-line is needed
// Divided by two to keep overview.
// 1 byte = 2 HEX-chars
$body_size = (strlen($body)/2);
$header_size = ($width*$height);
// Use end-line padding? Only when needed
$usePadding = ($body_size>($header_size*3)+4);
// Using a for-loop with index-calculation instaid of str_split to avoid large memory consumption
// Calculate the next DWORD-position in the body
for ($i=0;$i<$body_size;$i+=3)
{
// Calculate line-ending and padding
if ($x>=$width)
{
// If padding needed, ignore image-padding
// Shift i to the ending of the current 32-bit-block
if ($usePadding)
$i += $width%4;
// Reset horizontal position
$x = 0;
// Raise the height-position (bottom-up)
$y++;
// Reached the image-height? Break the for-loop
if ($y>$height)
break;
}
// Calculation of the RGB-pixel (defined as BGR in image-data)
// Define $i_pos as absolute position in the body
$i_pos = $i*2;
$r = hexdec($body[$i_pos+4].$body[$i_pos+5]);
$g = hexdec($body[$i_pos+2].$body[$i_pos+3]);
$b = hexdec($body[$i_pos].$body[$i_pos+1]);
// Calculate and draw the pixel
$color = imagecolorallocate($image,$r,$g,$b);
imagesetpixel($image,$x,$height-$y,$color);
// Raise the horizontal position
$x++;
}
// Unset the body / free the memory
unset($body);
// Return image-object
return $image;
}
}

View File

@ -330,7 +330,7 @@ abstract class IoMaster
* for per-queue and per-site records.
*
* @param string $key counter name
* @param array $owners list of owner keys like 'queue:jabber' or 'site:stat01'
* @param array $owners list of owner keys like 'queue:xmpp' or 'site:stat01'
*/
public function stats($key, $owners=array())
{

View File

@ -289,6 +289,7 @@ function get_all_languages() {
'ar' => array('q' => 0.8, 'lang' => 'ar', 'name' => 'Arabic', 'direction' => 'rtl'),
'arz' => array('q' => 0.8, 'lang' => 'arz', 'name' => 'Egyptian Spoken Arabic', 'direction' => 'rtl'),
'bg' => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'),
'br' => array('q' => 0.8, 'lang' => 'br', 'name' => 'Breton', 'direction' => 'ltr'),
'ca' => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'),
'cs' => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'),
'de' => array('q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'),

View File

@ -133,12 +133,13 @@ function mail_notify_from()
* @param User &$user user to send email to
* @param string $subject subject of the email
* @param string $body body of the email
* @param array $headers optional list of email headers
* @param string $address optional specification of email address
*
* @return boolean success flag
*/
function mail_to_user(&$user, $subject, $body, $address=null)
function mail_to_user(&$user, $subject, $body, $headers=array(), $address=null)
{
if (!$address) {
$address = $user->email;
@ -180,7 +181,9 @@ function mail_confirm_address($user, $code, $nickname, $address)
$nickname, common_config('site', 'name'),
common_local_url('confirmaddress', array('code' => $code)),
common_config('site', 'name'));
return mail_to_user($user, $subject, $body, $address);
$headers = array();
return mail_to_user($user, $subject, $body, $headers, $address);
}
/**
@ -231,6 +234,7 @@ function mail_subscribe_notify_profile($listenee, $other)
$recipients = $listenee->email;
$headers = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname);
$headers['From'] = mail_notify_from();
$headers['To'] = $name . ' <' . $listenee->email . '>';
$headers['Subject'] = sprintf(_('%1$s is now listening to '.
@ -476,7 +480,10 @@ function mail_notify_nudge($from, $to)
common_local_url('all', array('nickname' => $to->nickname)),
common_config('site', 'name'));
common_init_locale();
return mail_to_user($to, $subject, $body);
$headers = _mail_prepare_headers('nudge', $to->nickname, $from->nickname);
return mail_to_user($to, $subject, $body, $headers);
}
/**
@ -526,8 +533,10 @@ function mail_notify_message($message, $from=null, $to=null)
common_local_url('newmessage', array('to' => $from->id)),
common_config('site', 'name'));
$headers = _mail_prepare_headers('message', $to->nickname, $from->nickname);
common_init_locale();
return mail_to_user($to, $subject, $body);
return mail_to_user($to, $subject, $body, $headers);
}
/**
@ -578,8 +587,10 @@ function mail_notify_fave($other, $user, $notice)
common_config('site', 'name'),
$user->nickname);
$headers = _mail_prepare_headers('fave', $other->nickname, $user->nickname);
common_init_locale();
mail_to_user($other, $subject, $body);
mail_to_user($other, $subject, $body, $headers);
}
/**
@ -611,19 +622,19 @@ function mail_notify_attn($user, $notice)
common_init_locale($user->language);
if ($notice->conversation != $notice->id) {
$conversationEmailText = "The full conversation can be read here:\n\n".
"\t%5\$s\n\n ";
$conversationUrl = common_local_url('conversation',
if ($notice->conversation != $notice->id) {
$conversationEmailText = "The full conversation can be read here:\n\n".
"\t%5\$s\n\n ";
$conversationUrl = common_local_url('conversation',
array('id' => $notice->conversation)).'#notice-'.$notice->id;
} else {
$conversationEmailText = "%5\$s";
$conversationUrl = null;
}
} else {
$conversationEmailText = "%5\$s";
$conversationUrl = null;
}
$subject = sprintf(_('%s (@%s) sent a notice to your attention'), $bestname, $sender->nickname);
$body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
$body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
"The notice is here:\n\n".
"\t%3\$s\n\n" .
"It reads:\n\n".
@ -641,7 +652,7 @@ function mail_notify_attn($user, $notice)
common_local_url('shownotice',
array('notice' => $notice->id)),//%3
$notice->content,//%4
$conversationUrl,//%5
$conversationUrl,//%5
common_local_url('newnotice',
array('replyto' => $sender->nickname, 'inreplyto' => $notice->id)),//%6
common_local_url('replies',
@ -649,6 +660,30 @@ function mail_notify_attn($user, $notice)
common_local_url('emailsettings'), //%8
$sender->nickname); //%9
$headers = _mail_prepare_headers('mention', $user->nickname, $sender->nickname);
common_init_locale();
mail_to_user($user, $subject, $body);
mail_to_user($user, $subject, $body, $headers);
}
/**
* Prepare the common mail headers used in notification emails
*
* @param string $msg_type type of message being sent to the user
* @param string $to nickname of the receipient
* @param string $from nickname of the user triggering the notification
*
* @return array list of mail headers to include in the message
*/
function _mail_prepare_headers($msg_type, $to, $from)
{
$headers = array(
'X-StatusNet-MessageType' => $msg_type,
'X-StatusNet-TargetUser' => $to,
'X-StatusNet-SourceUser' => $from,
'X-StatusNet-Domain' => common_config('site', 'server')
);
return $headers;
}

View File

@ -262,7 +262,7 @@ class MediaFile
$filetype = MIME_Type::autoDetect($stream['uri']);
}
if (in_array($filetype, common_config('attachments', 'supported'))) {
if (common_config('attachments', 'supported') === true || in_array($filetype, common_config('attachments', 'supported'))) {
return $filetype;
}
$media = MIME_Type::getMedia($filetype);

View File

@ -175,6 +175,6 @@ class MessageForm extends Form
'class' => 'submit',
'name' => 'message_send',
'type' => 'submit',
'value' => _('Send')));
'value' => _m('Send button for sending notice', 'Send')));
}
}

View File

@ -90,15 +90,24 @@ class MysqlSchema extends Schema
* @param string $name Name of the table to get
*
* @return TableDef tabledef for that table.
* @throws SchemaTableMissingException
*/
public function getTableDef($name)
{
$res = $this->conn->query('DESCRIBE ' . $name);
$query = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS " .
"WHERE TABLE_SCHEMA='%s' AND TABLE_NAME='%s'";
$schema = $this->conn->dsn['database'];
$sql = sprintf($query, $schema, $name);
$res = $this->conn->query($sql);
if (PEAR::isError($res)) {
throw new Exception($res->getMessage());
}
if ($res->numRows() == 0) {
$res->free();
throw new SchemaTableMissingException("No such table: $name");
}
$td = new TableDef();
@ -111,9 +120,9 @@ class MysqlSchema extends Schema
$cd = new ColumnDef();
$cd->name = $row['Field'];
$cd->name = $row['COLUMN_NAME'];
$packed = $row['Type'];
$packed = $row['COLUMN_TYPE'];
if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
$cd->type = $match[1];
@ -122,17 +131,57 @@ class MysqlSchema extends Schema
$cd->type = $packed;
}
$cd->nullable = ($row['Null'] == 'YES') ? true : false;
$cd->key = $row['Key'];
$cd->default = $row['Default'];
$cd->extra = $row['Extra'];
$cd->nullable = ($row['IS_NULLABLE'] == 'YES') ? true : false;
$cd->key = $row['COLUMN_KEY'];
$cd->default = $row['COLUMN_DEFAULT'];
$cd->extra = $row['EXTRA'];
// Autoincrement is stuck into the extra column.
// Pull it out so we don't accidentally mod it every time...
$extra = preg_replace('/(^|\s)auto_increment(\s|$)/i', '$1$2', $cd->extra);
if ($extra != $cd->extra) {
$cd->extra = trim($extra);
$cd->auto_increment = true;
}
// mysql extensions -- not (yet) used by base class
$cd->charset = $row['CHARACTER_SET_NAME'];
$cd->collate = $row['COLLATION_NAME'];
$td->columns[] = $cd;
}
$res->free();
return $td;
}
/**
* Pull the given table properties from INFORMATION_SCHEMA.
* Most of the good stuff is MySQL extensions.
*
* @return array
* @throws Exception if table info can't be looked up
*/
function getTableProperties($table, $props)
{
$query = "SELECT %s FROM INFORMATION_SCHEMA.TABLES " .
"WHERE TABLE_SCHEMA='%s' AND TABLE_NAME='%s'";
$schema = $this->conn->dsn['database'];
$sql = sprintf($query, implode(',', $props), $schema, $table);
$res = $this->conn->query($sql);
$row = array();
$ok = $res->fetchInto($row, DB_FETCHMODE_ASSOC);
$res->free();
if ($ok) {
return $row;
} else {
throw new SchemaTableMissingException("No such table: $table");
}
}
/**
* Gets a ColumnDef object for a single column.
*
@ -185,35 +234,26 @@ class MysqlSchema extends Schema
}
$sql .= $this->_columnSql($cd);
switch ($cd->key) {
case 'UNI':
$uniques[] = $cd->name;
break;
case 'PRI':
$primary[] = $cd->name;
break;
case 'MUL':
$indices[] = $cd->name;
break;
}
}
if (count($primary) > 0) { // it really should be...
$sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")";
$idx = $this->_indexList($columns);
if ($idx['primary']) {
$sql .= ",\nconstraint primary key (" . implode(',', $idx['primary']) . ")";
}
foreach ($uniques as $u) {
$sql .= ",\nunique index {$name}_{$u}_idx ($u)";
foreach ($idx['uniques'] as $u) {
$key = $this->_uniqueKey($name, $u);
$sql .= ",\nunique index $key ($u)";
}
foreach ($indices as $i) {
$sql .= ",\nindex {$name}_{$i}_idx ($i)";
foreach ($idx['indices'] as $i) {
$key = $this->_key($name, $i);
$sql .= ",\nindex $key ($i)";
}
$sql .= "); ";
$sql .= ") ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; ";
common_log(LOG_INFO, $sql);
$res = $this->conn->query($sql);
if (PEAR::isError($res)) {
@ -223,6 +263,47 @@ class MysqlSchema extends Schema
return true;
}
/**
* Look over a list of column definitions and list up which
* indices will be present
*/
private function _indexList(array $columns)
{
$list = array('uniques' => array(),
'primary' => array(),
'indices' => array());
foreach ($columns as $cd) {
switch ($cd->key) {
case 'UNI':
$list['uniques'][] = $cd->name;
break;
case 'PRI':
$list['primary'][] = $cd->name;
break;
case 'MUL':
$list['indices'][] = $cd->name;
break;
}
}
return $list;
}
/**
* Get the unique index key name for a given column on this table
*/
function _uniqueKey($tableName, $columnName)
{
return $this->_key($tableName, $columnName);
}
/**
* Get the index key name for a given column on this table
*/
function _key($tableName, $columnName)
{
return "{$tableName}_{$columnName}_idx";
}
/**
* Drops a table from the schema
*
@ -394,21 +475,20 @@ class MysqlSchema extends Schema
try {
$td = $this->getTableDef($tableName);
} catch (Exception $e) {
if (preg_match('/no such table/', $e->getMessage())) {
return $this->createTable($tableName, $columns);
} else {
throw $e;
}
} catch (SchemaTableMissingException $e) {
return $this->createTable($tableName, $columns);
}
$cur = $this->_names($td->columns);
$new = $this->_names($columns);
$toadd = array_diff($new, $cur);
$todrop = array_diff($cur, $new);
$same = array_intersect($new, $cur);
$tomod = array();
$dropIndex = array();
$toadd = array_diff($new, $cur);
$todrop = array_diff($cur, $new);
$same = array_intersect($new, $cur);
$tomod = array();
$addIndex = array();
$tableProps = array();
foreach ($same as $m) {
$curCol = $this->_byName($td->columns, $m);
@ -416,10 +496,64 @@ class MysqlSchema extends Schema
if (!$newCol->equals($curCol)) {
$tomod[] = $newCol->name;
continue;
}
// Earlier versions may have accidentally left tables at default
// charsets which might be latin1 or other freakish things.
if ($this->_isString($curCol)) {
if ($curCol->charset != 'utf8') {
$tomod[] = $newCol->name;
continue;
}
}
}
if (count($toadd) + count($todrop) + count($tomod) == 0) {
// Find any indices we have to change...
$curIdx = $this->_indexList($td->columns);
$newIdx = $this->_indexList($columns);
if ($curIdx['primary'] != $newIdx['primary']) {
if ($curIdx['primary']) {
$dropIndex[] = 'drop primary key';
}
if ($newIdx['primary']) {
$keys = implode(',', $newIdx['primary']);
$addIndex[] = "add constraint primary key ($keys)";
}
}
$dropUnique = array_diff($curIdx['uniques'], $newIdx['uniques']);
$addUnique = array_diff($newIdx['uniques'], $curIdx['uniques']);
foreach ($dropUnique as $columnName) {
$dropIndex[] = 'drop key ' . $this->_uniqueKey($tableName, $columnName);
}
foreach ($addUnique as $columnName) {
$addIndex[] = 'add constraint unique key ' . $this->_uniqueKey($tableName, $columnName) . " ($columnName)";;
}
$dropMultiple = array_diff($curIdx['indices'], $newIdx['indices']);
$addMultiple = array_diff($newIdx['indices'], $curIdx['indices']);
foreach ($dropMultiple as $columnName) {
$dropIndex[] = 'drop key ' . $this->_key($tableName, $columnName);
}
foreach ($addMultiple as $columnName) {
$addIndex[] = 'add key ' . $this->_key($tableName, $columnName) . " ($columnName)";
}
// Check for table properties: make sure we're using a sane
// engine type and charset/collation.
// @fixme make the default engine configurable?
$oldProps = $this->getTableProperties($tableName, array('ENGINE', 'TABLE_COLLATION'));
if (strtolower($oldProps['ENGINE']) != 'innodb') {
$tableProps['ENGINE'] = 'InnoDB';
}
if (strtolower($oldProps['TABLE_COLLATION']) != 'utf8_bin') {
$tableProps['DEFAULT CHARSET'] = 'utf8';
$tableProps['COLLATE'] = 'utf8_bin';
}
if (count($dropIndex) + count($toadd) + count($todrop) + count($tomod) + count($addIndex) + count($tableProps) == 0) {
// nothing to do
return true;
}
@ -429,6 +563,10 @@ class MysqlSchema extends Schema
$phrase = array();
foreach ($dropIndex as $indexSql) {
$phrase[] = $indexSql;
}
foreach ($toadd as $columnName) {
$cd = $this->_byName($columns, $columnName);
@ -445,8 +583,17 @@ class MysqlSchema extends Schema
$phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
}
foreach ($addIndex as $indexSql) {
$phrase[] = $indexSql;
}
foreach ($tableProps as $key => $val) {
$phrase[] = "$key=$val";
}
$sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
common_log(LOG_DEBUG, __METHOD__ . ': ' . $sql);
$res = $this->conn->query($sql);
if (PEAR::isError($res)) {
@ -519,6 +666,10 @@ class MysqlSchema extends Schema
$sql .= "{$cd->type} ";
}
if ($this->_isString($cd)) {
$sql .= " CHARACTER SET utf8 ";
}
if (!empty($cd->default)) {
$sql .= "default {$cd->default} ";
} else {
@ -535,4 +686,13 @@ class MysqlSchema extends Schema
return $sql;
}
/**
* Is this column a string type?
*/
private function _isString(ColumnDef $cd)
{
$strings = array('char', 'varchar', 'text');
return in_array(strtolower($cd->type), $strings);
}
}

View File

@ -233,6 +233,6 @@ class NoticeForm extends Form
'class' => 'submit',
'name' => 'status_submit',
'type' => 'submit',
'value' => _('Send')));
'value' => _m('Send button for sending notice', 'Send')));
}
}

View File

@ -442,11 +442,13 @@ class NoticeListItem extends Widget
'title' => $latlon),
$name);
} else {
$this->out->elementStart('a', array('href' => $url));
$this->out->element('abbr', array('class' => 'geo',
'title' => $latlon),
$name);
$this->out->elementEnd('a');
$xstr = new XMLStringer(false);
$xstr->elementStart('a', array('href' => $url));
$xstr->element('abbr', array('class' => 'geo',
'title' => $latlon),
$name);
$xstr->elementEnd('a');
$this->out->raw($xstr->getString());
}
$this->out->elementEnd('span');
}

View File

@ -61,7 +61,8 @@ class PgsqlSchema extends Schema
public function getTableDef($name)
{
$res = $this->conn->query("select *, column_default as default, is_nullable as Null, udt_name as Type, column_name AS Field from INFORMATION_SCHEMA.COLUMNS where table_name = '$name'");
$res = $this->conn->query("SELECT *, column_default as default, is_nullable as Null,
udt_name as Type, column_name AS Field from INFORMATION_SCHEMA.COLUMNS where table_name = '$name'");
if (PEAR::isError($res)) {
throw new Exception($res->getMessage());
@ -72,6 +73,9 @@ class PgsqlSchema extends Schema
$td->name = $name;
$td->columns = array();
if ($res->numRows() == 0 ) {
throw new Exception('no such table'); //pretend to be the msyql error. yeah, this sucks.
}
$row = array();
while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
@ -166,12 +170,10 @@ class PgsqlSchema extends Schema
}
if (count($primary) > 0) { // it really should be...
$sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")";
$sql .= ",\n primary key (" . implode(',', $primary) . ")";
}
foreach ($uniques as $u) {
$sql .= ",\nunique index {$name}_{$u}_idx ($u)";
}
foreach ($indices as $i) {
$sql .= ",\nindex {$name}_{$i}_idx ($i)";
@ -179,6 +181,10 @@ class PgsqlSchema extends Schema
$sql .= "); ";
foreach ($uniques as $u) {
$sql .= "\n CREATE index {$name}_{$u}_idx ON {$name} ($u); ";
}
$res = $this->conn->query($sql);
if (PEAR::isError($res)) {
@ -209,6 +215,22 @@ class PgsqlSchema extends Schema
return true;
}
/**
* Translate the (mostly) mysql-ish column types into somethings more standard
* @param string column type
*
* @return string postgres happy column type
*/
private function _columnTypeTranslation($type) {
$map = array(
'datetime' => 'timestamp'
);
if(!empty($map[$type])) {
return $map[$type];
}
return $type;
}
/**
* Adds an index to a table.
*
@ -359,6 +381,7 @@ class PgsqlSchema extends Schema
try {
$td = $this->getTableDef($tableName);
} catch (Exception $e) {
if (preg_match('/no such table/', $e->getMessage())) {
return $this->createTable($tableName, $columns);
@ -477,11 +500,12 @@ class PgsqlSchema extends Schema
private function _columnSql($cd)
{
$sql = "{$cd->name} ";
$type = $this->_columnTypeTranslation($cd->type);
if (!empty($cd->size)) {
$sql .= "{$cd->type}({$cd->size}) ";
$sql .= "{$type}({$cd->size}) ";
} else {
$sql .= "{$cd->type} ";
$sql .= "{$type} ";
}
if (!empty($cd->default)) {

84
lib/processmanager.php Normal file
View File

@ -0,0 +1,84 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* i/o manager to watch for a dead parent process
*
* 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 QueueManager
* @package StatusNet
* @author Brion Vibber <brion@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/
*/
class ProcessManager extends IoManager
{
protected $socket;
public static function get()
{
throw new Exception("Must pass ProcessManager per-instance");
}
public function __construct($socket)
{
$this->socket = $socket;
}
/**
* Tell the i/o queue master if and how we can handle multi-site
* processes.
*
* Return one of:
* IoManager::SINGLE_ONLY
* IoManager::INSTANCE_PER_SITE
* IoManager::INSTANCE_PER_PROCESS
*/
public static function multiSite()
{
return IoManager::INSTANCE_PER_PROCESS;
}
/**
* We won't get any input on it, but if it's broken we'll
* know something's gone horribly awry.
*
* @return array of resources
*/
function getSockets()
{
return array($this->socket);
}
/**
* See if the parent died and request a shutdown...
*
* @param resource $socket
* @return boolean success
*/
function handleInput($socket)
{
if (feof($socket)) {
common_log(LOG_INFO, "Parent process exited; shutting down child.");
$this->master->requestShutdown();
}
return true;
}
}

View File

@ -213,7 +213,9 @@ abstract class QueueManager extends IoManager
{
if (isset($this->handlers[$queue])) {
$class = $this->handlers[$queue];
if (class_exists($class)) {
if(is_object($class)) {
return $class;
} else if (class_exists($class)) {
return new $class();
} else {
$this->_log(LOG_ERR, "Nonexistent handler class '$class' for queue '$queue'");
@ -286,7 +288,7 @@ abstract class QueueManager extends IoManager
* Only registered transports will be reliably picked up!
*
* @param string $transport
* @param string $class
* @param string $class class name or object instance
* @param string $group
*/
public function connect($transport, $class, $group='main')

View File

@ -485,3 +485,9 @@ class Schema
return $sql;
}
}
class SchemaTableMissingException extends Exception
{
// no-op
}

View File

@ -71,6 +71,8 @@ abstract class SpawningDaemon extends Daemon
*/
function run()
{
$this->initPipes();
$children = array();
for ($i = 1; $i <= $this->threads; $i++) {
$pid = pcntl_fork();
@ -128,6 +130,34 @@ abstract class SpawningDaemon extends Daemon
return true;
}
/**
* Create an IPC socket pair which child processes can use to detect
* if the parent process has been killed.
*/
function initPipes()
{
$sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
if ($sockets) {
$this->parentWriter = $sockets[0];
$this->parentReader = $sockets[1];
} else {
$this->log(LOG_ERROR, "Couldn't create inter-process sockets");
exit(1);
}
}
/**
* Build an IOManager that simply ensures that we have a connection
* to the parent process open. If it breaks, the child process will
* die.
*
* @return ProcessManager
*/
public function processManager()
{
return new ProcessManager($this->parentReader);
}
/**
* Determine whether to respawn an exited subprocess based on its exit code.
* Otherwise we'll respawn all exits by default.
@ -152,6 +182,8 @@ abstract class SpawningDaemon extends Daemon
*/
protected function initAndRunChild($thread)
{
// Close the writer end of our parent<->children pipe.
fclose($this->parentWriter);
$this->set_id($this->get_id() . "." . $thread);
$this->resetDb();
$exitCode = $this->runThread();

View File

@ -42,4 +42,4 @@ function subs_unsubscribe_to($user, $other)
} catch (Exception $e) {
return $e->getMessage();
}
}
}

View File

@ -1493,7 +1493,15 @@ function common_copy_args($from)
$to = array();
$strip = get_magic_quotes_gpc();
foreach ($from as $k => $v) {
$to[$k] = ($strip) ? stripslashes($v) : $v;
if($strip) {
if(is_array($v)) {
$to[$k] = common_copy_args($v);
} else {
$to[$k] = stripslashes($v);
}
} else {
$to[$k] = $v;
}
}
return $to;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More