Merge remote-tracking branch 'mainline/1.0.x' into people_tags_rebase

Conflicts:
	actions/tagother.php
	classes/Profile.php
	classes/Profile_tag.php
	js/util.min.js
This commit is contained in:
Shashi Gowda 2011-03-30 15:43:13 +05:30
commit 5a2bab07b2
253 changed files with 25537 additions and 9804 deletions

2
README
View File

@ -1472,6 +1472,8 @@ Configuration options specific to notices.
contentlimit: max length of the plain-text content of a notice.
Default is null, meaning to use the site-wide text limit.
0 means no limit.
defaultscope: default scope for notices. Defaults to 0; set to
1 to keep notices private to this site by default.
message
-------

View File

@ -78,22 +78,6 @@ class ApiStatusesRetweetAction extends ApiAuthAction
$this->user = $this->auth_user;
if ($this->user->id == $this->original->profile_id) {
// TRANS: Client error displayed trying to repeat an own notice through the API.
$this->clientError(_('Cannot repeat your own notice.'),
400, $this->format);
return false;
}
$profile = $this->user->getProfile();
if ($profile->hasRepeated($id)) {
// TRANS: Client error displayed trying to re-repeat a notice through the API.
$this->clientError(_('Already repeated that notice.'),
400, $this->format);
return false;
}
return true;
}

View File

@ -98,6 +98,7 @@ class ApprovegroupAction extends Action
$cur = common_current_user();
if (empty($cur)) {
// TRANS: Client error displayed trying to approve group membership while not logged in.
$this->clientError(_('Must be logged in.'), 403);
return false;
}
@ -105,10 +106,12 @@ class ApprovegroupAction extends Action
if ($cur->isAdmin($this->group)) {
$this->profile = Profile::staticGet('id', $this->arg('profile_id'));
} else {
// TRANS: Client error displayed trying to approve group membership while not a group administrator.
$this->clientError(_('Only group admin can approve or cancel join requests.'), 403);
return false;
}
} else {
// TRANS: Client error displayed trying to approve group membership without specifying a profile to approve.
$this->clientError(_('Must specify a profile.'));
return false;
}
@ -117,8 +120,21 @@ class ApprovegroupAction extends Action
'group_id' => $this->group->id));
if (empty($this->request)) {
// TRANS: Client error displayed trying to approve group membership for a non-existing request.
// TRANS: %s is a nickname.
$this->clientError(sprintf(_('%s is not in the moderation queue for this group.'), $this->profile->nickname), 403);
}
$this->approve = (bool)$this->arg('approve');
$this->cancel = (bool)$this->arg('cancel');
if (!$this->approve && !$this->cancel) {
// TRANS: Client error displayed trying to approve/deny group membership.
$this->clientError(_('Internal error: received neither cancel nor abort.'));
}
if ($this->approve && $this->cancel) {
// TRANS: Client error displayed trying to approve/deny group membership.
$this->clientError(_('Internal error: received both cancel and abort.'));
}
return true;
}
@ -136,9 +152,13 @@ class ApprovegroupAction extends Action
parent::handle($args);
try {
$this->profile->completeJoinGroup($this->group);
if ($this->approve) {
$this->request->complete();
} elseif ($this->cancel) {
$this->request->abort();
}
} catch (Exception $e) {
common_log(LOG_ERROR, "Exception canceling group sub: " . $e->getMessage());
common_log(LOG_ERR, "Exception canceling group sub: " . $e->getMessage());
// TRANS: Server error displayed when cancelling a queued group join request fails.
// TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed.
$this->serverError(sprintf(_('Could not cancel request for user %1$s to join group %2$s.'),
@ -149,14 +169,20 @@ class ApprovegroupAction extends Action
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title for leave group page after leaving.
$this->element('title', null, sprintf(_m('TITLE','%1$s left group %2$s'),
// TRANS: Title for leave group page after group join request is approved/disapproved.
// TRANS: %1$s is the user nickname, %2$s is the group nickname.
$this->element('title', null, sprintf(_m('TITLE','%1$s\'s request for %2$s'),
$this->profile->nickname,
$this->group->nickname));
$this->elementEnd('head');
$this->elementStart('body');
$jf = new JoinForm($this, $this->group);
$jf->show();
if ($this->approve) {
// TRANS: Message on page for group admin after approving a join request.
$this->element('p', 'success', _('Join request approved.'));
} elseif ($this->cancel) {
// TRANS: Message on page for group admin after rejecting a join request.
$this->element('p', 'success', _('Join request canceled.'));
}
$this->elementEnd('body');
$this->elementEnd('html');
} else {

145
actions/approvesub.php Normal file
View File

@ -0,0 +1,145 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Approve group subscription request
*
* 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 Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Leave a group
*
* This is the action for leaving a group. It works more or less like the subscribe action
* for users.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApprovesubAction extends Action
{
var $profile = null;
/**
* Prepare to run
*/
function prepare($args)
{
parent::prepare($args);
$cur = common_current_user();
if (empty($cur)) {
// TRANS: Client error displayed trying to approve group membership while not logged in.
$this->clientError(_('Must be logged in.'), 403);
return false;
}
if ($this->arg('profile_id')) {
$this->profile = Profile::staticGet('id', $this->arg('profile_id'));
} else {
// TRANS: Client error displayed trying to approve subscriptionswithout specifying a profile to approve.
$this->clientError(_('Must specify a profile.'));
return false;
}
$this->request = Subscription_queue::pkeyGet(array('subscriber' => $this->profile->id,
'subscribed' => $cur->id));
if (empty($this->request)) {
// TRANS: Client error displayed trying to approve subscription for a non-existing request.
$this->clientError(sprintf(_('%s is not in the moderation queue for your subscriptions.'), $this->profile->nickname), 403);
}
$this->approve = (bool)$this->arg('approve');
$this->cancel = (bool)$this->arg('cancel');
if (!$this->approve && !$this->cancel) {
// TRANS: Client error displayed trying to approve/deny subscription.
$this->clientError(_('Internal error: received neither cancel nor abort.'));
}
if ($this->approve && $this->cancel) {
// TRANS: Client error displayed trying to approve/deny subscription
$this->clientError(_('Internal error: received both cancel and abort.'));
}
return true;
}
/**
* Handle the request
*
* On POST, add the current user to the group
*
* @param array $args unused
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$cur = common_current_user();
try {
if ($this->approve) {
$this->request->complete();
} elseif ($this->cancel) {
$this->request->abort();
}
} catch (Exception $e) {
common_log(LOG_ERR, "Exception canceling sub: " . $e->getMessage());
// TRANS: Server error displayed when cancelling a queued subscription request fails.
// TRANS: %1$s is the leaving user's nickname, $2$s is the nickname for which the leave failed.
$this->serverError(sprintf(_('Could not cancel or approve request for user %1$s to join group %2$s.'),
$this->profile->nickname, $cur->nickname));
return;
}
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title for subscription approval ajax return
// TRANS: %1$s is the approved user's nickname
$this->element('title', null, sprintf(_m('TITLE','%1$s\'s request'),
$this->profile->nickname));
$this->elementEnd('head');
$this->elementStart('body');
if ($this->approve) {
// TRANS: Message on page for user after approving a subscription request.
$this->element('p', 'success', _('Subscription approved.'));
} elseif ($this->cancel) {
// TRANS: Message on page for user after rejecting a subscription request.
$this->element('p', 'success', _('Subscription canceled.'));
}
$this->elementEnd('body');
$this->elementEnd('html');
} else {
common_redirect(common_local_url('subqueue', array('nickname' =>
$cur->nickname)),
303);
}
}
}

View File

@ -141,7 +141,7 @@ class AtompubmembershipfeedAction extends ApiAuthAction
// TRANS: Title for group membership feed.
// TRANS: %s is a username.
$feed->setTitle(sprintf(_("%s group memberships"),
$feed->setTitle(sprintf(_('Group memberships of %s'),
$this->_profile->getBestName()));
// TRANS: Subtitle for group membership feed.
@ -237,8 +237,7 @@ class AtompubmembershipfeedAction extends ApiAuthAction
if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
if ($activity->verb != ActivityVerb::JOIN) {
// TRANS: Client error displayed when not using the POST verb.
// TRANS: Do not translate POST.
// TRANS: Client error displayed when not using the join verb.
throw new ClientException(_('Can only handle join activities.'));
return;
}

View File

@ -68,7 +68,6 @@ class CancelgroupAction extends Action
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
common_redirect(common_local_url('leavegroup', $args), 301);
@ -98,6 +97,7 @@ class CancelgroupAction extends Action
$cur = common_current_user();
if (empty($cur)) {
// TRANS: Client error displayed when trying to leave a group while not logged in.
$this->clientError(_('Must be logged in.'), 403);
return false;
}
@ -105,6 +105,8 @@ class CancelgroupAction extends Action
if ($cur->isAdmin($this->group)) {
$this->profile = Profile::staticGet('id', $this->arg('profile_id'));
} else {
// TRANS: Client error displayed when trying to approve or cancel a group join request without
// TRANS: being a group administrator.
$this->clientError(_('Only group admin can approve or cancel join requests.'), 403);
return false;
}
@ -116,6 +118,8 @@ class CancelgroupAction extends Action
'group_id' => $this->group->id));
if (empty($this->request)) {
// TRANS: Client error displayed when trying to approve a non-existing group join request.
// TRANS: %s is a user nickname.
$this->clientError(sprintf(_('%s is not in the moderation queue for this group.'), $this->profile->nickname), 403);
}
return true;
@ -135,9 +139,9 @@ class CancelgroupAction extends Action
parent::handle($args);
try {
$this->profile->cancelJoinGroup($this->group);
$this->request->abort();
} catch (Exception $e) {
common_log(LOG_ERROR, "Exception canceling group sub: " . $e->getMessage());
common_log(LOG_ERR, "Exception canceling group sub: " . $e->getMessage());
// TRANS: Server error displayed when cancelling a queued group join request fails.
// TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed.
$this->serverError(sprintf(_('Could not cancel request for user %1$s to join group %2$s.'),
@ -149,6 +153,7 @@ class CancelgroupAction extends Action
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title for leave group page after leaving.
// TRANS: %s$s is the leaving user's name, %2$s is the group name.
$this->element('title', null, sprintf(_m('TITLE','%1$s left group %2$s'),
$this->profile->nickname,
$this->group->nickname));

View File

@ -0,0 +1,123 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Leave a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Leave a group
*
* This is the action for leaving a group. It works more or less like the subscribe action
* for users.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class CancelsubscriptionAction extends Action
{
function handle($args)
{
parent::handle($args);
if ($this->boolean('ajax')) {
StatusNet::setApi(true);
}
if (!common_logged_in()) {
// TRANS: Client error displayed when trying to leave a group while not logged in.
$this->clientError(_('Not logged in.'));
return;
}
$user = common_current_user();
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
common_redirect(common_local_url('subscriptions',
array('nickname' => $user->nickname)));
return;
}
/* Use a session token for CSRF protection. */
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->clientError(_('There was a problem with your session token. ' .
'Try again, please.'));
return;
}
$other_id = $this->arg('unsubscribeto');
if (!$other_id) {
// TRANS: Client error displayed when trying to leave a group without specifying an ID.
$this->clientError(_('No profile ID in request.'));
return;
}
$other = Profile::staticGet('id', $other_id);
if (!$other) {
// TRANS: Client error displayed when trying to leave a non-existing group.
$this->clientError(_('No profile with that ID.'));
return;
}
$this->request = Subscription_queue::pkeyGet(array('subscriber' => $user->id,
'subscribed' => $other->id));
if (empty($this->request)) {
// TRANS: Client error displayed when trying to approve a non-existing group join request.
// TRANS: %s is a user nickname.
$this->clientError(sprintf(_('%s is not in the moderation queue for this group.'), $this->profile->nickname), 403);
}
$this->request->abort();
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title after unsubscribing from a group.
$this->element('title', null, _m('TITLE','Unsubscribed'));
$this->elementEnd('head');
$this->elementStart('body');
$subscribe = new SubscribeForm($this, $other);
$subscribe->show();
$this->elementEnd('body');
$this->elementEnd('html');
} else {
common_redirect(common_local_url('subscriptions',
array('nickname' => $user->nickname)),
303);
}
}
}

View File

@ -52,7 +52,7 @@ class GroupqueueAction extends GroupDesignAction
return true;
}
// fixme most of this belongs in a base class, sounds common to most group actions?
// @todo FIXME: most of this belongs in a base class, sounds common to most group actions?
function prepare($args)
{
parent::prepare($args);
@ -96,6 +96,7 @@ class GroupqueueAction extends GroupDesignAction
$cur = common_current_user();
if (!$cur || !$cur->isAdmin($this->group)) {
// TRANS: Client error displayed when trying to approve group applicants without being a group administrator.
$this->clientError(_('Only the group admin may approve users.'));
return false;
}
@ -105,12 +106,12 @@ class GroupqueueAction extends GroupDesignAction
function title()
{
if ($this->page == 1) {
// TRANS: Title of the page showing pending group members still awaiting approval to join the group.
// TRANS: Title of the first page showing pending group members still awaiting approval to join the group.
// TRANS: %s is the name of the group.
return sprintf(_('%s group members awaiting approval'),
$this->group->nickname);
} else {
// TRANS: Title of the page showing pending group members still awaiting approval to join the group.
// TRANS: Title of all but the first page showing pending group members still awaiting approval to join the group.
// TRANS: %1$s is the name of the group, %2$d is the page number of the members list.
return sprintf(_('%1$s group members awaiting approval, page %2$d'),
$this->group->nickname,
@ -155,11 +156,12 @@ class GroupqueueAction extends GroupDesignAction
$members->free();
$this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
$this->page, 'groupmembers',
$this->page, 'groupqueue',
array('nickname' => $this->group->nickname));
}
}
// @todo FIXME: documentation missing.
class GroupQueueList extends GroupMemberList
{
function newListItem($profile)
@ -168,32 +170,24 @@ class GroupQueueList extends GroupMemberList
}
}
// @todo FIXME: documentation missing.
class GroupQueueListItem extends GroupMemberListItem
{
function showActions()
{
$this->startActions();
if (Event::handle('StartProfileListItemActionElements', array($this))) {
$this->showApproveButton();
$this->showCancelButton();
$this->showApproveButtons();
Event::handle('EndProfileListItemActionElements', array($this));
}
$this->endActions();
}
function showApproveButton()
function showApproveButtons()
{
$this->out->elementStart('li', 'entity_join');
$this->out->elementStart('li', 'entity_approval');
$form = new ApproveGroupForm($this->out, $this->group, $this->profile);
$form->show();
$this->out->elementEnd('li');
}
function showCancelButton()
{
$this->out->elementStart('li', 'entity_leave');
$bf = new CancelGroupForm($this->out, $this->group, $this->profile);
$bf->show();
$this->out->elementEnd('li');
}
}

View File

@ -54,7 +54,7 @@ class InviteAction extends CurrentUserDesignAction
function sendInvitations()
{
# CSRF protection
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->showForm(_('There was a problem with your session token. Try again, please.'));

View File

@ -154,7 +154,8 @@ class JoingroupAction extends Action
$form = new CancelGroupForm($this, $this->group);
} else {
// wtf?
throw new Exception(_m("Unknown error joining group."));
// TRANS: Exception thrown when there is an unknown error joining a group.
throw new Exception(_("Unknown error joining group."));
}
$form->show();
$this->elementEnd('body');

View File

@ -209,6 +209,10 @@ class NewnoticeAction extends Action
$author_id = $user->id;
$text = $content_shortened;
// Does the heavy-lifting for getting "To:" information
ToSelector::fillOptions($this, $options);
if (Event::handle('StartNoticeSaveWeb', array($this, &$author_id, &$text, &$options))) {
$notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
@ -344,7 +348,9 @@ class NewnoticeAction extends Action
$inreplyto = null;
}
$notice_form = new NoticeForm($this, '', $content, null, $inreplyto);
$notice_form = new NoticeForm($this, array('content' => $content,
'inreplyto' => $inreplyto));
$notice_form->show();
}

View File

@ -156,7 +156,7 @@ class PasswordsettingsAction extends SettingsAction
$newpassword = $this->arg('newpassword');
$confirm = $this->arg('confirm');
# Some validation
// Some validation
if (strlen($newpassword) < 6) {
// TRANS: Form validation error on page where to change password.

View File

@ -33,8 +33,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Change profile settings
*
@ -46,7 +44,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ProfilesettingsAction extends SettingsAction
{
/**
@ -127,15 +124,15 @@ class ProfilesettingsAction extends SettingsAction
// TRANS: Tooltip for field label in form for profile settings. Plural
// TRANS: is decided by the number of characters available for the
// TRANS: biography (%d).
$bioInstr = sprintf(_m('Describe yourself and your interests in %d character',
'Describe yourself and your interests in %d characters',
$bioInstr = sprintf(_m('Describe yourself and your interests in %d character.',
'Describe yourself and your interests in %d characters.',
$maxBio),
$maxBio);
} else {
// TRANS: Tooltip for field label in form for profile settings.
$bioInstr = _('Describe yourself and your interests');
$bioInstr = _('Describe yourself and your interests.');
}
// TRANS: Text area label in form for profile settings where users can provide.
// TRANS: Text area label in form for profile settings where users can provide
// TRANS: their biography.
$this->textarea('bio', _('Bio'),
($this->arg('bio')) ? $this->arg('bio') : $profile->bio,
@ -146,7 +143,7 @@ class ProfilesettingsAction extends SettingsAction
$this->input('location', _('Location'),
($this->arg('location')) ? $this->arg('location') : $profile->location,
// TRANS: Tooltip for field label in form for profile settings.
_('Where you are, like "City, State (or Region), Country"'));
_('Where you are, like "City, State (or Region), Country".'));
$this->elementEnd('li');
if (common_config('location', 'share') == 'user') {
$this->elementStart('li');
@ -192,6 +189,19 @@ class ProfilesettingsAction extends SettingsAction
($this->arg('autosubscribe')) ?
$this->boolean('autosubscribe') : $user->autosubscribe);
$this->elementEnd('li');
$this->elementStart('li');
$this->dropdown('subscribe_policy',
// TRANS: Dropdown field label on profile settings, for what policies to apply when someone else tries to subscribe to your updates.
_('Subscription policy'),
// TRANS: Dropdown field option for following policy.
array(User::SUBSCRIBE_POLICY_OPEN => _('Let anyone follow me'),
// TRANS: Dropdown field option for following policy.
User::SUBSCRIBE_POLICY_MODERATE => _('Ask me first')),
// TRANS: Dropdown field title on group edit form.
_('Whether other users need your permission to follow your updates.'),
false,
(empty($user->subscribe_policy)) ? User::SUBSCRIBE_POLICY_OPEN : $user->subscribe_policy);
$this->elementEnd('li');
}
$this->elementEnd('ul');
// TRANS: Button to save input in profile settings.
@ -234,6 +244,7 @@ class ProfilesettingsAction extends SettingsAction
$bio = $this->trimmed('bio');
$location = $this->trimmed('location');
$autosubscribe = $this->boolean('autosubscribe');
$subscribe_policy = $this->trimmed('subscribe_policy');
$language = $this->trimmed('language');
$timezone = $this->trimmed('timezone');
$tagstring = $this->trimmed('tags');
@ -339,11 +350,12 @@ class ProfilesettingsAction extends SettingsAction
}
// XXX: XOR
if ($user->autosubscribe ^ $autosubscribe) {
if (($user->autosubscribe ^ $autosubscribe) || $user->subscribe_policy != $subscribe_policy) {
$original = clone($user);
$user->autosubscribe = $autosubscribe;
$user->subscribe_policy = $subscribe_policy;
$result = $user->update($original);
@ -351,7 +363,7 @@ class ProfilesettingsAction extends SettingsAction
common_log_db_error($user, 'UPDATE', __FILE__);
// TRANS: Server error thrown when user profile settings could not be updated to
// TRANS: automatically subscribe to any subscriber.
$this->serverError(_('Could not update user for autosubscribe.'));
$this->serverError(_('Could not update user for autosubscribe or subscribe_policy.'));
return;
}
}

View File

@ -85,8 +85,11 @@ class PublicAction extends Action
common_set_returnto($this->selfUrl());
$this->notice = Notice::publicStream(($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1);
$stream = new PublicNoticeStream(PublicNoticeStream::THREADED);
$this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1,
0,
0);
if (!$this->notice) {
// TRANS: Server error displayed when a public timeline cannot be retrieved.

View File

@ -100,7 +100,7 @@ class PublictagcloudAction extends Action
function showContent()
{
# This should probably be cached rather than recalculated
// This should probably be cached rather than recalculated
$tags = new Notice_tag();
#Need to clear the selection and then only re-add the field

View File

@ -19,7 +19,7 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
# You have 24 hours to claim your password
// You have 24 hours to claim your password
define('MAX_RECOVERY_TIME', 24 * 60 * 60);
@ -81,7 +81,7 @@ class RecoverpasswordAction extends Action
$touched = strtotime($confirm->modified);
$email = $confirm->address;
# Burn this code
// Burn this code
$result = $confirm->delete();
@ -92,8 +92,8 @@ class RecoverpasswordAction extends Action
return;
}
# These should be reaped, but for now we just check mod time
# Note: it's still deleted; let's avoid a second attempt!
// These should be reaped, but for now we just check mod time
// Note: it's still deleted; let's avoid a second attempt!
if ((time() - $touched) > MAX_RECOVERY_TIME) {
common_log(LOG_WARNING,
@ -105,8 +105,8 @@ class RecoverpasswordAction extends Action
return;
}
# If we used an outstanding confirmation to send the email,
# it's been confirmed at this point.
// If we used an outstanding confirmation to send the email,
// it's been confirmed at this point.
if (!$user->email) {
$orig = clone($user);
@ -120,7 +120,7 @@ class RecoverpasswordAction extends Action
}
}
# Success!
// Success!
$this->setTempUser($user);
$this->showPasswordForm();
@ -289,7 +289,7 @@ class RecoverpasswordAction extends Action
}
}
# See if it's an unconfirmed email address
// See if it's an unconfirmed email address
if (!$user) {
// Warning: it may actually be legit to have multiple folks
@ -314,7 +314,7 @@ class RecoverpasswordAction extends Action
return;
}
# Try to get an unconfirmed email address if they used a user name
// Try to get an unconfirmed email address if they used a user name
if (!$user->email && !$confirm_email) {
$confirm_email = new Confirm_address();
@ -332,7 +332,7 @@ class RecoverpasswordAction extends Action
return;
}
# Success! We have a valid user and a confirmed or unconfirmed email address
// Success! We have a valid user and a confirmed or unconfirmed email address
$confirm = new Confirm_address();
$confirm->code = common_confirmation_code(128);
@ -380,7 +380,7 @@ class RecoverpasswordAction extends Action
function resetPassword()
{
# CSRF protection
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Form validation error message.
@ -410,7 +410,7 @@ class RecoverpasswordAction extends Action
return;
}
# OK, we're ready to go
// OK, we're ready to go
$original = clone($user);

View File

@ -531,7 +531,7 @@ class RegisterAction extends Action
$this->elementEnd('li');
}
$this->elementEnd('ul');
// TRANS: Field label on account registration page.
// TRANS: Button text to register a user on account registration page.
$this->submit('submit', _m('BUTTON','Register'));
$this->elementEnd('fieldset');
$this->elementEnd('form');

View File

@ -73,12 +73,6 @@ class RepeatAction extends Action
return false;
}
if ($this->user->id == $this->notice->profile_id) {
// TRANS: Client error displayed when trying to repeat an own notice.
$this->clientError(_('You cannot repeat your own notice.'));
return false;
}
$token = $this->trimmed('token-'.$id);
if (empty($token) || $token != common_session_token()) {
@ -86,14 +80,6 @@ class RepeatAction extends Action
return false;
}
$profile = $this->user->getProfile();
if ($profile->hasRepeated($id)) {
// TRANS: Client error displayed when trying to repeat an already repeated notice.
$this->clientError(_('You already repeated that notice.'));
return false;
}
return true;
}

View File

@ -40,7 +40,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class SandboxAction extends ProfileFormAction
{
/**
@ -50,7 +49,6 @@ class SandboxAction extends ProfileFormAction
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
@ -62,6 +60,7 @@ class SandboxAction extends ProfileFormAction
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::SANDBOXUSER)) {
// TRANS: Client error displayed trying to sandbox users on a site where the feature is not enabled.
$this->clientError(_('You cannot sandbox users on this site.'));
return false;
}
@ -69,6 +68,7 @@ class SandboxAction extends ProfileFormAction
assert(!empty($this->profile)); // checked by parent
if ($this->profile->isSandboxed()) {
// TRANS: Client error displayed trying to sandbox an already sandboxed user.
$this->clientError(_('User is already sandboxed.'));
return false;
}
@ -81,7 +81,6 @@ class SandboxAction extends ProfileFormAction
*
* @return void
*/
function handlePost()
{
$this->profile->sandbox();

View File

@ -40,7 +40,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class SessionsadminpanelAction extends AdminPanelAction
{
/**
@ -48,10 +47,10 @@ class SessionsadminpanelAction extends AdminPanelAction
*
* @return string page title
*/
function title()
{
return _('Sessions');
// TRANS: Title for the sessions administration panel.
return _m('TITLE','Sessions');
}
/**
@ -59,9 +58,9 @@ class SessionsadminpanelAction extends AdminPanelAction
*
* @return string instructions
*/
function getInstructions()
{
// TRANS: Instructions for the sessions administration panel.
return _('Session settings for this StatusNet site');
}
@ -70,7 +69,6 @@ class SessionsadminpanelAction extends AdminPanelAction
*
* @return void
*/
function showForm()
{
$form = new SessionsAdminPanelForm($this);
@ -83,7 +81,6 @@ class SessionsadminpanelAction extends AdminPanelAction
*
* @return void
*/
function saveSettings()
{
static $booleans = array('sessions' => array('handle', 'debug'));
@ -123,6 +120,7 @@ class SessionsadminpanelAction extends AdminPanelAction
}
}
// @todo FIXME: Class documentation missing.
class SessionsAdminPanelForm extends AdminForm
{
/**
@ -130,7 +128,6 @@ class SessionsAdminPanelForm extends AdminForm
*
* @return int ID of the form
*/
function id()
{
return 'sessionsadminpanel';
@ -141,7 +138,6 @@ class SessionsAdminPanelForm extends AdminForm
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
@ -152,7 +148,6 @@ class SessionsAdminPanelForm extends AdminForm
*
* @return string URL of the action
*/
function action()
{
return common_local_url('sessionsadminpanel');
@ -163,24 +158,31 @@ class SessionsAdminPanelForm extends AdminForm
*
* @return void
*/
function formData()
{
$this->out->elementStart('fieldset', array('id' => 'settings_user_sessions'));
$this->out->element('legend', null, _('Sessions'));
// TRANS: Fieldset legend on the sessions administration panel.
$this->out->element('legend', null, _m('LEGEND','Sessions'));
$this->out->elementStart('ul', 'form_data');
$this->li();
// TRANS: Checkbox title on the sessions administration panel.
// TRANS: Indicates if StatusNet should handle session administration.
$this->out->checkbox('handle', _('Handle sessions'),
(bool) $this->value('handle', 'sessions'),
_('Whether to handle sessions ourselves.'));
// TRANS: Checkbox title on the sessions administration panel.
// TRANS: Indicates if StatusNet should handle session administration.
_('Handle sessions ourselves.'));
$this->unli();
$this->li();
// TRANS: Checkbox label on the sessions administration panel.
// TRANS: Indicates if StatusNet should write session debugging output.
$this->out->checkbox('debug', _('Session debugging'),
(bool) $this->value('debug', 'sessions'),
_('Turn on debugging output for sessions.'));
// TRANS: Checkbox title on the sessions administration panel.
_('Enable debugging output for sessions.'));
$this->unli();
$this->out->elementEnd('ul');
@ -193,9 +195,14 @@ class SessionsAdminPanelForm extends AdminForm
*
* @return void
*/
function formActions()
{
$this->out->submit('submit', _('Save'), 'submit', null, _('Save site settings'));
$this->out->submit('submit',
// TRANS: Submit button text on the sessions administration panel.
_m('BUTTON','Save'),
'submit',
null,
// TRANS: Title for submit button on the sessions administration panel.
_('Save session settings'));
}
}

View File

@ -75,11 +75,13 @@ class ShowApplicationAction extends OwnerDesignAction
$this->owner = User::staticGet($this->application->owner);
if (!common_logged_in()) {
// TRANS: Client error displayed trying to display an OAuth application while not logged in.
$this->clientError(_('You must be logged in to view an application.'));
return false;
}
if (empty($this->application)) {
// TRANS: Client error displayed trying to display a non-existing OAuth application.
$this->clientError(_('No such application.'), 404);
return false;
}
@ -87,6 +89,7 @@ class ShowApplicationAction extends OwnerDesignAction
$cur = common_current_user();
if ($cur->id != $this->owner->id) {
// TRANS: Client error displayed trying to display an OAuth application for which the logged in user is not the owner.
$this->clientError(_('You are not the owner of this application.'), 401);
return false;
}
@ -148,6 +151,7 @@ class ShowApplicationAction extends OwnerDesignAction
$consumer = $this->application->getConsumer();
$this->elementStart('div', 'entity_profile vcard');
// TRANS: Header on the OAuth application page.
$this->element('h2', null, _('Application profile'));
if (!empty($this->application->icon)) {
$this->element('img', array('src' => $this->application->icon,
@ -176,7 +180,12 @@ class ShowApplicationAction extends OwnerDesignAction
$userCnt = $appUsers->count();
$this->raw(sprintf(
_('Created by %1$s - %2$s access by default - %3$d users'),
// TRANS: Information output on an OAuth application page.
// TRANS: %1$s is the application creator, %2$s is "read-only" or "read-write",
// TRANS: %3$d is the number of users using the OAuth application.
_m('Created by %1$s - %2$s access by default - %3$d user',
'Created by %1$s - %2$s access by default - %3$d users',
$userCnt),
$profile->getBestName(),
$defaultAccess,
$userCnt
@ -186,13 +195,15 @@ class ShowApplicationAction extends OwnerDesignAction
$this->elementEnd('div');
$this->elementStart('div', 'entity_actions');
// TRANS: Header on the OAuth application page.
$this->element('h2', null, _('Application actions'));
$this->elementStart('ul');
$this->elementStart('li', 'entity_edit');
$this->element('a',
array('href' => common_local_url('editapplication',
array('id' => $this->application->id))),
'Edit');
// TRANS: Link text to edit application on the OAuth application page.
_m('EDITAPP','Edit'));
$this->elementEnd('li');
$this->elementStart('li', 'entity_reset_keysecret');
@ -209,6 +220,8 @@ class ShowApplicationAction extends OwnerDesignAction
'id' => 'reset',
'name' => 'reset',
'class' => 'submit',
// TRANS: Button text on the OAuth application page.
// TRANS: Resets the OAuth consumer key and secret.
'value' => _('Reset key & secret'),
'onClick' => 'return confirmReset()'));
$this->elementEnd('fieldset');
@ -225,7 +238,8 @@ class ShowApplicationAction extends OwnerDesignAction
$this->elementStart('fieldset');
$this->hidden('token', common_session_token());
$this->submit('delete', _('Delete'));
// TRANS: Submit button text the OAuth application page to delete an application.
$this->submit('delete', _m('BUTTON','Delete'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
$this->elementEnd('li');
@ -234,6 +248,7 @@ class ShowApplicationAction extends OwnerDesignAction
$this->elementEnd('div');
$this->elementStart('div', 'entity_data');
// TRANS: Header on the OAuth application page.
$this->element('h2', null, _('Application info'));
$this->element('div',
'entity_consumer_key',
@ -252,7 +267,8 @@ class ShowApplicationAction extends OwnerDesignAction
$this->element('div', 'entity_authorize_url', common_local_url('ApiOauthAuthorize'));
$this->element('p', 'note',
_('Note: We support HMAC-SHA1 signatures. We do not support the plaintext signature method.'));
// TRANS: Note on the OAuth application page about signature support.
_('Note: HMAC-SHA1 signatures are supported. The plaintext signature method is not supported.'));
$this->elementEnd('div');
$this->elementStart('p', array('id' => 'application_action'));
@ -272,6 +288,7 @@ class ShowApplicationAction extends OwnerDesignAction
{
parent::showScripts();
// TRANS: Text in confirmation dialog to reset consumer key and secret for an OAuth application.
$msg = _('Are you sure you want to reset your consumer key and secret?');
$js = 'function confirmReset() { ';

View File

@ -324,6 +324,8 @@ class ShowgroupAction extends GroupDesignAction
$this->element('h2', null, _('Statistics'));
$this->elementStart('dl');
// TRANS: Label for group creation date.
$this->element('dt', null, _m('LABEL','Created'));
$this->element('dd', 'entity_created', date('j M Y',
strtotime($this->group->created)));
@ -363,6 +365,18 @@ class ShowgroupAction extends GroupDesignAction
$this->raw(common_markup_to_html($m));
$this->elementEnd('div');
}
function noticeFormOptions()
{
$options = parent::noticeFormOptions();
$cur = common_current_user();
if (!empty($cur) && $cur->isMember($this->group)) {
$options['to_group'] = $this->group;
}
return $options;
}
}
class GroupAdminSection extends ProfileSection
@ -382,8 +396,8 @@ class GroupAdminSection extends ProfileSection
function title()
{
// TRANS: Header for list of group administrators on a group page (h2).
return _('Admins');
// TRANS: Title for list of group administrators on a group page.
return _m('TITLE','Admins');
}
function divId()

View File

@ -44,25 +44,21 @@ require_once INSTALLDIR.'/lib/feedlist.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ShownoticeAction extends OwnerDesignAction
{
/**
* Notice object to show
*/
var $notice = null;
/**
* Profile of the notice object
*/
var $profile = null;
/**
* Avatar of the profile of the notice object
*/
var $avatar = null;
/**
@ -74,7 +70,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return success flag
*/
function prepare($args)
{
parent::prepare($args);
@ -82,24 +77,25 @@ class ShownoticeAction extends OwnerDesignAction
StatusNet::setApi(true);
}
$id = $this->arg('notice');
$this->notice = $this->getNotice();
$this->notice = Notice::staticGet($id);
$cur = common_current_user();
if (empty($this->notice)) {
// Did we used to have it, and it got deleted?
$deleted = Deleted_notice::staticGet($id);
if (!empty($deleted)) {
$this->clientError(_('Notice deleted.'), 410);
} else {
$this->clientError(_('No such notice.'), 404);
}
return false;
if (!empty($cur)) {
$curProfile = $cur->getProfile();
} else {
$curProfile = null;
}
if (!$this->notice->inScope($curProfile)) {
// TRANS: Client exception thrown when trying a view a notice the user has no access to.
throw new ClientException(_('Not available.'), 403);
}
$this->profile = $this->notice->getProfile();
if (empty($this->profile)) {
// TRANS: Server error displayed trying to show a notice without a connected profile.
$this->serverError(_('Notice has no profile.'), 500);
return false;
}
@ -111,12 +107,38 @@ class ShownoticeAction extends OwnerDesignAction
return true;
}
/**
* Fetch the notice to show. This may be overridden by child classes to
* customize what we fetch without duplicating all of the prepare() method.
*
* @return Notice
*/
function getNotice()
{
$id = $this->arg('notice');
$notice = Notice::staticGet('id', $id);
if (empty($notice)) {
// Did we used to have it, and it got deleted?
$deleted = Deleted_notice::staticGet($id);
if (!empty($deleted)) {
// TRANS: Client error displayed trying to show a deleted notice.
$this->clientError(_('Notice deleted.'), 410);
} else {
// TRANS: Client error displayed trying to show a non-existing notice.
$this->clientError(_('No such notice.'), 404);
}
return false;
}
return $notice;
}
/**
* Is this action read-only?
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
@ -130,7 +152,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return int last-modified date as unix timestamp
*/
function lastModified()
{
return max(strtotime($this->notice->modified),
@ -147,7 +168,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return string etag
*/
function etag()
{
$avtime = ($this->avatar) ?
@ -167,11 +187,12 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return string title of the page
*/
function title()
{
$base = $this->profile->getFancyName();
// TRANS: Title of the page that shows a notice.
// TRANS: %1$s is a user name, %2$s is the notice creation date/time.
return sprintf(_('%1$s\'s status on %2$s'),
$base,
common_exact_date($this->notice->created));
@ -186,7 +207,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -218,7 +238,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return void
*/
function showLocalNavBlock()
{
}
@ -230,7 +249,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return void
*/
function showContent()
{
$this->elementStart('ol', array('class' => 'notices xoxo'));
@ -245,7 +263,8 @@ class ShownoticeAction extends OwnerDesignAction
$this->xw->startDocument('1.0', 'UTF-8');
$this->elementStart('html');
$this->elementStart('head');
$this->element('title', null, _('Notice'));
// TRANS: Title for page that shows a notice.
$this->element('title', null, _m('TITLE','Notice'));
$this->elementEnd('head');
$this->elementStart('body');
$nli = new NoticeListItem($this->notice, $this);
@ -259,7 +278,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return void
*/
function showPageNoticeBlock()
{
}
@ -269,7 +287,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return void
*/
function showAside() {
}
@ -280,7 +297,6 @@ class ShownoticeAction extends OwnerDesignAction
*
* @return void
*/
function extraHead()
{
$user = User::staticGet($this->profile->id);
@ -323,16 +339,16 @@ class ShownoticeAction extends OwnerDesignAction
}
}
// @todo FIXME: Class documentation missing.
class SingleNoticeItem extends DoFollowListItem
{
/**
* recipe function for displaying a single notice.
* Recipe function for displaying a single notice.
*
* We overload to show attachments.
*
* @return void
*/
function show()
{
$this->showStart();
@ -363,7 +379,6 @@ class SingleNoticeItem extends DoFollowListItem
*
* @return void
*/
function showAvatar()
{
$avatar_size = AVATAR_PROFILE_SIZE;

View File

@ -65,7 +65,8 @@ class ShowstreamAction extends ProfileAction
$base = $this->profile->getFancyName();
if (!empty($this->tag)) {
if ($this->page == 1) {
// TRANS: Page title showing tagged notices in one user's stream. %1$s is the username, %2$s is the hash tag.
// TRANS: Page title showing tagged notices in one user's stream.
// TRANS: %1$s is the username, %2$s is the hash tag.
return sprintf(_('%1$s tagged %2$s'), $base, $this->tag);
} else {
// TRANS: Page title showing tagged notices in one user's stream.
@ -153,6 +154,8 @@ class ShowstreamAction extends ProfileAction
array(
'id' => $this->user->id,
'format' => 'atom')),
// TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (Atom)'),
$this->user->nickname)),
new Feed(Feed::FOAF,
@ -275,6 +278,18 @@ class ShowstreamAction extends ProfileAction
$cloud = new PersonalTagCloudSection($this, $this->user);
$cloud->show();
}
function noticeFormOptions()
{
$options = parent::noticeFormOptions();
$cur = common_current_user();
if (empty($cur) || $cur->id != $this->profile->id) {
$options['to_profile'] = $this->profile;
}
return $options;
}
}
// We don't show the author for a profile, since we already know who it is!

View File

@ -40,7 +40,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class SilenceAction extends ProfileFormAction
{
/**
@ -50,7 +49,6 @@ class SilenceAction extends ProfileFormAction
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
@ -62,6 +60,7 @@ class SilenceAction extends ProfileFormAction
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::SILENCEUSER)) {
// TRANS: Client error displayed trying to silence a user on a site where the feature is not enabled.
$this->clientError(_('You cannot silence users on this site.'));
return false;
}
@ -69,6 +68,7 @@ class SilenceAction extends ProfileFormAction
assert(!empty($this->profile)); // checked by parent
if ($this->profile->isSilenced()) {
// TRANS: Client error displayed trying to silence an already silenced user.
$this->clientError(_('User is already silenced.'));
return false;
}
@ -81,7 +81,6 @@ class SilenceAction extends ProfileFormAction
*
* @return void
*/
function handlePost()
{
$this->profile->silence();

View File

@ -44,7 +44,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class SiteadminpanelAction extends AdminPanelAction
{
/**
@ -52,10 +51,10 @@ class SiteadminpanelAction extends AdminPanelAction
*
* @return string page title
*/
function title()
{
return _('Site');
// TRANS: Title for site administration panel.
return _m('TITLE','Site');
}
/**
@ -63,9 +62,9 @@ class SiteadminpanelAction extends AdminPanelAction
*
* @return string instructions
*/
function getInstructions()
{
// TRANS: Instructions for site administration panel.
return _('Basic settings for this StatusNet site');
}
@ -74,7 +73,6 @@ class SiteadminpanelAction extends AdminPanelAction
*
* @return void
*/
function showForm()
{
$form = new SiteAdminPanelForm($this);
@ -87,7 +85,6 @@ class SiteadminpanelAction extends AdminPanelAction
*
* @return void
*/
function saveSettings()
{
static $settings = array(
@ -130,6 +127,7 @@ class SiteadminpanelAction extends AdminPanelAction
// Validate site name
if (empty($values['site']['name'])) {
// TRANS: Client error displayed trying to save an empty site name.
$this->clientError(_('Site name must have non-zero length.'));
}
@ -138,9 +136,11 @@ class SiteadminpanelAction extends AdminPanelAction
$values['site']['email'] = common_canonical_email($values['site']['email']);
if (empty($values['site']['email'])) {
// TRANS: Client error displayed trying to save site settings without a contact address.
$this->clientError(_('You must have a valid contact email address.'));
}
if (!Validate::email($values['site']['email'], common_config('email', 'check_domain'))) {
// TRANS: Client error displayed trying to save site settings without a valid contact address.
$this->clientError(_('Not a valid email address.'));
}
@ -148,6 +148,7 @@ class SiteadminpanelAction extends AdminPanelAction
if (is_null($values['site']['timezone']) ||
!in_array($values['site']['timezone'], DateTimeZone::listIdentifiers())) {
// TRANS: Client error displayed trying to save site settings without a timezone.
$this->clientError(_('Timezone not selected.'));
return;
}
@ -156,24 +157,28 @@ class SiteadminpanelAction extends AdminPanelAction
if (!is_null($values['site']['language']) &&
!in_array($values['site']['language'], array_keys(get_nice_language_list()))) {
// TRANS: Client error displayed trying to save site settings with an invalid language code.
// TRANS: %s is the invalid language code.
$this->clientError(sprintf(_('Unknown language "%s".'), $values['site']['language']));
}
// Validate text limit
if (!Validate::number($values['site']['textlimit'], array('min' => 0))) {
$this->clientError(_("Minimum text limit is 0 (unlimited)."));
// TRANS: Client error displayed trying to save site settings with a text limit below 0.
$this->clientError(_('Minimum text limit is 0 (unlimited).'));
}
// Validate dupe limit
if (!Validate::number($values['site']['dupelimit'], array('min' => 1))) {
// TRANS: Client error displayed trying to save site settings with a text limit below 1.
$this->clientError(_("Dupe limit must be one or more seconds."));
}
}
}
// @todo FIXME: Class documentation missing.
class SiteAdminPanelForm extends AdminForm
{
/**
@ -181,7 +186,6 @@ class SiteAdminPanelForm extends AdminForm
*
* @return int ID of the form
*/
function id()
{
return 'form_site_admin_panel';
@ -192,7 +196,6 @@ class SiteAdminPanelForm extends AdminForm
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
@ -203,7 +206,6 @@ class SiteAdminPanelForm extends AdminForm
*
* @return string URL of the action
*/
function action()
{
return common_local_url('siteadminpanel');
@ -214,35 +216,44 @@ class SiteAdminPanelForm extends AdminForm
*
* @return void
*/
function formData()
{
$this->out->elementStart('fieldset', array('id' => 'settings_admin_general'));
$this->out->element('legend', null, _('General'));
// TRANS: Fieldset legend on site settings panel.
$this->out->element('legend', null, _m('LEGEND','General'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->input('name', _('Site name'),
_('The name of your site, like "Yourcompany Microblog"'));
// TRANS: Field label on site settings panel.
$this->input('name', _m('LABEL','Site name'),
// TRANS: Field title on site settings panel.
_('The name of your site, like "Yourcompany Microblog".'));
$this->unli();
$this->li();
// TRANS: Field label on site settings panel.
$this->input('broughtby', _('Brought by'),
_('Text used for credits link in footer of each page'));
// TRANS: Field title on site settings panel.
_('Text used for credits link in footer of each page.'));
$this->unli();
$this->li();
// TRANS: Field label on site settings panel.
$this->input('broughtbyurl', _('Brought by URL'),
_('URL used for credits link in footer of each page'));
// TRANS: Field title on site settings panel.
_('URL used for credits link in footer of each page.'));
$this->unli();
$this->li();
// TRANS: Field label on site settings panel.
$this->input('email', _('Email'),
_('Contact email address for your site'));
// TRANS: Field title on site settings panel.
_('Contact email address for your site.'));
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_admin_local'));
$this->out->element('legend', null, _('Local'));
// TRANS: Fieldset legend on site settings panel.
$this->out->element('legend', null, _m('LEGEND','Local'));
$this->out->elementStart('ul', 'form_data');
$timezones = array();
@ -253,14 +264,20 @@ class SiteAdminPanelForm extends AdminForm
asort($timezones);
$this->li();
// TRANS: Dropdown label on site settings panel.
$this->out->dropdown('timezone', _('Default timezone'),
// TRANS: Dropdown title on site settings panel.
$timezones, _('Default timezone for the site; usually UTC.'),
true, $this->value('timezone'));
$this->unli();
$this->li();
$this->out->dropdown('language', _('Default language'),
get_nice_language_list(), _('Site language when autodetection from browser settings is not available'),
$this->out->dropdown('language',
// TRANS: Dropdown label on site settings panel.
_('Default language'),
get_nice_language_list(),
// TRANS: Dropdown title on site settings panel.
_('Site language when autodetection from browser settings is not available'),
false, $this->value('language'));
$this->unli();
@ -268,14 +285,23 @@ class SiteAdminPanelForm extends AdminForm
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_admin_limits'));
$this->out->element('legend', null, _('Limits'));
// TRANS: Fieldset legend on site settings panel.
$this->out->element('legend', null, _m('LEGEND','Limits'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->input('textlimit', _('Text limit'), _('Maximum number of characters for notices.'));
$this->input('textlimit',
// TRANS: Field label on site settings panel.
_('Text limit'),
// TRANS: Field title on site settings panel.
_('Maximum number of characters for notices.'));
$this->unli();
$this->li();
$this->input('dupelimit', _('Dupe limit'), _('How long users must wait (in seconds) to post the same thing again.'));
$this->input('dupelimit',
// TRANS: Field label on site settings panel.
_('Dupe limit'),
// TRANS: Field title on site settings panel.
_('How long users must wait (in seconds) to post the same thing again.'));
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
@ -286,9 +312,14 @@ class SiteAdminPanelForm extends AdminForm
*
* @return void
*/
function formActions()
{
$this->out->submit('submit', _('Save'), 'submit', null, _('Save site settings'));
$this->out->submit('submit',
// TRANS: Button text for saving site settings.
_m('BUTTON','Save'),
'submit',
null,
// TRANS: Button title for saving site settings.
_('Save site settings'));
}
}

View File

@ -40,7 +40,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class SnapshotadminpanelAction extends AdminPanelAction
{
/**
@ -48,10 +47,10 @@ class SnapshotadminpanelAction extends AdminPanelAction
*
* @return string page title
*/
function title()
{
return _('Snapshots');
// TRANS: Title for admin panel to configure snapshots.
return _m('TITLE','Snapshots');
}
/**
@ -59,9 +58,9 @@ class SnapshotadminpanelAction extends AdminPanelAction
*
* @return string instructions
*/
function getInstructions()
{
// TRANS: Instructions for admin panel to configure snapshots.
return _('Manage snapshot configuration');
}
@ -70,7 +69,6 @@ class SnapshotadminpanelAction extends AdminPanelAction
*
* @return void
*/
function showForm()
{
$form = new SnapshotAdminPanelForm($this);
@ -83,7 +81,6 @@ class SnapshotadminpanelAction extends AdminPanelAction
*
* @return void
*/
function saveSettings()
{
static $settings = array(
@ -124,12 +121,14 @@ class SnapshotadminpanelAction extends AdminPanelAction
// Validate snapshot run value
if (!in_array($values['snapshot']['run'], array('web', 'cron', 'never'))) {
// TRANS: Client error displayed on admin panel for snapshots when providing an invalid run value.
$this->clientError(_('Invalid snapshot run value.'));
}
// Validate snapshot frequency value
if (!Validate::number($values['snapshot']['frequency'])) {
// TRANS: Client error displayed on admin panel for snapshots when providing an invalid value for frequency.
$this->clientError(_('Snapshot frequency must be a number.'));
}
@ -141,11 +140,13 @@ class SnapshotadminpanelAction extends AdminPanelAction
array('allowed_schemes' => array('http', 'https')
)
)) {
// TRANS: Client error displayed on admin panel for snapshots when providing an invalid report URL.
$this->clientError(_('Invalid snapshot report URL.'));
}
}
}
// @todo FIXME: add documentation
class SnapshotAdminPanelForm extends AdminForm
{
/**
@ -153,7 +154,6 @@ class SnapshotAdminPanelForm extends AdminForm
*
* @return int ID of the form
*/
function id()
{
return 'form_snapshot_admin_panel';
@ -164,7 +164,6 @@ class SnapshotAdminPanelForm extends AdminForm
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
@ -175,7 +174,6 @@ class SnapshotAdminPanelForm extends AdminForm
*
* @return string URL of the action
*/
function action()
{
return common_local_url('snapshotadminpanel');
@ -186,26 +184,31 @@ class SnapshotAdminPanelForm extends AdminForm
*
* @return void
*/
function formData()
{
$this->out->elementStart(
'fieldset',
array('id' => 'settings_admin_snapshots')
);
$this->out->element('legend', null, _('Snapshots'));
// TRANS: Fieldset legend on admin panel for snapshots.
$this->out->element('legend', null, _m('LEGEND','Snapshots'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$snapshot = array(
// TRANS: Option in dropdown for snapshot method in admin panel for snapshots.
'web' => _('Randomly during web hit'),
// TRANS: Option in dropdown for snapshot method in admin panel for snapshots.
'cron' => _('In a scheduled job'),
// TRANS: Option in dropdown for snapshot method in admin panel for snapshots.
'never' => _('Never')
);
$this->out->dropdown(
'run',
// TRANS: Dropdown label for snapshot method in admin panel for snapshots.
_('Data snapshots'),
$snapshot,
_('When to send statistical data to status.net servers'),
// TRANS: Dropdown title for snapshot method in admin panel for snapshots.
_('When to send statistical data to status.net servers.'),
false,
$this->value('run', 'snapshot')
);
@ -214,8 +217,10 @@ class SnapshotAdminPanelForm extends AdminForm
$this->li();
$this->input(
'frequency',
// TRANS: Input field label for snapshot frequency in admin panel for snapshots.
_('Frequency'),
_('Snapshots will be sent once every N web hits'),
// TRANS: Input field title for snapshot frequency in admin panel for snapshots.
_('Snapshots will be sent once every N web hits.'),
'snapshot'
);
$this->unli();
@ -223,8 +228,10 @@ class SnapshotAdminPanelForm extends AdminForm
$this->li();
$this->input(
'reporturl',
// TRANS: Input field label for snapshot report URL in admin panel for snapshots.
_('Report URL'),
_('Snapshots will be sent to this URL'),
// TRANS: Input field title for snapshot report URL in admin panel for snapshots.
_('Snapshots will be sent to this URL.'),
'snapshot'
);
$this->unli();
@ -237,15 +244,16 @@ class SnapshotAdminPanelForm extends AdminForm
*
* @return void
*/
function formActions()
{
$this->out->submit(
'submit',
_('Save'),
// TRANS: Button text to save snapshot settings.
_m('BUTTON','Save'),
'submit',
null,
_('Save snapshot settings')
// TRANS: Title for button to save snapshot settings.
_('Save snapshot settings.')
);
}
}

142
actions/subqueue.php Normal file
View File

@ -0,0 +1,142 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* List of group members
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once(INSTALLDIR.'/lib/profilelist.php');
/**
* List of group members
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class SubqueueAction extends GalleryAction
{
var $page = null;
function isReadOnly($args)
{
return true;
}
// @todo FIXME: most of this belongs in a base class, sounds common to most group actions?
function prepare($args)
{
parent::prepare($args);
$cur = common_current_user();
if (!$cur || $cur->id != $this->profile->id) {
// TRANS: Client error displayed when trying to approve group applicants without being a group administrator.
$this->clientError(_('You may only approve your own pending subscriptions.'));
return false;
}
return true;
}
function title()
{
if ($this->page == 1) {
// TRANS: Title of the first page showing pending subscribers still awaiting approval.
// TRANS: %s is the name of the user.
return sprintf(_('%s subscribers awaiting approval'),
$this->profile->nickname);
} else {
// TRANS: Title of all but the first page showing pending subscribersmembers still awaiting approval.
// TRANS: %1$s is the name of the user, %2$d is the page number of the members list.
return sprintf(_('%1$s subscribers awaiting approval, page %2$d'),
$this->profile->nickname,
$this->page);
}
}
function showPageNotice()
{
$this->element('p', 'instructions',
// TRANS: Page notice for group members page.
_('A list of users awaiting approval to subscribe to you.'));
}
function showContent()
{
$offset = ($this->page-1) * PROFILES_PER_PAGE;
$limit = PROFILES_PER_PAGE + 1;
$cnt = 0;
$members = $this->profile->getRequests($offset, $limit);
if ($members) {
// @fixme change!
$member_list = new SubQueueList($members, $this);
$cnt = $member_list->show();
}
$members->free();
$this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
$this->page, 'subqueue',
array('nickname' => $this->profile->nickname)); // urgh
}
}
class SubQueueList extends ProfileList
{
function newListItem($profile)
{
return new SubQueueListItem($profile, $this->action);
}
}
class SubQueueListItem extends ProfileListItem
{
function showActions()
{
$this->startActions();
if (Event::handle('StartProfileListItemActionElements', array($this))) {
$this->showApproveButtons();
Event::handle('EndProfileListItemActionElements', array($this));
}
$this->endActions();
}
function showApproveButtons()
{
$this->out->elementStart('li', 'entity_approval');
$form = new ApproveSubForm($this->out, $this->profile);
$form->show();
$this->out->elementEnd('li');
}
}

View File

@ -139,8 +139,8 @@ class SubscribeAction extends Action
{
// Throws exception on error
Subscription::start($this->user->getProfile(),
$this->other);
$sub = Subscription::start($this->user->getProfile(),
$this->other);
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
@ -149,8 +149,12 @@ class SubscribeAction extends Action
$this->element('title', null, _('Subscribed'));
$this->elementEnd('head');
$this->elementStart('body');
$unsubscribe = new UnsubscribeForm($this, $this->other);
$unsubscribe->show();
if ($sub instanceof Subscription) {
$form = new UnsubscribeForm($this, $this->other);
} else {
$form = new CancelSubscriptionForm($this, $this->other);
}
$form->show();
$this->elementEnd('body');
$this->elementEnd('html');
} else {

View File

@ -61,8 +61,8 @@ class SupAction extends Action
{
$notice = new Notice();
# XXX: cache this. Depends on how big this protocol becomes;
# Re-doing this query every 15 seconds isn't the end of the world.
// XXX: cache this. Depends on how big this protocol becomes;
// Re-doing this query every 15 seconds isn't the end of the world.
$divider = common_sql_date(time() - $seconds);

View File

@ -19,9 +19,9 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
// @todo FIXME: documentation missing.
class TagAction extends Action
{
var $notice;
function prepare($args)
@ -48,7 +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)
// TRANS: Server error when page not found (404).
$this->serverError(_('No such page.'),$code=404);
}
@ -88,18 +88,24 @@ class TagAction extends Action
return array(new Feed(Feed::RSS1,
common_local_url('tagrss',
array('tag' => $this->tag)),
// TRANS: Link label for feed on "notices with tag" page.
// TRANS: %s is the tag the feed is for.
sprintf(_('Notice feed for tag %s (RSS 1.0)'),
$this->tag)),
new Feed(Feed::RSS2,
common_local_url('ApiTimelineTag',
array('format' => 'rss',
'tag' => $this->tag)),
// TRANS: Link label for feed on "notices with tag" page.
// TRANS: %s is the tag the feed is for.
sprintf(_('Notice feed for tag %s (RSS 2.0)'),
$this->tag)),
new Feed(Feed::ATOM,
common_local_url('ApiTimelineTag',
array('format' => 'atom',
'tag' => $this->tag)),
// TRANS: Link label for feed on "notices with tag" page.
// TRANS: %s is the tag the feed is for.
sprintf(_('Notice feed for tag %s (Atom)'),
$this->tag)));
}
@ -107,7 +113,7 @@ class TagAction extends Action
function showContent()
{
if(Event::handle('StartTagShowContent', array($this))) {
$nl = new NoticeList($this->notice, $this);
$cnt = $nl->show();

View File

@ -22,7 +22,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
require_once(INSTALLDIR.'/lib/rssaction.php');
// Formatting of RSS handled by Rss10Action
class TagrssAction extends Rss10Action
{
var $tag;
@ -32,6 +31,7 @@ class TagrssAction extends Rss10Action
$tag = common_canonical_tag($this->trimmed('tag'));
$this->tag = Notice_tag::staticGet('tag', $tag);
if (!$this->tag) {
// TRANS: Client error when requesting a tag feed for a non-existing tag.
$this->clientError(_('No such tag.'));
return false;
} else {
@ -62,6 +62,8 @@ class TagrssAction extends Rss10Action
$c = array('url' => common_local_url('tagrss', array('tag' => $tagname)),
'title' => $tagname,
'link' => common_local_url('tagrss', array('tag' => $tagname)),
// TRANS: Tag feed description.
// TRANS: %1$s is the tag name, %2$s is the StatusNet sitename.
'description' => sprintf(_('Updates tagged with %1$s on %2$s!'),
$tagname, common_config('site', 'name')));
return $c;

View File

@ -40,7 +40,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class UnsandboxAction extends ProfileFormAction
{
/**
@ -50,7 +49,6 @@ class UnsandboxAction extends ProfileFormAction
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
@ -62,6 +60,7 @@ class UnsandboxAction extends ProfileFormAction
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::SANDBOXUSER)) {
// TRANS: Client error on page to unsandbox a user when the feature is not enabled.
$this->clientError(_('You cannot sandbox users on this site.'));
return false;
}
@ -69,6 +68,7 @@ class UnsandboxAction extends ProfileFormAction
assert(!empty($this->profile)); // checked by parent
if (!$this->profile->isSandboxed()) {
// TRANS: Client error on page to unsilence a user when the to be unsandboxed user has not been sandboxed.
$this->clientError(_('User is not sandboxed.'));
return false;
}
@ -81,7 +81,6 @@ class UnsandboxAction extends ProfileFormAction
*
* @return void
*/
function handlePost()
{
$this->profile->unsandbox();

View File

@ -40,7 +40,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class UnsilenceAction extends ProfileFormAction
{
/**
@ -50,7 +49,6 @@ class UnsilenceAction extends ProfileFormAction
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
@ -62,6 +60,7 @@ class UnsilenceAction extends ProfileFormAction
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::SILENCEUSER)) {
// TRANS: Client error on page to unsilence a user when the feature is not enabled.
$this->clientError(_('You cannot silence users on this site.'));
return false;
}
@ -69,6 +68,7 @@ class UnsilenceAction extends ProfileFormAction
assert(!empty($this->profile)); // checked by parent
if (!$this->profile->isSilenced()) {
// TRANS: Client error on page to unsilence a user when the to be unsilenced user has not been silenced.
$this->clientError(_('User is not silenced.'));
return false;
}
@ -81,7 +81,6 @@ class UnsilenceAction extends ProfileFormAction
*
* @return void
*/
function handlePost()
{
$this->profile->unsilence();

View File

@ -44,11 +44,11 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
*/
class UnsubscribeAction extends Action
{
function handle($args)
{
parent::handle($args);
if (!common_logged_in()) {
// TRANS: Client error displayed when trying to unsubscribe while not logged in.
$this->clientError(_('Not logged in.'));
return;
}
@ -74,6 +74,7 @@ class UnsubscribeAction extends Action
$other_id = $this->arg('unsubscribeto');
if (!$other_id) {
// TRANS: Client error displayed when trying to unsubscribe without providing a profile ID.
$this->clientError(_('No profile ID in request.'));
return;
}
@ -81,6 +82,7 @@ class UnsubscribeAction extends Action
$other = Profile::staticGet('id', $other_id);
if (!$other) {
// TRANS: Client error displayed when trying to unsubscribe while providing a non-existing profile ID.
$this->clientError(_('No profile with that ID.'));
return;
}
@ -95,6 +97,7 @@ class UnsubscribeAction extends Action
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Page title for page to unsubscribe.
$this->element('title', null, _('Unsubscribed'));
$this->elementEnd('head');
$this->elementStart('body');

View File

@ -45,7 +45,6 @@ require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
*/
class UpdateprofileAction extends Action
{
/**
* For initializing members of the class.
*
@ -61,8 +60,10 @@ class UpdateprofileAction extends Action
$license = $_POST['omb_listenee_license'];
$site_license = common_config('license', 'url');
if (!common_compatible_license($license, $site_license)) {
$this->clientError(sprintf(_('Listenee stream license %1$s is not '.
'compatible with site license %2$s.'),
// TRANS: Client error displayed when trying to update profile with an incompatible license.
// TRANS: %1$s is the license incompatible with site license %2$s.
$this->clientError(sprintf(_('Listenee stream license "%1$s" is not '.
'compatible with site license "%2$s".'),
$license, $site_license));
return false;
}

View File

@ -32,8 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Miscellaneous settings actions
*
@ -46,7 +44,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class UrlsettingsAction extends SettingsAction
{
/**
@ -54,9 +51,9 @@ class UrlsettingsAction extends SettingsAction
*
* @return string Title of the page
*/
function title()
{
// TRANS: Title of URL settings tab in profile settings.
return _('URL settings');
}
@ -65,7 +62,6 @@ class UrlsettingsAction extends SettingsAction
*
* @return instructions for use
*/
function getInstructions()
{
// TRANS: Instructions for tab "Other" in user profile settings.
@ -85,7 +81,6 @@ class UrlsettingsAction extends SettingsAction
*
* @return void
*/
function showContent()
{
$user = common_current_user();
@ -118,11 +113,12 @@ class UrlsettingsAction extends SettingsAction
// Include default values
// TRANS: Default value for URL shortening settings.
$services['none'] = _('[none]');
// TRANS: Default value for URL shortening settings.
$services['internal'] = _('[internal]');
if ($services) {
asort($services);
$this->elementStart('li');
@ -135,16 +131,20 @@ class UrlsettingsAction extends SettingsAction
}
$this->elementStart('li');
$this->input('maxurllength',
// TRANS: Field label in URL settings in profile.
_('URL longer than'),
(!is_null($this->arg('maxurllength'))) ?
$this->arg('maxurllength') : User_urlshortener_prefs::maxUrlLength($user),
// TRANS: Field title in URL settings in profile.
_('URLs longer than this will be shortened, 0 means always shorten.'));
$this->elementEnd('li');
$this->elementStart('li');
$this->input('maxnoticelength',
// TRANS: Field label in URL settings in profile.
_('Text longer than'),
(!is_null($this->arg('maxnoticelength'))) ?
$this->arg('maxnoticelength') : User_urlshortener_prefs::maxNoticeLength($user),
// TRANS: Field title in URL settings in profile.
_('URLs in notices longer than this will be shortened, 0 means always shorten.'));
$this->elementEnd('li');
$this->elementEnd('ul');
@ -162,7 +162,6 @@ class UrlsettingsAction extends SettingsAction
*
* @return void
*/
function handlePost()
{
// CSRF protection
@ -184,13 +183,15 @@ class UrlsettingsAction extends SettingsAction
$maxurllength = $this->trimmed('maxurllength');
if (!Validate::number($maxurllength, array('min' => 0))) {
throw new ClientException(_('Invalid number for max url length.'));
// TRANS: Client exception thrown when the maximum URL settings value is invalid in profile URL settings.
throw new ClientException(_('Invalid number for maximum URL length.'));
}
$maxnoticelength = $this->trimmed('maxnoticelength');
if (!Validate::number($maxnoticelength, array('min' => 0))) {
throw new ClientException(_('Invalid number for max notice length.'));
// TRANS: Client exception thrown when the maximum notice length settings value is invalid in profile URL settings.
throw new ClientException(_('Invalid number for maximum notice length.'));
}
$user = common_current_user();
@ -235,6 +236,7 @@ class UrlsettingsAction extends SettingsAction
}
if (!$result) {
// TRANS: Server exception thrown in profile URL settings when preferences could not be saved.
throw new ServerException(_('Error saving user URL shortening preferences.'));
}

View File

@ -111,7 +111,7 @@ class UserauthorizationAction extends Action
function showPageNotice()
{
// TRANS: Page notice on "Auhtorize subscription" page.
// TRANS: Page notice on "Authorize subscription" page.
$this->element('p', null, _('Please check these details to make sure '.
'that you want to subscribe to this ' .
'users notices. If you didnt just ask ' .
@ -243,10 +243,10 @@ class UserauthorizationAction extends Action
{
// TRANS: Accept message header from Authorise subscription page.
common_show_header(_('Subscription authorized'));
// TRANS: Accept message text from Authorise subscription page.
$this->element('p', null,
// TRANS: Accept message text from Authorise subscription page.
_('The subscription has been authorized, but no '.
'callback URL was passed. Check with the sites ' .
'callback URL was passed. Check with the site\'s ' .
'instructions for details on how to authorize the ' .
'subscription. Your subscription token is:'));
$this->element('blockquote', 'token', $tok);
@ -257,10 +257,10 @@ class UserauthorizationAction extends Action
{
// TRANS: Reject message header from Authorise subscription page.
common_show_header(_('Subscription rejected'));
// TRANS: Reject message from Authorise subscription page.
$this->element('p', null,
// TRANS: Reject message from Authorise subscription page.
_('The subscription has been rejected, but no '.
'callback URL was passed. Check with the sites ' .
'callback URL was passed. Check with the site\'s ' .
'instructions for details on how to fully reject ' .
'the subscription.'));
common_show_footer();

View File

@ -112,7 +112,7 @@ class UserrssAction extends Rss10Action
return ($avatar) ? $avatar->url : null;
}
# override parent to add X-SUP-ID URL
// override parent to add X-SUP-ID URL
function initRss($limit=0)
{

View File

@ -27,7 +27,7 @@ class Avatar extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
# We clean up the file, too
// We clean up the file, too
function delete()
{

View File

@ -79,70 +79,16 @@ class Fave extends Memcached_DataObject
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Fave', '_streamDirect'),
array($user_id, $own),
($own) ? 'fave:ids_by_user_own:'.$user_id :
'fave:ids_by_user:'.$user_id,
$offset, $limit, $since_id, $max_id);
return $ids;
$stream = new FaveNoticeStream($user_id, $own);
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
/**
* Note that the sorting for this is by order of *fave* not order of *notice*.
*
* @fixme add since_id, max_id support?
*
* @param <type> $user_id
* @param <type> $own
* @param <type> $offset
* @param <type> $limit
* @param <type> $since_id
* @param <type> $max_id
* @return <type>
*/
function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id)
function idStream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false, $since_id=0, $max_id=0)
{
$fav = new Fave();
$qry = null;
$stream = new FaveNoticeStream($user_id, $own);
if ($own) {
$qry = 'SELECT fave.* FROM fave ';
$qry .= 'WHERE fave.user_id = ' . $user_id . ' ';
} else {
$qry = 'SELECT fave.* FROM fave ';
$qry .= 'INNER JOIN notice ON fave.notice_id = notice.id ';
$qry .= 'WHERE fave.user_id = ' . $user_id . ' ';
$qry .= 'AND notice.is_local != ' . Notice::GATEWAY . ' ';
}
if ($since_id != 0) {
$qry .= 'AND notice_id > ' . $since_id . ' ';
}
if ($max_id != 0) {
$qry .= 'AND notice_id <= ' . $max_id . ' ';
}
// NOTE: we sort by fave time, not by notice time!
$qry .= 'ORDER BY modified DESC ';
if (!is_null($offset)) {
$qry .= "LIMIT $limit OFFSET $offset";
}
$fav->query($qry);
$ids = array();
while ($fav->fetch()) {
$ids[] = $fav->notice_id;
}
$fav->free();
unset($fav);
return $ids;
return $stream->getNoticeIds($offset, $limit, $since_id, $max_id);
}
function asActivity()

View File

@ -449,52 +449,8 @@ class File extends Memcached_DataObject
function stream($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array($this, '_streamDirect'),
array(),
'file:notice-ids:'.$this->url,
$offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
/**
* Stream of notices linking to this URL
*
* @param integer $offset Offset to show; default is 0
* @param integer $limit Limit of notices to show
* @param integer $since_id Since this notice
* @param integer $max_id Before this notice
*
* @return array ids of notices that link to this file
*/
function _streamDirect($offset, $limit, $since_id, $max_id)
{
$f2p = new File_to_post();
$f2p->selectAdd();
$f2p->selectAdd('post_id');
$f2p->file_id = $this->id;
Notice::addWhereSinceId($f2p, $since_id, 'post_id', 'modified');
Notice::addWhereMaxId($f2p, $max_id, 'post_id', 'modified');
$f2p->orderBy('modified DESC, post_id DESC');
if (!is_null($offset)) {
$f2p->limit($offset, $limit);
}
$ids = array();
if ($f2p->find()) {
while ($f2p->fetch()) {
$ids[] = $f2p->post_id;
}
}
return $ids;
$stream = new FileNoticeStream($this);
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function noticeCount()

View File

@ -91,7 +91,7 @@ class Foreign_link extends Memcached_DataObject
$this->profilesync = 0;
}
# Convenience methods
// Convenience methods
function getForeignUser()
{
$fuser = new Foreign_user();

View File

@ -65,7 +65,7 @@ class Foreign_user extends Memcached_DataObject
}
}
if (count($parts) == 0) {
# No changes
// No changes
return true;
}
$toupdate = implode(', ', $parts);

View File

@ -55,4 +55,80 @@ class Group_join_queue extends Managed_DataObject
$rq->insert();
return $rq;
}
function getMember()
{
$member = Profile::staticGet('id', $this->profile_id);
if (empty($member)) {
// TRANS: Exception thrown providing an invalid profile ID.
// TRANS: %s is the invalid profile ID.
throw new Exception(sprintf(_('Profile ID %s is invalid.'),$this->profile_id));
}
return $member;
}
function getGroup()
{
$group = User_group::staticGet('id', $this->group_id);
if (empty($group)) {
// TRANS: Exception thrown providing an invalid group ID.
// TRANS: %s is the invalid group ID.
throw new Exception(sprintf(_('Group ID %s is invalid.'),$this->group_id));
}
return $group;
}
/**
* Abort the pending group join...
*
* @param User_group $group
*/
function abort()
{
$profile = $this->getMember();
$group = $this->getGroup();
if ($request) {
if (Event::handle('StartCancelJoinGroup', array($profile, $group))) {
$this->delete();
Event::handle('EndCancelJoinGroup', array($profile, $group));
}
}
}
/**
* Complete a pending group join...
*
* @return Group_member object on success
*/
function complete()
{
$join = null;
$profile = $this->getMember();
$group = $this->getGroup();
if (Event::handle('StartJoinGroup', array($profile, $group))) {
$join = Group_member::join($group->id, $profile->id);
$this->delete();
Event::handle('EndJoinGroup', array($profile, $group));
}
if (!$join) {
throw new Exception('Internal error: group join failed.');
}
$join->notify();
return $join;
}
/**
* Send notifications via email etc to group administrators about
* this exciting new pending moderation queue item!
*/
public function notify()
{
$joiner = Profile::staticGet('id', $this->profile_id);
$group = User_group::staticGet('id', $this->group_id);
mail_notify_group_join_pending($group, $joiner);
}
}

View File

@ -162,4 +162,13 @@ class Group_member extends Memcached_DataObject
return $act;
}
/**
* Send notifications via email etc to group administrators about
* this exciting new membership!
*/
public function notify()
{
mail_notify_group_join($this->getGroup(), $this->getMember());
}
}

View File

@ -233,7 +233,7 @@ class Inbox extends Memcached_DataObject
// Do a bulk lookup for the first $limit items
// Fast path when nothing's deleted.
$firstChunk = array_slice($ids, 0, $offset + $limit);
$notices = Notice::getStreamByIds($firstChunk);
$notices = NoticeStream::getStreamByIds($firstChunk);
assert($notices instanceof ArrayWrapper);
$items = $notices->_items;
@ -292,7 +292,7 @@ class Inbox extends Memcached_DataObject
// Do a bulk lookup for the first $limit items
// Fast path when nothing's deleted.
$firstChunk = array_slice($ids, 0, $limit);
$notices = Notice::getStreamByIds($firstChunk);
$notices = NoticeStream::getStreamByIds($firstChunk);
$wanted = count($firstChunk); // raw entry count in the inbox up to our $limit
if ($notices->N >= $wanted) {

View File

@ -34,7 +34,7 @@ class Memcached_DataObject extends Safe_DataObject
{
if (is_null($v)) {
$v = $k;
# XXX: HACK!
// XXX: HACK!
$i = new $cls;
$keys = $i->keys();
$k = $keys[0];

View File

@ -45,7 +45,7 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
/* We keep 200 notices, the max number of notices available per API request,
* in the memcached cache. */
define('NOTICE_CACHE_WINDOW', 200);
define('NOTICE_CACHE_WINDOW', CachingNoticeStream::CACHE_WINDOW);
define('MAX_BOXCARS', 128);
@ -73,6 +73,7 @@ class Notice extends Memcached_DataObject
public $location_ns; // int(4)
public $repeat_of; // int(4)
public $object_type; // varchar(255)
public $scope; // int(4)
/* Static get */
function staticGet($k,$v=NULL)
@ -89,6 +90,12 @@ class Notice extends Memcached_DataObject
const LOCAL_NONPUBLIC = -1;
const GATEWAY = -2;
const PUBLIC_SCOPE = 0; // Useful fake constant
const SITE_SCOPE = 1;
const ADDRESSEE_SCOPE = 2;
const GROUP_SCOPE = 4;
const FOLLOWER_SCOPE = 8;
function getProfile()
{
$profile = Profile::staticGet('id', $this->profile_id);
@ -243,6 +250,7 @@ class Notice extends Memcached_DataObject
* notice in place of extracting links from content
* boolean 'distribute' whether to distribute the notice, default true
* string 'object_type' URL of the associated object type (default ActivityObject::NOTE)
* int 'scope' Scope bitmask; default to SITE_SCOPE on private sites, 0 otherwise
*
* @fixme tag override
*
@ -254,6 +262,7 @@ class Notice extends Memcached_DataObject
'url' => null,
'reply_to' => null,
'repeat_of' => null,
'scope' => null,
'distribute' => true);
if (!empty($options)) {
@ -312,7 +321,7 @@ class Notice extends Memcached_DataObject
$autosource = common_config('public', 'autosource');
# Sandboxed are non-false, but not 1, either
// Sandboxed are non-false, but not 1, either
if (!$profile->hasRight(Right::PUBLICNOTICE) ||
($source && $autosource && in_array($source, $autosource))) {
@ -336,6 +345,37 @@ class Notice extends Memcached_DataObject
// Handle repeat case
if (isset($repeat_of)) {
// Check for a private one
$repeat = Notice::staticGet('id', $repeat_of);
if (empty($repeat)) {
throw new ClientException(_('Cannot repeat; original notice is missing or deleted.'));
}
if ($profile->id == $repeat->profile_id) {
// TRANS: Client error displayed when trying to repeat an own notice.
throw new ClientException(_('You cannot repeat your own notice.'));
}
if ($repeat->scope != Notice::SITE_SCOPE &&
$repeat->scope != Notice::PUBLIC_SCOPE) {
// TRANS: Client error displayed when trying to repeat a non-public notice.
throw new ClientException(_('Cannot repeat a private notice.'), 403);
}
if (!$repeat->inScope($profile)) {
// The generic checks above should cover this, but let's be sure!
// TRANS: Client error displayed when trying to repeat a notice you cannot access.
throw new ClientException(_('Cannot repeat a notice you cannot read.'), 403);
}
if ($profile->hasRepeated($repeat->id)) {
// TRANS: Client error displayed when trying to repeat an already repeated notice.
throw new ClientException(_('You already repeated that notice.'));
}
$notice->repeat_of = $repeat_of;
} else {
$notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final);
@ -343,6 +383,12 @@ class Notice extends Memcached_DataObject
if (!empty($notice->reply_to)) {
$reply = Notice::staticGet('id', $notice->reply_to);
if (!$reply->inScope($profile)) {
// TRANS: Client error displayed when trying to reply to a notice a the target has no access to.
// TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
throw new ClientException(sprintf(_('%1$s has no access to notice %2$d.'),
$profile->nickname, $reply->id), 403);
}
$notice->conversation = $reply->conversation;
}
@ -368,6 +414,12 @@ class Notice extends Memcached_DataObject
$notice->object_type = $object_type;
}
if (is_null($scope)) { // 0 is a valid value
$notice->scope = common_config('notice', 'defaultscope');
} else {
$notice->scope = $scope;
}
if (Event::handle('StartNoticeSave', array(&$notice))) {
// XXX: some of these functions write to the DB
@ -410,8 +462,8 @@ class Notice extends Memcached_DataObject
}
# Clear the cache for subscribed users, so they'll update at next request
# XXX: someone clever could prepend instead of clearing the cache
// Clear the cache for subscribed users, so they'll update at next request
// XXX: someone clever could prepend instead of clearing the cache
$notice->blowOnInsert();
@ -554,7 +606,7 @@ class Notice extends Memcached_DataObject
if (empty($profile)) {
return false;
}
$notice = $profile->getNotices(0, NOTICE_CACHE_WINDOW);
$notice = $profile->getNotices(0, CachingNoticeStream::CACHE_WINDOW);
if (!empty($notice)) {
$last = 0;
while ($notice->fetch()) {
@ -565,8 +617,8 @@ class Notice extends Memcached_DataObject
}
}
}
# If we get here, oldest item in cache window is not
# old enough for dupe limit; do direct check against DB
// If we get here, oldest item in cache window is not
// old enough for dupe limit; do direct check against DB
$notice = new Notice();
$notice->profile_id = $profile_id;
$notice->content = $content;
@ -582,16 +634,16 @@ class Notice extends Memcached_DataObject
if (empty($profile)) {
return false;
}
# Get the Nth notice
// Get the Nth notice
$notice = $profile->getNotices(common_config('throttle', 'count') - 1, 1);
if ($notice && $notice->fetch()) {
# If the Nth notice was posted less than timespan seconds ago
// If the Nth notice was posted less than timespan seconds ago
if (time() - strtotime($notice->created) <= common_config('throttle', 'timespan')) {
# Then we throttle
// Then we throttle
return false;
}
}
# Either not N notices in the stream, OR the Nth was not posted within timespan seconds
// Either not N notices in the stream, OR the Nth was not posted within timespan seconds
return true;
}
@ -635,134 +687,19 @@ class Notice extends Memcached_DataObject
return $att;
}
function getStreamByIds($ids)
{
$cache = Cache::instance();
if (!empty($cache)) {
$notices = array();
foreach ($ids as $id) {
$n = Notice::staticGet('id', $id);
if (!empty($n)) {
$notices[] = $n;
}
}
return new ArrayWrapper($notices);
} else {
$notice = new Notice();
if (empty($ids)) {
//if no IDs requested, just return the notice object
return $notice;
}
$notice->whereAdd('id in (' . implode(', ', $ids) . ')');
$notice->find();
$temp = array();
while ($notice->fetch()) {
$temp[$notice->id] = clone($notice);
}
$wrapped = array();
foreach ($ids as $id) {
if (array_key_exists($id, $temp)) {
$wrapped[] = $temp[$id];
}
}
return new ArrayWrapper($wrapped);
}
}
function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Notice', '_publicStreamDirect'),
array(),
'public',
$offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
$stream = new PublicNoticeStream();
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0)
{
$notice = new Notice();
$notice->selectAdd(); // clears it
$notice->selectAdd('id');
$notice->orderBy('created DESC, id DESC');
if (!is_null($offset)) {
$notice->limit($offset, $limit);
}
if (common_config('public', 'localonly')) {
$notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
} else {
# -1 == blacklisted, -2 == gateway (i.e. Twitter)
$notice->whereAdd('is_local !='. Notice::LOCAL_NONPUBLIC);
$notice->whereAdd('is_local !='. Notice::GATEWAY);
}
Notice::addWhereSinceId($notice, $since_id);
Notice::addWhereMaxId($notice, $max_id);
$ids = array();
if ($notice->find()) {
while ($notice->fetch()) {
$ids[] = $notice->id;
}
}
$notice->free();
$notice = NULL;
return $ids;
}
function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Notice', '_conversationStreamDirect'),
array($id),
'notice:conversation_ids:'.$id,
$offset, $limit, $since_id, $max_id);
$stream = new ConversationNoticeStream($id);
return Notice::getStreamByIds($ids);
}
function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$notice = new Notice();
$notice->selectAdd(); // clears it
$notice->selectAdd('id');
$notice->conversation = $id;
$notice->orderBy('created DESC, id DESC');
if (!is_null($offset)) {
$notice->limit($offset, $limit);
}
Notice::addWhereSinceId($notice, $since_id);
Notice::addWhereMaxId($notice, $max_id);
$ids = array();
if ($notice->find()) {
while ($notice->fetch()) {
$ids[] = $notice->id;
}
}
$notice->free();
$notice = NULL;
return $ids;
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
/**
@ -1655,61 +1592,6 @@ class Notice extends Memcached_DataObject
}
}
function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$cache = Cache::instance();
if (empty($cache) ||
$since_id != 0 || $max_id != 0 ||
is_null($limit) ||
($offset + $limit) > NOTICE_CACHE_WINDOW) {
return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id,
$max_id)));
}
$idkey = Cache::key($cachekey);
$idstr = $cache->get($idkey);
if ($idstr !== false) {
// Cache hit! Woohoo!
$window = explode(',', $idstr);
$ids = array_slice($window, $offset, $limit);
return $ids;
}
$laststr = $cache->get($idkey.';last');
if ($laststr !== false) {
$window = explode(',', $laststr);
$last_id = $window[0];
$new_ids = call_user_func_array($fn, array_merge($args, array(0, NOTICE_CACHE_WINDOW,
$last_id, 0, null)));
$new_window = array_merge($new_ids, $window);
$new_windowstr = implode(',', $new_window);
$result = $cache->set($idkey, $new_windowstr);
$result = $cache->set($idkey . ';last', $new_windowstr);
$ids = array_slice($new_window, $offset, $limit);
return $ids;
}
$window = call_user_func_array($fn, array_merge($args, array(0, NOTICE_CACHE_WINDOW,
0, 0, null)));
$windowstr = implode(',', $window);
$result = $cache->set($idkey, $windowstr);
$result = $cache->set($idkey . ';last', $windowstr);
$ids = array_slice($window, $offset, $limit);
return $ids;
}
/**
* Determine which notice, if any, a new notice is in reply to.
@ -1820,6 +1702,15 @@ class Notice extends Memcached_DataObject
return $location;
}
/**
* Convenience function for posting a repeat of an existing message.
*
* @param int $repeater_id: profile ID of user doing the repeat
* @param string $source: posting source key, eg 'web', 'api', etc
* @return Notice
*
* @throws Exception on failure or permission problems
*/
function repeat($repeater_id, $source)
{
$author = Profile::staticGet('id', $this->profile_id);
@ -1841,8 +1732,13 @@ class Notice extends Memcached_DataObject
$content = mb_substr($content, 0, $maxlen - 4) . ' ...';
}
return self::saveNew($repeater_id, $content, $source,
array('repeat_of' => $this->id));
// Scope is same as this one's
return self::saveNew($repeater_id,
$content,
$source,
array('repeat_of' => $this->id,
'scope' => $this->scope));
}
// These are supposed to be in chron order!
@ -1867,7 +1763,7 @@ class Notice extends Memcached_DataObject
}
}
return Notice::getStreamByIds($ids);
return NoticeStream::getStreamByIds($ids);
}
function _repeatStreamDirect($limit)
@ -2296,4 +2192,94 @@ class Notice extends Memcached_DataObject
($this->is_local != Notice::GATEWAY));
}
}
/**
* Check that the given profile is allowed to read, respond to, or otherwise
* act on this notice.
*
* The $scope member is a bitmask of scopes, representing a logical AND of the
* scope requirement. So, 0x03 (Notice::ADDRESSEE_SCOPE | Notice::SITE_SCOPE) means
* "only visible to people who are mentioned in the notice AND are users on this site."
* Users on the site who are not mentioned in the notice will not be able to see the
* notice.
*
* @param Profile $profile The profile to check; pass null to check for public/unauthenticated users.
*
* @return boolean whether the profile is in the notice's scope
*/
function inScope($profile)
{
// If there's no scope, anyone (even anon) is in scope.
if ($this->scope == 0) {
return true;
}
// If there's scope, anon cannot be in scope
if (empty($profile)) {
return false;
}
// Author is always in scope
if ($this->profile_id == $profile->id) {
return true;
}
// Only for users on this site
if ($this->scope & Notice::SITE_SCOPE) {
$user = $profile->getUser();
if (empty($user)) {
return false;
}
}
// Only for users mentioned in the notice
if ($this->scope & Notice::ADDRESSEE_SCOPE) {
// XXX: just query for the single reply
$replies = $this->getReplies();
if (!in_array($profile->id, $replies)) {
return false;
}
}
// Only for members of the given group
if ($this->scope & Notice::GROUP_SCOPE) {
// XXX: just query for the single membership
$groups = $this->getGroups();
$foundOne = false;
foreach ($groups as $group) {
if ($profile->isMember($group)) {
$foundOne = true;
break;
}
}
if (!$foundOne) {
return false;
}
}
// Only for followers of the author
if ($this->scope & Notice::FOLLOWER_SCOPE) {
$author = $this->getProfile();
if (!Subscription::exists($profile, $author)) {
return false;
}
}
return true;
}
}

View File

@ -36,43 +36,11 @@ class Notice_tag extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
static function getStream($tag, $offset=0, $limit=20) {
$ids = Notice::stream(array('Notice_tag', '_streamDirect'),
array($tag),
'notice_tag:notice_ids:' . Cache::keyize($tag),
$offset, $limit);
return Notice::getStreamByIds($ids);
}
function _streamDirect($tag, $offset, $limit, $since_id, $max_id)
static function getStream($tag, $offset=0, $limit=20, $sinceId=0, $maxId=0)
{
$nt = new Notice_tag();
$nt->tag = $tag;
$nt->selectAdd();
$nt->selectAdd('notice_id');
Notice::addWhereSinceId($nt, $since_id, 'notice_id');
Notice::addWhereMaxId($nt, $max_id, 'notice_id');
$nt->orderBy('created DESC, notice_id DESC');
if (!is_null($offset)) {
$nt->limit($offset, $limit);
}
$ids = array();
if ($nt->find()) {
while ($nt->fetch()) {
$ids[] = $nt->notice_id;
}
}
return $ids;
$stream = new TagNoticeStream($tag);
return $stream->getNotices($offset, $limit, $sinceId, $maxId);
}
function blowCache($blowLast=false)

View File

@ -51,7 +51,7 @@ class Oauth_application_user extends Memcached_DataObject
}
}
if (count($parts) == 0) {
# No changes
// No changes
return true;
}
$toupdate = implode(', ', $parts);

View File

@ -93,7 +93,7 @@ class Profile extends Memcached_DataObject
$avatar->url = Avatar::url($filename);
$avatar->created = DB_DataObject_Cast::dateTime(); # current time
# XXX: start a transaction here
// XXX: start a transaction here
if (!$this->delete_avatars() || !$avatar->insert()) {
@unlink(Avatar::path($filename));
@ -101,7 +101,7 @@ class Profile extends Memcached_DataObject
}
foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) {
# We don't do a scaled one if original is our scaled size
// We don't do a scaled one if original is our scaled size
if (!($avatar->width == $size && $avatar->height == $size)) {
$scaled_filename = $imagefile->resize($size);
@ -168,7 +168,7 @@ class Profile extends Memcached_DataObject
function getFancyName()
{
if ($this->fullname) {
// TRANS: Full name of a profile or group followed by nickname in parens
// TRANS: Full name of a profile or group (%1$s) followed by nickname (%2$s) in parentheses.
return sprintf(_m('FANCYNAME','%1$s (%2$s)'), $this->fullname, $this->nickname);
} else {
return $this->nickname;
@ -180,7 +180,6 @@ class Profile extends Memcached_DataObject
*
* @return mixed Notice or null
*/
function getCurrentNotice()
{
$notice = $this->getNotices(0, 1);
@ -198,90 +197,16 @@ class Profile extends Memcached_DataObject
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array($this, '_streamTaggedDirect'),
array($tag),
'profile:notice_ids_tagged:' . $this->id . ':' . $tag,
$offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
$stream = new TaggedProfileNoticeStream($this, $tag);
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
// XXX: I'm not sure this is going to be any faster. It probably isn't.
$ids = Notice::stream(array($this, '_streamDirect'),
array(),
'profile:notice_ids:' . $this->id,
$offset, $limit, $since_id, $max_id);
$stream = new ProfileNoticeStream($this);
return Notice::getStreamByIds($ids);
}
function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id)
{
// XXX It would be nice to do this without a join
// (necessary to do it efficiently on accounts with long history)
$notice = new Notice();
$query =
"select id from notice join notice_tag on id=notice_id where tag='".
$notice->escape($tag) .
"' and profile_id=" . intval($this->id);
$since = Notice::whereSinceId($since_id, 'id', 'notice.created');
if ($since) {
$query .= " and ($since)";
}
$max = Notice::whereMaxId($max_id, 'id', 'notice.created');
if ($max) {
$query .= " and ($max)";
}
$query .= ' order by notice.created DESC, id DESC';
if (!is_null($offset)) {
$query .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset);
}
$notice->query($query);
$ids = array();
while ($notice->fetch()) {
$ids[] = $notice->id;
}
return $ids;
}
function _streamDirect($offset, $limit, $since_id, $max_id)
{
$notice = new Notice();
$notice->profile_id = $this->id;
$notice->selectAdd();
$notice->selectAdd('id');
Notice::addWhereSinceId($notice, $since_id);
Notice::addWhereMaxId($notice, $max_id);
$notice->orderBy('created DESC, id DESC');
if (!is_null($offset)) {
$notice->limit($offset, $limit);
}
$notice->find();
$ids = array();
while ($notice->fetch()) {
$ids[] = $notice->id;
}
return $ids;
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function isMember($group)
@ -532,61 +457,26 @@ class Profile extends Memcached_DataObject
*/
function joinGroup(User_group $group)
{
$ok = null;
$join = null;
if ($group->join_policy == User_group::JOIN_POLICY_MODERATE) {
$ok = Group_join_queue::saveNew($this, $group);
$join = Group_join_queue::saveNew($this, $group);
} else {
if (Event::handle('StartJoinGroup', array($group, $this))) {
$ok = Group_member::join($group->id, $this->id);
$join = Group_member::join($group->id, $this->id);
Event::handle('EndJoinGroup', array($group, $this));
}
}
return $ok;
}
/**
* Cancel a pending group join...
*
* @param User_group $group
*/
function cancelJoinGroup(User_group $group)
{
$request = Group_join_queue::pkeyGet(array('profile_id' => $this->id,
'group_id' => $group->id));
if ($request) {
if (Event::handle('StartCancelJoinGroup', array($group, $this))) {
$request->delete();
Event::handle('EndCancelJoinGroup', array($group, $this));
}
if ($join) {
// Send any applicable notifications...
$join->notify();
}
}
/**
* Complete a pending group join on our end...
*
* @param User_group $group
*/
function completeJoinGroup(User_group $group)
{
$ok = null;
$request = Group_join_queue::pkeyGet(array('profile_id' => $this->id,
'group_id' => $group->id));
if ($request) {
if (Event::handle('StartJoinGroup', array($group, $this))) {
$ok = Group_member::join($group->id, $this->id);
$request->delete();
Event::handle('EndJoinGroup', array($group, $this));
}
} else {
throw new Exception(_m('Invalid group join approval: not pending.'));
}
return $ok;
return $join;
}
/**
* Leave a group that this profile is a member of.
*
* @param User_group $group
* @param User_group $group
*/
function leaveGroup(User_group $group)
{
@ -642,7 +532,6 @@ class Profile extends Memcached_DataObject
return new ArrayWrapper($profiles);
}
function getTaggedSubscribers($tag)
{
$qry =
@ -668,6 +557,36 @@ class Profile extends Memcached_DataObject
return $tagged;
}
/**
* Get pending subscribers, who have not yet been approved.
*
* @param int $offset
* @param int $limit
* @return Profile
*/
function getRequests($offset=0, $limit=null)
{
$qry =
'SELECT profile.* ' .
'FROM profile JOIN subscription_queue '.
'ON profile.id = subscription_queue.subscriber ' .
'WHERE subscription_queue.subscribed = %d ' .
'ORDER BY subscription_queue.created DESC ';
if ($limit != null) {
if (common_config('db','type') == 'pgsql') {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
} else {
$qry .= ' LIMIT ' . $offset . ', ' . $limit;
}
}
$members = new Profile();
$members->query(sprintf($qry, $this->id));
return $members;
}
function subscriptionCount()
{
$c = Cache::instance();
@ -726,6 +645,17 @@ class Profile extends Memcached_DataObject
return Subscription::exists($this, $other);
}
/**
* Check if a pending subscription request is outstanding for this...
*
* @param Profile $other
* @return boolean
*/
function hasPendingSubscription($other)
{
return Subscription_queue::exists($this, $other);
}
/**
* Are these two profiles subscribed to each other?
*
@ -748,7 +678,7 @@ class Profile extends Memcached_DataObject
// This is the stream of favorite notices, in rev chron
// order. This forces it into cache.
$ids = Fave::stream($this->id, 0, NOTICE_CACHE_WINDOW);
$ids = Fave::idStream($this->id, 0, CachingNoticeStream::CACHE_WINDOW);
// If it's in the list, then it's a fave
@ -760,7 +690,7 @@ class Profile extends Memcached_DataObject
// then the cache has all available faves, so this one
// is not a fave.
if (count($ids) < NOTICE_CACHE_WINDOW) {
if (count($ids) < CachingNoticeStream::CACHE_WINDOW) {
return false;
}
@ -1359,4 +1289,44 @@ class Profile extends Memcached_DataObject
return $profile;
}
function canRead(Notice $notice)
{
if ($notice->scope & Notice::SITE_SCOPE) {
$user = $this->getUser();
if (empty($user)) {
return false;
}
}
if ($notice->scope & Notice::ADDRESSEE_SCOPE) {
$replies = $notice->getReplies();
if (!in_array($this->id, $replies)) {
$groups = $notice->getGroups();
$foundOne = false;
foreach ($groups as $group) {
if ($this->isMember($group)) {
$foundOne = true;
break;
}
}
if (!$foundOne) {
return false;
}
}
}
if ($notice->scope & Notice::FOLLOWER_SCOPE) {
$author = $notice->getProfile();
if (!Subscription::exists($this, $author)) {
return false;
}
}
return true;
}
}

View File

@ -106,11 +106,11 @@ class Profile_tag extends Memcached_DataObject
$ptag = new Profile_tag();
# Delete stuff that's in old and not in new
// Delete stuff that's in old and not in new
$to_delete = array_diff($oldtags, $newtags);
# Insert stuff that's in new and not in old
// Insert stuff that's in new and not in old
$to_insert = array_diff($newtags, $oldtags);
@ -271,4 +271,19 @@ class Profile_tag extends Memcached_DataObject
}
return true;
}
// Return profiles with a given tag
static function getTagged($tagger, $tag) {
$profile = new Profile();
$profile->query('SELECT profile.* ' .
'FROM profile JOIN profile_tag ' .
'ON profile.id = profile_tag.tagged ' .
'WHERE profile_tag.tagger = ' . $tagger . ' ' .
'AND profile_tag.tag = "' . $tag . '" ');
$tagged = array();
while ($profile->fetch()) {
$tagged[] = clone($profile);
}
return true;
}
}

View File

@ -46,9 +46,9 @@ class Queue_item extends Memcached_DataObject
$cnt = $qi->find(true);
if ($cnt) {
# XXX: potential race condition
# can we force it to only update if claimed is still null
# (or old)?
// XXX: potential race condition
// can we force it to only update if claimed is still null
// (or old)?
common_log(LOG_INFO, 'claiming queue item id = ' . $qi->id .
' for transport ' . $qi->transport);
$orig = clone($qi);

View File

@ -38,35 +38,8 @@ class Reply extends Memcached_DataObject
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Reply', '_streamDirect'),
array($user_id),
'reply:stream:' . $user_id,
$offset, $limit, $since_id, $max_id);
return $ids;
}
$stream = new ReplyNoticeStream($user_id);
function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$reply = new Reply();
$reply->profile_id = $user_id;
Notice::addWhereSinceId($reply, $since_id, 'notice_id', 'modified');
Notice::addWhereMaxId($reply, $max_id, 'notice_id', 'modified');
$reply->orderBy('modified DESC, notice_id DESC');
if (!is_null($offset)) {
$reply->limit($offset, $limit);
}
$ids = array();
if ($reply->find()) {
while ($reply->fetch()) {
$ids[] = $reply->notice_id;
}
}
return $ids;
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
}

View File

@ -27,6 +27,7 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Subscription extends Memcached_DataObject
{
const CACHE_WINDOW = 201;
const FORCE = true;
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@ -58,11 +59,12 @@ class Subscription extends Memcached_DataObject
*
* @param Profile $subscriber party to receive new notices
* @param Profile $other party sending notices; publisher
* @param bool $force pass Subscription::FORCE to override local subscription approval
*
* @return Subscription new subscription
* @return mixed Subscription or Subscription_queue: new subscription info
*/
static function start($subscriber, $other)
static function start($subscriber, $other, $force=false)
{
// @fixme should we enforce this as profiles in callers instead?
if ($subscriber instanceof User) {
@ -88,35 +90,39 @@ class Subscription extends Memcached_DataObject
}
if (Event::handle('StartSubscribe', array($subscriber, $other))) {
$sub = self::saveNew($subscriber->id, $other->id);
$sub->notify();
self::blow('user:notices_with_friends:%d', $subscriber->id);
self::blow('subscription:by-subscriber:'.$subscriber->id);
self::blow('subscription:by-subscribed:'.$other->id);
$subscriber->blowSubscriptionCount();
$other->blowSubscriberCount();
$otherUser = User::staticGet('id', $other->id);
if ($otherUser && $otherUser->subscribe_policy == User::SUBSCRIBE_POLICY_MODERATE && !$force) {
$sub = Subscription_queue::saveNew($subscriber, $other);
$sub->notify();
} else {
$sub = self::saveNew($subscriber->id, $other->id);
$sub->notify();
if (!empty($otherUser) &&
$otherUser->autosubscribe &&
!self::exists($other, $subscriber) &&
!$subscriber->hasBlocked($other)) {
self::blow('user:notices_with_friends:%d', $subscriber->id);
try {
self::start($other, $subscriber);
} catch (Exception $e) {
common_log(LOG_ERR, "Exception during autosubscribe of {$other->nickname} to profile {$subscriber->id}: {$e->getMessage()}");
self::blow('subscription:by-subscriber:'.$subscriber->id);
self::blow('subscription:by-subscribed:'.$other->id);
$subscriber->blowSubscriptionCount();
$other->blowSubscriberCount();
if (!empty($otherUser) &&
$otherUser->autosubscribe &&
!self::exists($other, $subscriber) &&
!$subscriber->hasBlocked($other)) {
try {
self::start($other, $subscriber);
} catch (Exception $e) {
common_log(LOG_ERR, "Exception during autosubscribe of {$other->nickname} to profile {$subscriber->id}: {$e->getMessage()}");
}
}
}
Event::handle('EndSubscribe', array($subscriber, $other));
}
return true;
return $sub;
}
/**
@ -146,9 +152,9 @@ class Subscription extends Memcached_DataObject
function notify()
{
# XXX: add other notifications (Jabber, SMS) here
# XXX: queue this and handle it offline
# XXX: Whatever happens, do it in Twitter-like API, too
// XXX: add other notifications (Jabber, SMS) here
// XXX: queue this and handle it offline
// XXX: Whatever happens, do it in Twitter-like API, too
$this->notifyEmail();
}
@ -261,8 +267,8 @@ class Subscription extends Memcached_DataObject
common_date_iso8601($this->created));
$act->time = strtotime($this->created);
// TRANS: Activity tile when subscribing to another person.
$act->title = _("Follow");
// TRANS: Activity title when subscribing to another person.
$act->title = _m('TITLE','Follow');
// TRANS: Notification given when one person starts following another.
// TRANS: %1$s is the subscriber, %2$s is the subscribed.
$act->content = sprintf(_('%1$s is now following %2$s.'),
@ -295,7 +301,6 @@ class Subscription extends Memcached_DataObject
*
* @return Subscription stream of subscriptions; use fetch() to iterate
*/
static function bySubscriber($subscriberId,
$offset = 0,
$limit = PROFILES_PER_PAGE)
@ -356,7 +361,6 @@ class Subscription extends Memcached_DataObject
*
* @return Subscription stream of subscriptions; use fetch() to iterate
*/
static function bySubscribed($subscribedId,
$offset = 0,
$limit = PROFILES_PER_PAGE)
@ -414,7 +418,6 @@ class Subscription extends Memcached_DataObject
*
* @return boolean success flag.
*/
function update($orig=null)
{
$result = parent::update($orig);

View File

@ -0,0 +1,105 @@
<?php
/**
* Table Definition for subscription_queue
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Subscription_queue extends Managed_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'subscription_queue'; // table name
public $subscriber;
public $subscribed;
public $created;
/* Static get */
function staticGet($k,$v=null)
{ return Memcached_DataObject::staticGet('Subscription_queue',$k,$v); }
/* Pkey get */
function pkeyGet($k)
{ return Memcached_DataObject::pkeyGet('Subscription_queue',$k); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
public static function schemaDef()
{
return array(
'description' => 'Holder for subscription requests awaiting moderation.',
'fields' => array(
'subscriber' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local profile making the request'),
'subscribed' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local profile being subscribed to'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
),
'primary key' => array('subscriber', 'subscribed'),
'indexes' => array(
'subscription_queue_subscriber_created_idx' => array('subscriber', 'created'),
'subscription_queue_subscribed_created_idx' => array('subscribed', 'created'),
),
'foreign keys' => array(
'subscription_queue_subscriber_fkey' => array('profile', array('subscriber' => 'id')),
'subscription_queue_subscribed_fkey' => array('profile', array('subscribed' => 'id')),
)
);
}
public static function saveNew(Profile $subscriber, Profile $subscribed)
{
$rq = new Subscription_queue();
$rq->subscriber = $subscriber->id;
$rq->subscribed = $subscribed->id;
$rq->created = common_sql_now();
$rq->insert();
return $rq;
}
function exists($subscriber, $other)
{
$sub = Subscription_queue::pkeyGet(array('subscriber' => $subscriber->id,
'subscribed' => $other->id));
return (empty($sub)) ? false : true;
}
/**
* Complete a pending subscription, as we've got approval of some sort.
*
* @return Subscription
*/
public function complete()
{
$subscriber = Profile::staticGet('id', $this->subscriber);
$subscribed = Profile::staticGet('id', $this->subscribed);
$sub = Subscription::start($subscriber, $subscribed, Subscription::FORCE);
if ($sub) {
$this->delete();
}
return $sub;
}
/**
* Cancel an outstanding subscription request to the other profile.
*/
public function abort()
{
$subscriber = Profile::staticGet('id', $this->subscriber);
$subscribed = Profile::staticGet('id', $this->subscribed);
if (Event::handle('StartCancelSubscription', array($subscriber, $subscribed))) {
$this->delete();
Event::handle('EndCancelSubscription', array($subscriber, $subscribed));
}
}
/**
* Send notifications via email etc to group administrators about
* this exciting new pending moderation queue item!
*/
public function notify()
{
$other = Profile::staticGet('id', $this->subscriber);
$listenee = User::staticGet('id', $this->subscribed);
mail_subscribe_pending_notify_profile($listenee, $other);
}
}

View File

@ -30,6 +30,9 @@ require_once 'Validate.php';
class User extends Memcached_DataObject
{
const SUBSCRIBE_POLICY_OPEN = 0;
const SUBSCRIBE_POLICY_MODERATE = 1;
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@ -55,6 +58,7 @@ class User extends Memcached_DataObject
public $smsemail; // varchar(255)
public $uri; // varchar(255) unique_key
public $autosubscribe; // tinyint(1)
public $subscribe_policy; // tinyint(1)
public $urlshorteningservice; // varchar(50) default_ur1.ca
public $inboxed; // tinyint(1)
public $design_id; // int(4)
@ -86,6 +90,12 @@ class User extends Memcached_DataObject
return $profile->isSubscribed($other);
}
function hasPendingSubscription($other)
{
$profile = $this->getProfile();
return $profile->hasPendingSubscription($other);
}
// 'update' won't write key columns, so we have to do it ourselves.
function updateKeys(&$orig)
@ -448,8 +458,7 @@ class User extends Memcached_DataObject
function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
$ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id);
return Notice::getStreamByIds($ids);
return Reply::stream($this->id, $offset, $limit, $since_id, $before_id);
}
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) {
@ -465,8 +474,7 @@ class User extends Memcached_DataObject
function favoriteNotices($own=false, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$ids = Fave::stream($this->id, $offset, $limit, $own, $since_id, $max_id);
return Notice::getStreamByIds($ids);
return Fave::stream($this->id, $offset, $limit, $own, $since_id, $max_id);
}
function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
@ -769,95 +777,18 @@ class User extends Memcached_DataObject
function repeatedByMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
$ids = Notice::stream(array($this, '_repeatedByMeDirect'),
array(),
'user:repeated_by_me:'.$this->id,
$offset, $limit, $since_id, $max_id, null);
return Notice::getStreamByIds($ids);
$stream = new RepeatedByMeNoticeStream($this);
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function _repeatedByMeDirect($offset, $limit, $since_id, $max_id)
{
$notice = new Notice();
$notice->selectAdd(); // clears it
$notice->selectAdd('id');
$notice->profile_id = $this->id;
$notice->whereAdd('repeat_of IS NOT NULL');
$notice->orderBy('created DESC, id DESC');
if (!is_null($offset)) {
$notice->limit($offset, $limit);
}
Notice::addWhereSinceId($notice, $since_id);
Notice::addWhereMaxId($notice, $max_id);
$ids = array();
if ($notice->find()) {
while ($notice->fetch()) {
$ids[] = $notice->id;
}
}
$notice->free();
$notice = NULL;
return $ids;
}
function repeatsOfMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
$ids = Notice::stream(array($this, '_repeatsOfMeDirect'),
array(),
'user:repeats_of_me:'.$this->id,
$offset, $limit, $since_id, $max_id);
$stream = new RepeatsOfMeNoticeStream($this);
return Notice::getStreamByIds($ids);
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function _repeatsOfMeDirect($offset, $limit, $since_id, $max_id)
{
$qry =
'SELECT DISTINCT original.id AS id ' .
'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' .
'WHERE original.profile_id = ' . $this->id . ' ';
$since = Notice::whereSinceId($since_id, 'original.id', 'original.created');
if ($since) {
$qry .= "AND ($since) ";
}
$max = Notice::whereMaxId($max_id, 'original.id', 'original.created');
if ($max) {
$qry .= "AND ($max) ";
}
$qry .= 'ORDER BY original.created, original.id DESC ';
if (!is_null($offset)) {
$qry .= "LIMIT $limit OFFSET $offset";
}
$ids = array();
$notice = new Notice();
$notice->query($qry);
while ($notice->fetch()) {
$ids[] = $notice->id;
}
$notice->free();
$notice = NULL;
return $ids;
}
function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
@ -1034,5 +965,4 @@ class User extends Memcached_DataObject
return $apps;
}
}

View File

@ -87,42 +87,11 @@ class User_group extends Memcached_DataObject
function getNotices($offset, $limit, $since_id=null, $max_id=null)
{
$ids = Notice::stream(array($this, '_streamDirect'),
array(),
'user_group:notice_ids:' . $this->id,
$offset, $limit, $since_id, $max_id);
$stream = new GroupNoticeStream($this);
return Notice::getStreamByIds($ids);
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function _streamDirect($offset, $limit, $since_id, $max_id)
{
$inbox = new Group_inbox();
$inbox->group_id = $this->id;
$inbox->selectAdd();
$inbox->selectAdd('notice_id');
Notice::addWhereSinceId($inbox, $since_id, 'notice_id');
Notice::addWhereMaxId($inbox, $max_id, 'notice_id');
$inbox->orderBy('created DESC, notice_id DESC');
if (!is_null($offset)) {
$inbox->limit($offset, $limit);
}
$ids = array();
if ($inbox->find()) {
while ($inbox->fetch()) {
$ids[] = $inbox->notice_id;
}
}
return $ids;
}
function allowedNickname($nickname)
{
@ -306,11 +275,11 @@ class User_group extends Memcached_DataObject
$oldaliases = $this->getAliases();
# Delete stuff that's old that not in new
// Delete stuff that's old that not in new
$to_delete = array_diff($oldaliases, $newaliases);
# Insert stuff that's in new and not in old
// Insert stuff that's in new and not in old
$to_insert = array_diff($newaliases, $oldaliases);

View File

@ -337,6 +337,7 @@ location_id = 1
location_ns = 1
repeat_of = 1
object_type = 2
scope = 1
[notice__keys]
id = N
@ -627,6 +628,7 @@ smsreplies = 17
smsemail = 2
uri = 2
autosubscribe = 17
subscribe_policy = 17
urlshorteningservice = 2
inboxed = 17
design_id = 1

View File

@ -118,6 +118,7 @@ $schema['user'] = array(
'smsemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'built from sms and carrier'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
'autosubscribe' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'automatically subscribe to users who subscribe to us'),
'subscribe_policy' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'),
'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'),
'inboxed' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'has an inbox been created for this user?'),
'design_id' => array('type' => 'int', 'description' => 'id of a design'),
@ -202,6 +203,9 @@ $schema['notice'] = array(
'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
'object_type' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
'scope' => array('type' => 'int',
'default' => '1',
'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = followers'),
),
'primary key' => array('id'),
'unique keys' => array(
@ -1099,3 +1103,5 @@ $schema['schema_version'] = array(
);
$schema['group_join_queue'] = Group_join_queue::schemaDef();
$schema['subscription_queue'] = Subscription_queue::schemaDef();

View File

@ -1,32 +1,40 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at the following url: |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Andrei Zmievski <andrei@php.net> |
// +----------------------------------------------------------------------+
//
// $Id: Getopt.php,v 1.4 2007/06/12 14:58:56 cellog Exp $
/**
* PHP Version 5
*
* Copyright (c) 1997-2004 The PHP Group
*
* This source file is subject to version 3.0 of the PHP license,
* that is bundled with this package in the file LICENSE, and is
* available through the world-wide-web at the following url:
* http://www.php.net/license/3_0.txt.
* If you did not receive a copy of the PHP license and are unable to
* obtain it through the world-wide-web, please send a note to
* license@php.net so we can mail you a copy immediately.
*
* @category Console
* @package Console_Getopt
* @author Andrei Zmievski <andrei@php.net>
* @license http://www.php.net/license/3_0.txt PHP 3.0
* @version CVS: $Id: Getopt.php 306067 2010-12-08 00:13:31Z dufuz $
* @link http://pear.php.net/package/Console_Getopt
*/
require_once 'PEAR.php';
/**
* Command-line options parsing class.
*
* @author Andrei Zmievski <andrei@php.net>
*
* @category Console
* @package Console_Getopt
* @author Andrei Zmievski <andrei@php.net>
* @license http://www.php.net/license/3_0.txt PHP 3.0
* @link http://pear.php.net/package/Console_Getopt
*/
class Console_Getopt {
class Console_Getopt
{
/**
* Parses the command-line options.
*
@ -53,45 +61,60 @@ class Console_Getopt {
*
* Most of the semantics of this function are based on GNU getopt_long().
*
* @param array $args an array of command-line arguments
* @param string $short_options specifies the list of allowed short options
* @param array $long_options specifies the list of allowed long options
* @param array $args an array of command-line arguments
* @param string $short_options specifies the list of allowed short options
* @param array $long_options specifies the list of allowed long options
* @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
*
* @return array two-element array containing the list of parsed options and
* the non-option arguments
*
* @access public
*
*/
function getopt2($args, $short_options, $long_options = null)
function getopt2($args, $short_options, $long_options = null, $skip_unknown = false)
{
return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown);
}
/**
* This function expects $args to start with the script name (POSIX-style).
* Preserved for backwards compatibility.
*
* @param array $args an array of command-line arguments
* @param string $short_options specifies the list of allowed short options
* @param array $long_options specifies the list of allowed long options
*
* @see getopt2()
*/
function getopt($args, $short_options, $long_options = null)
* @return array two-element array containing the list of parsed options and
* the non-option arguments
*/
function getopt($args, $short_options, $long_options = null, $skip_unknown = false)
{
return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown);
}
/**
* The actual implementation of the argument parsing code.
*
* @param int $version Version to use
* @param array $args an array of command-line arguments
* @param string $short_options specifies the list of allowed short options
* @param array $long_options specifies the list of allowed long options
* @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
*
* @return array
*/
function doGetopt($version, $args, $short_options, $long_options = null)
function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false)
{
// in case you pass directly readPHPArgv() as the first arg
if (PEAR::isError($args)) {
return $args;
}
if (empty($args)) {
return array(array(), array());
}
$opts = array();
$non_opts = array();
$non_opts = $opts = array();
settype($args, 'array');
@ -111,7 +134,6 @@ class Console_Getopt {
reset($args);
while (list($i, $arg) = each($args)) {
/* The special element '--' means explicit end of
options. Treat the rest of the arguments as non-options
and end the loop. */
@ -124,17 +146,27 @@ class Console_Getopt {
$non_opts = array_merge($non_opts, array_slice($args, $i));
break;
} elseif (strlen($arg) > 1 && $arg{1} == '-') {
$error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
if (PEAR::isError($error))
$error = Console_Getopt::_parseLongOption(substr($arg, 2),
$long_options,
$opts,
$args,
$skip_unknown);
if (PEAR::isError($error)) {
return $error;
}
} elseif ($arg == '-') {
// - is stdin
$non_opts = array_merge($non_opts, array_slice($args, $i));
break;
} else {
$error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
if (PEAR::isError($error))
$error = Console_Getopt::_parseShortOption(substr($arg, 1),
$short_options,
$opts,
$args,
$skip_unknown);
if (PEAR::isError($error)) {
return $error;
}
}
}
@ -142,19 +174,31 @@ class Console_Getopt {
}
/**
* @access private
* Parse short option
*
* @param string $arg Argument
* @param string[] $short_options Available short options
* @param string[][] &$opts
* @param string[] &$args
* @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
*
* @access private
* @return void
*/
function _parseShortOption($arg, $short_options, &$opts, &$args)
function _parseShortOption($arg, $short_options, &$opts, &$args, $skip_unknown)
{
for ($i = 0; $i < strlen($arg); $i++) {
$opt = $arg{$i};
$opt = $arg{$i};
$opt_arg = null;
/* Try to find the short option in the specifier string. */
if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
{
return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') {
if ($skip_unknown === true) {
break;
}
$msg = "Console_Getopt: unrecognized option -- $opt";
return PEAR::raiseError($msg);
}
if (strlen($spec) > 1 && $spec{1} == ':') {
@ -173,11 +217,14 @@ class Console_Getopt {
break;
} else if (list(, $opt_arg) = each($args)) {
/* Else use the next argument. */;
if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
if (Console_Getopt::_isShortOpt($opt_arg)
|| Console_Getopt::_isLongOpt($opt_arg)) {
$msg = "option requires an argument --$opt";
return PEAR::raiseError("Console_Getopt:" . $msg);
}
} else {
return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
$msg = "option requires an argument --$opt";
return PEAR::raiseError("Console_Getopt:" . $msg);
}
}
}
@ -187,36 +234,54 @@ class Console_Getopt {
}
/**
* @access private
* Checks if an argument is a short option
*
* @param string $arg Argument to check
*
* @access private
* @return bool
*/
function _isShortOpt($arg)
{
return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]);
return strlen($arg) == 2 && $arg[0] == '-'
&& preg_match('/[a-zA-Z]/', $arg[1]);
}
/**
* @access private
* Checks if an argument is a long option
*
* @param string $arg Argument to check
*
* @access private
* @return bool
*/
function _isLongOpt($arg)
{
return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
preg_match('/[a-zA-Z]+$/', substr($arg, 2));
preg_match('/[a-zA-Z]+$/', substr($arg, 2));
}
/**
* @access private
* Parse long option
*
* @param string $arg Argument
* @param string[] $long_options Available long options
* @param string[][] &$opts
* @param string[] &$args
*
* @access private
* @return void|PEAR_Error
*/
function _parseLongOption($arg, $long_options, &$opts, &$args)
function _parseLongOption($arg, $long_options, &$opts, &$args, $skip_unknown)
{
@list($opt, $opt_arg) = explode('=', $arg, 2);
$opt_len = strlen($opt);
for ($i = 0; $i < count($long_options); $i++) {
$long_opt = $long_options[$i];
$opt_start = substr($long_opt, 0, $opt_len);
$long_opt_name = str_replace('=', '', $long_opt);
/* Option doesn't match. Go on to the next one. */
@ -224,7 +289,7 @@ class Console_Getopt {
continue;
}
$opt_rest = substr($long_opt, $opt_len);
$opt_rest = substr($long_opt, $opt_len);
/* Check that the options uniquely matches one of the allowed
options. */
@ -233,12 +298,15 @@ class Console_Getopt {
} else {
$next_option_rest = '';
}
if ($opt_rest != '' && $opt{0} != '=' &&
$i + 1 < count($long_options) &&
$opt == substr($long_options[$i+1], 0, $opt_len) &&
$next_option_rest != '' &&
$next_option_rest{0} != '=') {
return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
$msg = "Console_Getopt: option --$opt is ambiguous";
return PEAR::raiseError($msg);
}
if (substr($long_opt, -1) == '=') {
@ -246,37 +314,47 @@ class Console_Getopt {
/* Long option requires an argument.
Take the next argument if one wasn't specified. */;
if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
$msg = "Console_Getopt: option requires an argument --$opt";
return PEAR::raiseError($msg);
}
if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
return PEAR::raiseError("Console_Getopt: option requires an argument --$opt");
if (Console_Getopt::_isShortOpt($opt_arg)
|| Console_Getopt::_isLongOpt($opt_arg)) {
$msg = "Console_Getopt: option requires an argument --$opt";
return PEAR::raiseError($msg);
}
}
} else if ($opt_arg) {
return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
$msg = "Console_Getopt: option --$opt doesn't allow an argument";
return PEAR::raiseError($msg);
}
$opts[] = array('--' . $opt, $opt_arg);
return;
}
if ($skip_unknown === true) {
return;
}
return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
}
/**
* Safely read the $argv PHP array across different PHP configurations.
* Will take care on register_globals and register_argc_argv ini directives
*
* @access public
* @return mixed the $argv PHP array or PEAR error if not registered
*/
* Safely read the $argv PHP array across different PHP configurations.
* Will take care on register_globals and register_argc_argv ini directives
*
* @access public
* @return mixed the $argv PHP array or PEAR error if not registered
*/
function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if (!@is_array($_SERVER['argv'])) {
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
$msg = "Could not read cmd args (register_argc_argv=Off?)";
return PEAR::raiseError("Console_Getopt: " . $msg);
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
@ -285,6 +363,4 @@ class Console_Getopt {
return $argv;
}
}
?>
}

View File

@ -15,7 +15,7 @@
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version CVS: $Id: DataObject.php 291349 2009-11-27 09:15:18Z alan_k $
* @version CVS: $Id: DataObject.php 301030 2010-07-07 02:26:31Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
@ -235,7 +235,7 @@ class DB_DataObject extends DB_DataObject_Overload
* @access private
* @var string
*/
var $_DB_DataObject_version = "1.9.0";
var $_DB_DataObject_version = "1.9.5";
/**
* The Database table (used by table extends)
@ -369,6 +369,32 @@ class DB_DataObject extends DB_DataObject_Overload
return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
}
/**
* build the basic select query.
*
* @access private
*/
function _build_select()
{
global $_DB_DATAOBJECT;
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
if ($quoteIdentifiers) {
$this->_connect();
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
}
$sql = 'SELECT ' .
$this->_query['data_select'] . " \n" .
' FROM ' . ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table) . " \n" .
$this->_join . " \n" .
$this->_query['condition'] . " \n" .
$this->_query['group_by'] . " \n" .
$this->_query['having'] . " \n";
return $sql;
}
/**
* find results, either normal or crosstable
*
@ -411,20 +437,21 @@ class DB_DataObject extends DB_DataObject_Overload
$query_before = $this->_query;
$this->_build_condition($this->table()) ;
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
$this->_connect();
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
/* We are checking for method modifyLimitQuery as it is PEAR DB specific */
$sql = 'SELECT ' .
$this->_query['data_select'] . " \n" .
' FROM ' . ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table) . " \n" .
$this->_join . " \n" .
$this->_query['condition'] . " \n" .
$this->_query['group_by'] . " \n" .
$this->_query['having'] . " \n" .
$this->_query['order_by'] . " \n";
$sql = $this->_build_select();
foreach ($this->_query['unions'] as $union_ar) {
$sql .= $union_ar[1] . $union_ar[0]->_build_select() . " \n";
}
$sql .= $this->_query['order_by'] . " \n";
/* We are checking for method modifyLimitQuery as it is PEAR DB specific */
if ((!isset($_DB_DATAOBJECT['CONFIG']['db_driver'])) ||
($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) {
/* PEAR DB specific */
@ -578,6 +605,85 @@ class DB_DataObject extends DB_DataObject_Overload
return true;
}
/**
* fetches all results as an array,
*
* return format is dependant on args.
* if selectAdd() has not been called on the object, then it will add the correct columns to the query.
*
* A) Array of values (eg. a list of 'id')
*
* $x = DB_DataObject::factory('mytable');
* $x->whereAdd('something = 1')
* $ar = $x->fetchAll('id');
* -- returns array(1,2,3,4,5)
*
* B) Array of values (not from table)
*
* $x = DB_DataObject::factory('mytable');
* $x->whereAdd('something = 1');
* $x->selectAdd();
* $x->selectAdd('distinct(group_id) as group_id');
* $ar = $x->fetchAll('group_id');
* -- returns array(1,2,3,4,5)
* *
* C) A key=>value associative array
*
* $x = DB_DataObject::factory('mytable');
* $x->whereAdd('something = 1')
* $ar = $x->fetchAll('id','name');
* -- returns array(1=>'fred',2=>'blogs',3=> .......
*
* D) array of objects
* $x = DB_DataObject::factory('mytable');
* $x->whereAdd('something = 1');
* $ar = $x->fetchAll();
*
* E) array of arrays (for example)
* $x = DB_DataObject::factory('mytable');
* $x->whereAdd('something = 1');
* $ar = $x->fetchAll(false,false,'toArray');
*
*
* @param string|false $k key
* @param string|false $v value
* @param string|false $method method to call on each result to get array value (eg. 'toArray')
* @access public
* @return array format dependant on arguments, may be empty
*/
function fetchAll($k= false, $v = false, $method = false)
{
// should it even do this!!!?!?
if ($k !== false &&
( // only do this is we have not been explicit..
empty($this->_query['data_select']) ||
($this->_query['data_select'] == '*')
)
) {
$this->selectAdd();
$this->selectAdd($k);
if ($v !== false) {
$this->selectAdd($v);
}
}
$this->find();
$ret = array();
while ($this->fetch()) {
if ($v !== false) {
$ret[$this->$k] = $this->$v;
continue;
}
$ret[] = $k === false ?
($method == false ? clone($this) : $this->$method())
: $this->$k;
}
return $ret;
}
/**
* Adds a condition to the WHERE statement, defaults to AND
*
@ -622,6 +728,47 @@ class DB_DataObject extends DB_DataObject_Overload
return $r;
}
/**
* Adds a 'IN' condition to the WHERE statement
*
* $object->whereAddIn('id', $array, 'int'); //minimal usage
* $object->whereAddIn('price', $array, 'float', 'OR'); // cast to float, and call whereAdd with 'OR'
* $object->whereAddIn('name', $array, 'string'); // quote strings
*
* @param string $key key column to match
* @param array $list list of values to match
* @param string $type string|int|integer|float|bool cast to type.
* @param string $logic optional logic to call whereAdd with eg. "OR" (defaults to "AND")
* @access public
* @return string|PEAR::Error - previous condition or Error when invalid args found
*/
function whereAddIn($key, $list, $type, $logic = 'AND')
{
$not = '';
if ($key[0] == '!') {
$not = 'NOT ';
$key = substr($key, 1);
}
// fix type for short entry.
$type = $type == 'int' ? 'integer' : $type;
if ($type == 'string') {
$this->_connect();
}
$ar = array();
foreach($list as $k) {
settype($k, $type);
$ar[] = $type =='string' ? $this->_quote($k) : $k;
}
if (!$ar) {
return $not ? $this->_query['condition'] : $this->whereAdd("1=0");
}
return $this->whereAdd("$key $not IN (". implode(',', $ar). ')', $logic );
}
/**
* Adds a order by condition
*
@ -1175,7 +1322,7 @@ class DB_DataObject extends DB_DataObject_Overload
$seq = $this->sequenceKey();
if ($seq[0] !== false) {
$keys = array($seq[0]);
if (empty($this->{$keys[0]}) && $dataObject !== true) {
if (!isset($this->{$keys[0]}) && $dataObject !== true) {
$this->raiseError("update: trying to perform an update without
the key set, and argument to update is not
DB_DATAOBJECT_WHEREADD_ONLY
@ -1670,6 +1817,7 @@ class DB_DataObject extends DB_DataObject_Overload
'limit_start' => '', // the LIMIT condition
'limit_count' => '', // the LIMIT condition
'data_select' => '*', // the columns to be SELECTed
'unions' => array(), // the added unions
);
@ -2032,9 +2180,9 @@ class DB_DataObject extends DB_DataObject_Overload
$seqname = false;
if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
$usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
if (strpos($usekey,':') !== false) {
list($usekey,$seqname) = explode(':',$usekey);
$seqname = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
if (strpos($seqname,':') !== false) {
list($usekey,$seqname) = explode(':',$seqname);
}
}
@ -3068,9 +3216,9 @@ class DB_DataObject extends DB_DataObject_Overload
}
/**
* IS THIS SUPPORTED/USED ANYMORE????
*return a list of options for a linked table
*
* getLinkArray
* Fetch an array of related objects. This should be used in conjunction with a <dbname>.links.ini file configuration (see the introduction on linking for details on this).
* You may also use this with all parameters to specify, the column and related table.
* This is highly dependant on naming columns 'correctly' :)
* using colname = xxxxx_yyyyyy
* xxxxxx = related table; (yyyyy = user defined..)
@ -3078,7 +3226,21 @@ class DB_DataObject extends DB_DataObject_Overload
* stores it in $this->_xxxxx_yyyyy
*
* @access public
* @return array of results (empty array on failure)
* @param string $column - either column or column.xxxxx
* @param string $table - name of table to look up value in
* @return array - array of results (empty array on failure)
*
* Example - Getting the related objects
*
* $person = new DataObjects_Person;
* $person->get(12);
* $children = $person->getLinkArray('children');
*
* echo 'There are ', count($children), ' descendant(s):<br />';
* foreach ($children as $child) {
* echo $child->name, '<br />';
* }
*
*/
function &getLinkArray($row, $table = null)
{
@ -3123,6 +3285,46 @@ class DB_DataObject extends DB_DataObject_Overload
return $ret;
}
/**
* unionAdd - adds another dataobject to this, building a unioned query.
*
* usage:
* $doTable1 = DB_DataObject::factory("table1");
* $doTable2 = DB_DataObject::factory("table2");
*
* $doTable1->selectAdd();
* $doTable1->selectAdd("col1,col2");
* $doTable1->whereAdd("col1 > 100");
* $doTable1->orderBy("col1");
*
* $doTable2->selectAdd();
* $doTable2->selectAdd("col1, col2");
* $doTable2->whereAdd("col2 = 'v'");
*
* $doTable1->unionAdd($doTable2);
* $doTable1->find();
*
* Note: this model may be a better way to implement joinAdd?, eg. do the building in find?
*
*
* @param $obj object|false the union object or false to reset
* @param optional $is_all string 'ALL' to do all.
* @returns $obj object|array the added object, or old list if reset.
*/
function unionAdd($obj,$is_all= '')
{
if ($obj === false) {
$ret = $this->_query['unions'];
$this->_query['unions'] = array();
return $ret;
}
$this->_query['unions'][] = array($obj, 'UNION ' . $is_all . ' ') ;
return $obj;
}
/**
* The JOIN condition
*
@ -3248,31 +3450,46 @@ class DB_DataObject extends DB_DataObject_Overload
/* otherwise see if there are any links from this table to the obj. */
//print_r($this->links());
if (($ofield === false) && ($links = $this->links())) {
foreach ($links as $k => $v) {
/* link contains {this column} = {linked table}:{linked column} */
$ar = explode(':', $v);
// Feature Request #4266 - Allow joins with multiple keys
if (strpos($k, ',') !== false) {
$k = explode(',', $k);
}
if (strpos($ar[1], ',') !== false) {
$ar[1] = explode(',', $ar[1]);
// this enables for support for arrays of links in ini file.
// link contains this_column[] = linked_table:linked_column
// or standard way.
// link contains this_column = linked_table:linked_column
foreach ($links as $k => $linkVar) {
if (!is_array($linkVar)) {
$linkVar = array($linkVar);
}
foreach($linkVar as $v) {
if ($ar[0] == $obj->__table) {
/* link contains {this column} = {linked table}:{linked column} */
$ar = explode(':', $v);
// Feature Request #4266 - Allow joins with multiple keys
if (strpos($k, ',') !== false) {
$k = explode(',', $k);
}
if (strpos($ar[1], ',') !== false) {
$ar[1] = explode(',', $ar[1]);
}
if ($ar[0] != $obj->__table) {
continue;
}
if ($joinCol !== false) {
if ($k == $joinCol) {
// got it!?
$tfield = $k;
$ofield = $ar[1];
break;
} else {
continue;
}
} else {
$tfield = $k;
$ofield = $ar[1];
break;
}
}
continue;
}
$tfield = $k;
$ofield = $ar[1];
break;
}
}
}
@ -3280,23 +3497,30 @@ class DB_DataObject extends DB_DataObject_Overload
//print_r($obj->links());
if (!$ofield && ($olinks = $obj->links())) {
foreach ($olinks as $k => $v) {
/* link contains {this column} = {linked table}:{linked column} */
$ar = explode(':', $v);
// Feature Request #4266 - Allow joins with multiple keys
$links_key_array = strpos($k,',');
if ($links_key_array !== false) {
$k = explode(',', $k);
foreach ($olinks as $k => $linkVar) {
/* link contains {this column} = array ( {linked table}:{linked column} )*/
if (!is_array($linkVar)) {
$linkVar = array($linkVar);
}
$ar_array = strpos($ar[1],',');
if ($ar_array !== false) {
$ar[1] = explode(',', $ar[1]);
}
if ($ar[0] == $this->__table) {
foreach($linkVar as $v) {
/* link contains {this column} = {linked table}:{linked column} */
$ar = explode(':', $v);
// Feature Request #4266 - Allow joins with multiple keys
$links_key_array = strpos($k,',');
if ($links_key_array !== false) {
$k = explode(',', $k);
}
$ar_array = strpos($ar[1],',');
if ($ar_array !== false) {
$ar[1] = explode(',', $ar[1]);
}
if ($ar[0] != $this->__table) {
continue;
}
// you have explictly specified the column
// and the col is listed here..
@ -3315,6 +3539,7 @@ class DB_DataObject extends DB_DataObject_Overload
$ofield = $k;
$tfield = $ar[1];
break;
}
}
}
@ -3573,7 +3798,14 @@ class DB_DataObject extends DB_DataObject_Overload
if (!$k) {
continue; // ignore empty keys!!! what
}
if (is_object($from) && isset($from->{sprintf($format,$k)})) {
$chk = is_object($from) &&
(version_compare(phpversion(), "5.1.0" , ">=") ?
property_exists($from, sprintf($format,$k)) : // php5.1
array_key_exists( sprintf($format,$k), get_class_vars($from)) //older
);
// if from has property ($format($k)
if ($chk) {
$kk = (strtolower($k) == 'from') ? '_from' : $k;
if (method_exists($this,'set'.$kk)) {
$ret = $this->{'set'.$kk}($from->{sprintf($format,$k)});

View File

@ -15,7 +15,7 @@
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version CVS: $Id: Generator.php 289384 2009-10-09 00:17:26Z alan_k $
* @version CVS: $Id: Generator.php 298560 2010-04-25 23:01:51Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
@ -383,8 +383,8 @@ class DB_DataObject_Generator extends DB_DataObject
return false;
}
$__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
echo "WARNING: cant handle non-mysql introspection for defaults.";
if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
return; // cant handle non-mysql introspection for defaults.
}
@ -392,33 +392,72 @@ class DB_DataObject_Generator extends DB_DataObject
$fk = array();
foreach($this->tables as $this->table) {
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;
$res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable );
if (PEAR::isError($res)) {
die($res->getMessage());
}
switch ($DB->phptype) {
$text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
$treffer = array();
// Extract FOREIGN KEYS
preg_match_all(
"/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i",
$text[1],
$treffer,
PREG_SET_ORDER);
if (count($treffer) < 1) {
continue;
}
for ($i = 0; $i < count($treffer); $i++) {
$fk[$this->table][$treffer[$i][1]] = $treffer[$i][2] . ":" . $treffer[$i][3];
}
case 'pgsql':
foreach($this->tables as $this->table) {
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;
$res =& $DB->query("SELECT
pg_catalog.pg_get_constraintdef(r.oid, true) AS condef
FROM pg_catalog.pg_constraint r,
pg_catalog.pg_class c
WHERE c.oid=r.conrelid
AND r.contype = 'f'
AND c.relname = '" . $quotedTable . "'");
if (PEAR::isError($res)) {
die($res->getMessage());
}
while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
$treffer = array();
// this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
preg_match(
"/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",
$row['condef'],
$treffer);
if (!count($treffer)) {
continue;
}
$fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];
}
}
break;
case 'mysql':
case 'mysqli':
default:
foreach($this->tables as $this->table) {
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;
$res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable );
if (PEAR::isError($res)) {
die($res->getMessage());
}
$text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
$treffer = array();
// Extract FOREIGN KEYS
preg_match_all(
"/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i",
$text[1],
$treffer,
PREG_SET_ORDER);
if (!count($treffer)) {
continue;
}
foreach($treffer as $i=> $tref) {
$fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
}
}
}
$links_ini = "";
foreach($fk as $table => $details) {
@ -861,10 +900,8 @@ class DB_DataObject_Generator extends DB_DataObject
$body = "\n ###START_AUTOCODE\n";
$body .= " /* the code below is auto generated do not remove the above tag */\n\n";
// table
$padding = (30 - strlen($this->table));
$padding = ($padding < 2) ? 2 : $padding;
$p = str_repeat(' ',$padding) ;
$p = str_repeat(' ',max(2, (18 - strlen($this->table)))) ;
$options = &PEAR::getStaticProperty('DB_DataObject','options');
@ -887,6 +924,7 @@ class DB_DataObject_Generator extends DB_DataObject
// Only include the $_database property if the omit_database_var is unset or false
if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
$p = str_repeat(' ', max(2, (16 - strlen($this->table))));
$body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n";
}
@ -900,6 +938,7 @@ class DB_DataObject_Generator extends DB_DataObject
// show nice information!
$connections = array();
$sets = array();
foreach($defs as $t) {
if (!strlen(trim($t->name))) {
continue;
@ -915,19 +954,18 @@ class DB_DataObject_Generator extends DB_DataObject
continue;
}
$padding = (30 - strlen($t->name));
if ($padding < 2) $padding =2;
$p = str_repeat(' ',$padding) ;
$p = str_repeat(' ',max(2, (30 - strlen($t->name))));
$length = empty($t->len) ? '' : '('.$t->len.')';
$body .=" {$var} \${$t->name}; {$p}// {$t->type}$length {$t->flags}\n";
// can not do set as PEAR::DB table info doesnt support it.
//if (substr($t->Type,0,3) == "set")
// $sets[$t->Field] = "array".substr($t->Type,3);
$body .= $this->derivedHookVar($t,$padding);
$body .= $this->derivedHookVar($t,strlen($p));
}
$body .= $this->derivedHookPostVar($defs);
// THIS IS TOTALLY BORKED old FC creation
// IT WILL BE REMOVED!!!!! in DataObjects 1.6
@ -1078,7 +1116,21 @@ class DB_DataObject_Generator extends DB_DataObject
// It MUST NOT be changed here!!!
return "";
}
/**
* hook for after var lines (
* called at the end of the output of var line have generated, override to add extra var
* lines
*
* @param array cols containing array of objects with type,len,flags etc. from tableInfo call
* @access public
* @return string added to class eg. functions.
*/
function derivedHookPostVar($t)
{
// This is so derived generator classes can generate variabels
// It MUST NOT be changed here!!!
return "";
}
/**
* hook to add extra page-level (in terms of phpDocumentor) DocBlock
*

View File

@ -6,21 +6,15 @@
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category pear
* @package PEAR
* @author Sterling Hughes <sterling@php.net>
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2008 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
* @copyright 1997-2010 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: PEAR.php 307683 2011-01-23 21:56:12Z dufuz $
* @link http://pear.php.net/package/PEAR
* @since File available since Release 0.1
*/
@ -52,15 +46,6 @@ if (substr(PHP_OS, 0, 3) == 'WIN') {
define('PEAR_OS', 'Unix'); // blatant assumption
}
// instant backwards compatibility
if (!defined('PATH_SEPARATOR')) {
if (OS_WINDOWS) {
define('PATH_SEPARATOR', ';');
} else {
define('PATH_SEPARATOR', ':');
}
}
$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
$GLOBALS['_PEAR_destructor_object_list'] = array();
@ -92,8 +77,8 @@ $GLOBALS['_PEAR_error_handler_stack'] = array();
* @author Tomas V.V. Cox <cox@idecnet.com>
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.2
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version Release: 1.9.2
* @link http://pear.php.net/package/PEAR
* @see PEAR_Error
* @since Class available since PHP 4.0.2
@ -101,8 +86,6 @@ $GLOBALS['_PEAR_error_handler_stack'] = array();
*/
class PEAR
{
// {{{ properties
/**
* Whether to enable internal debug messages.
*
@ -153,10 +136,6 @@ class PEAR
*/
var $_expected_errors = array();
// }}}
// {{{ constructor
/**
* Constructor. Registers this object in
* $_PEAR_destructor_object_list for destructor emulation if a
@ -173,9 +152,11 @@ class PEAR
if ($this->_debug) {
print "PEAR constructor called, class=$classname\n";
}
if ($error_class !== null) {
$this->_error_class = $error_class;
}
while ($classname && strcasecmp($classname, "pear")) {
$destructor = "_$classname";
if (method_exists($this, $destructor)) {
@ -192,9 +173,6 @@ class PEAR
}
}
// }}}
// {{{ destructor
/**
* Destructor (the emulated type of...). Does nothing right now,
* but is included for forward compatibility, so subclass
@ -212,9 +190,6 @@ class PEAR
}
}
// }}}
// {{{ getStaticProperty()
/**
* If you have a class that's mostly/entirely static, and you need static
* properties, you can use this method to simulate them. Eg. in your method(s)
@ -233,15 +208,14 @@ class PEAR
if (!isset($properties[$class])) {
$properties[$class] = array();
}
if (!array_key_exists($var, $properties[$class])) {
$properties[$class][$var] = null;
}
return $properties[$class][$var];
}
// }}}
// {{{ registerShutdownFunc()
/**
* Use this function to register a shutdown method for static
* classes.
@ -262,9 +236,6 @@ class PEAR
$GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
}
// }}}
// {{{ isError()
/**
* Tell whether a value is a PEAR error.
*
@ -278,20 +249,18 @@ class PEAR
*/
function isError($data, $code = null)
{
if (is_a($data, 'PEAR_Error')) {
if (is_null($code)) {
return true;
} elseif (is_string($code)) {
return $data->getMessage() == $code;
} else {
return $data->getCode() == $code;
}
if (!is_a($data, 'PEAR_Error')) {
return false;
}
return false;
}
// }}}
// {{{ setErrorHandling()
if (is_null($code)) {
return true;
} elseif (is_string($code)) {
return $data->getMessage() == $code;
}
return $data->getCode() == $code;
}
/**
* Sets how errors generated by this object should be handled.
@ -331,7 +300,6 @@ class PEAR
*
* @since PHP 4.0.5
*/
function setErrorHandling($mode = null, $options = null)
{
if (isset($this) && is_a($this, 'PEAR')) {
@ -369,9 +337,6 @@ class PEAR
}
}
// }}}
// {{{ expectError()
/**
* This method is used to tell which errors you expect to get.
* Expected errors are always returned with error mode
@ -394,12 +359,9 @@ class PEAR
} else {
array_push($this->_expected_errors, array($code));
}
return sizeof($this->_expected_errors);
return count($this->_expected_errors);
}
// }}}
// {{{ popExpect()
/**
* This method pops one element off the expected error codes
* stack.
@ -411,9 +373,6 @@ class PEAR
return array_pop($this->_expected_errors);
}
// }}}
// {{{ _checkDelExpect()
/**
* This method checks unsets an error code if available
*
@ -425,8 +384,7 @@ class PEAR
function _checkDelExpect($error_code)
{
$deleted = false;
foreach ($this->_expected_errors AS $key => $error_array) {
foreach ($this->_expected_errors as $key => $error_array) {
if (in_array($error_code, $error_array)) {
unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
$deleted = true;
@ -437,12 +395,10 @@ class PEAR
unset($this->_expected_errors[$key]);
}
}
return $deleted;
}
// }}}
// {{{ delExpect()
/**
* This method deletes all occurences of the specified element from
* the expected error codes stack.
@ -455,34 +411,26 @@ class PEAR
function delExpect($error_code)
{
$deleted = false;
if ((is_array($error_code) && (0 != count($error_code)))) {
// $error_code is a non-empty array here;
// we walk through it trying to unset all
// values
foreach($error_code as $key => $error) {
if ($this->_checkDelExpect($error)) {
$deleted = true;
} else {
$deleted = false;
}
// $error_code is a non-empty array here; we walk through it trying
// to unset all values
foreach ($error_code as $key => $error) {
$deleted = $this->_checkDelExpect($error) ? true : false;
}
return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
} elseif (!empty($error_code)) {
// $error_code comes alone, trying to unset it
if ($this->_checkDelExpect($error_code)) {
return true;
} else {
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
}
} else {
// $error_code is empty
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
}
}
// }}}
// {{{ raiseError()
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
}
// $error_code is empty
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
}
/**
* This method is a wrapper that returns an instance of the
@ -538,13 +486,20 @@ class PEAR
$message = $message->getMessage();
}
if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
if (
isset($this) &&
isset($this->_expected_errors) &&
count($this->_expected_errors) > 0 &&
count($exp = end($this->_expected_errors))
) {
if ($exp[0] == "*" ||
(is_int(reset($exp)) && in_array($code, $exp)) ||
(is_string(reset($exp)) && in_array($message, $exp))) {
(is_string(reset($exp)) && in_array($message, $exp))
) {
$mode = PEAR_ERROR_RETURN;
}
}
// No mode given, try global ones
if ($mode === null) {
// Class error handler
@ -565,46 +520,52 @@ class PEAR
} else {
$ec = 'PEAR_Error';
}
if (intval(PHP_VERSION) < 5) {
// little non-eval hack to fix bug #12147
include 'PEAR/FixPHP5PEARWarnings.php';
return $a;
}
if ($skipmsg) {
$a = new $ec($code, $mode, $options, $userinfo);
} else {
$a = new $ec($message, $code, $mode, $options, $userinfo);
}
return $a;
}
// }}}
// {{{ throwError()
/**
* Simpler form of raiseError with fewer options. In most cases
* message, code and userinfo are enough.
*
* @param string $message
* @param mixed $message a text error message or a PEAR error object
*
* @param int $code a numeric error code (it is up to your class
* to define these if you want to use codes)
*
* @param string $userinfo If you need to pass along for example debug
* information, this parameter is meant for that.
*
* @access public
* @return object a PEAR error object
* @see PEAR::raiseError
*/
function &throwError($message = null,
$code = null,
$userinfo = null)
function &throwError($message = null, $code = null, $userinfo = null)
{
if (isset($this) && is_a($this, 'PEAR')) {
$a = &$this->raiseError($message, $code, null, null, $userinfo);
return $a;
} else {
$a = &PEAR::raiseError($message, $code, null, null, $userinfo);
return $a;
}
$a = &PEAR::raiseError($message, $code, null, null, $userinfo);
return $a;
}
// }}}
function staticPushErrorHandling($mode, $options = null)
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
$def_options = &$GLOBALS['_PEAR_default_error_options'];
$stack[] = array($def_mode, $def_options);
@ -673,8 +634,6 @@ class PEAR
return true;
}
// {{{ pushErrorHandling()
/**
* Push a new error handler on top of the error handler options stack. With this
* you can easily override the actual error handler for some code and restore
@ -708,9 +667,6 @@ class PEAR
return true;
}
// }}}
// {{{ popErrorHandling()
/**
* Pop the last error handler used
*
@ -732,9 +688,6 @@ class PEAR
return true;
}
// }}}
// {{{ loadExtension()
/**
* OS independant PHP extension load. Remember to take care
* on the correct extension name for case sensitive OSes.
@ -744,31 +697,38 @@ class PEAR
*/
function loadExtension($ext)
{
if (!extension_loaded($ext)) {
// if either returns true dl() will produce a FATAL error, stop that
if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
return false;
}
if (OS_WINDOWS) {
$suffix = '.dll';
} elseif (PHP_OS == 'HP-UX') {
$suffix = '.sl';
} elseif (PHP_OS == 'AIX') {
$suffix = '.a';
} elseif (PHP_OS == 'OSX') {
$suffix = '.bundle';
} else {
$suffix = '.so';
}
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
if (extension_loaded($ext)) {
return true;
}
return true;
}
// }}}
// if either returns true dl() will produce a FATAL error, stop that
if (
function_exists('dl') === false ||
ini_get('enable_dl') != 1 ||
ini_get('safe_mode') == 1
) {
return false;
}
if (OS_WINDOWS) {
$suffix = '.dll';
} elseif (PHP_OS == 'HP-UX') {
$suffix = '.sl';
} elseif (PHP_OS == 'AIX') {
$suffix = '.a';
} elseif (PHP_OS == 'OSX') {
$suffix = '.bundle';
} else {
$suffix = '.so';
}
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
}
}
// {{{ _PEAR_call_destructors()
if (PEAR_ZE2) {
include_once 'PEAR5.php';
}
function _PEAR_call_destructors()
{
@ -777,9 +737,16 @@ function _PEAR_call_destructors()
sizeof($_PEAR_destructor_object_list))
{
reset($_PEAR_destructor_object_list);
if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
if (PEAR_ZE2) {
$destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo');
} else {
$destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo');
}
if ($destructLifoExists) {
$_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
}
while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
$classname = get_class($objref);
while ($classname) {
@ -798,14 +765,17 @@ function _PEAR_call_destructors()
}
// Now call the shutdown functions
if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
if (
isset($GLOBALS['_PEAR_shutdown_funcs']) &&
is_array($GLOBALS['_PEAR_shutdown_funcs']) &&
!empty($GLOBALS['_PEAR_shutdown_funcs'])
) {
foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
call_user_func_array($value[0], $value[1]);
}
}
}
// }}}
/**
* Standard PEAR error class for PHP 4
*
@ -817,16 +787,14 @@ function _PEAR_call_destructors()
* @author Tomas V.V. Cox <cox@idecnet.com>
* @author Gregory Beaver <cellog@php.net>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.2
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version Release: 1.9.2
* @link http://pear.php.net/manual/en/core.pear.pear-error.php
* @see PEAR::raiseError(), PEAR::throwError()
* @since Class available since PHP 4.0.2
*/
class PEAR_Error
{
// {{{ properties
var $error_message_prefix = '';
var $mode = PEAR_ERROR_RETURN;
var $level = E_USER_NOTICE;
@ -835,9 +803,6 @@ class PEAR_Error
var $userinfo = '';
var $backtrace = null;
// }}}
// {{{ constructor
/**
* PEAR_Error constructor
*
@ -868,12 +833,20 @@ class PEAR_Error
$this->code = $code;
$this->mode = $mode;
$this->userinfo = $userinfo;
if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
if (PEAR_ZE2) {
$skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace');
} else {
$skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
}
if (!$skiptrace) {
$this->backtrace = debug_backtrace();
if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
unset($this->backtrace[0]['object']);
}
}
if ($mode & PEAR_ERROR_CALLBACK) {
$this->level = E_USER_NOTICE;
$this->callback = $options;
@ -881,20 +854,25 @@ class PEAR_Error
if ($options === null) {
$options = E_USER_NOTICE;
}
$this->level = $options;
$this->callback = null;
}
if ($this->mode & PEAR_ERROR_PRINT) {
if (is_null($options) || is_int($options)) {
$format = "%s";
} else {
$format = $options;
}
printf($format, $this->getMessage());
}
if ($this->mode & PEAR_ERROR_TRIGGER) {
trigger_error($this->getMessage(), $this->level);
}
if ($this->mode & PEAR_ERROR_DIE) {
$msg = $this->getMessage();
if (is_null($options) || is_int($options)) {
@ -907,47 +885,39 @@ class PEAR_Error
}
die(sprintf($format, $msg));
}
if ($this->mode & PEAR_ERROR_CALLBACK) {
if (is_callable($this->callback)) {
call_user_func($this->callback, $this);
}
if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) {
call_user_func($this->callback, $this);
}
if ($this->mode & PEAR_ERROR_EXCEPTION) {
trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
eval('$e = new Exception($this->message, $this->code);throw($e);');
}
}
// }}}
// {{{ getMode()
/**
* Get the error mode from an error object.
*
* @return int error mode
* @access public
*/
function getMode() {
function getMode()
{
return $this->mode;
}
// }}}
// {{{ getCallback()
/**
* Get the callback function/method from an error object.
*
* @return mixed callback function or object/method array
* @access public
*/
function getCallback() {
function getCallback()
{
return $this->callback;
}
// }}}
// {{{ getMessage()
/**
* Get the error message from an error object.
*
@ -959,10 +929,6 @@ class PEAR_Error
return ($this->error_message_prefix . $this->message);
}
// }}}
// {{{ getCode()
/**
* Get error code from an error object
*
@ -974,9 +940,6 @@ class PEAR_Error
return $this->code;
}
// }}}
// {{{ getType()
/**
* Get the name of this error/exception.
*
@ -988,9 +951,6 @@ class PEAR_Error
return get_class($this);
}
// }}}
// {{{ getUserInfo()
/**
* Get additional user-supplied information.
*
@ -1002,9 +962,6 @@ class PEAR_Error
return $this->userinfo;
}
// }}}
// {{{ getDebugInfo()
/**
* Get additional debug information supplied by the application.
*
@ -1016,9 +973,6 @@ class PEAR_Error
return $this->getUserInfo();
}
// }}}
// {{{ getBacktrace()
/**
* Get the call backtrace from where the error was generated.
* Supported with PHP 4.3.0 or newer.
@ -1038,9 +992,6 @@ class PEAR_Error
return $this->backtrace[$frame];
}
// }}}
// {{{ addUserInfo()
function addUserInfo($info)
{
if (empty($this->userinfo)) {
@ -1050,14 +1001,10 @@ class PEAR_Error
}
}
// }}}
// {{{ toString()
function __toString()
{
return $this->getMessage();
}
// }}}
// {{{ toString()
/**
* Make a string representation of this object.
@ -1065,7 +1012,8 @@ class PEAR_Error
* @return string a string with an object summary
* @access public
*/
function toString() {
function toString()
{
$modes = array();
$levels = array(E_USER_NOTICE => 'notice',
E_USER_WARNING => 'warning',
@ -1104,8 +1052,6 @@ class PEAR_Error
$this->error_message_prefix,
$this->userinfo);
}
// }}}
}
/*
@ -1115,4 +1061,3 @@ class PEAR_Error
* c-basic-offset: 4
* End:
*/
?>

985
extlib/PEAR/ErrorStack.php Normal file
View File

@ -0,0 +1,985 @@
<?php
/**
* Error Stack Implementation
*
* This is an incredibly simple implementation of a very complex error handling
* facility. It contains the ability
* to track multiple errors from multiple packages simultaneously. In addition,
* it can track errors of many levels, save data along with the error, context
* information such as the exact file, line number, class and function that
* generated the error, and if necessary, it can raise a traditional PEAR_Error.
* It has built-in support for PEAR::Log, to log errors as they occur
*
* Since version 0.2alpha, it is also possible to selectively ignore errors,
* through the use of an error callback, see {@link pushCallback()}
*
* Since version 0.3alpha, it is possible to specify the exception class
* returned from {@link push()}
*
* Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can
* still be done quite handily in an error callback or by manipulating the returned array
* @category Debugging
* @package PEAR_ErrorStack
* @author Greg Beaver <cellog@php.net>
* @copyright 2004-2008 Greg Beaver
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: ErrorStack.php 307683 2011-01-23 21:56:12Z dufuz $
* @link http://pear.php.net/package/PEAR_ErrorStack
*/
/**
* Singleton storage
*
* Format:
* <pre>
* array(
* 'package1' => PEAR_ErrorStack object,
* 'package2' => PEAR_ErrorStack object,
* ...
* )
* </pre>
* @access private
* @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
*/
$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
/**
* Global error callback (default)
*
* This is only used if set to non-false. * is the default callback for
* all packages, whereas specific packages may set a default callback
* for all instances, regardless of whether they are a singleton or not.
*
* To exclude non-singletons, only set the local callback for the singleton
* @see PEAR_ErrorStack::setDefaultCallback()
* @access private
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
*/
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
'*' => false,
);
/**
* Global Log object (default)
*
* This is only used if set to non-false. Use to set a default log object for
* all stacks, regardless of instantiation order or location
* @see PEAR_ErrorStack::setDefaultLogger()
* @access private
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
*/
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
/**
* Global Overriding Callback
*
* This callback will override any error callbacks that specific loggers have set.
* Use with EXTREME caution
* @see PEAR_ErrorStack::staticPushCallback()
* @access private
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
*/
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
/**#@+
* One of four possible return values from the error Callback
* @see PEAR_ErrorStack::_errorCallback()
*/
/**
* If this is returned, then the error will be both pushed onto the stack
* and logged.
*/
define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
/**
* If this is returned, then the error will only be pushed onto the stack,
* and not logged.
*/
define('PEAR_ERRORSTACK_PUSH', 2);
/**
* If this is returned, then the error will only be logged, but not pushed
* onto the error stack.
*/
define('PEAR_ERRORSTACK_LOG', 3);
/**
* If this is returned, then the error is completely ignored.
*/
define('PEAR_ERRORSTACK_IGNORE', 4);
/**
* If this is returned, then the error is logged and die() is called.
*/
define('PEAR_ERRORSTACK_DIE', 5);
/**#@-*/
/**
* Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
* the singleton method.
*/
define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
/**
* Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
* that has no __toString() method
*/
define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
/**
* Error Stack Implementation
*
* Usage:
* <code>
* // global error stack
* $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
* // local error stack
* $local_stack = new PEAR_ErrorStack('MyPackage');
* </code>
* @author Greg Beaver <cellog@php.net>
* @version 1.9.2
* @package PEAR_ErrorStack
* @category Debugging
* @copyright 2004-2008 Greg Beaver
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: ErrorStack.php 307683 2011-01-23 21:56:12Z dufuz $
* @link http://pear.php.net/package/PEAR_ErrorStack
*/
class PEAR_ErrorStack {
/**
* Errors are stored in the order that they are pushed on the stack.
* @since 0.4alpha Errors are no longer organized by error level.
* This renders pop() nearly unusable, and levels could be more easily
* handled in a callback anyway
* @var array
* @access private
*/
var $_errors = array();
/**
* Storage of errors by level.
*
* Allows easy retrieval and deletion of only errors from a particular level
* @since PEAR 1.4.0dev
* @var array
* @access private
*/
var $_errorsByLevel = array();
/**
* Package name this error stack represents
* @var string
* @access protected
*/
var $_package;
/**
* Determines whether a PEAR_Error is thrown upon every error addition
* @var boolean
* @access private
*/
var $_compat = false;
/**
* If set to a valid callback, this will be used to generate the error
* message from the error code, otherwise the message passed in will be
* used
* @var false|string|array
* @access private
*/
var $_msgCallback = false;
/**
* If set to a valid callback, this will be used to generate the error
* context for an error. For PHP-related errors, this will be a file
* and line number as retrieved from debug_backtrace(), but can be
* customized for other purposes. The error might actually be in a separate
* configuration file, or in a database query.
* @var false|string|array
* @access protected
*/
var $_contextCallback = false;
/**
* If set to a valid callback, this will be called every time an error
* is pushed onto the stack. The return value will be used to determine
* whether to allow an error to be pushed or logged.
*
* The return value must be one an PEAR_ERRORSTACK_* constant
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
* @var false|string|array
* @access protected
*/
var $_errorCallback = array();
/**
* PEAR::Log object for logging errors
* @var false|Log
* @access protected
*/
var $_logger = false;
/**
* Error messages - designed to be overridden
* @var array
* @abstract
*/
var $_errorMsgs = array();
/**
* Set up a new error stack
*
* @param string $package name of the package this error stack represents
* @param callback $msgCallback callback used for error message generation
* @param callback $contextCallback callback used for context generation,
* defaults to {@link getFileLine()}
* @param boolean $throwPEAR_Error
*/
function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
$throwPEAR_Error = false)
{
$this->_package = $package;
$this->setMessageCallback($msgCallback);
$this->setContextCallback($contextCallback);
$this->_compat = $throwPEAR_Error;
}
/**
* Return a single error stack for this package.
*
* Note that all parameters are ignored if the stack for package $package
* has already been instantiated
* @param string $package name of the package this error stack represents
* @param callback $msgCallback callback used for error message generation
* @param callback $contextCallback callback used for context generation,
* defaults to {@link getFileLine()}
* @param boolean $throwPEAR_Error
* @param string $stackClass class to instantiate
* @static
* @return PEAR_ErrorStack
*/
function &singleton($package, $msgCallback = false, $contextCallback = false,
$throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
{
if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
}
if (!class_exists($stackClass)) {
if (function_exists('debug_backtrace')) {
$trace = debug_backtrace();
}
PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
'exception', array('stackclass' => $stackClass),
'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
false, $trace);
}
$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
}
/**
* Internal error handler for PEAR_ErrorStack class
*
* Dies if the error is an exception (and would have died anyway)
* @access private
*/
function _handleError($err)
{
if ($err['level'] == 'exception') {
$message = $err['message'];
if (isset($_SERVER['REQUEST_URI'])) {
echo '<br />';
} else {
echo "\n";
}
var_dump($err['context']);
die($message);
}
}
/**
* Set up a PEAR::Log object for all error stacks that don't have one
* @param Log $log
* @static
*/
function setDefaultLogger(&$log)
{
if (is_object($log) && method_exists($log, 'log') ) {
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
} elseif (is_callable($log)) {
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
}
}
/**
* Set up a PEAR::Log object for this error stack
* @param Log $log
*/
function setLogger(&$log)
{
if (is_object($log) && method_exists($log, 'log') ) {
$this->_logger = &$log;
} elseif (is_callable($log)) {
$this->_logger = &$log;
}
}
/**
* Set an error code => error message mapping callback
*
* This method sets the callback that can be used to generate error
* messages for any instance
* @param array|string Callback function/method
*/
function setMessageCallback($msgCallback)
{
if (!$msgCallback) {
$this->_msgCallback = array(&$this, 'getErrorMessage');
} else {
if (is_callable($msgCallback)) {
$this->_msgCallback = $msgCallback;
}
}
}
/**
* Get an error code => error message mapping callback
*
* This method returns the current callback that can be used to generate error
* messages
* @return array|string|false Callback function/method or false if none
*/
function getMessageCallback()
{
return $this->_msgCallback;
}
/**
* Sets a default callback to be used by all error stacks
*
* This method sets the callback that can be used to generate error
* messages for a singleton
* @param array|string Callback function/method
* @param string Package name, or false for all packages
* @static
*/
function setDefaultCallback($callback = false, $package = false)
{
if (!is_callable($callback)) {
$callback = false;
}
$package = $package ? $package : '*';
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
}
/**
* Set a callback that generates context information (location of error) for an error stack
*
* This method sets the callback that can be used to generate context
* information for an error. Passing in NULL will disable context generation
* and remove the expensive call to debug_backtrace()
* @param array|string|null Callback function/method
*/
function setContextCallback($contextCallback)
{
if ($contextCallback === null) {
return $this->_contextCallback = false;
}
if (!$contextCallback) {
$this->_contextCallback = array(&$this, 'getFileLine');
} else {
if (is_callable($contextCallback)) {
$this->_contextCallback = $contextCallback;
}
}
}
/**
* Set an error Callback
* If set to a valid callback, this will be called every time an error
* is pushed onto the stack. The return value will be used to determine
* whether to allow an error to be pushed or logged.
*
* The return value must be one of the ERRORSTACK_* constants.
*
* This functionality can be used to emulate PEAR's pushErrorHandling, and
* the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
* the error stack or logging
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
* @see popCallback()
* @param string|array $cb
*/
function pushCallback($cb)
{
array_push($this->_errorCallback, $cb);
}
/**
* Remove a callback from the error callback stack
* @see pushCallback()
* @return array|string|false
*/
function popCallback()
{
if (!count($this->_errorCallback)) {
return false;
}
return array_pop($this->_errorCallback);
}
/**
* Set a temporary overriding error callback for every package error stack
*
* Use this to temporarily disable all existing callbacks (can be used
* to emulate the @ operator, for instance)
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
* @see staticPopCallback(), pushCallback()
* @param string|array $cb
* @static
*/
function staticPushCallback($cb)
{
array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
}
/**
* Remove a temporary overriding error callback
* @see staticPushCallback()
* @return array|string|false
* @static
*/
function staticPopCallback()
{
$ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
}
return $ret;
}
/**
* Add an error to the stack
*
* If the message generator exists, it is called with 2 parameters.
* - the current Error Stack object
* - an array that is in the same format as an error. Available indices
* are 'code', 'package', 'time', 'params', 'level', and 'context'
*
* Next, if the error should contain context information, this is
* handled by the context grabbing method.
* Finally, the error is pushed onto the proper error stack
* @param int $code Package-specific error code
* @param string $level Error level. This is NOT spell-checked
* @param array $params associative array of error parameters
* @param string $msg Error message, or a portion of it if the message
* is to be generated
* @param array $repackage If this error re-packages an error pushed by
* another package, place the array returned from
* {@link pop()} in this parameter
* @param array $backtrace Protected parameter: use this to pass in the
* {@link debug_backtrace()} that should be used
* to find error context
* @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
* thrown. If a PEAR_Error is returned, the userinfo
* property is set to the following array:
*
* <code>
* array(
* 'code' => $code,
* 'params' => $params,
* 'package' => $this->_package,
* 'level' => $level,
* 'time' => time(),
* 'context' => $context,
* 'message' => $msg,
* //['repackage' => $err] repackaged error array/Exception class
* );
* </code>
*
* Normally, the previous array is returned.
*/
function push($code, $level = 'error', $params = array(), $msg = false,
$repackage = false, $backtrace = false)
{
$context = false;
// grab error context
if ($this->_contextCallback) {
if (!$backtrace) {
$backtrace = debug_backtrace();
}
$context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
}
// save error
$time = explode(' ', microtime());
$time = $time[1] + $time[0];
$err = array(
'code' => $code,
'params' => $params,
'package' => $this->_package,
'level' => $level,
'time' => $time,
'context' => $context,
'message' => $msg,
);
if ($repackage) {
$err['repackage'] = $repackage;
}
// set up the error message, if necessary
if ($this->_msgCallback) {
$msg = call_user_func_array($this->_msgCallback,
array(&$this, $err));
$err['message'] = $msg;
}
$push = $log = true;
$die = false;
// try the overriding callback first
$callback = $this->staticPopCallback();
if ($callback) {
$this->staticPushCallback($callback);
}
if (!is_callable($callback)) {
// try the local callback next
$callback = $this->popCallback();
if (is_callable($callback)) {
$this->pushCallback($callback);
} else {
// try the default callback
$callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
}
}
if (is_callable($callback)) {
switch(call_user_func($callback, $err)){
case PEAR_ERRORSTACK_IGNORE:
return $err;
break;
case PEAR_ERRORSTACK_PUSH:
$log = false;
break;
case PEAR_ERRORSTACK_LOG:
$push = false;
break;
case PEAR_ERRORSTACK_DIE:
$die = true;
break;
// anything else returned has the same effect as pushandlog
}
}
if ($push) {
array_unshift($this->_errors, $err);
if (!isset($this->_errorsByLevel[$err['level']])) {
$this->_errorsByLevel[$err['level']] = array();
}
$this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
}
if ($log) {
if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
$this->_log($err);
}
}
if ($die) {
die();
}
if ($this->_compat && $push) {
return $this->raiseError($msg, $code, null, null, $err);
}
return $err;
}
/**
* Static version of {@link push()}
*
* @param string $package Package name this error belongs to
* @param int $code Package-specific error code
* @param string $level Error level. This is NOT spell-checked
* @param array $params associative array of error parameters
* @param string $msg Error message, or a portion of it if the message
* is to be generated
* @param array $repackage If this error re-packages an error pushed by
* another package, place the array returned from
* {@link pop()} in this parameter
* @param array $backtrace Protected parameter: use this to pass in the
* {@link debug_backtrace()} that should be used
* to find error context
* @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
* thrown. see docs for {@link push()}
* @static
*/
function staticPush($package, $code, $level = 'error', $params = array(),
$msg = false, $repackage = false, $backtrace = false)
{
$s = &PEAR_ErrorStack::singleton($package);
if ($s->_contextCallback) {
if (!$backtrace) {
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
}
}
}
return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
}
/**
* Log an error using PEAR::Log
* @param array $err Error array
* @param array $levels Error level => Log constant map
* @access protected
*/
function _log($err)
{
if ($this->_logger) {
$logger = &$this->_logger;
} else {
$logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
}
if (is_a($logger, 'Log')) {
$levels = array(
'exception' => PEAR_LOG_CRIT,
'alert' => PEAR_LOG_ALERT,
'critical' => PEAR_LOG_CRIT,
'error' => PEAR_LOG_ERR,
'warning' => PEAR_LOG_WARNING,
'notice' => PEAR_LOG_NOTICE,
'info' => PEAR_LOG_INFO,
'debug' => PEAR_LOG_DEBUG);
if (isset($levels[$err['level']])) {
$level = $levels[$err['level']];
} else {
$level = PEAR_LOG_INFO;
}
$logger->log($err['message'], $level, $err);
} else { // support non-standard logs
call_user_func($logger, $err);
}
}
/**
* Pop an error off of the error stack
*
* @return false|array
* @since 0.4alpha it is no longer possible to specify a specific error
* level to return - the last error pushed will be returned, instead
*/
function pop()
{
$err = @array_shift($this->_errors);
if (!is_null($err)) {
@array_pop($this->_errorsByLevel[$err['level']]);
if (!count($this->_errorsByLevel[$err['level']])) {
unset($this->_errorsByLevel[$err['level']]);
}
}
return $err;
}
/**
* Pop an error off of the error stack, static method
*
* @param string package name
* @return boolean
* @since PEAR1.5.0a1
*/
function staticPop($package)
{
if ($package) {
if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
return false;
}
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
}
}
/**
* Determine whether there are any errors on the stack
* @param string|array Level name. Use to determine if any errors
* of level (string), or levels (array) have been pushed
* @return boolean
*/
function hasErrors($level = false)
{
if ($level) {
return isset($this->_errorsByLevel[$level]);
}
return count($this->_errors);
}
/**
* Retrieve all errors since last purge
*
* @param boolean set in order to empty the error stack
* @param string level name, to return only errors of a particular severity
* @return array
*/
function getErrors($purge = false, $level = false)
{
if (!$purge) {
if ($level) {
if (!isset($this->_errorsByLevel[$level])) {
return array();
} else {
return $this->_errorsByLevel[$level];
}
} else {
return $this->_errors;
}
}
if ($level) {
$ret = $this->_errorsByLevel[$level];
foreach ($this->_errorsByLevel[$level] as $i => $unused) {
// entries are references to the $_errors array
$this->_errorsByLevel[$level][$i] = false;
}
// array_filter removes all entries === false
$this->_errors = array_filter($this->_errors);
unset($this->_errorsByLevel[$level]);
return $ret;
}
$ret = $this->_errors;
$this->_errors = array();
$this->_errorsByLevel = array();
return $ret;
}
/**
* Determine whether there are any errors on a single error stack, or on any error stack
*
* The optional parameter can be used to test the existence of any errors without the need of
* singleton instantiation
* @param string|false Package name to check for errors
* @param string Level name to check for a particular severity
* @return boolean
* @static
*/
function staticHasErrors($package = false, $level = false)
{
if ($package) {
if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
return false;
}
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
}
foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
if ($obj->hasErrors($level)) {
return true;
}
}
return false;
}
/**
* Get a list of all errors since last purge, organized by package
* @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
* @param boolean $purge Set to purge the error stack of existing errors
* @param string $level Set to a level name in order to retrieve only errors of a particular level
* @param boolean $merge Set to return a flat array, not organized by package
* @param array $sortfunc Function used to sort a merged array - default
* sorts by time, and should be good for most cases
* @static
* @return array
*/
function staticGetErrors($purge = false, $level = false, $merge = false,
$sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
{
$ret = array();
if (!is_callable($sortfunc)) {
$sortfunc = array('PEAR_ErrorStack', '_sortErrors');
}
foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
$test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
if ($test) {
if ($merge) {
$ret = array_merge($ret, $test);
} else {
$ret[$package] = $test;
}
}
}
if ($merge) {
usort($ret, $sortfunc);
}
return $ret;
}
/**
* Error sorting function, sorts by time
* @access private
*/
function _sortErrors($a, $b)
{
if ($a['time'] == $b['time']) {
return 0;
}
if ($a['time'] < $b['time']) {
return 1;
}
return -1;
}
/**
* Standard file/line number/function/class context callback
*
* This function uses a backtrace generated from {@link debug_backtrace()}
* and so will not work at all in PHP < 4.3.0. The frame should
* reference the frame that contains the source of the error.
* @return array|false either array('file' => file, 'line' => line,
* 'function' => function name, 'class' => class name) or
* if this doesn't work, then false
* @param unused
* @param integer backtrace frame.
* @param array Results of debug_backtrace()
* @static
*/
function getFileLine($code, $params, $backtrace = null)
{
if ($backtrace === null) {
return false;
}
$frame = 0;
$functionframe = 1;
if (!isset($backtrace[1])) {
$functionframe = 0;
} else {
while (isset($backtrace[$functionframe]['function']) &&
$backtrace[$functionframe]['function'] == 'eval' &&
isset($backtrace[$functionframe + 1])) {
$functionframe++;
}
}
if (isset($backtrace[$frame])) {
if (!isset($backtrace[$frame]['file'])) {
$frame++;
}
$funcbacktrace = $backtrace[$functionframe];
$filebacktrace = $backtrace[$frame];
$ret = array('file' => $filebacktrace['file'],
'line' => $filebacktrace['line']);
// rearrange for eval'd code or create function errors
if (strpos($filebacktrace['file'], '(') &&
preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
$matches)) {
$ret['file'] = $matches[1];
$ret['line'] = $matches[2] + 0;
}
if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
if ($funcbacktrace['function'] != 'eval') {
if ($funcbacktrace['function'] == '__lambda_func') {
$ret['function'] = 'create_function() code';
} else {
$ret['function'] = $funcbacktrace['function'];
}
}
}
if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
$ret['class'] = $funcbacktrace['class'];
}
return $ret;
}
return false;
}
/**
* Standard error message generation callback
*
* This method may also be called by a custom error message generator
* to fill in template values from the params array, simply
* set the third parameter to the error message template string to use
*
* The special variable %__msg% is reserved: use it only to specify
* where a message passed in by the user should be placed in the template,
* like so:
*
* Error message: %msg% - internal error
*
* If the message passed like so:
*
* <code>
* $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
* </code>
*
* The returned error message will be "Error message: server error 500 -
* internal error"
* @param PEAR_ErrorStack
* @param array
* @param string|false Pre-generated error message template
* @static
* @return string
*/
function getErrorMessage(&$stack, $err, $template = false)
{
if ($template) {
$mainmsg = $template;
} else {
$mainmsg = $stack->getErrorMessageTemplate($err['code']);
}
$mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
if (is_array($err['params']) && count($err['params'])) {
foreach ($err['params'] as $name => $val) {
if (is_array($val)) {
// @ is needed in case $val is a multi-dimensional array
$val = @implode(', ', $val);
}
if (is_object($val)) {
if (method_exists($val, '__toString')) {
$val = $val->__toString();
} else {
PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
'warning', array('obj' => get_class($val)),
'object %obj% passed into getErrorMessage, but has no __toString() method');
$val = 'Object';
}
}
$mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
}
}
return $mainmsg;
}
/**
* Standard Error Message Template generator from code
* @return string
*/
function getErrorMessageTemplate($code)
{
if (!isset($this->_errorMsgs[$code])) {
return '%__msg%';
}
return $this->_errorMsgs[$code];
}
/**
* Set the Error Message Template array
*
* The array format must be:
* <pre>
* array(error code => 'message template',...)
* </pre>
*
* Error message parameters passed into {@link push()} will be used as input
* for the error message. If the template is 'message %foo% was %bar%', and the
* parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
* be 'message one was six'
* @return string
*/
function setErrorMessageTemplate($template)
{
$this->_errorMsgs = $template;
}
/**
* emulate PEAR::raiseError()
*
* @return PEAR_Error
*/
function raiseError()
{
require_once 'PEAR.php';
$args = func_get_args();
return call_user_func_array(array('PEAR', 'raiseError'), $args);
}
}
$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
?>

View File

@ -5,21 +5,15 @@
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category pear
* @package PEAR
* @author Tomas V. V. Cox <cox@idecnet.com>
* @author Hans Lellelid <hans@velum.net>
* @author Bertrand Mansion <bmansion@mamasam.com>
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2008 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Exception.php,v 1.29 2008/01/03 20:26:35 cellog Exp $
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: Exception.php 307683 2011-01-23 21:56:12Z dufuz $
* @link http://pear.php.net/package/PEAR
* @since File available since Release 1.3.3
*/
@ -93,9 +87,9 @@
* @author Hans Lellelid <hans@velum.net>
* @author Bertrand Mansion <bmansion@mamasam.com>
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2008 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.2
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version Release: 1.9.2
* @link http://pear.php.net/package/PEAR
* @since Class available since Release 1.3.3
*
@ -295,7 +289,7 @@ class PEAR_Exception extends Exception
}
public function getTraceSafe()
{
{
if (!isset($this->_trace)) {
$this->_trace = $this->getTrace();
if (empty($this->_trace)) {
@ -331,21 +325,21 @@ class PEAR_Exception extends Exception
$trace = $this->getTraceSafe();
$causes = array();
$this->getCauseMessage($causes);
$html = '<table border="1" cellspacing="0">' . "\n";
$html = '<table style="border: 1px" cellspacing="0">' . "\n";
foreach ($causes as $i => $cause) {
$html .= '<tr><td colspan="3" bgcolor="#ff9999">'
$html .= '<tr><td colspan="3" style="background: #ff9999">'
. str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
. htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
. 'on line <b>' . $cause['line'] . '</b>'
. "</td></tr>\n";
}
$html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n"
. '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>'
. '<td align="center" bgcolor="#cccccc"><b>Function</b></td>'
. '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n";
$html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
. '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
. '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
. '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
foreach ($trace as $k => $v) {
$html .= '<tr><td align="center">' . $k . '</td>'
$html .= '<tr><td style="text-align: center;">' . $k . '</td>'
. '<td>';
if (!empty($v['class'])) {
$html .= $v['class'] . $v['type'];
@ -373,7 +367,7 @@ class PEAR_Exception extends Exception
. ':' . (isset($v['line']) ? $v['line'] : 'unknown')
. '</td></tr>' . "\n";
}
$html .= '<tr><td align="center">' . ($k+1) . '</td>'
$html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
. '<td>{main}</td>'
. '<td>&nbsp;</td></tr>' . "\n"
. '</table>';
@ -392,6 +386,4 @@ class PEAR_Exception extends Exception
}
return $causeMsg . $this->getTraceAsString();
}
}
?>
}

33
extlib/PEAR5.php Normal file
View File

@ -0,0 +1,33 @@
<?php
/**
* This is only meant for PHP 5 to get rid of certain strict warning
* that doesn't get hidden since it's in the shutdown function
*/
class PEAR5
{
/**
* If you have a class that's mostly/entirely static, and you need static
* properties, you can use this method to simulate them. Eg. in your method(s)
* do this: $myVar = &PEAR5::getStaticProperty('myclass', 'myVar');
* You MUST use a reference, or they will not persist!
*
* @access public
* @param string $class The calling classname, to prevent clashes
* @param string $var The variable to retrieve.
* @return mixed A reference to the variable. If not set it will be
* auto initialised to NULL.
*/
static function &getStaticProperty($class, $var)
{
static $properties;
if (!isset($properties[$class])) {
$properties[$class] = array();
}
if (!array_key_exists($var, $properties[$class])) {
$properties[$class][$var] = null;
}
return $properties[$class][$var];
}
}

621
extlib/System.php Normal file
View File

@ -0,0 +1,621 @@
<?php
/**
* File/Directory manipulation
*
* PHP versions 4 and 5
*
* @category pear
* @package System
* @author Tomas V.V.Cox <cox@idecnet.com>
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: System.php 307683 2011-01-23 21:56:12Z dufuz $
* @link http://pear.php.net/package/PEAR
* @since File available since Release 0.1
*/
/**
* base class
*/
require_once 'PEAR.php';
require_once 'Console/Getopt.php';
$GLOBALS['_System_temp_files'] = array();
/**
* System offers cross plattform compatible system functions
*
* Static functions for different operations. Should work under
* Unix and Windows. The names and usage has been taken from its respectively
* GNU commands. The functions will return (bool) false on error and will
* trigger the error with the PHP trigger_error() function (you can silence
* the error by prefixing a '@' sign after the function call, but this
* is not recommended practice. Instead use an error handler with
* {@link set_error_handler()}).
*
* Documentation on this class you can find in:
* http://pear.php.net/manual/
*
* Example usage:
* if (!@System::rm('-r file1 dir1')) {
* print "could not delete file1 or dir1";
* }
*
* In case you need to to pass file names with spaces,
* pass the params as an array:
*
* System::rm(array('-r', $file1, $dir1));
*
* @category pear
* @package System
* @author Tomas V.V. Cox <cox@idecnet.com>
* @copyright 1997-2006 The PHP Group
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version Release: 1.9.2
* @link http://pear.php.net/package/PEAR
* @since Class available since Release 0.1
* @static
*/
class System
{
/**
* returns the commandline arguments of a function
*
* @param string $argv the commandline
* @param string $short_options the allowed option short-tags
* @param string $long_options the allowed option long-tags
* @return array the given options and there values
* @static
* @access private
*/
function _parseArgs($argv, $short_options, $long_options = null)
{
if (!is_array($argv) && $argv !== null) {
$argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY);
}
return Console_Getopt::getopt2($argv, $short_options);
}
/**
* Output errors with PHP trigger_error(). You can silence the errors
* with prefixing a "@" sign to the function call: @System::mkdir(..);
*
* @param mixed $error a PEAR error or a string with the error message
* @return bool false
* @static
* @access private
*/
function raiseError($error)
{
if (PEAR::isError($error)) {
$error = $error->getMessage();
}
trigger_error($error, E_USER_WARNING);
return false;
}
/**
* Creates a nested array representing the structure of a directory
*
* System::_dirToStruct('dir1', 0) =>
* Array
* (
* [dirs] => Array
* (
* [0] => dir1
* )
*
* [files] => Array
* (
* [0] => dir1/file2
* [1] => dir1/file3
* )
* )
* @param string $sPath Name of the directory
* @param integer $maxinst max. deep of the lookup
* @param integer $aktinst starting deep of the lookup
* @param bool $silent if true, do not emit errors.
* @return array the structure of the dir
* @static
* @access private
*/
function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
{
$struct = array('dirs' => array(), 'files' => array());
if (($dir = @opendir($sPath)) === false) {
if (!$silent) {
System::raiseError("Could not open dir $sPath");
}
return $struct; // XXX could not open error
}
$struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
$list = array();
while (false !== ($file = readdir($dir))) {
if ($file != '.' && $file != '..') {
$list[] = $file;
}
}
closedir($dir);
natsort($list);
if ($aktinst < $maxinst || $maxinst == 0) {
foreach ($list as $val) {
$path = $sPath . DIRECTORY_SEPARATOR . $val;
if (is_dir($path) && !is_link($path)) {
$tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
$struct = array_merge_recursive($struct, $tmp);
} else {
$struct['files'][] = $path;
}
}
}
return $struct;
}
/**
* Creates a nested array representing the structure of a directory and files
*
* @param array $files Array listing files and dirs
* @return array
* @static
* @see System::_dirToStruct()
*/
function _multipleToStruct($files)
{
$struct = array('dirs' => array(), 'files' => array());
settype($files, 'array');
foreach ($files as $file) {
if (is_dir($file) && !is_link($file)) {
$tmp = System::_dirToStruct($file, 0);
$struct = array_merge_recursive($tmp, $struct);
} else {
if (!in_array($file, $struct['files'])) {
$struct['files'][] = $file;
}
}
}
return $struct;
}
/**
* The rm command for removing files.
* Supports multiple files and dirs and also recursive deletes
*
* @param string $args the arguments for rm
* @return mixed PEAR_Error or true for success
* @static
* @access public
*/
function rm($args)
{
$opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
if (PEAR::isError($opts)) {
return System::raiseError($opts);
}
foreach ($opts[0] as $opt) {
if ($opt[0] == 'r') {
$do_recursive = true;
}
}
$ret = true;
if (isset($do_recursive)) {
$struct = System::_multipleToStruct($opts[1]);
foreach ($struct['files'] as $file) {
if (!@unlink($file)) {
$ret = false;
}
}
rsort($struct['dirs']);
foreach ($struct['dirs'] as $dir) {
if (!@rmdir($dir)) {
$ret = false;
}
}
} else {
foreach ($opts[1] as $file) {
$delete = (is_dir($file)) ? 'rmdir' : 'unlink';
if (!@$delete($file)) {
$ret = false;
}
}
}
return $ret;
}
/**
* Make directories.
*
* The -p option will create parent directories
* @param string $args the name of the director(y|ies) to create
* @return bool True for success
* @static
* @access public
*/
function mkDir($args)
{
$opts = System::_parseArgs($args, 'pm:');
if (PEAR::isError($opts)) {
return System::raiseError($opts);
}
$mode = 0777; // default mode
foreach ($opts[0] as $opt) {
if ($opt[0] == 'p') {
$create_parents = true;
} elseif ($opt[0] == 'm') {
// if the mode is clearly an octal number (starts with 0)
// convert it to decimal
if (strlen($opt[1]) && $opt[1]{0} == '0') {
$opt[1] = octdec($opt[1]);
} else {
// convert to int
$opt[1] += 0;
}
$mode = $opt[1];
}
}
$ret = true;
if (isset($create_parents)) {
foreach ($opts[1] as $dir) {
$dirstack = array();
while ((!file_exists($dir) || !is_dir($dir)) &&
$dir != DIRECTORY_SEPARATOR) {
array_unshift($dirstack, $dir);
$dir = dirname($dir);
}
while ($newdir = array_shift($dirstack)) {
if (!is_writeable(dirname($newdir))) {
$ret = false;
break;
}
if (!mkdir($newdir, $mode)) {
$ret = false;
}
}
}
} else {
foreach($opts[1] as $dir) {
if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
$ret = false;
}
}
}
return $ret;
}
/**
* Concatenate files
*
* Usage:
* 1) $var = System::cat('sample.txt test.txt');
* 2) System::cat('sample.txt test.txt > final.txt');
* 3) System::cat('sample.txt test.txt >> final.txt');
*
* Note: as the class use fopen, urls should work also (test that)
*
* @param string $args the arguments
* @return boolean true on success
* @static
* @access public
*/
function &cat($args)
{
$ret = null;
$files = array();
if (!is_array($args)) {
$args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
}
$count_args = count($args);
for ($i = 0; $i < $count_args; $i++) {
if ($args[$i] == '>') {
$mode = 'wb';
$outputfile = $args[$i+1];
break;
} elseif ($args[$i] == '>>') {
$mode = 'ab+';
$outputfile = $args[$i+1];
break;
} else {
$files[] = $args[$i];
}
}
$outputfd = false;
if (isset($mode)) {
if (!$outputfd = fopen($outputfile, $mode)) {
$err = System::raiseError("Could not open $outputfile");
return $err;
}
$ret = true;
}
foreach ($files as $file) {
if (!$fd = fopen($file, 'r')) {
System::raiseError("Could not open $file");
continue;
}
while ($cont = fread($fd, 2048)) {
if (is_resource($outputfd)) {
fwrite($outputfd, $cont);
} else {
$ret .= $cont;
}
}
fclose($fd);
}
if (is_resource($outputfd)) {
fclose($outputfd);
}
return $ret;
}
/**
* Creates temporary files or directories. This function will remove
* the created files when the scripts finish its execution.
*
* Usage:
* 1) $tempfile = System::mktemp("prefix");
* 2) $tempdir = System::mktemp("-d prefix");
* 3) $tempfile = System::mktemp();
* 4) $tempfile = System::mktemp("-t /var/tmp prefix");
*
* prefix -> The string that will be prepended to the temp name
* (defaults to "tmp").
* -d -> A temporary dir will be created instead of a file.
* -t -> The target dir where the temporary (file|dir) will be created. If
* this param is missing by default the env vars TMP on Windows or
* TMPDIR in Unix will be used. If these vars are also missing
* c:\windows\temp or /tmp will be used.
*
* @param string $args The arguments
* @return mixed the full path of the created (file|dir) or false
* @see System::tmpdir()
* @static
* @access public
*/
function mktemp($args = null)
{
static $first_time = true;
$opts = System::_parseArgs($args, 't:d');
if (PEAR::isError($opts)) {
return System::raiseError($opts);
}
foreach ($opts[0] as $opt) {
if ($opt[0] == 'd') {
$tmp_is_dir = true;
} elseif ($opt[0] == 't') {
$tmpdir = $opt[1];
}
}
$prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
if (!isset($tmpdir)) {
$tmpdir = System::tmpdir();
}
if (!System::mkDir(array('-p', $tmpdir))) {
return false;
}
$tmp = tempnam($tmpdir, $prefix);
if (isset($tmp_is_dir)) {
unlink($tmp); // be careful possible race condition here
if (!mkdir($tmp, 0700)) {
return System::raiseError("Unable to create temporary directory $tmpdir");
}
}
$GLOBALS['_System_temp_files'][] = $tmp;
if (isset($tmp_is_dir)) {
//$GLOBALS['_System_temp_files'][] = dirname($tmp);
}
if ($first_time) {
PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
$first_time = false;
}
return $tmp;
}
/**
* Remove temporary files created my mkTemp. This function is executed
* at script shutdown time
*
* @static
* @access private
*/
function _removeTmpFiles()
{
if (count($GLOBALS['_System_temp_files'])) {
$delete = $GLOBALS['_System_temp_files'];
array_unshift($delete, '-r');
System::rm($delete);
$GLOBALS['_System_temp_files'] = array();
}
}
/**
* Get the path of the temporal directory set in the system
* by looking in its environments variables.
* Note: php.ini-recommended removes the "E" from the variables_order setting,
* making unavaible the $_ENV array, that s why we do tests with _ENV
*
* @static
* @return string The temporary directory on the system
*/
function tmpdir()
{
if (OS_WINDOWS) {
if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
return $var;
}
if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
return $var;
}
if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
return $var;
}
if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
return $var;
}
return getenv('SystemRoot') . '\temp';
}
if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
return $var;
}
return realpath('/tmp');
}
/**
* The "which" command (show the full path of a command)
*
* @param string $program The command to search for
* @param mixed $fallback Value to return if $program is not found
*
* @return mixed A string with the full path or false if not found
* @static
* @author Stig Bakken <ssb@php.net>
*/
function which($program, $fallback = false)
{
// enforce API
if (!is_string($program) || '' == $program) {
return $fallback;
}
// full path given
if (basename($program) != $program) {
$path_elements[] = dirname($program);
$program = basename($program);
} else {
// Honor safe mode
if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
$path = getenv('PATH');
if (!$path) {
$path = getenv('Path'); // some OSes are just stupid enough to do this
}
}
$path_elements = explode(PATH_SEPARATOR, $path);
}
if (OS_WINDOWS) {
$exe_suffixes = getenv('PATHEXT')
? explode(PATH_SEPARATOR, getenv('PATHEXT'))
: array('.exe','.bat','.cmd','.com');
// allow passing a command.exe param
if (strpos($program, '.') !== false) {
array_unshift($exe_suffixes, '');
}
// is_executable() is not available on windows for PHP4
$pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
} else {
$exe_suffixes = array('');
$pear_is_executable = 'is_executable';
}
foreach ($exe_suffixes as $suff) {
foreach ($path_elements as $dir) {
$file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
if (@$pear_is_executable($file)) {
return $file;
}
}
}
return $fallback;
}
/**
* The "find" command
*
* Usage:
*
* System::find($dir);
* System::find("$dir -type d");
* System::find("$dir -type f");
* System::find("$dir -name *.php");
* System::find("$dir -name *.php -name *.htm*");
* System::find("$dir -maxdepth 1");
*
* Params implmented:
* $dir -> Start the search at this directory
* -type d -> return only directories
* -type f -> return only files
* -maxdepth <n> -> max depth of recursion
* -name <pattern> -> search pattern (bash style). Multiple -name param allowed
*
* @param mixed Either array or string with the command line
* @return array Array of found files
* @static
*
*/
function find($args)
{
if (!is_array($args)) {
$args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
}
$dir = realpath(array_shift($args));
if (!$dir) {
return array();
}
$patterns = array();
$depth = 0;
$do_files = $do_dirs = true;
$args_count = count($args);
for ($i = 0; $i < $args_count; $i++) {
switch ($args[$i]) {
case '-type':
if (in_array($args[$i+1], array('d', 'f'))) {
if ($args[$i+1] == 'd') {
$do_files = false;
} else {
$do_dirs = false;
}
}
$i++;
break;
case '-name':
$name = preg_quote($args[$i+1], '#');
// our magic characters ? and * have just been escaped,
// so now we change the escaped versions to PCRE operators
$name = strtr($name, array('\?' => '.', '\*' => '.*'));
$patterns[] = '('.$name.')';
$i++;
break;
case '-maxdepth':
$depth = $args[$i+1];
break;
}
}
$path = System::_dirToStruct($dir, $depth, 0, true);
if ($do_files && $do_dirs) {
$files = array_merge($path['files'], $path['dirs']);
} elseif ($do_dirs) {
$files = $path['dirs'];
} else {
$files = $path['files'];
}
if (count($patterns)) {
$dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
$pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
$ret = array();
$files_count = count($files);
for ($i = 0; $i < $files_count; $i++) {
// only search in the part of the file below the current directory
$filepart = basename($files[$i]);
if (preg_match($pattern, $filepart)) {
$ret[] = $files[$i];
}
}
return $ret;
}
return $files;
}
}

View File

@ -1590,6 +1590,18 @@ var SN = { // StatusNet
SN.U.FormXHR($(this));
return false;
});
$('form.ajax input[type=submit]').live('click', function() {
// Some forms rely on knowing which submit button was clicked.
// Save a hidden input field which'll be picked up during AJAX
// submit...
var button = $(this);
var form = button.closest('form');
form.find('.hidden-submit-button').remove();
$('<input class="hidden-submit-button" type="hidden" />')
.attr('name', button.attr('name'))
.val(button.val())
.appendTo(form);
});
},
/**

2
js/util.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -46,7 +46,6 @@ require_once INSTALLDIR.'/lib/peopletags.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class AccountProfileBlock extends ProfileBlock
{
protected $profile = null;
@ -149,9 +148,9 @@ class AccountProfileBlock extends ProfileBlock
$this->out->elementStart('li', 'entity_edit');
$this->out->element('a', array('href' => common_local_url('profilesettings'),
// TRANS: Link title for link on user profile.
'title' => _('Edit profile settings')),
'title' => _('Edit profile settings.')),
// TRANS: Link text for link on user profile.
_('Edit'));
_m('BUTTON','Edit'));
$this->out->elementEnd('li');
} else { // someone else's page
@ -162,6 +161,9 @@ class AccountProfileBlock extends ProfileBlock
if ($cur->isSubscribed($this->profile)) {
$usf = new UnsubscribeForm($this->out, $this->profile);
$usf->show();
} else if ($cur->hasPendingSubscription($this->profile)) {
$sf = new CancelSubscriptionForm($this->out, $this->profile);
$sf->show();
} else {
$sf = new SubscribeForm($this->out, $this->profile);
$sf->show();
@ -175,9 +177,9 @@ class AccountProfileBlock extends ProfileBlock
$this->out->elementStart('li', 'entity_send-a-message');
$this->out->element('a', array('href' => common_local_url('newmessage', array('to' => $this->user->id)),
// TRANS: Link title for link on user profile.
'title' => _('Send a direct message to this user')),
'title' => _('Send a direct message to this user.')),
// TRANS: Link text for link on user profile.
_('Message'));
_m('BUTTON','Message'));
$this->out->elementEnd('li');
// nudge
@ -303,6 +305,6 @@ class AccountProfileBlock extends ProfileBlock
$this->out->element('a', array('href' => $url,
'class' => 'entity_remote_subscribe'),
// TRANS: Link text for link that will subscribe to a remote profile.
_('Subscribe'));
_m('BUTTON','Subscribe'));
}
}

View File

@ -663,7 +663,8 @@ class Action extends HTMLOutputter // lawsuit
if (Event::handle('StartMakeEntryForm', array($tag, $this, &$form))) {
if ($tag == 'status') {
$form = new NoticeForm($this);
$options = $this->noticeFormOptions();
$form = new NoticeForm($this, $options);
}
Event::handle('EndMakeEntryForm', array($tag, $this, $form));
}
@ -679,6 +680,11 @@ class Action extends HTMLOutputter // lawsuit
$this->elementEnd('div');
}
function noticeFormOptions()
{
return array();
}
/**
* Show anonymous message.
*

View File

@ -292,7 +292,7 @@ class ApiAction extends Action
if ($get_notice) {
$notice = $profile->getCurrentNotice();
if ($notice) {
# don't get user!
// don't get user!
$twitter_user['status'] = $this->twitterStatusArray($notice, false);
}
}
@ -397,7 +397,7 @@ class ApiAction extends Action
}
if ($include_user && $profile) {
# Don't get notice (recursive!)
// Don't get notice (recursive!)
$twitter_user = $this->twitterUserArray($profile, false);
$twitter_status['user'] = $twitter_user;
}
@ -738,7 +738,7 @@ class ApiAction extends Action
$this->element('guid', null, $entry['guid']);
$this->element('link', null, $entry['link']);
# RSS only supports 1 enclosure per item
// RSS only supports 1 enclosure per item
if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){
$enclosure = $entry['enclosures'][0];
$this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
@ -873,7 +873,7 @@ class ApiAction extends Action
}
if (!is_null($suplink)) {
# For FriendFeed's SUP protocol
// For FriendFeed's SUP protocol
$this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
'href' => $suplink,
'type' => 'application/json'));

View File

@ -46,7 +46,6 @@ require_once INSTALLDIR.'/lib/form.php';
*
* @see UnsubscribeForm
*/
class ApproveGroupForm extends Form
{
/**
@ -62,7 +61,6 @@ class ApproveGroupForm extends Form
* @param HTMLOutputter $out output channel
* @param group $group group to leave
*/
function __construct($out=null, $group=null, $profile=null)
{
parent::__construct($out);
@ -76,10 +74,9 @@ class ApproveGroupForm extends Form
*
* @return string ID of the form
*/
function id()
{
return 'group-cancel-' . $this->group->id;
return 'group-queue-' . $this->group->id;
}
/**
@ -87,10 +84,9 @@ class ApproveGroupForm extends Form
*
* @return string of the form class
*/
function formClass()
{
return 'form_group_join ajax';
return 'form_group_queue ajax';
}
/**
@ -98,7 +94,6 @@ class ApproveGroupForm extends Form
*
* @return string URL of the action
*/
function action()
{
$params = array();
@ -117,6 +112,9 @@ class ApproveGroupForm extends Form
function formActions()
{
$this->out->submit('submit', _('Approve'));
// TRANS: Submit button text to accept a group membership request on approve group form.
$this->out->submit('approve', _m('BUTTON','Accept'));
// TRANS: Submit button text to reject a group membership request on approve group form.
$this->out->submit('cancel', _m('BUTTON','Reject'));
}
}

114
lib/approvesubform.php Normal file
View File

@ -0,0 +1,114 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for leaving a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/form.php';
/**
* Form for leaving a group
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
* @see UnsubscribeForm
*/
class ApproveSubForm extends Form
{
var $profile = null;
/**
* Constructor
*
* @param HTMLOutputter $out output channel
* @param Profile $profile user whose request to accept or drop
*/
function __construct($out=null, $profile=null)
{
parent::__construct($out);
$this->profile = $profile;
}
/**
* ID of the form
*
* @return string ID of the form
*/
function id()
{
return 'sub-queue-' . $this->profile->id;
}
/**
* class of the form
*
* @return string of the form class
*/
function formClass()
{
return 'form_sub_queue ajax';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
$params = array();
if ($this->profile) {
$params['profile_id'] = $this->profile->id;
}
return common_local_url('approvesub',
array(), $params);
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
// TRANS: Submit button text to accept a subscription request on approve sub form.
$this->out->submit('approve', _m('BUTTON','Accept'));
// TRANS: Submit button text to reject a subscription request on approve sub form.
$this->out->submit('cancel', _m('BUTTON','Reject'));
}
}

129
lib/cachingnoticestream.php Normal file
View File

@ -0,0 +1,129 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* A stream of notices
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* Class for notice streams
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class CachingNoticeStream extends NoticeStream
{
const CACHE_WINDOW = 200;
public $stream = null;
public $cachekey = null;
function __construct($stream, $cachekey)
{
$this->stream = $stream;
$this->cachekey = $cachekey;
}
function getNoticeIds($offset, $limit, $sinceId, $maxId)
{
$cache = Cache::instance();
// We cache self::CACHE_WINDOW elements at the tip of the stream.
// If the cache won't be hit, just generate directly.
if (empty($cache) ||
$sinceId != 0 || $maxId != 0 ||
is_null($limit) ||
($offset + $limit) > self::CACHE_WINDOW) {
return $this->stream->getNoticeIds($offset, $limit, $sinceId, $maxId);
}
// Check the cache to see if we have the stream.
$idkey = Cache::key($this->cachekey);
$idstr = $cache->get($idkey);
if ($idstr !== false) {
// Cache hit! Woohoo!
$window = explode(',', $idstr);
$ids = array_slice($window, $offset, $limit);
return $ids;
}
// Check the cache to see if we have a "last-known-good" version.
// The actual cache gets blown away when new notices are added, but
// the "last" value holds a lot of info. We might need to only generate
// a few at the "tip", which can bound our queries and save lots
// of time.
$laststr = $cache->get($idkey.';last');
if ($laststr !== false) {
$window = explode(',', $laststr);
$last_id = $window[0];
$new_ids = $this->stream->getNoticeIds(0, self::CACHE_WINDOW, $last_id, 0);
$new_window = array_merge($new_ids, $window);
$new_windowstr = implode(',', $new_window);
$result = $cache->set($idkey, $new_windowstr);
$result = $cache->set($idkey . ';last', $new_windowstr);
$ids = array_slice($new_window, $offset, $limit);
return $ids;
}
// No cache hits :( Generate directly and stick the results
// into the cache. Note we generate the full cache window.
$window = $this->stream->getNoticeIds(0, self::CACHE_WINDOW, 0, 0);
$windowstr = implode(',', $window);
$result = $cache->set($idkey, $windowstr);
$result = $cache->set($idkey . ';last', $windowstr);
// Return just the slice that was requested
$ids = array_slice($window, $offset, $limit);
return $ids;
}
}

View File

@ -46,7 +46,6 @@ require_once INSTALLDIR.'/lib/form.php';
*
* @see UnsubscribeForm
*/
class CancelGroupForm extends Form
{
/**
@ -62,7 +61,6 @@ class CancelGroupForm extends Form
* @param HTMLOutputter $out output channel
* @param group $group group to leave
*/
function __construct($out=null, $group=null, $profile=null)
{
parent::__construct($out);
@ -76,7 +74,6 @@ class CancelGroupForm extends Form
*
* @return string ID of the form
*/
function id()
{
return 'group-cancel-' . $this->group->id;
@ -87,7 +84,6 @@ class CancelGroupForm extends Form
*
* @return string of the form class
*/
function formClass()
{
return 'form_group_leave ajax';
@ -98,7 +94,6 @@ class CancelGroupForm extends Form
*
* @return string URL of the action
*/
function action()
{
$params = array();
@ -114,9 +109,9 @@ class CancelGroupForm extends Form
*
* @return void
*/
function formActions()
{
$this->out->submit('submit', _('Cancel join request'));
// TRANS: Submit button text on form to cancel group join request.
$this->out->submit('submit', _m('BUTTON','Cancel join request'));
}
}

View File

@ -0,0 +1,124 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for leaving a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/form.php';
/**
* Form for leaving a group
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
* @see UnsubscribeForm
*/
class CancelSubscriptionForm extends Form
{
/**
* user being subscribed to
*/
var $profile = null;
/**
* Constructor
*
* @param HTMLOutputter $out output channel
* @param Profile $profile being subscribed to
*/
function __construct($out=null, $profile=null)
{
parent::__construct($out);
$this->profile = $profile;
}
/**
* ID of the form
*
* @return string ID of the form
*/
function id()
{
return 'subscription-cancel-' . $this->profile->id;
}
/**
* class of the form
*
* @return string of the form class
*/
function formClass()
{
return 'form_unsubscribe ajax';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
return common_local_url('cancelsubscription',
array(),
array('id' => $this->profile->id));
}
/**
* Data elements of the form
*
* @return void
*/
function formData()
{
$this->out->hidden('unsubscribeto-' . $this->profile->id,
$this->profile->id,
'unsubscribeto');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
// TRANS: Button text for form action to cancel a subscription request.
$this->out->submit('submit', _m('BUTTON','Cancel subscription request'));
}
}

View File

@ -95,9 +95,9 @@ class WebChannel extends Channel
function output($user, $text)
{
# XXX: buffer all output and send it at the end
# XXX: even better, redirect to appropriate page
# depending on what command was run
// XXX: buffer all output and send it at the end
// XXX: even better, redirect to appropriate page
// depending on what command was run
$this->out->startHTML();
$this->out->elementStart('head');
// TRANS: Title for command results.

View File

@ -635,29 +635,15 @@ class RepeatCommand extends Command
{
$notice = $this->getNotice($this->other);
if($this->user->id == $notice->profile_id)
{
// TRANS: Error text shown when trying to repeat an own notice.
$channel->error($this->user, _('Cannot repeat your own notice.'));
return;
}
if ($this->user->getProfile()->hasRepeated($notice->id)) {
// TRANS: Error text shown when trying to repeat an notice that was already repeated by the user.
$channel->error($this->user, _('Already repeated that notice.'));
return;
}
$repeat = $notice->repeat($this->user->id, $channel->source);
if ($repeat) {
try {
$repeat = $notice->repeat($this->user->id, $channel->source());
$recipient = $notice->getProfile();
// TRANS: Message given having repeated a notice from another user.
// TRANS: %s is the name of the user for which the notice was repeated.
$channel->output($this->user, sprintf(_('Notice from %s repeated.'), $recipient->nickname));
} else {
// TRANS: Error text shown when repeating a notice fails with an unknown reason.
$channel->error($this->user, _('Error repeating notice.'));
} catch (Exception $e) {
$channel->error($this->user, $e->getMessage());
}
}
}

View File

@ -0,0 +1,106 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Notice stream for a conversation
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Cache
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* Notice stream for a conversation
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class ConversationNoticeStream extends ScopingNoticeStream
{
function __construct($id)
{
parent::__construct(new CachingNoticeStream(new RawConversationNoticeStream($id),
'notice:conversation_ids:'.$id));
}
}
/**
* Notice stream for a conversation
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class RawConversationNoticeStream extends NoticeStream
{
protected $id;
function __construct($id)
{
$this->id = $id;
}
function getNoticeIds($offset, $limit, $since_id, $max_id)
{
$notice = new Notice();
$notice->selectAdd(); // clears it
$notice->selectAdd('id');
$notice->conversation = $this->id;
$notice->orderBy('created DESC, id DESC');
if (!is_null($offset)) {
$notice->limit($offset, $limit);
}
Notice::addWhereSinceId($notice, $since_id);
Notice::addWhereMaxId($notice, $max_id);
$ids = array();
if ($notice->find()) {
while ($notice->fetch()) {
$ids[] = $notice->id;
}
}
$notice->free();
$notice = NULL;
return $ids;
}
}

View File

@ -293,7 +293,8 @@ $default =
array('enabled' => true,
'css' => ''),
'notice' =>
array('contentlimit' => null),
array('contentlimit' => null,
'defaultscope' => 0), // set to 0 for default open
'message' =>
array('contentlimit' => null),
'location' =>

140
lib/favenoticestream.php Normal file
View File

@ -0,0 +1,140 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Notice stream for favorites
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* Notice stream for favorites
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class FaveNoticeStream extends ScopingNoticeStream
{
function __construct($user_id, $own)
{
$stream = new RawFaveNoticeStream($user_id, $own);
if ($own) {
$key = 'fave:ids_by_user_own:'.$user_id;
} else {
$key = 'fave:ids_by_user:'.$user_id;
}
parent::__construct(new CachingNoticeStream($stream, $key));
}
}
/**
* Raw notice stream for favorites
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class RawFaveNoticeStream extends NoticeStream
{
protected $user_id;
protected $own;
function __construct($user_id, $own)
{
$this->user_id = $user_id;
$this->own = $own;
}
/**
* Note that the sorting for this is by order of *fave* not order of *notice*.
*
* @fixme add since_id, max_id support?
*
* @param <type> $user_id
* @param <type> $own
* @param <type> $offset
* @param <type> $limit
* @param <type> $since_id
* @param <type> $max_id
* @return <type>
*/
function getNoticeIds($offset, $limit, $since_id, $max_id)
{
$fav = new Fave();
$qry = null;
if ($this->own) {
$qry = 'SELECT fave.* FROM fave ';
$qry .= 'WHERE fave.user_id = ' . $this->user_id . ' ';
} else {
$qry = 'SELECT fave.* FROM fave ';
$qry .= 'INNER JOIN notice ON fave.notice_id = notice.id ';
$qry .= 'WHERE fave.user_id = ' . $this->user_id . ' ';
$qry .= 'AND notice.is_local != ' . Notice::GATEWAY . ' ';
}
if ($since_id != 0) {
$qry .= 'AND notice_id > ' . $since_id . ' ';
}
if ($max_id != 0) {
$qry .= 'AND notice_id <= ' . $max_id . ' ';
}
// NOTE: we sort by fave time, not by notice time!
$qry .= 'ORDER BY modified DESC ';
if (!is_null($offset)) {
$qry .= "LIMIT $limit OFFSET $offset";
}
$fav->query($qry);
$ids = array();
while ($fav->fetch()) {
$ids[] = $fav->notice_id;
}
$fav->free();
unset($fav);
return $ids;
}
}

105
lib/filenoticestream.php Normal file
View File

@ -0,0 +1,105 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Stream of notices that reference an URL
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
class FileNoticeStream extends ScopingNoticeStream
{
function __construct($file)
{
parent::__construct(new CachingNoticeStream(new RawFileNoticeStream($file),
'file:notice-ids:'.$this->url));
}
}
/**
* Raw stream for a file
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class RawFileNoticeStream extends NoticeStream
{
protected $file = null;
function __construct($file)
{
parent::__construct();
$this->file = $file;
}
/**
* Stream of notices linking to this URL
*
* @param integer $offset Offset to show; default is 0
* @param integer $limit Limit of notices to show
* @param integer $since_id Since this notice
* @param integer $max_id Before this notice
*
* @return array ids of notices that link to this file
*/
function getNoticeIds($offset, $limit, $since_id, $max_id)
{
$f2p = new File_to_post();
$f2p->selectAdd();
$f2p->selectAdd('post_id');
$f2p->file_id = $this->file->id;
Notice::addWhereSinceId($f2p, $since_id, 'post_id', 'modified');
Notice::addWhereMaxId($f2p, $max_id, 'post_id', 'modified');
$f2p->orderBy('modified DESC, post_id DESC');
if (!is_null($offset)) {
$f2p->limit($offset, $limit);
}
$ids = array();
if ($f2p->find()) {
while ($f2p->fetch()) {
$ids[] = $f2p->post_id;
}
}
return $ids;
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* A notice stream that filters its upstream content
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* A class for presenting a filtered notice stream based on an upstream stream
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
abstract class FilteringNoticeStream extends NoticeStream
{
protected $upstream;
function __construct($upstream)
{
$this->upstream = $upstream;
}
abstract function filter($notice);
function getNotices($offset, $limit, $sinceId, $maxId)
{
// "offset" is virtual; we have to get a lot
$total = $offset + $limit;
$filtered = array();
$startAt = 0;
$askFor = $total;
// Keep going till we have $total notices in $notices array,
// or we get nothing from upstream.
$results = null;
do {
$raw = $this->upstream->getNotices($startAt, $askFor, $sinceId, $maxId);
$results = $raw->N;
if ($results == 0) {
break;
}
while ($raw->fetch()) {
if ($this->filter($raw)) {
$filtered[] = clone($raw);
if (count($filtered) >= $total) {
break;
}
}
}
// XXX: make these smarter; factor hit rate into $askFor
$startAt += $askFor;
$askFor = max($total - count($filtered), NOTICES_PER_PAGE);
} while (count($filtered) < $total && $results !== 0);
return new ArrayWrapper(array_slice($filtered, $offset, $limit));
}
function getNoticeIds($offset, $limit, $sinceId, $maxId)
{
$notices = $this->getNotices($offset, $limit, $sinceId, $maxId);
$ids = array();
while ($notices->fetch()) {
$ids[] = $notices->id;
}
return $ids;
}
}

View File

@ -49,7 +49,7 @@ define('NOTICE_INBOX_SOURCE_FORWARD', 4);
define('NOTICE_INBOX_SOURCE_PROFILE_TAG', 5);
define('NOTICE_INBOX_SOURCE_GATEWAY', -1);
# append our extlib dir as the last-resort place to find libs
// append our extlib dir as the last-resort place to find libs
set_include_path(get_include_path() . PATH_SEPARATOR . INSTALLDIR . '/extlib/');
@ -70,7 +70,7 @@ if (!function_exists('dl')) {
}
}
# global configuration object
// global configuration object
require_once('PEAR.php');
require_once('PEAR/Exception.php');

View File

@ -84,7 +84,7 @@ class GalleryAction extends OwnerDesignAction
{
parent::handle($args);
# Post from the tag dropdown; redirect to a GET
// Post from the tag dropdown; redirect to a GET
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
common_redirect($this->selfUrl(), 303);

View File

@ -1,5 +1,5 @@
<?php
// @todo FIXME: standard file header missing.
/**
* Form for blocking a user from a group
*

View File

@ -112,6 +112,7 @@ class GroupEditForm extends Form
*/
function formLegend()
{
// TRANS: Form legend for group edit form.
$this->out->element('legend', null, _('Create a new group'));
}
@ -142,44 +143,58 @@ class GroupEditForm extends Form
if (Event::handle('StartGroupEditFormData', array($this))) {
$this->out->elementStart('li');
$this->out->hidden('groupid', $id);
// TRANS: Field label on group edit form.
$this->out->input('nickname', _('Nickname'),
($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
_('1-64 lowercase letters or numbers, no punctuation or spaces'));
// TRANS: Field title on group edit form.
_('1-64 lowercase letters or numbers, no punctuation or spaces.'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
// TRANS: Field label on group edit form.
$this->out->input('fullname', _('Full name'),
($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
$this->out->elementEnd('li');
$this->out->elementStart('li');
// TRANS: Field label on group edit form; points to "more info" for a group.
$this->out->input('homepage', _('Homepage'),
($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
// TRANS: Field title on group edit form.
_('URL of the homepage or blog of the group or topic.'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$desclimit = User_group::maxDescription();
if ($desclimit == 0) {
$descinstr = _('Describe the group or topic');
// TRANS: Text area title for group description when there is no text limit.
$descinstr = _('Describe the group or topic.');
} else {
$descinstr = sprintf(_m('Describe the group or topic in %d character or less',
'Describe the group or topic in %d characters or less',
// TRANS: Text area title for group description.
// TRANS: %d is the number of characters available for the description.
$descinstr = sprintf(_m('Describe the group or topic in %d character or less.',
'Describe the group or topic in %d characters or less.',
$desclimit),
$desclimit);
}
// TRANS: Text area label on group edit form; contains description of group.
$this->out->textarea('description', _('Description'),
($this->out->arg('description')) ? $this->out->arg('description') : $description,
$descinstr);
$this->out->elementEnd('li');
$this->out->elementStart('li');
// TRANS: Field label on group edit form.
$this->out->input('location', _('Location'),
($this->out->arg('location')) ? $this->out->arg('location') : $location,
// TRANS: Field title on group edit form.
_('Location for the group, if any, like "City, State (or Region), Country".'));
$this->out->elementEnd('li');
if (common_config('group', 'maxaliases') > 0) {
$aliases = (empty($this->group)) ? array() : $this->group->getAliases();
$this->out->elementStart('li');
// TRANS: Field label on group edit form.
$this->out->input('aliases', _('Aliases'),
($this->out->arg('aliases')) ? $this->out->arg('aliases') :
(!empty($aliases)) ? implode(' ', $aliases) : '',
// TRANS: Input field title for group aliases.
// TRANS: %d is the maximum number of group aliases available.
sprintf(_m('Extra nicknames for the group, separated with commas or spaces. Maximum %d alias allowed.',
'Extra nicknames for the group, separated with commas or spaces. Maximum %d aliases allowed.',
common_config('group', 'maxaliases')),
@ -188,9 +203,11 @@ class GroupEditForm extends Form
}
$this->out->elementStart('li');
$this->out->dropdown('join_policy',
// TRANS: Dropdown fieldd label on group edit form.
_('Membership policy'),
array(User_group::JOIN_POLICY_OPEN => _('Open to all'),
User_group::JOIN_POLICY_MODERATE => _('Admin must approve all members')),
// TRANS: Dropdown field title on group edit form.
_('Whether admin approval is required to join this group.'),
false,
(empty($this->group->join_policy)) ? User_group::JOIN_POLICY_OPEN : $this->group->join_policy);
@ -207,6 +224,7 @@ class GroupEditForm extends Form
*/
function formActions()
{
// TRANS: Text for save button on group edit form.
$this->out->submit('submit', _m('BUTTON','Save'));
}
}

View File

@ -137,7 +137,7 @@ class GroupList extends Widget
$this->out->elementEnd('p');
}
# If we're on a list with an owner (subscriptions or subscribers)...
// If we're on a list with an owner (subscriptions or subscribers)...
if (!empty($user) && !empty($this->owner) && $user->id == $this->owner->id) {
$this->showOwnerControls();
@ -149,8 +149,8 @@ class GroupList extends Widget
$this->out->elementStart('div', 'entity_actions');
$this->out->elementStart('ul');
$this->out->elementStart('li', 'entity_subscribe');
# XXX: special-case for user looking at own
# subscriptions page
// XXX: special-case for user looking at own
// subscriptions page
if ($user->isMember($this->group)) {
$lf = new LeaveForm($this->out, $this->group);
$lf->show();

View File

@ -1,4 +1,5 @@
<?php
// @todo FIXME: add documentation.
class GroupMemberList extends ProfileList
{

View File

@ -1,4 +1,5 @@
<?php
// @todo FIXME: add documentation.
class GroupMemberListItem extends ProfileListItem
{
@ -17,7 +18,7 @@ class GroupMemberListItem extends ProfileListItem
if ($this->profile->isAdmin($this->group)) {
$this->out->text(' '); // for separating the classes.
// TRANS: Indicator in group members list that this user is a group administrator.
$this->out->element('span', 'role', _('Admin'));
$this->out->element('span', 'role', _m('GROUPADMIN','Admin'));
}
}
@ -102,4 +103,3 @@ class GroupMemberListItem extends ProfileListItem
return $args;
}
}

View File

@ -48,7 +48,6 @@ require_once INSTALLDIR.'/lib/widget.php';
*
* @see HTMLOutputter
*/
class GroupNav extends Menu
{
var $group = null;
@ -58,7 +57,6 @@ class GroupNav extends Menu
*
* @param Action $action current action, used for output
*/
function __construct($action=null, $group=null)
{
parent::__construct($action);
@ -70,7 +68,6 @@ class GroupNav extends Menu
*
* @return void
*/
function show()
{
$action_name = $this->action->trimmed('action');
@ -105,7 +102,8 @@ class GroupNav extends Menu
$this->out->menuItem(common_local_url('groupqueue', array('nickname' =>
$nickname)),
// TRANS: Menu item in the group navigation page. Only shown for group administrators.
sprintf(_m('MENU','Pending members (%d)'), $pending),
// TRANS: %d is the number of pending members.
sprintf(_m('MENU','Pending members (%d)','Pending members (%d)',$pending), $pending),
// TRANS: Tooltip for menu item in the group navigation page. Only shown for group administrators.
// TRANS: %s is the nickname of the group.
sprintf(_m('TOOLTIP','%s pending members'), $nickname),

103
lib/groupnoticestream.php Normal file
View File

@ -0,0 +1,103 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Stream of notices for a group
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* Stream of notices for a group
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class GroupNoticeStream extends ScopingNoticeStream
{
function __construct($group)
{
parent::__construct(new CachingNoticeStream(new RawGroupNoticeStream($group),
'user_group:notice_ids:' . $group->id));
}
}
/**
* Stream of notices for a group
*
* @category Stream
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class RawGroupNoticeStream extends NoticeStream
{
protected $group;
function __construct($group)
{
$this->group = $group;
}
function getNoticeIds($offset, $limit, $since_id, $max_id)
{
$inbox = new Group_inbox();
$inbox->group_id = $this->group->id;
$inbox->selectAdd();
$inbox->selectAdd('notice_id');
Notice::addWhereSinceId($inbox, $since_id, 'notice_id');
Notice::addWhereMaxId($inbox, $max_id, 'notice_id');
$inbox->orderBy('created DESC, notice_id DESC');
if (!is_null($offset)) {
$inbox->limit($offset, $limit);
}
$ids = array();
if ($inbox->find()) {
while ($inbox->fetch()) {
$ids[] = $inbox->notice_id;
}
}
return $ids;
}
}

View File

@ -482,9 +482,12 @@ abstract class ImPlugin extends Plugin
$body = trim(strip_tags($body));
$content_shortened = common_shorten_links($body);
if (Notice::contentTooLong($content_shortened)) {
$this->sendFromSite($screenname, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d.'),
Notice::maxContent(),
mb_strlen($content_shortened)));
$this->sendFromSite($screenname,
sprintf(_m('Message too long - maximum is %1$d character, you sent %2$d.',
'Message too long - maximum is %1$d characters, you sent %2$d.',
Notice::maxContent()),
Notice::maxContent(),
mb_strlen($content_shortened)));
return;
}

View File

@ -245,44 +245,13 @@ function mail_subscribe_notify_profile($listenee, $other)
$other->getBestName(),
common_config('site', 'name'));
// TRANS: This is a paragraph in a new-subscriber e-mail.
// TRANS: %s is a URL where the subscriber can be reported as abusive.
$blocklink = sprintf(_("If you believe this account is being used abusively, " .
"you can block them from your subscribers list and " .
"report as spam to site administrators at %s"),
common_local_url('block', array('profileid' => $other->id)));
// TRANS: Main body of new-subscriber notification e-mail.
// TRANS: %1$s is the subscriber's long name, %2$s is the StatusNet sitename,
// TRANS: %3$s is the subscriber's profile URL, %4$s is the subscriber's location (or empty)
// TRANS: %5$s is the subscriber's homepage URL (or empty), %6%s is the subscriber's bio (or empty)
// TRANS: %7$s is a link to the addressed user's e-mail settings.
$body = sprintf(_('%1$s is now listening to your notices on %2$s.'."\n\n".
"\t".'%3$s'."\n\n".
'%4$s'.
'%5$s'.
'%6$s'.
"\n".'Faithfully yours,'."\n".'%2$s.'."\n\n".
"----\n".
"Change your email address or ".
"notification options at ".'%7$s' ."\n"),
// TRANS: %1$s is the subscriber's long name, %2$s is the StatusNet sitename.
$body = sprintf(_('%1$s is now listening to your notices on %2$s.'),
$long_name,
common_config('site', 'name'),
$other->profileurl,
($other->location) ?
// TRANS: Profile info line in new-subscriber notification e-mail.
// TRANS: %s is a location.
sprintf(_("Location: %s"), $other->location) . "\n" : '',
($other->homepage) ?
// TRANS: Profile info line in new-subscriber notification e-mail.
// TRANS: %s is a homepage.
sprintf(_("Homepage: %s"), $other->homepage) . "\n" : '',
(($other->bio) ?
// TRANS: Profile info line in new-subscriber notification e-mail.
// TRANS: %s is biographical information.
sprintf(_("Bio: %s"), $other->bio) . "\n" : '') .
"\n\n" . $blocklink . "\n",
common_local_url('emailsettings'));
common_config('site', 'name')) .
mail_profile_block($other) .
mail_footer_block();
// reset localization
common_switch_locale();
@ -290,6 +259,112 @@ function mail_subscribe_notify_profile($listenee, $other)
}
}
function mail_subscribe_pending_notify_profile($listenee, $other)
{
if ($other->hasRight(Right::EMAILONSUBSCRIBE) &&
$listenee->email && $listenee->emailnotifysub) {
$profile = $listenee->getProfile();
$name = $profile->getBestName();
$long_name = ($other->fullname) ?
($other->fullname . ' (' . $other->nickname . ')') : $other->nickname;
$recipients = $listenee->email;
// use the recipient's localization
common_switch_locale($listenee->language);
$headers = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname);
$headers['From'] = mail_notify_from();
$headers['To'] = $name . ' <' . $listenee->email . '>';
// TRANS: Subject of pending new-subscriber notification e-mail.
// TRANS: %1$s is the subscribing user's nickname, %2$s is the StatusNet sitename.
$headers['Subject'] = sprintf(_('%1$s would like to listen to '.
'your notices on %2$s.'),
$other->getBestName(),
common_config('site', 'name'));
// TRANS: Main body of pending new-subscriber notification e-mail.
// TRANS: %1$s is the subscriber's long name, %2$s is the StatusNet sitename.
$body = sprintf(_('%1$s would like to listen to your notices on %2$s. ' .
'You may approve or reject their subscription at %3$s'),
$long_name,
common_config('site', 'name'),
common_local_url('subqueue', array('nickname' => $listenee->nickname))) .
mail_profile_block($other) .
mail_footer_block();
// reset localization
common_switch_locale();
mail_send($recipients, $headers, $body);
}
}
function mail_footer_block()
{
// TRANS: Common footer block for StatusNet notification emails.
// TRANS: %1$s is the StatusNet sitename,
// TRANS: %2$s is a link to the addressed user's e-mail settings.
return "\n\n" . sprintf(_('Faithfully yours,'.
"\n".'%1$s.'."\n\n".
"----\n".
"Change your email address or ".
"notification options at ".'%2$s'),
common_config('site', 'name'),
common_local_url('emailsettings')) . "\n";
}
/**
* Format a block of profile info for a plaintext notification email.
*
* @param Profile $profile
* @return string
*/
function mail_profile_block($profile)
{
// TRANS: Layout for
// TRANS: %1$s is the subscriber's profile URL, %2$s is the subscriber's location (or empty)
// TRANS: %3$s is the subscriber's homepage URL (or empty), %4%s is the subscriber's bio (or empty)
$out = array();
$out[] = "";
$out[] = "";
// TRANS: Profile info line in notification e-mail.
// TRANS: %s is a URL.
$out[] = sprintf(_("Profile: %s"), $profile->profileurl);
if ($profile->location) {
// TRANS: Profile info line in notification e-mail.
// TRANS: %s is a location.
$out[] = sprintf(_("Location: %s"), $profile->location);
}
if ($profile->homepage) {
// TRANS: Profile info line in notification e-mail.
// TRANS: %s is a homepage.
$out[] = sprintf(_("Homepage: %s"), $profile->homepage);
}
if ($profile->bio) {
// TRANS: Profile info line in notification e-mail.
// TRANS: %s is biographical information.
$out[] = sprintf(_("Bio: %s"), $profile->bio);
}
$blocklink = common_local_url('block', array('profileid' => $profile->id));
// This'll let ModPlus add the remote profile info so it's possible
// to block remote users directly...
Event::handle('MailProfileInfoBlockLink', array($profile, &$blocklink));
// TRANS: This is a paragraph in a new-subscriber e-mail.
// TRANS: %s is a URL where the subscriber can be reported as abusive.
$out[] = sprintf(_('If you believe this account is being used abusively, ' .
'you can block them from your subscribers list and ' .
'report as spam to site administrators at %s.'),
$blocklink);
$out[] = "";
return implode("\n", $out);
}
/**
* notify a user of their new incoming email address
*
@ -317,11 +392,11 @@ function mail_new_incoming_notify($user)
// TRANS: to to post by e-mail, %3$s is a URL to more instructions.
$body = sprintf(_("You have a new posting address on %1\$s.\n\n".
"Send email to %2\$s to post new messages.\n\n".
"More email instructions at %3\$s.\n\n".
"Faithfully yours,\n%1\$s"),
"More email instructions at %3\$s."),
common_config('site', 'name'),
$user->incomingemail,
common_local_url('doc', array('title' => 'email')));
common_local_url('doc', array('title' => 'email'))) .
mail_footer_block();
mail_send($user->email, $headers, $body);
}
@ -466,7 +541,7 @@ function mail_confirm_sms($code, $nickname, $address)
// TRANS: Main body heading for SMS-by-email address confirmation message.
// TRANS: %s is the addressed user's nickname.
$body = sprintf(_("%s: confirm you own this phone number with this code:"), $nickname);
$body = sprintf(_('%s: confirm you own this phone number with this code:'), $nickname);
$body .= "\n\n";
$body .= $code;
$body .= "\n\n";
@ -493,18 +568,16 @@ function mail_notify_nudge($from, $to)
// TRANS: Body for 'nudge' notification email.
// TRANS: %1$s is the nuding user's long name, $2$s is the nudging user's nickname,
// TRANS: %3$s is a URL to post notices at, %4$s is the StatusNet sitename.
// TRANS: %3$s is a URL to post notices at.
$body = sprintf(_("%1\$s (%2\$s) is wondering what you are up to ".
"these days and is inviting you to post some news.\n\n".
"So let's hear from you :)\n\n".
"%3\$s\n\n".
"Don't reply to this email; it won't get to them.\n\n".
"With kind regards,\n".
"%4\$s\n"),
"Don't reply to this email; it won't get to them."),
$from_profile->getBestName(),
$from->nickname,
common_local_url('all', array('nickname' => $to->nickname)),
common_config('site', 'name'));
common_local_url('all', array('nickname' => $to->nickname))) .
mail_footer_block();
common_switch_locale();
$headers = _mail_prepare_headers('nudge', $to->nickname, $from->nickname);
@ -548,21 +621,18 @@ function mail_notify_message($message, $from=null, $to=null)
// TRANS: Body for direct-message notification email.
// TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname,
// TRANS: %3$s is the message content, %4$s a URL to the message,
// TRANS: %5$s is the StatusNet sitename.
$body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n".
"------------------------------------------------------\n".
"%3\$s\n".
"------------------------------------------------------\n\n".
"You can reply to their message here:\n\n".
"%4\$s\n\n".
"Don't reply to this email; it won't get to them.\n\n".
"With kind regards,\n".
"%5\$s\n"),
"Don't reply to this email; it won't get to them."),
$from_profile->getBestName(),
$from->nickname,
$message->content,
common_local_url('newmessage', array('to' => $from->id)),
common_config('site', 'name'));
common_local_url('newmessage', array('to' => $from->id))) .
mail_footer_block();
$headers = _mail_prepare_headers('message', $to->nickname, $from->nickname);
@ -571,7 +641,7 @@ function mail_notify_message($message, $from=null, $to=null)
}
/**
* notify a user that one of their notices has been chosen as a 'fave'
* Notify a user that one of their notices has been chosen as a 'fave'
*
* Doesn't check that the user has an email address nor if they
* want to receive notification of faves. Maybe this happens higher
@ -615,9 +685,7 @@ function mail_notify_fave($other, $user, $notice)
"The text of your notice is:\n\n" .
"%4\$s\n\n" .
"You can see the list of %1\$s's favorites here:\n\n" .
"%5\$s\n\n" .
"Faithfully yours,\n" .
"%6\$s\n"),
"%5\$s"),
$bestname,
common_exact_date($notice->created),
common_local_url('shownotice',
@ -626,7 +694,8 @@ function mail_notify_fave($other, $user, $notice)
common_local_url('showfavorites',
array('nickname' => $user->nickname)),
common_config('site', 'name'),
$user->nickname);
$user->nickname) .
mail_footer_block();
$headers = _mail_prepare_headers('fave', $other->nickname, $user->nickname);
@ -635,7 +704,7 @@ function mail_notify_fave($other, $user, $notice)
}
/**
* notify a user that they have received an "attn:" message AKA "@-reply"
* Notify a user that they have received an "attn:" message AKA "@-reply"
*
* @param User $user The user who recevied the notice
* @param Notice $notice The notice that was sent
@ -677,12 +746,11 @@ function mail_notify_attn($user, $notice)
$subject = sprintf(_('%1$s (@%2$s) sent a notice to your attention'), $bestname, $sender->nickname);
// TRANS: Body of @-reply notification e-mail.
// TRANS: %1$s is the sending user's long name, $2$s is the StatusNet sitename,
// TRANS: %1$s is the sending user's name, $2$s is the StatusNet sitename,
// TRANS: %3$s is a URL to the notice, %4$s is the notice text,
// TRANS: %5$s is a URL to the full conversion if it exists (otherwise empty),
// TRANS: %6$s is a URL to reply to the notice, %7$s is a URL to all @-replied for the addressed user,
// TRANS: %8$s is a URL to the addressed user's e-mail settings, %9$s is the sender's nickname.
$body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
// TRANS: %6$s is a URL to reply to the notice, %7$s is a URL to all @-replies for the addressed user,
$body = sprintf(_("%1\$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".
@ -691,11 +759,8 @@ function mail_notify_attn($user, $notice)
"You can reply back here:\n\n".
"\t%6\$s\n\n" .
"The list of all @-replies for you here:\n\n" .
"%7\$s\n\n" .
"Faithfully yours,\n" .
"%2\$s\n\n" .
"P.S. You can turn off these email notifications here: %8\$s\n"),
$bestname,//%1
"%7\$s"),
$sender->getFancyName(),//%1
common_config('site', 'name'),//%2
common_local_url('shownotice',
array('notice' => $notice->id)),//%3
@ -704,10 +769,8 @@ function mail_notify_attn($user, $notice)
common_local_url('newnotice',
array('replyto' => $sender->nickname, 'inreplyto' => $notice->id)),//%6
common_local_url('replies',
array('nickname' => $user->nickname)),//%7
common_local_url('emailsettings'), //%8
$sender->nickname); //%9
array('nickname' => $user->nickname))) . //%7
mail_footer_block();
$headers = _mail_prepare_headers('mention', $user->nickname, $sender->nickname);
common_switch_locale();
@ -734,3 +797,97 @@ function _mail_prepare_headers($msg_type, $to, $from)
return $headers;
}
/**
* Send notification emails to group administrator.
*
* @param User_group $group
* @param Profile $joiner
*/
function mail_notify_group_join($group, $joiner)
{
// This returns a Profile query...
$admin = $group->getAdmins();
while ($admin->fetch()) {
// We need a local user for email notifications...
$adminUser = User::staticGet('id', $admin->id);
// @fixme check for email preference?
if ($adminUser && $adminUser->email) {
// use the recipient's localization
common_switch_locale($adminUser->language);
$headers = _mail_prepare_headers('join', $admin->nickname, $joiner->nickname);
$headers['From'] = mail_notify_from();
$headers['To'] = $admin->getBestName() . ' <' . $adminUser->email . '>';
// TRANS: Subject of group join notification e-mail.
// TRANS: %1$s is the joining user's nickname, %2$s is the group name, and %3$s is the StatusNet sitename.
$headers['Subject'] = sprintf(_('%1$s has joined '.
'your group %2$s on %3$s.'),
$joiner->getBestName(),
$group->getBestName(),
common_config('site', 'name'));
// TRANS: Main body of group join notification e-mail.
// TRANS: %1$s is the subscriber's long name, %2$s is the group name, and %3$s is the StatusNet sitename,
// TRANS: %4$s is a block of profile info about the subscriber.
// TRANS: %5$s is a link to the addressed user's e-mail settings.
$body = sprintf(_('%1$s has joined your group %2$s on %3$s.'),
$joiner->getFancyName(),
$group->getFancyName(),
common_config('site', 'name')) .
mail_profile_block($joiner) .
mail_footer_block();
// reset localization
common_switch_locale();
mail_send($adminUser->email, $headers, $body);
}
}
}
/**
* Send notification emails to group administrator.
*
* @param User_group $group
* @param Profile $joiner
*/
function mail_notify_group_join_pending($group, $joiner)
{
$admin = $group->getAdmins();
while ($admin->fetch()) {
// We need a local user for email notifications...
$adminUser = User::staticGet('id', $admin->id);
// @fixme check for email preference?
if ($adminUser && $adminUser->email) {
// use the recipient's localization
common_switch_locale($adminUser->language);
$headers = _mail_prepare_headers('join', $admin->nickname, $joiner->nickname);
$headers['From'] = mail_notify_from();
$headers['To'] = $admin->getBestName() . ' <' . $adminUser->email . '>';
// TRANS: Subject of pending group join request notification e-mail.
// TRANS: %1$s is the joining user's nickname, %2$s is the group name, and %3$s is the StatusNet sitename.
$headers['Subject'] = sprintf(_('%1$s wants to join your group %2$s on %3$s.'),
$joiner->getBestName(),
$group->getBestName(),
common_config('site', 'name'));
// TRANS: Main body of pending group join request notification e-mail.
// TRANS: %1$s is the subscriber's long name, %2$s is the group name, and %3$s is the StatusNet sitename,
// TRANS: %4$s is the URL to the moderation queue page.
$body = sprintf(_('%1$s would like to join your group %2$s on %3$s. ' .
'You may approve or reject their group membership at %4$s'),
$joiner->getFancyName(),
$group->getFancyName(),
common_config('site', 'name'),
common_local_url('groupqueue', array('nickname' => $group->nickname))) .
mail_profile_block($joiner) .
mail_footer_block();
// reset localization
common_switch_locale();
mail_send($adminUser->email, $headers, $body);
}
}
}

View File

@ -21,8 +21,8 @@ require_once(INSTALLDIR . '/lib/mail.php');
require_once(INSTALLDIR . '/lib/mediafile.php');
require_once('Mail/mimeDecode.php');
# FIXME: we use both Mail_mimeDecode and mailparse
# Need to move everything to mailparse
// FIXME: we use both Mail_mimeDecode and mailparse
// Need to move everything to mailparse
class MailHandler
{

View File

@ -1,4 +1,5 @@
<?php
// @todo FIXME: add standard file header.
/**
* Form for making a user an admin for a group

View File

@ -512,7 +512,7 @@ abstract class MicroAppPlugin extends Plugin
function onEndActivityObjectOutputJson(ActivityObject $obj, array &$out)
{
if (in_array($obj->type, $this->types())) {
$this->activityObjectOutputJson($obj, &$out);
$this->activityObjectOutputJson($obj, $out);
}
return true;
}

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