Merge branch 'master' into 0.9.x

This commit is contained in:
Brion Vibber 2011-02-10 12:04:13 -08:00
commit b09276635c
78 changed files with 4861 additions and 1740 deletions

View File

@ -1057,3 +1057,43 @@ StartCloseNoticeListItemElement: Before the closing </li> of a notice list eleme
EndCloseNoticeListItemElement: After the closing </li> of a notice list element
- $nli: The notice list item being shown
StartGroupEditFormData: Beginning the group edit form entries
- $form: The form widget being shown
EndGroupEditFormData: Ending the group edit form entries
- $form: The form widget being shown
StartGroupSave: After initializing but before saving a group
- &$group: group about to be saved
EndGroupSave: After saving a group, aliases, and first member
- $group: group that was saved
StartInterpretCommand: Before running a command
- $cmd: First word in the string, 'foo' in 'foo argument'
- $arg: Argument, if any, like 'argument' in 'foo argument'
- $user: User who issued the command
- &$result: Resulting command; you can set this!
EndInterpretCommand: Before running a command
- $cmd: First word in the string, 'foo' in 'foo argument'
- $arg: Argument, if any, like 'argument' in 'foo argument'
- $user: User who issued the command
- $result: Resulting command
StartGroupActionsList: Start the list of actions on a group profile page (after <ul>, before first <li>)
- $action: action being executed (for output and params)
- $group: group for the page
EndGroupActionsList: End the list of actions on a group profile page (before </ul>, after last </li>)
- $action: action being executed (for output and params)
- $group: group for the page
StartGroupProfileElements: Start showing stuff about the group on its profile page
- $action: action being executed (for output and params)
- $group: group for the page
EndGroupProfileElements: Start showing stuff about the group on its profile page
- $action: action being executed (for output and params)
- $group: group for the page

19
README
View File

@ -1279,7 +1279,7 @@ biolimit: max character length of bio; 0 means no limit; null means to use
backup: whether users can backup their own profiles. Defaults to true.
restore: whether users can restore their profiles from backup files. Defaults
to true.
delete: whether users can delete their own accounts. Defaults to true.
delete: whether users can delete their own accounts. Defaults to false.
move: whether users can move their accounts to another server. Defaults
to true.
@ -1572,6 +1572,23 @@ proxy_user: Username to use for authenticating to the HTTP proxy. Default null.
proxy_password: Password to use for authenticating to the HTTP proxy. Default null.
proxy_auth_scheme: Scheme to use for authenticating to the HTTP proxy. Default null.
plugins
-------
default: associative array mapping plugin name to array of arguments. To disable
a default plugin, unset its value in this array.
locale_path: path for finding plugin locale files. In the plugin's directory
by default.
server: Server to find static files for a plugin when the page is plain old HTTP.
Defaults to site/server (same as pages). Use this to move plugin CSS and
JS files to a CDN.
sslserver: Server to find static files for a plugin when the page is HTTPS. Defaults
to site/server (same as pages). Use this to move plugin CSS and JS files
to a CDN.
path: Path to the plugin files. defaults to site/path + '/plugins/'. Expects that
each plugin will have a subdirectory at plugins/NameOfPlugin. Change this
if you're using a CDN.
Plugins
=======

View File

@ -177,116 +177,121 @@ class EditgroupAction extends GroupDesignAction
return;
}
$nickname = Nickname::normalize($this->trimmed('nickname'));
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
$aliasstring = $this->trimmed('aliases');
if (Event::handle('StartGroupSaveForm', array($this))) {
if ($this->nicknameExists($nickname)) {
// TRANS: Group edit form validation error.
$this->showForm(_('Nickname already in use. Try another one.'));
return;
} else if (!User_group::allowedNickname($nickname)) {
// TRANS: Group edit form validation error.
$this->showForm(_('Not a valid nickname.'));
return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
// TRANS: Group edit form validation error.
$this->showForm(_('Homepage is not a valid URL.'));
return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Group edit form validation error.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
return;
} else if (User_group::descriptionTooLong($description)) {
$this->showForm(sprintf(
$nickname = Nickname::normalize($this->trimmed('nickname'));
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
$aliasstring = $this->trimmed('aliases');
if ($this->nicknameExists($nickname)) {
// TRANS: Group edit form validation error.
_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
User_group::maxDescription()),
$this->showForm(_('Nickname already in use. Try another one.'));
return;
} else if (!User_group::allowedNickname($nickname)) {
// TRANS: Group edit form validation error.
$this->showForm(_('Not a valid nickname.'));
return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
// TRANS: Group edit form validation error.
$this->showForm(_('Homepage is not a valid URL.'));
return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Group edit form validation error.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
return;
} else if (User_group::descriptionTooLong($description)) {
$this->showForm(sprintf(
// TRANS: Group edit form validation error.
_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
User_group::maxDescription()),
User_group::maxDescription()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Group edit form validation error.
$this->showForm(_('Location is too long (maximum 255 characters).'));
return;
}
if (!empty($aliasstring)) {
$aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
} else {
$aliases = array();
}
if (count($aliases) > common_config('group', 'maxaliases')) {
// TRANS: Group edit form validation error.
// TRANS: %d is the maximum number of allowed aliases.
$this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')));
return;
}
foreach ($aliases as $alias) {
if (!Nickname::isValid($alias)) {
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Group edit form validation error.
$this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
$this->showForm(_('Location is too long (maximum 255 characters).'));
return;
}
if ($this->nicknameExists($alias)) {
if (!empty($aliasstring)) {
$aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
} else {
$aliases = array();
}
if (count($aliases) > common_config('group', 'maxaliases')) {
// TRANS: Group edit form validation error.
$this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
$alias));
// TRANS: %d is the maximum number of allowed aliases.
$this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')));
return;
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $nickname) == 0) {
// TRANS: Group edit form validation error.
$this->showForm(_('Alias can\'t be the same as nickname.'));
return;
foreach ($aliases as $alias) {
if (!Nickname::isValid($alias)) {
// TRANS: Group edit form validation error.
$this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
return;
}
if ($this->nicknameExists($alias)) {
// TRANS: Group edit form validation error.
$this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
$alias));
return;
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $nickname) == 0) {
// TRANS: Group edit form validation error.
$this->showForm(_('Alias can\'t be the same as nickname.'));
return;
}
}
$this->group->query('BEGIN');
$orig = clone($this->group);
$this->group->nickname = $nickname;
$this->group->fullname = $fullname;
$this->group->homepage = $homepage;
$this->group->description = $description;
$this->group->location = $location;
$this->group->mainpage = common_local_url('showgroup', array('nickname' => $nickname));
$result = $this->group->update($orig);
if (!$result) {
common_log_db_error($this->group, 'UPDATE', __FILE__);
// TRANS: Server error displayed when editing a group fails.
$this->serverError(_('Could not update group.'));
}
$result = $this->group->setAliases($aliases);
if (!$result) {
// TRANS: Server error displayed when group aliases could not be added.
$this->serverError(_('Could not create aliases.'));
}
if ($nickname != $orig->nickname) {
common_log(LOG_INFO, "Saving local group info.");
$local = Local_group::staticGet('group_id', $this->group->id);
$local->setNickname($nickname);
}
$this->group->query('COMMIT');
Event::handle('EndGroupSaveForm', array($this));
}
$this->group->query('BEGIN');
$orig = clone($this->group);
$this->group->nickname = $nickname;
$this->group->fullname = $fullname;
$this->group->homepage = $homepage;
$this->group->description = $description;
$this->group->location = $location;
$this->group->mainpage = common_local_url('showgroup', array('nickname' => $nickname));
$result = $this->group->update($orig);
if (!$result) {
common_log_db_error($this->group, 'UPDATE', __FILE__);
// TRANS: Server error displayed when editing a group fails.
$this->serverError(_('Could not update group.'));
}
$result = $this->group->setAliases($aliases);
if (!$result) {
// TRANS: Server error displayed when group aliases could not be added.
$this->serverError(_('Could not create aliases.'));
}
if ($nickname != $orig->nickname) {
common_log(LOG_INFO, "Saving local group info.");
$local = Local_group::staticGet('group_id', $this->group->id);
$local->setNickname($nickname);
}
$this->group->query('COMMIT');
if ($this->group->nickname != $orig->nickname) {
common_redirect(common_local_url('editgroup',
array('nickname' => $nickname)),

View File

@ -90,18 +90,9 @@ class InboxAction extends MailboxAction
}
}
/**
* Returns the profile we want to show with the message
*
* For inboxes, we show the sender; for outboxes, the recipient.
*
* @param Message $message The message to get the profile for
*
* @return Profile The profile that matches the message
*/
function getMessageProfile($message)
function getMessageList($message)
{
return $message->getFrom();
return new InboxMessageList($this, $message);
}
/**
@ -115,3 +106,24 @@ class InboxAction extends MailboxAction
return _('This is your inbox, which lists your incoming private messages.');
}
}
class InboxMessageList extends MessageList
{
function newItem($message)
{
return new InboxMessageListItem($this->out, $message);
}
}
class InboxMessageListItem extends MessageListItem
{
/**
* Returns the profile we want to show with the message
*
* @return Profile The profile that matches the message
*/
function getMessageProfile()
{
return $this->message->getFrom();
}
}

View File

@ -120,103 +120,109 @@ class NewgroupAction extends Action
function trySave()
{
try {
$nickname = Nickname::normalize($this->trimmed('nickname'));
} catch (NicknameException $e) {
$this->showForm($e->getMessage());
}
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
$aliasstring = $this->trimmed('aliases');
if (Event::handle('StartGroupSaveForm', array($this))) {
try {
$nickname = Nickname::normalize($this->trimmed('nickname'));
} catch (NicknameException $e) {
$this->showForm($e->getMessage());
}
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
$aliasstring = $this->trimmed('aliases');
if ($this->nicknameExists($nickname)) {
// TRANS: Group create form validation error.
$this->showForm(_('Nickname already in use. Try another one.'));
return;
} else if (!User_group::allowedNickname($nickname)) {
// TRANS: Group create form validation error.
$this->showForm(_('Not a valid nickname.'));
return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
// TRANS: Group create form validation error.
$this->showForm(_('Homepage is not a valid URL.'));
return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Group create form validation error.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
return;
} else if (User_group::descriptionTooLong($description)) {
// TRANS: Group create form validation error.
// TRANS: %d is the maximum number of allowed characters.
$this->showForm(sprintf(_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
User_group::maxDescription()),
User_group::maxDescription()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Group create form validation error.
$this->showForm(_('Location is too long (maximum 255 characters).'));
return;
}
if (!empty($aliasstring)) {
$aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
} else {
$aliases = array();
}
if (count($aliases) > common_config('group', 'maxaliases')) {
// TRANS: Group create form validation error.
// TRANS: %d is the maximum number of allowed aliases.
$this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')));
return;
}
foreach ($aliases as $alias) {
if (!Nickname::isValid($alias)) {
if ($this->nicknameExists($nickname)) {
// TRANS: Group create form validation error.
// TRANS: %s is the invalid alias.
$this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
$this->showForm(_('Nickname already in use. Try another one.'));
return;
}
if ($this->nicknameExists($alias)) {
// TRANS: Group create form validation error. %s is the already used alias.
$this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
$alias));
return;
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $nickname) == 0) {
} else if (!User_group::allowedNickname($nickname)) {
// TRANS: Group create form validation error.
$this->showForm(_('Alias cannot be the same as nickname.'));
$this->showForm(_('Not a valid nickname.'));
return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
// TRANS: Group create form validation error.
$this->showForm(_('Homepage is not a valid URL.'));
return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Group create form validation error.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
return;
} else if (User_group::descriptionTooLong($description)) {
// TRANS: Group create form validation error.
// TRANS: %d is the maximum number of allowed characters.
$this->showForm(sprintf(_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
User_group::maxDescription()),
User_group::maxDescription()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Group create form validation error.
$this->showForm(_('Location is too long (maximum 255 characters).'));
return;
}
if (!empty($aliasstring)) {
$aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
} else {
$aliases = array();
}
if (count($aliases) > common_config('group', 'maxaliases')) {
// TRANS: Group create form validation error.
// TRANS: %d is the maximum number of allowed aliases.
$this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')));
return;
}
foreach ($aliases as $alias) {
if (!Nickname::isValid($alias)) {
// TRANS: Group create form validation error.
// TRANS: %s is the invalid alias.
$this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
return;
}
if ($this->nicknameExists($alias)) {
// TRANS: Group create form validation error. %s is the already used alias.
$this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
$alias));
return;
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $nickname) == 0) {
// TRANS: Group create form validation error.
$this->showForm(_('Alias cannot be the same as nickname.'));
return;
}
}
$cur = common_current_user();
// Checked in prepare() above
assert(!is_null($cur));
$group = User_group::register(array('nickname' => $nickname,
'fullname' => $fullname,
'homepage' => $homepage,
'description' => $description,
'location' => $location,
'aliases' => $aliases,
'userid' => $cur->id,
'local' => true));
$this->group = $group;
Event::handle('EndGroupSaveForm', array($this));
common_redirect($group->homeUrl(), 303);
}
$cur = common_current_user();
// Checked in prepare() above
assert(!is_null($cur));
$group = User_group::register(array('nickname' => $nickname,
'fullname' => $fullname,
'homepage' => $homepage,
'description' => $description,
'location' => $location,
'aliases' => $aliases,
'userid' => $cur->id,
'local' => true));
common_redirect($group->homeUrl(), 303);
}
function nicknameExists($nickname)

View File

@ -88,21 +88,9 @@ class OutboxAction extends MailboxAction
}
}
/**
* returns the profile we want to show with the message
*
* For outboxes, we show the recipient.
*
* @param Message $message The message to get the profile for
*
* @return Profile The profile of the message recipient
*
* @see MailboxAction::getMessageProfile()
*/
function getMessageProfile($message)
function getMessageList($message)
{
return $message->getTo();
return new OutboxMessageList($this, $message);
}
/**
@ -116,3 +104,24 @@ class OutboxAction extends MailboxAction
return _('This is your outbox, which lists private messages you have sent.');
}
}
class OutboxMessageList extends MessageList
{
function newItem($message)
{
return new OutboxMessageListItem($this->out, $message);
}
}
class OutboxMessageListItem extends MessageListItem
{
/**
* Returns the profile we want to show with the message
*
* @return Profile The profile that matches the message
*/
function getMessageProfile()
{
return $this->message->getTo();
}
}

View File

@ -181,6 +181,7 @@ class ShowgroupAction extends GroupDesignAction
function showContent()
{
$this->showGroupProfile();
$this->showGroupActions();
$this->showGroupNotices();
}
@ -216,112 +217,123 @@ class ShowgroupAction extends GroupDesignAction
$this->elementStart('div', array('id' => 'i',
'class' => 'entity_profile vcard author'));
// TRANS: Group profile header (h2). Text hidden by default.
$this->element('h2', null, _('Group profile'));
if (Event::handle('StartGroupProfileElements', array($this, $this->group))) {
$this->elementStart('dl', 'entity_depiction');
// TRANS: Label for group avatar (dt). Text hidden by default.
$this->element('dt', null, _('Avatar'));
$this->elementStart('dd');
// TRANS: Group profile header (h2). Text hidden by default.
$this->element('h2', null, _('Group profile'));
$logo = ($this->group->homepage_logo) ?
$this->group->homepage_logo : User_group::defaultLogo(AVATAR_PROFILE_SIZE);
$this->element('img', array('src' => $logo,
'class' => 'photo avatar',
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $this->group->nickname));
$this->elementEnd('dd');
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_nickname');
// TRANS: Label for group nickname (dt). Text hidden by default.
$this->element('dt', null, _('Nickname'));
$this->elementStart('dd');
$hasFN = ($this->group->fullname) ? 'nickname url uid' : 'fn org nickname url uid';
$this->element('a', array('href' => $this->group->homeUrl(),
'rel' => 'me', 'class' => $hasFN),
$this->group->nickname);
$this->elementEnd('dd');
$this->elementEnd('dl');
if ($this->group->fullname) {
$this->elementStart('dl', 'entity_fn');
// TRANS: Label for full group name (dt). Text hidden by default.
$this->element('dt', null, _('Full name'));
$this->elementStart('dl', 'entity_depiction');
// TRANS: Label for group avatar (dt). Text hidden by default.
$this->element('dt', null, _('Avatar'));
$this->elementStart('dd');
$this->element('span', 'fn org', $this->group->fullname);
$logo = ($this->group->homepage_logo) ?
$this->group->homepage_logo : User_group::defaultLogo(AVATAR_PROFILE_SIZE);
$this->element('img', array('src' => $logo,
'class' => 'photo avatar',
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $this->group->nickname));
$this->elementEnd('dd');
$this->elementEnd('dl');
}
if ($this->group->location) {
$this->elementStart('dl', 'entity_location');
// TRANS: Label for group location (dt). Text hidden by default.
$this->element('dt', null, _('Location'));
$this->element('dd', 'label', $this->group->location);
$this->elementEnd('dl');
}
if ($this->group->homepage) {
$this->elementStart('dl', 'entity_url');
// TRANS: Label for group URL (dt). Text hidden by default.
$this->element('dt', null, _('URL'));
$this->elementStart('dl', 'entity_nickname');
// TRANS: Label for group nickname (dt). Text hidden by default.
$this->element('dt', null, _('Nickname'));
$this->elementStart('dd');
$this->element('a', array('href' => $this->group->homepage,
'rel' => 'me', 'class' => 'url'),
$this->group->homepage);
$hasFN = ($this->group->fullname) ? 'nickname url uid' : 'fn org nickname url uid';
$this->element('a', array('href' => $this->group->homeUrl(),
'rel' => 'me', 'class' => $hasFN),
$this->group->nickname);
$this->elementEnd('dd');
$this->elementEnd('dl');
}
if ($this->group->description) {
$this->elementStart('dl', 'entity_note');
// TRANS: Label for group description or group note (dt). Text hidden by default.
$this->element('dt', null, _('Note'));
$this->element('dd', 'note', $this->group->description);
$this->elementEnd('dl');
}
if (common_config('group', 'maxaliases') > 0) {
$aliases = $this->group->getAliases();
if (!empty($aliases)) {
$this->elementStart('dl', 'entity_aliases');
// TRANS: Label for group aliases (dt). Text hidden by default.
$this->element('dt', null, _('Aliases'));
$this->element('dd', 'aliases', implode(' ', $aliases));
if ($this->group->fullname) {
$this->elementStart('dl', 'entity_fn');
// TRANS: Label for full group name (dt). Text hidden by default.
$this->element('dt', null, _('Full name'));
$this->elementStart('dd');
$this->element('span', 'fn org', $this->group->fullname);
$this->elementEnd('dd');
$this->elementEnd('dl');
}
if ($this->group->location) {
$this->elementStart('dl', 'entity_location');
// TRANS: Label for group location (dt). Text hidden by default.
$this->element('dt', null, _('Location'));
$this->element('dd', 'label', $this->group->location);
$this->elementEnd('dl');
}
if ($this->group->homepage) {
$this->elementStart('dl', 'entity_url');
// TRANS: Label for group URL (dt). Text hidden by default.
$this->element('dt', null, _('URL'));
$this->elementStart('dd');
$this->element('a', array('href' => $this->group->homepage,
'rel' => 'me', 'class' => 'url'),
$this->group->homepage);
$this->elementEnd('dd');
$this->elementEnd('dl');
}
if ($this->group->description) {
$this->elementStart('dl', 'entity_note');
// TRANS: Label for group description or group note (dt). Text hidden by default.
$this->element('dt', null, _('Note'));
$this->element('dd', 'note', $this->group->description);
$this->elementEnd('dl');
}
if (common_config('group', 'maxaliases') > 0) {
$aliases = $this->group->getAliases();
if (!empty($aliases)) {
$this->elementStart('dl', 'entity_aliases');
// TRANS: Label for group aliases (dt). Text hidden by default.
$this->element('dt', null, _('Aliases'));
$this->element('dd', 'aliases', implode(' ', $aliases));
$this->elementEnd('dl');
}
}
Event::handle('EndGroupProfileElements', array($this, $this->group));
}
$this->elementEnd('div');
}
function showGroupActions()
{
$cur = common_current_user();
$this->elementStart('div', 'entity_actions');
// TRANS: Group actions header (h2). Text hidden by default.
$this->element('h2', null, _('Group actions'));
$this->elementStart('ul');
$this->elementStart('li', 'entity_subscribe');
if (Event::handle('StartGroupSubscribe', array($this, $this->group))) {
if ($cur) {
if ($cur->isMember($this->group)) {
$lf = new LeaveForm($this, $this->group);
$lf->show();
} else if (!Group_block::isBlocked($this->group, $cur->getProfile())) {
$jf = new JoinForm($this, $this->group);
$jf->show();
if (Event::handle('StartGroupActionsList', array($this, $this->group))) {
$this->elementStart('li', 'entity_subscribe');
if (Event::handle('StartGroupSubscribe', array($this, $this->group))) {
if ($cur) {
if ($cur->isMember($this->group)) {
$lf = new LeaveForm($this, $this->group);
$lf->show();
} else if (!Group_block::isBlocked($this->group, $cur->getProfile())) {
$jf = new JoinForm($this, $this->group);
$jf->show();
}
}
Event::handle('EndGroupSubscribe', array($this, $this->group));
}
Event::handle('EndGroupSubscribe', array($this, $this->group));
}
$this->elementEnd('li');
if ($cur && $cur->hasRight(Right::DELETEGROUP)) {
$this->elementStart('li', 'entity_delete');
$df = new DeleteGroupForm($this, $this->group);
$df->show();
$this->elementEnd('li');
if ($cur && $cur->hasRight(Right::DELETEGROUP)) {
$this->elementStart('li', 'entity_delete');
$df = new DeleteGroupForm($this, $this->group);
$df->show();
$this->elementEnd('li');
}
Event::handle('EndGroupActionsList', array($this, $this->group));
}
$this->elementEnd('ul');
$this->elementEnd('div');

View File

@ -30,20 +30,17 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/mailbox.php';
/**
* Show a single message
*
* // XXX: It is totally weird how this works!
*
* @category Personal
* @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 ShowmessageAction extends MailboxAction
class ShowmessageAction extends Action
{
/**
* Message object to show
@ -82,22 +79,20 @@ class ShowmessageAction extends MailboxAction
$this->user = common_current_user();
if (empty($this->user) ||
($this->user->id != $this->message->from_profile &&
$this->user->id != $this->message->to_profile)) {
// TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
throw new ClientException(_('Only the sender and recipient ' .
'may read this message.'), 403);
}
return true;
}
function handle($args)
{
Action::handle($args);
if ($this->user && ($this->user->id == $this->message->from_profile ||
$this->user->id == $this->message->to_profile)) {
$this->showPage();
} else {
// TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
$this->clientError(_('Only the sender and recipient ' .
'may read this message.'), 403);
return;
}
$this->showPage();
}
function title()
@ -121,12 +116,38 @@ class ShowmessageAction extends MailboxAction
}
}
function getMessages()
function showContent()
{
$message = new Message();
$message->id = $this->message->id;
$message->find();
return $message;
$this->elementStart('ul', 'notices messages');
$ml = new ShowMessageListItem($this, $this->message, $this->user);
$ml->show();
$this->elementEnd('ul');
}
function isReadOnly($args)
{
return true;
}
/**
* Don't show aside
*
* @return void
*/
function showAside() {
}
}
class ShowMessageListItem extends MessageListItem
{
var $user;
function __construct($out, $message, $user)
{
parent::__construct($out, $message);
$this->user = $user;
}
function getMessageProfile()
@ -140,46 +161,4 @@ class ShowmessageAction extends MailboxAction
return null;
}
}
/**
* Don't show local navigation
*
* @return void
*/
function showLocalNavBlock()
{
}
/**
* Don't show page notice
*
* @return void
*/
function showPageNoticeBlock()
{
}
/**
* Don't show aside
*
* @return void
*/
function showAside()
{
}
/**
* Don't show any instructions
*
* @return string
*/
function getInstructions()
{
return '';
}
function isReadOnly($args)
{
return true;
}
}

View File

@ -55,14 +55,20 @@ class File extends Memcached_DataObject
return 'http://www.facebook.com/login.php' === $url;
}
function getAttachments($post_id) {
$query = "select file.* from file join file_to_post on (file_id = file.id) join notice on (post_id = notice.id) where post_id = " . $this->escape($post_id);
$this->query($query);
/**
* Get the attachments for a particlar notice.
*
* @param int $post_id
* @return array of File objects
*/
static function getAttachments($post_id) {
$file = new File();
$query = "select file.* from file join file_to_post on (file_id = file.id) where post_id = " . $file->escape($post_id);
$file = Memcached_DataObject::cachedQuery('File', $query);
$att = array();
while ($this->fetch()) {
$att[] = clone($this);
while ($file->fetch()) {
$att[] = clone($file);
}
$this->free();
return $att;
}

View File

@ -340,6 +340,7 @@ class Memcached_DataObject extends Safe_DataObject
$start = microtime(true);
$result = null;
if (Event::handle('StartDBQuery', array($this, $string, &$result))) {
common_perf_counter('query', $string);
$result = parent::_query($string);
Event::handle('EndDBQuery', array($this, $string, &$result));
}

View File

@ -446,7 +446,10 @@ class Notice extends Memcached_DataObject
function blowOnInsert($conversation = false)
{
self::blow('profile:notice_ids:%d', $this->profile_id);
self::blow('public');
if ($this->isPublic()) {
self::blow('public');
}
// XXX: Before we were blowing the casche only if the notice id
// was not the root of the conversation. What to do now?
@ -481,7 +484,10 @@ class Notice extends Memcached_DataObject
$this->blowOnInsert();
self::blow('profile:notice_ids:%d;last', $this->profile_id);
self::blow('public;last');
if ($this->isPublic()) {
self::blow('public;last');
}
}
/** save all urls in the notice to the db
@ -958,7 +964,7 @@ class Notice extends Memcached_DataObject
$groups = array();
/* extract all !group */
$count = preg_match_all('/(?:^|\s)!([A-Za-z0-9]{1,64})/',
$count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
strtolower($this->content),
$match);
if (!$count) {
@ -2107,4 +2113,14 @@ class Notice extends Memcached_DataObject
$obj->whereAdd($max);
}
}
function isPublic()
{
if (common_config('public', 'localonly')) {
return ($this->is_local == Notice::LOCAL_PUBLIC);
} else {
return (($this->is_local != Notice::LOCAL_NONPUBLIC) &&
($this->is_local != Notice::GATEWAY));
}
}
}

View File

@ -918,6 +918,31 @@ class Profile extends Memcached_DataObject
return $xs->getString();
}
/**
* Extra profile info for atom entries
*
* Clients use some extra profile info in the atom stream.
* This gives it to them.
*
* @param User $cur Current user
*
* @return array representation of <statusnet:profile_info> element
*/
function profileInfo($cur)
{
$profileInfoAttr = array();
if ($cur != null) {
// Whether the current user is a subscribed to this profile
$profileInfoAttr['following'] = $cur->isSubscribed($this) ? 'true' : 'false';
// Whether the current user is has blocked this profile
$profileInfoAttr['blocking'] = $cur->hasBlocked($this) ? 'true' : 'false';
}
return array('statusnet:profile_info', $profileInfoAttr, null);
}
/**
* Returns an XML string fragment with profile information as an
* Activity Streams <activity:actor> element.

View File

@ -156,6 +156,13 @@ class Session extends Memcached_DataObject
$session->selectAdd();
$session->selectAdd('id');
$limit = common_config('sessions', 'gc_limit');
if ($limit > 0) {
// On large sites, too many sessions to expire
// at once will just result in failure.
$session->limit($limit);
}
$session->find();
while ($session->fetch()) {

View File

@ -512,64 +512,70 @@ class User_group extends Memcached_DataObject
$group->mainpage = $mainpage;
$group->created = common_sql_now();
$result = $group->insert();
if (Event::handle('StartGroupSave', array(&$group))) {
if (!$result) {
common_log_db_error($group, 'INSERT', __FILE__);
// TRANS: Server exception thrown when creating a group failed.
throw new ServerException(_('Could not create group.'));
}
if (!isset($uri) || empty($uri)) {
$orig = clone($group);
$group->uri = common_local_url('groupbyid', array('id' => $group->id));
$result = $group->update($orig);
if (!$result) {
common_log_db_error($group, 'UPDATE', __FILE__);
// TRANS: Server exception thrown when updating a group URI failed.
throw new ServerException(_('Could not set group URI.'));
}
}
$result = $group->setAliases($aliases);
if (!$result) {
// TRANS: Server exception thrown when creating group aliases failed.
throw new ServerException(_('Could not create aliases.'));
}
$member = new Group_member();
$member->group_id = $group->id;
$member->profile_id = $userid;
$member->is_admin = 1;
$member->created = $group->created;
$result = $member->insert();
if (!$result) {
common_log_db_error($member, 'INSERT', __FILE__);
// TRANS: Server exception thrown when setting group membership failed.
throw new ServerException(_('Could not set group membership.'));
}
if ($local) {
$local_group = new Local_group();
$local_group->group_id = $group->id;
$local_group->nickname = $nickname;
$local_group->created = common_sql_now();
$result = $local_group->insert();
$result = $group->insert();
if (!$result) {
common_log_db_error($local_group, 'INSERT', __FILE__);
// TRANS: Server exception thrown when saving local group information failed.
throw new ServerException(_('Could not save local group info.'));
common_log_db_error($group, 'INSERT', __FILE__);
// TRANS: Server exception thrown when creating a group failed.
throw new ServerException(_('Could not create group.'));
}
if (!isset($uri) || empty($uri)) {
$orig = clone($group);
$group->uri = common_local_url('groupbyid', array('id' => $group->id));
$result = $group->update($orig);
if (!$result) {
common_log_db_error($group, 'UPDATE', __FILE__);
// TRANS: Server exception thrown when updating a group URI failed.
throw new ServerException(_('Could not set group URI.'));
}
}
$result = $group->setAliases($aliases);
if (!$result) {
// TRANS: Server exception thrown when creating group aliases failed.
throw new ServerException(_('Could not create aliases.'));
}
$member = new Group_member();
$member->group_id = $group->id;
$member->profile_id = $userid;
$member->is_admin = 1;
$member->created = $group->created;
$result = $member->insert();
if (!$result) {
common_log_db_error($member, 'INSERT', __FILE__);
// TRANS: Server exception thrown when setting group membership failed.
throw new ServerException(_('Could not set group membership.'));
}
if ($local) {
$local_group = new Local_group();
$local_group->group_id = $group->id;
$local_group->nickname = $nickname;
$local_group->created = common_sql_now();
$result = $local_group->insert();
if (!$result) {
common_log_db_error($local_group, 'INSERT', __FILE__);
// TRANS: Server exception thrown when saving local group information failed.
throw new ServerException(_('Could not save local group info.'));
}
}
$group->query('COMMIT');
Event::handle('EndGroupSave', array($group));
}
$group->query('COMMIT');
return $group;
}

104
extlib/Auth/SASL.php Normal file
View File

@ -0,0 +1,104 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id: SASL.php 286825 2009-08-05 06:23:42Z cweiske $
/**
* Client implementation of various SASL mechanisms
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
require_once('PEAR.php');
class Auth_SASL
{
/**
* Factory class. Returns an object of the request
* type.
*
* @param string $type One of: Anonymous
* Plain
* CramMD5
* DigestMD5
* Types are not case sensitive
*/
function &factory($type)
{
switch (strtolower($type)) {
case 'anonymous':
$filename = 'Auth/SASL/Anonymous.php';
$classname = 'Auth_SASL_Anonymous';
break;
case 'login':
$filename = 'Auth/SASL/Login.php';
$classname = 'Auth_SASL_Login';
break;
case 'plain':
$filename = 'Auth/SASL/Plain.php';
$classname = 'Auth_SASL_Plain';
break;
case 'external':
$filename = 'Auth/SASL/External.php';
$classname = 'Auth_SASL_External';
break;
case 'crammd5':
$filename = 'Auth/SASL/CramMD5.php';
$classname = 'Auth_SASL_CramMD5';
break;
case 'digestmd5':
$filename = 'Auth/SASL/DigestMD5.php';
$classname = 'Auth_SASL_DigestMD5';
break;
default:
return PEAR::raiseError('Invalid SASL mechanism type');
break;
}
require_once($filename);
$obj = new $classname();
return $obj;
}
}
?>

View File

@ -0,0 +1,71 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id: Anonymous.php 286825 2009-08-05 06:23:42Z cweiske $
/**
* Implmentation of ANONYMOUS SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
require_once('Auth/SASL/Common.php');
class Auth_SASL_Anonymous extends Auth_SASL_Common
{
/**
* Not much to do here except return the token supplied.
* No encoding, hashing or encryption takes place for this
* mechanism, simply one of:
* o An email address
* o An opaque string not containing "@" that can be interpreted
* by the sysadmin
* o Nothing
*
* We could have some logic here for the second option, but this
* would by no means create something interpretable.
*
* @param string $token Optional email address or string to provide
* as trace information.
* @return string The unaltered input token
*/
function getResponse($token = '')
{
return $token;
}
}
?>

View File

@ -0,0 +1,74 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id: Common.php 286825 2009-08-05 06:23:42Z cweiske $
/**
* Common functionality to SASL mechanisms
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
class Auth_SASL_Common
{
/**
* Function which implements HMAC MD5 digest
*
* @param string $key The secret key
* @param string $data The data to protect
* @return string The HMAC MD5 digest
*/
function _HMAC_MD5($key, $data)
{
if (strlen($key) > 64) {
$key = pack('H32', md5($key));
}
if (strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}
$k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
$k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
$inner = pack('H32', md5($k_ipad . $data));
$digest = md5($k_opad . $inner);
return $digest;
}
}
?>

View File

@ -0,0 +1,68 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id: CramMD5.php 286825 2009-08-05 06:23:42Z cweiske $
/**
* Implmentation of CRAM-MD5 SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
require_once('Auth/SASL/Common.php');
class Auth_SASL_CramMD5 extends Auth_SASL_Common
{
/**
* Implements the CRAM-MD5 SASL mechanism
* This DOES NOT base64 encode the return value,
* you will need to do that yourself.
*
* @param string $user Username
* @param string $pass Password
* @param string $challenge The challenge supplied by the server.
* this should be already base64_decoded.
*
* @return string The string to pass back to the server, of the form
* "<user> <digest>". This is NOT base64_encoded.
*/
function getResponse($user, $pass, $challenge)
{
return $user . ' ' . $this->_HMAC_MD5($pass, $challenge);
}
}
?>

View File

@ -0,0 +1,197 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id: DigestMD5.php 294702 2010-02-07 16:03:55Z cweiske $
/**
* Implmentation of DIGEST-MD5 SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
require_once('Auth/SASL/Common.php');
class Auth_SASL_DigestMD5 extends Auth_SASL_Common
{
/**
* Provides the (main) client response for DIGEST-MD5
* requires a few extra parameters than the other
* mechanisms, which are unavoidable.
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $challenge The digest challenge sent by the server
* @param string $hostname The hostname of the machine you're connecting to
* @param string $service The servicename (eg. imap, pop, acap etc)
* @param string $authzid Authorization id (username to proxy as)
* @return string The digest response (NOT base64 encoded)
* @access public
*/
function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '')
{
$challenge = $this->_parseChallenge($challenge);
$authzid_string = '';
if ($authzid != '') {
$authzid_string = ',authzid="' . $authzid . '"';
}
if (!empty($challenge)) {
$cnonce = $this->_getCnonce();
$digest_uri = sprintf('%s/%s', $service, $hostname);
$response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid);
if ($challenge['realm']) {
return sprintf('username="%s",realm="%s"' . $authzid_string .
',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
} else {
return sprintf('username="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
}
} else {
return PEAR::raiseError('Invalid digest challenge');
}
}
/**
* Parses and verifies the digest challenge*
*
* @param string $challenge The digest challenge
* @return array The parsed challenge as an assoc
* array in the form "directive => value".
* @access private
*/
function _parseChallenge($challenge)
{
$tokens = array();
while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches)) {
// Ignore these as per rfc2831
if ($matches[1] == 'opaque' OR $matches[1] == 'domain') {
$challenge = substr($challenge, strlen($matches[0]) + 1);
continue;
}
// Allowed multiple "realm" and "auth-param"
if (!empty($tokens[$matches[1]]) AND ($matches[1] == 'realm' OR $matches[1] == 'auth-param')) {
if (is_array($tokens[$matches[1]])) {
$tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
} else {
$tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
}
// Any other multiple instance = failure
} elseif (!empty($tokens[$matches[1]])) {
$tokens = array();
break;
} else {
$tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
}
// Remove the just parsed directive from the challenge
$challenge = substr($challenge, strlen($matches[0]) + 1);
}
/**
* Defaults and required directives
*/
// Realm
if (empty($tokens['realm'])) {
$tokens['realm'] = "";
}
// Maxbuf
if (empty($tokens['maxbuf'])) {
$tokens['maxbuf'] = 65536;
}
// Required: nonce, algorithm
if (empty($tokens['nonce']) OR empty($tokens['algorithm'])) {
return array();
}
return $tokens;
}
/**
* Creates the response= part of the digest response
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $realm Realm as provided by the server
* @param string $nonce Nonce as provided by the server
* @param string $cnonce Client nonce
* @param string $digest_uri The digest-uri= value part of the response
* @param string $authzid Authorization id
* @return string The response= part of the digest response
* @access private
*/
function _getResponseValue($authcid, $pass, $realm, $nonce, $cnonce, $digest_uri, $authzid = '')
{
if ($authzid == '') {
$A1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce);
} else {
$A1 = sprintf('%s:%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce, $authzid);
}
$A2 = 'AUTHENTICATE:' . $digest_uri;
return md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($A1), $nonce, $cnonce, md5($A2)));
}
/**
* Creates the client nonce for the response
*
* @return string The cnonce value
* @access private
*/
function _getCnonce()
{
if (@file_exists('/dev/urandom') && $fd = @fopen('/dev/urandom', 'r')) {
return base64_encode(fread($fd, 32));
} elseif (@file_exists('/dev/random') && $fd = @fopen('/dev/random', 'r')) {
return base64_encode(fread($fd, 32));
} else {
$str = '';
for ($i=0; $i<32; $i++) {
$str .= chr(mt_rand(0, 255));
}
return base64_encode($str);
}
}
}
?>

View File

@ -0,0 +1,63 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2008 Christoph Schulz |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Christoph Schulz <develop@kristov.de> |
// +-----------------------------------------------------------------------+
//
// $Id: External.php 286825 2009-08-05 06:23:42Z cweiske $
/**
* Implmentation of EXTERNAL SASL mechanism
*
* @author Christoph Schulz <develop@kristov.de>
* @access public
* @version 1.0.3
* @package Auth_SASL
*/
require_once('Auth/SASL/Common.php');
class Auth_SASL_External extends Auth_SASL_Common
{
/**
* Returns EXTERNAL response
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $authzid Autorization id
* @return string EXTERNAL Response
*/
function getResponse($authcid, $pass, $authzid = '')
{
return $authzid;
}
}
?>

View File

@ -0,0 +1,65 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id: Login.php 286825 2009-08-05 06:23:42Z cweiske $
/**
* This is technically not a SASL mechanism, however
* it's used by Net_Sieve, Net_Cyrus and potentially
* other protocols , so here is a good place to abstract
* it.
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
require_once('Auth/SASL/Common.php');
class Auth_SASL_Login extends Auth_SASL_Common
{
/**
* Pseudo SASL LOGIN mechanism
*
* @param string $user Username
* @param string $pass Password
* @return string LOGIN string
*/
function getResponse($user, $pass)
{
return sprintf('LOGIN %s %s', $user, $pass);
}
}
?>

View File

@ -0,0 +1,63 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id: Plain.php 286825 2009-08-05 06:23:42Z cweiske $
/**
* Implmentation of PLAIN SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
require_once('Auth/SASL/Common.php');
class Auth_SASL_Plain extends Auth_SASL_Common
{
/**
* Returns PLAIN response
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $authzid Autorization id
* @return string PLAIN Response
*/
function getResponse($authcid, $pass, $authzid = '')
{
return $authzid . chr(0) . $authcid . chr(0) . $pass;
}
}
?>

View File

@ -38,6 +38,7 @@
*/
$_startTime = microtime(true);
$_perfCounters = array();
define('INSTALLDIR', dirname(__FILE__));
define('STATUSNET', true);
@ -45,6 +46,8 @@ define('LACONICA', true); // compatibility
require_once INSTALLDIR . '/lib/common.php';
register_shutdown_function('common_log_perf_counters');
$user = null;
$action = null;

File diff suppressed because it is too large Load Diff

12
js/jquery.form.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -392,6 +392,18 @@ class Activity
if ($author) {
$this->actor->outputTo($xs, 'author');
// XXX: Remove <activity:actor> ASAP! Author information
// has been moved to the author element in the Activity
// Streams spec. We're outputting actor only for backward
// compatibility with clients that can only parse
// activities based on older versions of the spec.
$depMsg = 'Deprecation warning: activity:actor is present '
. 'only for backward compatibility. It will be '
. 'removed in the next version of StatusNet.';
$xs->comment($depMsg);
$this->actor->outputTo($xs, 'activity:actor');
}
if ($this->verb != ActivityVerb::POST || count($this->objects) != 1) {

View File

@ -263,8 +263,7 @@ class ApiAction extends Action
? Design::url($design->backgroundimage) : '';
$twitter_user['profile_background_tile']
= empty($design->disposition)
? '' : ($design->disposition & BACKGROUND_TILE) ? 'true' : 'false';
= (bool)($design->disposition & BACKGROUND_TILE);
$twitter_user['statuses_count'] = $profile->noticeCount();
@ -1236,9 +1235,12 @@ class ApiAction extends Action
return;
}
function clientError($msg, $code = 400, $format = 'xml')
function clientError($msg, $code = 400, $format = null)
{
$action = $this->trimmed('action');
if ($format === null) {
$format = $this->format;
}
common_debug("User error '$code' on '$action': $msg", __FILE__);
@ -1278,9 +1280,12 @@ class ApiAction extends Action
}
}
function serverError($msg, $code = 500, $content_type = 'xml')
function serverError($msg, $code = 500, $content_type = null)
{
$action = $this->trimmed('action');
if ($content_type === null) {
$content_type = $this->format;
}
common_debug("Server error '$code' on '$action': $msg", __FILE__);

View File

@ -91,8 +91,16 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed
$ao = ActivityObject::fromGroup($group);
$this->addAuthorRaw($ao->asString('author').
$ao->asString('activity:subject'));
$this->addAuthorRaw($ao->asString('author'));
$depMsg = 'Deprecation warning: activity:subject is present '
. 'only for backward compatibility. It will be '
. 'removed in the next version of StatusNet.';
$this->addAuthorRaw(
"<!--$depMsg-->\n"
. $ao->asString('activity:subject')
);
$this->addLink($group->homeUrl());
}

View File

@ -59,9 +59,29 @@ class AtomUserNoticeFeed extends AtomNoticeFeed
parent::__construct($cur, $indent);
$this->user = $user;
if (!empty($user)) {
$profile = $user->getProfile();
$ao = ActivityObject::fromProfile($profile);
$ao->extra[] = $profile->profileInfo($cur);
// XXX: For users, we generate an author _AND_ an <activity:subject>
// This is for backward compatibility with clients (especially
// StatusNet's clients) that assume the Atom will conform to an
// older version of the Activity Streams API. Subject should be
// removed in future versions of StatusNet.
$this->addAuthorRaw($ao->asString('author'));
$depMsg = 'Deprecation warning: activity:subject is present '
. 'only for backward compatibility. It will be '
. 'removed in the next version of StatusNet.';
$this->addAuthorRaw(
"<!--$depMsg-->\n"
. $ao->asString('activity:subject')
);
}
// TRANS: Title in atom user notice feed. %s is a user name.

View File

@ -76,8 +76,7 @@ class AttachmentList extends Widget
*/
function show()
{
$atts = new File;
$att = $atts->getAttachments($this->notice->id);
$att = File::getAttachments($this->notice->id);
if (empty($att)) return 0;
$this->showListStart();

View File

@ -164,6 +164,7 @@ class Cache
{
$value = false;
common_perf_counter('Cache::get', $key);
if (Event::handle('StartCacheGet', array(&$key, &$value))) {
if (array_key_exists($key, $this->_items)) {
$value = unserialize($this->_items[$key]);
@ -188,6 +189,7 @@ class Cache
{
$success = false;
common_perf_counter('Cache::set', $key);
if (Event::handle('StartCacheSet', array(&$key, &$value, &$flag,
&$expiry, &$success))) {
@ -214,6 +216,7 @@ class Cache
function increment($key, $step=1)
{
$value = false;
common_perf_counter('Cache::increment', $key);
if (Event::handle('StartCacheIncrement', array(&$key, &$step, &$value))) {
// Fallback is not guaranteed to be atomic,
// and may original expiry value.
@ -239,6 +242,7 @@ class Cache
{
$success = false;
common_perf_counter('Cache::delete', $key);
if (Event::handle('StartCacheDelete', array(&$key, &$success))) {
if (array_key_exists($key, $this->_items)) {
unset($this->_items[$key]);

View File

@ -25,252 +25,287 @@ class CommandInterpreter
{
function handle_command($user, $text)
{
# XXX: localise
// XXX: localise
$text = preg_replace('/\s+/', ' ', trim($text));
list($cmd, $arg) = $this->split_arg($text);
# We try to support all the same commands as Twitter, see
# http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
# There are a few compatibility commands from earlier versions of
# StatusNet
// We try to support all the same commands as Twitter, see
// http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
// There are a few compatibility commands from earlier versions of
// StatusNet
switch(strtolower($cmd)) {
case 'help':
if ($arg) {
return null;
}
return new HelpCommand($user);
case 'login':
if ($arg) {
return null;
} else {
return new LoginCommand($user);
}
case 'lose':
if ($arg) {
$cmd = strtolower($cmd);
if (Event::handle('StartIntepretCommand', array($cmd, $arg, $user, &$result))) {
switch($cmd) {
case 'help':
if ($arg) {
$result = null;
}
$result = new HelpCommand($user);
break;
case 'login':
if ($arg) {
$result = null;
} else {
$result = new LoginCommand($user);
}
break;
case 'lose':
if ($arg) {
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new LoseCommand($user, $other);
}
} else {
$result = null;
}
break;
case 'subscribers':
if ($arg) {
$result = null;
} else {
$result = new SubscribersCommand($user);
}
break;
case 'subscriptions':
if ($arg) {
$result = null;
} else {
$result = new SubscriptionsCommand($user);
}
break;
case 'groups':
if ($arg) {
$result = null;
} else {
$result = new GroupsCommand($user);
}
break;
case 'on':
if ($arg) {
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new OnCommand($user, $other);
}
} else {
$result = new OnCommand($user);
}
break;
case 'off':
if ($arg) {
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new OffCommand($user, $other);
}
} else {
$result = new OffCommand($user);
}
break;
case 'stop':
case 'quit':
if ($arg) {
$result = null;
} else {
$result = new OffCommand($user);
}
break;
case 'join':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
$result = null;
} else {
return new LoseCommand($user, $other);
$result = new JoinCommand($user, $other);
}
break;
case 'drop':
if (!$arg) {
$result = null;
}
} else {
return null;
}
case 'subscribers':
if ($arg) {
return null;
} else {
return new SubscribersCommand($user);
}
case 'subscriptions':
if ($arg) {
return null;
} else {
return new SubscriptionsCommand($user);
}
case 'groups':
if ($arg) {
return null;
} else {
return new GroupsCommand($user);
}
case 'on':
if ($arg) {
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
$result = null;
} else {
return new OnCommand($user, $other);
$result = new DropCommand($user, $other);
}
} else {
return new OnCommand($user);
}
case 'off':
if ($arg) {
break;
case 'follow':
case 'sub':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
$result = null;
} else {
return new OffCommand($user, $other);
$result = new SubCommand($user, $other);
}
} else {
return new OffCommand($user);
break;
case 'leave':
case 'unsub':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new UnsubCommand($user, $other);
}
break;
case 'get':
case 'last':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new GetCommand($user, $other);
}
break;
case 'd':
case 'dm':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if (!$extra) {
$result = null;
} else {
$result = new MessageCommand($user, $other, $extra);
}
break;
case 'r':
case 'reply':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if (!$extra) {
$result = null;
} else {
$result = new ReplyCommand($user, $other, $extra);
}
break;
case 'repeat':
case 'rp':
case 'rt':
case 'rd':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new RepeatCommand($user, $other);
}
break;
case 'whois':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new WhoisCommand($user, $other);
}
break;
case 'fav':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new FavCommand($user, $other);
}
break;
case 'nudge':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new NudgeCommand($user, $other);
}
break;
case 'stats':
if ($arg) {
$result = null;
}
$result = new StatsCommand($user);
break;
case 'invite':
if (!$arg) {
$result = null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new InviteCommand($user, $other);
}
break;
case 'track':
if (!$arg) {
$result = null;
}
list($word, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else if ($word == 'off') {
$result = new TrackOffCommand($user);
} else {
$result = new TrackCommand($user, $word);
}
break;
case 'untrack':
if (!$arg) {
$result = null;
}
list($word, $extra) = $this->split_arg($arg);
if ($extra) {
$result = null;
} else if ($word == 'all') {
$result = new TrackOffCommand($user);
} else {
$result = new UntrackCommand($user, $word);
}
break;
case 'tracks':
case 'tracking':
if ($arg) {
$result = null;
}
$result = new TrackingCommand($user);
break;
default:
$result = false;
}
case 'stop':
case 'quit':
if ($arg) {
return null;
} else {
return new OffCommand($user);
}
case 'join':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new JoinCommand($user, $other);
}
case 'drop':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new DropCommand($user, $other);
}
case 'follow':
case 'sub':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new SubCommand($user, $other);
}
case 'leave':
case 'unsub':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new UnsubCommand($user, $other);
}
case 'get':
case 'last':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new GetCommand($user, $other);
}
case 'd':
case 'dm':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if (!$extra) {
return null;
} else {
return new MessageCommand($user, $other, $extra);
}
case 'r':
case 'reply':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if (!$extra) {
return null;
} else {
return new ReplyCommand($user, $other, $extra);
}
case 'repeat':
case 'rp':
case 'rt':
case 'rd':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new RepeatCommand($user, $other);
}
case 'whois':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new WhoisCommand($user, $other);
}
case 'fav':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new FavCommand($user, $other);
}
case 'nudge':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new NudgeCommand($user, $other);
}
case 'stats':
if ($arg) {
return null;
}
return new StatsCommand($user);
case 'invite':
if (!$arg) {
return null;
}
list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
return new InviteCommand($user, $other);
}
case 'track':
if (!$arg) {
return null;
}
list($word, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else if ($word == 'off') {
return new TrackOffCommand($user);
} else {
return new TrackCommand($user, $word);
}
case 'untrack':
if (!$arg) {
return null;
}
list($word, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else if ($word == 'all') {
return new TrackOffCommand($user);
} else {
return new UntrackCommand($user, $word);
}
case 'tracks':
case 'tracking':
if ($arg) {
return null;
}
return new TrackingCommand($user);
default:
return false;
Event::handle('EndInterpretCommand', array($cmd, $arg, $user, $result));
}
return $result;
}
/**

View File

@ -23,7 +23,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
define('STATUSNET_BASE_VERSION', '0.9.7');
define('STATUSNET_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('STATUSNET_LIFECYCLE', 'beta2'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('STATUSNET_VERSION', STATUSNET_BASE_VERSION . STATUSNET_LIFECYCLE);
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
@ -36,6 +36,7 @@ define('AVATAR_MINI_SIZE', 24);
define('NOTICES_PER_PAGE', 20);
define('PROFILES_PER_PAGE', 20);
define('MESSAGES_PER_PAGE', 20);
define('FOREIGN_NOTICE_SEND', 1);
define('FOREIGN_NOTICE_RECV', 2);

View File

@ -39,6 +39,8 @@ $default =
'logo' => null,
'ssllogo' => null,
'logdebug' => false,
'logperf' => false, // Enable to dump performance counters to syslog
'logperf_detail' => false, // Enable to dump every counter hit
'fancy' => false,
'locale_path' => INSTALLDIR.'/locale',
'language' => 'en',
@ -269,8 +271,9 @@ $default =
'search' =>
array('type' => 'fulltext'),
'sessions' =>
array('handle' => false, // whether to handle sessions ourselves
'debug' => false), // debugging output for sessions
array('handle' => false, // whether to handle sessions ourselves
'debug' => false, // debugging output for sessions
'gc_limit' => 1000), // max sessions to expire at a time
'design' =>
array('backgroundcolor' => null, // null -> 'use theme default'
'contentcolor' => null,
@ -311,6 +314,9 @@ $default =
'RSSCloud' => null,
'OpenID' => null),
'locale_path' => false, // Set to a path to use *instead of* each plugin's own locale subdirectories
'server' => null,
'sslserver' => null,
'path' => null,
),
'admin' =>
array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'license')),

View File

@ -139,51 +139,54 @@ class GroupEditForm extends Form
}
$this->out->elementStart('ul', 'form_data');
$this->out->elementStart('li');
$this->out->hidden('groupid', $id);
$this->out->input('nickname', _('Nickname'),
($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
_('1-64 lowercase letters or numbers, no punctuation or spaces.'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('fullname', _('Full name'),
($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('homepage', _('Homepage'),
($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
_('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');
} else {
$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);
}
$this->out->textarea('description', _('Description'),
($this->out->arg('description')) ? $this->out->arg('description') : $description,
$descinstr);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('location', _('Location'),
($this->out->arg('location')) ? $this->out->arg('location') : $location,
_('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();
if (Event::handle('StartGroupEditFormData', array($this))) {
$this->out->elementStart('li');
$this->out->input('aliases', _('Aliases'),
($this->out->arg('aliases')) ? $this->out->arg('aliases') :
(!empty($aliases)) ? implode(' ', $aliases) : '',
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')),
common_config('group', 'maxaliases')));;
$this->out->hidden('groupid', $id);
$this->out->input('nickname', _('Nickname'),
($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
_('1-64 lowercase letters or numbers, no punctuation or spaces'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('fullname', _('Full name'),
($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('homepage', _('Homepage'),
($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
_('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');
} else {
$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);
}
$this->out->textarea('description', _('Description'),
($this->out->arg('description')) ? $this->out->arg('description') : $description,
$descinstr);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('location', _('Location'),
($this->out->arg('location')) ? $this->out->arg('location') : $location,
_('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');
$this->out->input('aliases', _('Aliases'),
($this->out->arg('aliases')) ? $this->out->arg('aliases') :
(!empty($aliases)) ? implode(' ', $aliases) : '',
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')),
common_config('group', 'maxaliases')));;
$this->out->elementEnd('li');
}
Event::handle('EndGroupEditFormData', array($this));
}
$this->out->elementEnd('ul');
}

View File

@ -31,8 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
define('MESSAGES_PER_PAGE', 20);
/**
* common superclass for direct messages inbox and outbox
*
@ -111,32 +109,22 @@ class MailboxAction extends CurrentUserDesignAction
$message = $this->getMessages();
if ($message) {
$cnt = 0;
$this->elementStart('div', array('id' =>'notices_primary'));
$this->element('h2', null, _('Notices'));
$this->elementStart('ul', 'notices');
while ($message->fetch() && $cnt <= MESSAGES_PER_PAGE) {
$cnt++;
$ml = $this->getMessageList($message);
if ($cnt > MESSAGES_PER_PAGE) {
break;
}
$cnt = $ml->show();
$this->showMessage($message);
}
$this->elementEnd('ul');
$this->pagination($this->page > 1, $cnt > MESSAGES_PER_PAGE,
$this->page, $this->trimmed('action'),
$this->pagination($this->page > 1,
$cnt > MESSAGES_PER_PAGE,
$this->page,
$this->trimmed('action'),
array('nickname' => $this->user->nickname));
$this->elementEnd('div');
$message->free();
unset($message);
}
else {
$this->element('p', 'guide', _('You have no private messages. You can send private message to engage other users in conversation. People can send you messages for your eyes only.'));
} else {
$this->element('p',
'guide',
_('You have no private messages. '.
'You can send private message to engage other users in conversation. '.
'People can send you messages for your eyes only.'));
}
}
@ -145,95 +133,11 @@ class MailboxAction extends CurrentUserDesignAction
return null;
}
/**
* returns the profile we want to show with the message
*
* For inboxes, we show the sender; for outboxes, the recipient.
*
* @param Message $message The message to get the profile for
*
* @return Profile The profile that matches the message
*/
function getMessageProfile($message)
function getMessageList($message)
{
return null;
}
/**
* show a single message in the list format
*
* XXX: This needs to be extracted out into a MessageList similar
* to NoticeList.
*
* @param Message $message the message to show
*
* @return void
*/
function showMessage($message)
{
$this->elementStart('li', array('class' => 'hentry notice',
'id' => 'message-' . $message->id));
$profile = $this->getMessageProfile($message);
$this->elementStart('div', 'entry-title');
$this->elementStart('span', 'vcard author');
$this->elementStart('a', array('href' => $profile->profileurl,
'class' => 'url'));
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
$this->element('img', array('src' => ($avatar) ?
$avatar->displayUrl() :
Avatar::defaultImage(AVATAR_STREAM_SIZE),
'class' => 'photo avatar',
'width' => AVATAR_STREAM_SIZE,
'height' => AVATAR_STREAM_SIZE,
'alt' =>
($profile->fullname) ? $profile->fullname :
$profile->nickname));
$this->element('span', array('class' => 'nickname fn'),
$profile->nickname);
$this->elementEnd('a');
$this->elementEnd('span');
// FIXME: URL, image, video, audio
$this->elementStart('p', array('class' => 'entry-content'));
$this->raw($message->rendered);
$this->elementEnd('p');
$this->elementEnd('div');
$messageurl = common_local_url('showmessage',
array('message' => $message->id));
// XXX: we need to figure this out better. Is this right?
if (strcmp($message->uri, $messageurl) != 0 &&
preg_match('/^http/', $message->uri)) {
$messageurl = $message->uri;
}
$this->elementStart('div', 'entry-content');
$this->elementStart('a', array('rel' => 'bookmark',
'class' => 'timestamp',
'href' => $messageurl));
$dt = common_date_iso8601($message->created);
$this->element('abbr', array('class' => 'published',
'title' => $dt),
common_date_string($message->created));
$this->elementEnd('a');
if ($message->source) {
$this->elementStart('span', 'source');
// FIXME: bad i18n. Device should be a parameter (from %s).
$this->text(_('from'));
$this->element('span', 'device', $this->showSource($message->source));
$this->elementEnd('span');
}
$this->elementEnd('div');
$this->elementEnd('li');
}
/**
* Show the page notice
*
@ -252,44 +156,6 @@ class MailboxAction extends CurrentUserDesignAction
$this->elementEnd('div');
}
/**
* Show the source of the message
*
* Returns either the name (and link) of the API client that posted the notice,
* or one of other other channels.
*
* @param string $source the source of the message
*
* @return void
*/
function showSource($source)
{
$source_name = _($source);
switch ($source) {
case 'web':
case 'xmpp':
case 'mail':
case 'omb':
case 'api':
$this->element('span', 'device', $source_name);
break;
default:
$ns = Notice_source::staticGet($source);
if ($ns) {
$this->elementStart('span', 'device');
$this->element('a', array('href' => $ns->url,
'rel' => 'external'),
$ns->name);
$this->elementEnd('span');
} else {
$this->element('span', 'device', $source_name);
}
break;
}
return;
}
/**
* Mailbox actions are read only
*
@ -302,5 +168,4 @@ class MailboxAction extends CurrentUserDesignAction
{
return true;
}
}

107
lib/messagelist.php Normal file
View File

@ -0,0 +1,107 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* The message list widget
*
* 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 Widget
* @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);
}
/**
* Message list widget
*
* @category Widget
* @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 MessageList extends Widget
{
var $message;
/**
* Constructor
*
* @param HTMLOutputter $out Output context
* @param Message $message Stream of messages to show
*/
function __construct($out, $message)
{
parent::__construct($out);
$this->message = $message;
}
/**
* Show the widget
*
* Uses newItem() to create each new item.
*
* @return integer count of messages seen.
*/
function show()
{
$cnt = 0;
$this->out->elementStart('div', array('id' =>'notices_primary'));
$this->out->element('h2', null, _('Messages'));
$this->out->elementStart('ul', 'notices messages');
while ($this->message->fetch() && $cnt <= MESSAGES_PER_PAGE) {
$cnt++;
if ($cnt > MESSAGES_PER_PAGE) {
break;
}
$mli = $this->newItem($this->message);
$mli->show();
}
$this->out->elementEnd('ul');
$this->out->elementEnd('div');
}
/**
* Create a new message item for a message
*
* @param Message $message The message to show
*
* @return MessageListItem an item to show
*/
abstract function newItem($message);
}

178
lib/messagelistitem.php Normal file
View File

@ -0,0 +1,178 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* A single list item for showing in a message list
*
* 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 Widget
* @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 single item in a message list
*
* @category Widget
* @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 MessageListItem extends Widget
{
var $message;
/**
* Constructor
*
* @param HTMLOutputter $out Output context
* @param Message $message Message to show
*/
function __construct($out, $message)
{
parent::__construct($out);
$this->message = $message;
}
/**
* Show the widget
*
* @return void
*/
function show()
{
$this->out->elementStart('li', array('class' => 'hentry notice',
'id' => 'message-' . $this->message->id));
$profile = $this->getMessageProfile();
$this->out->elementStart('div', 'entry-title');
$this->out->elementStart('span', 'vcard author');
$this->out->elementStart('a', array('href' => $profile->profileurl,
'class' => 'url'));
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
$this->out->element('img', array('src' => ($avatar) ?
$avatar->displayUrl() :
Avatar::defaultImage(AVATAR_STREAM_SIZE),
'class' => 'photo avatar',
'width' => AVATAR_STREAM_SIZE,
'height' => AVATAR_STREAM_SIZE,
'alt' =>
($profile->fullname) ? $profile->fullname :
$profile->nickname));
$this->out->element('span', array('class' => 'nickname fn'),
$profile->nickname);
$this->out->elementEnd('a');
$this->out->elementEnd('span');
// FIXME: URL, image, video, audio
$this->out->elementStart('p', array('class' => 'entry-content'));
$this->out->raw($this->message->rendered);
$this->out->elementEnd('p');
$this->out->elementEnd('div');
$messageurl = common_local_url('showmessage',
array('message' => $this->message->id));
// XXX: we need to figure this out better. Is this right?
if (strcmp($this->message->uri, $messageurl) != 0 &&
preg_match('/^http/', $this->message->uri)) {
$messageurl = $this->message->uri;
}
$this->out->elementStart('div', 'entry-content');
$this->out->elementStart('a', array('rel' => 'bookmark',
'class' => 'timestamp',
'href' => $messageurl));
$dt = common_date_iso8601($this->message->created);
$this->out->element('abbr', array('class' => 'published',
'title' => $dt),
common_date_string($this->message->created));
$this->out->elementEnd('a');
if ($this->message->source) {
$this->out->elementStart('span', 'source');
// FIXME: bad i18n. Device should be a parameter (from %s).
$this->out->text(_('from'));
$this->showSource($this->message->source);
$this->out->elementEnd('span');
}
$this->out->elementEnd('div');
$this->out->elementEnd('li');
}
/**
* Show the source of the message
*
* Returns either the name (and link) of the API client that posted the notice,
* or one of other other channels.
*
* @param string $source the source of the message
*
* @return void
*/
function showSource($source)
{
$source_name = _($source);
switch ($source) {
case 'web':
case 'xmpp':
case 'mail':
case 'omb':
case 'api':
$this->out->element('span', 'device', $source_name);
break;
default:
$ns = Notice_source::staticGet($source);
if ($ns) {
$this->out->elementStart('span', 'device');
$this->out->element('a', array('href' => $ns->url,
'rel' => 'external'),
$ns->name);
$this->out->elementEnd('span');
} else {
$this->out->element('span', 'device', $source_name);
}
break;
}
return;
}
/**
* Return the profile to show in the message item
*
* Overridden in sub-classes to show sender, receiver, or whatever
*
* @return Profile profile to show avatar and name of
*/
abstract function getMessageProfile();
}

View File

@ -111,10 +111,15 @@ class Plugin
$this->log(LOG_DEBUG, $msg);
}
function onPluginVersion(&$versions)
function name()
{
$cls = get_class($this);
$name = mb_substr($cls, 0, -6);
return mb_substr($cls, 0, -6);
}
function onPluginVersion(&$versions)
{
$name = $this->name();
$versions[] = array('name' => $name,
// TRANS: Displayed as version information for a plugin if no version information was found.
@ -122,4 +127,39 @@ class Plugin
return true;
}
function path($relative)
{
return self::staticPath($this->name(), $relative);
}
static function staticPath($plugin, $relative)
{
$isHTTPS = StatusNet::isHTTPS();
if ($isHTTPS) {
$server = common_config('plugins', 'sslserver');
} else {
$server = common_config('plugins', 'server');
}
if (empty($server)) {
if ($isHTTPS) {
$server = common_config('site', 'sslserver');
}
if (empty($server)) {
$server = common_config('site', 'server');
}
}
$path = common_config('plugins', 'path');
if (empty($path)) {
$path = common_config('site', 'path') . '/plugins/';
}
$protocol = ($isHTTPS) ? 'https' : 'http';
return $protocol.'://'.$server.$path.$plugin.'/'.$relative;
}
}

View File

@ -2184,3 +2184,40 @@ function common_nicknamize($str)
$str = preg_replace('/\W/', '', $str);
return strtolower($str);
}
function common_perf_counter($key, $val=null)
{
global $_perfCounters;
if (isset($_perfCounters)) {
if (common_config('site', 'logperf')) {
if (array_key_exists($key, $_perfCounters)) {
$_perfCounters[$key][] = $val;
} else {
$_perfCounters[$key] = array($val);
}
if (common_config('site', 'logperf_detail')) {
common_log(LOG_DEBUG, "PERF COUNTER HIT: $key $val");
}
}
}
}
function common_log_perf_counters()
{
if (common_config('site', 'logperf')) {
global $_startTime, $_perfCounters;
if (isset($_startTime)) {
$endTime = microtime(true);
$diff = round(($endTime - $_startTime) * 1000);
common_log(LOG_DEBUG, "PERF runtime: ${diff}ms");
}
$counters = $_perfCounters;
ksort($counters);
foreach ($counters as $key => $values) {
$count = count($values);
$unique = count(array_unique($values));
common_log(LOG_DEBUG, "PERF COUNTER: $key $count ($unique unique)");
}
}
}

View File

@ -51,15 +51,15 @@ class AutocompletePlugin extends Plugin
function onEndShowScripts($action){
if (common_logged_in()) {
$action->script('plugins/Autocomplete/jquery-autocomplete/jquery.autocomplete.pack.js');
$action->script('plugins/Autocomplete/Autocomplete.js');
$action->script($this->path('jquery-autocomplete/jquery.autocomplete.pack.js'));
$action->script($this->path('Autocomplete.js'));
}
}
function onEndShowStatusNetStyles($action)
{
if (common_logged_in()) {
$action->cssLink('plugins/Autocomplete/jquery-autocomplete/jquery.autocomplete.css');
$action->cssLink($this->path('jquery-autocomplete/jquery.autocomplete.css'));
}
}

View File

@ -65,7 +65,7 @@ class BlankAdPlugin extends UAPPlugin
$action->element('img',
array('width' => 300,
'height' => 250,
'src' => common_path('plugins/BlankAd/redpixel.png')),
'src' => $this->path('redpixel.png')),
'');
}
@ -81,7 +81,7 @@ class BlankAdPlugin extends UAPPlugin
$action->element('img',
array('width' => 180,
'height' => 150,
'src' => common_path('plugins/BlankAd/redpixel.png')),
'src' => $this->path('redpixel.png')),
'');
}
@ -97,7 +97,7 @@ class BlankAdPlugin extends UAPPlugin
$action->element('img',
array('width' => 160,
'height' => 600,
'src' => common_path('plugins/BlankAd/redpixel.png')),
'src' => $this->path('redpixel.png')),
'');
}
@ -113,7 +113,7 @@ class BlankAdPlugin extends UAPPlugin
$action->element('img',
array('width' => 728,
'height' => 90,
'src' => common_path('plugins/BlankAd/redpixel.png')),
'src' => $this->path('redpixel.png')),
'');
}

View File

@ -149,7 +149,7 @@ class BookmarkPlugin extends Plugin
function onEndShowStyles($action)
{
$action->cssLink('plugins/Bookmark/bookmark.css');
$action->cssLink($this->path('bookmark.css'));
return true;
}

View File

@ -107,6 +107,6 @@ class BookmarkpopupAction extends NewbookmarkAction
function showScripts()
{
parent::showScripts();
$this->script(common_path('plugins/Bookmark/bookmarkpopup.js'));
$this->script(Plugin::staticPath('Bookmark', 'bookmarkpopup.js'));
}
}

View File

@ -53,7 +53,7 @@ class ClientSideShortenPlugin extends Plugin
function onEndShowScripts($action){
$action->inlineScript('var Notice_maxContent = ' . Notice::maxContent());
if (common_logged_in()) {
$action->script('plugins/ClientSideShorten/shorten.js');
$action->script($this->path('shorten.js'));
}
}

View File

@ -129,7 +129,7 @@ class DirectionDetectorPlugin extends Plugin {
*/
function onEndShowScripts($action){
if (common_logged_in()) {
$action->script('plugins/DirectionDetector/jquery.DirectionDetector.js');
$action->script($this->path('jquery.DirectionDetector.js'));
}
}

View File

@ -24,7 +24,7 @@
* @category Pugin
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
@ -47,8 +47,9 @@ define("FACEBOOK_SERVICE", 2);
*/
class FacebookBridgePlugin extends Plugin
{
public $appId = null; // Facebook application ID
public $secret = null; // Facebook application secret
public $appId; // Facebook application ID
public $secret; // Facebook application secret
public $facebook = null; // Facebook application instance
public $dir = null; // Facebook plugin dir
@ -61,6 +62,28 @@ class FacebookBridgePlugin extends Plugin
*/
function initialize()
{
// Allow the id and key to be passed in
// Control panel will override
if (isset($this->appId)) {
$appId = common_config('facebook', 'appid');
if (empty($appId)) {
Config::save(
'facebook',
'appid',
$this->appId
);
}
}
if (isset($this->secret)) {
$secret = common_config('facebook', 'secret');
if (empty($secret)) {
Config::save('facebook', 'secret', $this->secret);
}
}
$this->facebook = Facebookclient::getFacebook(
$this->appId,
$this->secret

View File

@ -89,10 +89,7 @@ class FacebookloginAction extends Action
);
$attrs = array(
'src' => common_path(
'plugins/FacebookBridge/images/login-button.png',
true
),
'src' => Plugin::staticPath('FacebookBridge', 'images/login-button.png'),
'alt' => 'Login with Facebook',
'title' => 'Login with Facebook'
);

View File

@ -115,14 +115,7 @@ class Facebookclient
function isFacebookBound() {
if (empty($this->flink)) {
common_log(
LOG_WARN,
sprintf(
"No Foreign_link to Facebook for the author of notice %d.",
$this->notice->id
),
__FILE__
);
// User hasn't setup bridging
return false;
}
@ -180,15 +173,6 @@ class Facebookclient
// Otherwise we most likely have an access token
return $this->sendGraph();
}
} else {
common_debug(
sprintf(
"Skipping notice %d - not bound for Facebook",
$this->notice->id,
__FILE__
)
);
}
}

View File

@ -0,0 +1,505 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Private groups for StatusNet 0.9.x
*
* 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 Privacy
* @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);
}
/**
* Private groups
*
* This plugin allows users to send private messages to a group.
*
* @category Privacy
* @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 GroupPrivateMessagePlugin extends Plugin
{
/**
* Database schema setup
*
* @see Schema
* @see ColumnDef
*
* @return boolean hook value
*/
function onCheckSchema()
{
$schema = Schema::get();
// For storing user-submitted flags on profiles
$schema->ensureTable('group_privacy_settings',
array(new ColumnDef('group_id',
'integer',
null,
false,
'PRI'),
new ColumnDef('allow_privacy',
'integer'),
new ColumnDef('allow_sender',
'integer'),
new ColumnDef('created',
'datetime'),
new ColumnDef('modified',
'timestamp')));
$schema->ensureTable('group_message',
array(new ColumnDef('id',
'char',
36,
false,
'PRI'),
new ColumnDef('uri',
'varchar',
255,
false,
'UNI'),
new ColumnDef('from_profile',
'integer',
null,
false,
'MUL'),
new ColumnDef('to_group',
'integer',
null,
false,
'MUL'),
new ColumnDef('content',
'text'),
new ColumnDef('rendered',
'text'),
new ColumnDef('url',
'varchar',
255,
false,
'UNI'),
new ColumnDef('created',
'datetime')));
$schema->ensureTable('group_message_profile',
array(new ColumnDef('to_profile',
'integer',
null,
false,
'PRI'),
new ColumnDef('group_message_id',
'char',
36,
false,
'PRI'),
new ColumnDef('created',
'datetime')));
return true;
}
/**
* Load related modules when needed
*
* @param string $cls Name of the class to be loaded
*
* @return boolean hook value
*/
function onAutoload($cls)
{
$dir = dirname(__FILE__);
switch ($cls)
{
case 'GroupinboxAction':
case 'ShowgroupmessageAction':
case 'NewgroupmessageAction':
include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
case 'Group_privacy_settings':
case 'Group_message':
case 'Group_message_profile':
include_once $dir . '/'.$cls.'.php';
return false;
case 'GroupMessageCommand':
case 'GroupMessageList':
case 'GroupMessageListItem':
case 'GroupMessageForm':
include_once $dir . '/'.strtolower($cls).'.php';
return false;
default:
return true;
}
}
/**
* Map URLs to actions
*
* @param Net_URL_Mapper $m path-to-action mapper
*
* @return boolean hook value
*/
function onRouterInitialized($m)
{
$m->connect('group/:nickname/inbox',
array('action' => 'groupinbox'),
array('nickname' => Nickname::DISPLAY_FMT));
$m->connect('group/message/:id',
array('action' => 'showgroupmessage'),
array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
$m->connect('group/:nickname/message/new',
array('action' => 'newgroupmessage'),
array('nickname' => Nickname::DISPLAY_FMT));
return true;
}
/**
* Add group inbox to the menu
*
* @param Action $action The current action handler. Use this to
* do any output.
*
* @return boolean hook value; true means continue processing, false means stop.
*
* @see Action
*/
function onEndGroupGroupNav($groupnav)
{
$action = $groupnav->action;
$group = $groupnav->group;
$action->menuItem(common_local_url('groupinbox',
array('nickname' => $group->nickname)),
_m('Inbox'),
_m('Private messages for this group'),
$action->trimmed('action') == 'groupinbox',
'nav_group_inbox');
return true;
}
/**
* Create default group privacy settings at group create time
*
* @param User_group $group Group that was just created
*
* @result boolean hook value
*/
function onEndGroupSave($group)
{
$gps = new Group_privacy_settings();
$gps->group_id = $group->id;
$gps->allow_privacy = Group_privacy_settings::SOMETIMES;
$gps->allow_sender = Group_privacy_settings::MEMBER;
$gps->created = common_sql_now();
$gps->modified = $gps->created;
// This will throw an exception on error
$gps->insert();
return true;
}
/**
* Show group privacy controls on group edit form
*
* @param GroupEditForm $form form being shown
*/
function onEndGroupEditFormData($form)
{
$gps = null;
if (!empty($form->group)) {
$gps = Group_privacy_settings::staticGet('group_id', $form->group->id);
}
$form->out->elementStart('li');
$form->out->dropdown('allow_privacy',
_('Private messages'),
array(Group_privacy_settings::SOMETIMES => _('Sometimes'),
Group_privacy_settings::ALWAYS => _('Always'),
Group_privacy_settings::NEVER => _('Never')),
_('Whether to allow private messages to this group'),
false,
(empty($gps)) ? Group_privacy_settings::SOMETIMES : $gps->allow_privacy);
$form->out->elementEnd('li');
$form->out->elementStart('li');
$form->out->dropdown('allow_sender',
_('Private sender'),
array(Group_privacy_settings::EVERYONE => _('Everyone'),
Group_privacy_settings::MEMBER => _('Member'),
Group_privacy_settings::ADMIN => _('Admin')),
_('Who can send private messages to the group'),
false,
(empty($gps)) ? Group_privacy_settings::MEMBER : $gps->allow_sender);
$form->out->elementEnd('li');
return true;
}
function onEndGroupSaveForm($action)
{
$gps = null;
if (!empty($action->group)) {
$gps = Group_privacy_settings::staticGet('group_id', $action->group->id);
}
$orig = null;
if (empty($gps)) {
$gps = new Group_privacy_settings();
$gps->group_id = $action->group->id;
} else {
$orig = clone($gps);
}
$gps->allow_privacy = $action->trimmed('allow_privacy');
$gps->allow_sender = $action->trimmed('allow_sender');
if (empty($orig)) {
$gps->created = common_sql_now();
$gps->insert();
} else {
$gps->update($orig);
}
return true;
}
/**
* Overload 'd' command to send private messages to groups.
*
* 'd !group word word word' will send the private message
* 'word word word' to the group 'group'.
*
* @param string $cmd Command being run
* @param string $arg Rest of the message (including address)
* @param User $user User sending the message
* @param Command &$result The resulting command object to be run.
*
* @return boolean hook value
*/
function onStartIntepretCommand($cmd, $arg, $user, &$result)
{
if ($cmd == 'd' || $cmd == 'dm') {
$this->debug('Got a d command');
// Break off the first word as the address
$pieces = explode(' ', $arg, 2);
if (count($pieces) == 1) {
$pieces[] = null;
}
list($addr, $msg) = $pieces;
if (!empty($addr) && $addr[0] == '!') {
$result = new GroupMessageCommand($user, substr($addr, 1), $msg);
Event::handle('EndInterpretCommand', array($cmd, $arg, $user, $result));
return false;
}
}
return true;
}
/**
* To add a "Message" button to the group profile page
*
* @param Action $action The showgroup action being shown
* @param User_group $group The current group
*
* @return boolean hook value
*/
function onEndGroupActionsList($action, $group)
{
$cur = common_current_user();
if (empty($cur)) {
return true;
}
try {
Group_privacy_settings::ensurePost($cur, $group);
} catch (Exception $e) {
return true;
}
$action->elementStart('li', 'entity_send-a-message');
$action->element('a', array('href' => common_local_url('newgroupmessage', array('nickname' => $group->nickname)),
'title' => _('Send a direct message to this group')),
_('Message'));
// $form = new GroupMessageForm($action, $group);
// $form->hidden = true;
// $form->show();
$action->elementEnd('li');
return true;
}
/**
* When saving a notice, check its groups. If any of them has
* privacy == always, force a group private message to all mentioned groups.
* If any of the groups disallows private messages, skip it.
*
* @param
*
*/
function onStartNoticeSave(&$notice) {
// Look for group tags
// FIXME: won't work for remote groups
// @fixme if Notice::saveNew is refactored so we can just pull its list
// of groups between processing and saving, make use of it
$count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
strtolower($notice->content),
$match);
$groups = array();
$ignored = array();
$forcePrivate = false;
if ($count > 0) {
/* Add them to the database */
foreach (array_unique($match[1]) as $nickname) {
$group = User_group::getForNickname($nickname, $profile);
if (empty($group)) {
continue;
}
$gps = Group_privacy_settings::forGroup($group);
switch ($gps->allow_privacy) {
case Group_privacy_settings::ALWAYS:
$forcePrivate = true;
// fall through
case Group_privacy_settings::SOMETIMES:
$groups[] = $group;
break;
case Group_privacy_settings::NEVER:
$ignored[] = $group;
break;
}
}
if ($forcePrivate) {
foreach ($ignored as $group) {
common_log(LOG_NOTICE,
"Notice forced to group direct message ".
"but group ".$group->nickname." does not allow them.");
}
$user = User::staticGet('id', $notice->profile_id);
if (empty($user)) {
common_log(LOG_WARNING,
"Notice forced to group direct message ".
"but profile ".$notice->profile_id." is not a local user.");
} else {
foreach ($groups as $group) {
Group_message::send($user, $group, $notice->content);
}
}
// Don't save the notice!
// FIXME: this is probably cheating.
throw new ClientException(sprintf(_('Forced notice to private group message.')),
200);
}
}
return true;
}
/**
* Show an indicator that the group is (essentially) private on the group page
*
* @param Action $action The action being shown
* @param User_group $group The group being shown
*
* @return boolean hook value
*/
function onEndGroupProfileElements($action, $group)
{
$gps = Group_privacy_settings::forGroup($group);
if ($gps->allow_privacy == Group_privacy_settings::ALWAYS) {
$action->element('p', 'privategroupindicator', _('Private'));
}
return true;
}
function onStartShowExportData($action)
{
if ($action instanceof ShowgroupAction) {
$gps = Group_privacy_settings::forGroup($action->group);
if ($gps->allow_privacy == Group_privacy_settings::ALWAYS) {
return false;
}
}
return true;
}
function onPluginVersion(&$versions)
{
$versions[] = array('name' => 'GroupPrivateMessage',
'version' => STATUSNET_VERSION,
'author' => 'Evan Prodromou',
'homepage' => 'http://status.net/wiki/Plugin:GroupPrivateMessage',
'rawdescription' =>
_m('Allow posting DMs to a group.'));
return true;
}
}

View File

@ -0,0 +1,208 @@
<?php
/**
* Data class for group direct messages
*
* PHP version 5
*
* @category Data
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
/**
* Data class for group direct messages
*
* @category GroupPrivateMessage
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* @see DB_DataObject
*/
class Group_message extends Memcached_DataObject
{
public $__table = 'group_message'; // table name
public $id; // char(36) primary_key not_null
public $uri; // varchar(255)
public $from_profile; // int
public $to_group; // int
public $content;
public $rendered;
public $url;
public $created;
/**
* Get an instance by key
*
* This is a utility method to get a single instance with a given key value.
*
* @param string $k Key to use to lookup (usually 'user_id' for this class)
* @param mixed $v Value to lookup
*
* @return Group_message object found, or null for no hits
*
*/
function staticGet($k, $v=null)
{
return Memcached_DataObject::staticGet('Group_message', $k, $v);
}
/**
* return table definition for DB_DataObject
*
* DB_DataObject needs to know something about the table to manipulate
* instances. This method provides all the DB_DataObject needs to know.
*
* @return array array of column definitions
*/
function table()
{
return array('id' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
'uri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
'from_profile' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'to_group' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'content' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
'rendered' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
'url' => DB_DATAOBJECT_STR,
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
}
/**
* return key definitions for DB_DataObject
*
* DB_DataObject needs to know about keys that the table has, since it
* won't appear in StatusNet's own keys list. In most cases, this will
* simply reference your keyTypes() function.
*
* @return array list of key field names
*/
function keys()
{
return array_keys($this->keyTypes());
}
/**
* return key definitions for Memcached_DataObject
*
* @return array associative array of key definitions, field name to type:
* 'K' for primary key: for compound keys, add an entry for each component;
* 'U' for unique keys: compound keys are not well supported here.
*/
function keyTypes()
{
return array('id' => 'K', 'uri' => 'U');
}
static function send($user, $group, $text)
{
if (!$user->hasRight(Right::NEWMESSAGE)) {
// XXX: maybe break this out into a separate right
throw new Exception(sprintf(_('User %s not allowed to send private messages.'),
$user->nickname));
}
Group_privacy_settings::ensurePost($user, $group);
$text = $user->shortenLinks($text);
// We use the same limits as for 'regular' private messages.
if (Message::contentTooLong($text)) {
throw new Exception(sprintf(_m('That\'s too long. Maximum message size is %d character.',
'That\'s too long. Maximum message size is %d characters.',
Message::maxContent()),
Message::maxContent()));
}
// Valid! Let's do this thing!
$gm = new Group_message();
$gm->id = UUID::gen();
$gm->uri = common_local_url('showgroupmessage', array('id' => $gm->id));
$gm->from_profile = $user->id;
$gm->to_group = $group->id;
$gm->content = $text; // XXX: is this cool?!
$gm->rendered = common_render_text($text);
$gm->url = $gm->uri;
$gm->created = common_sql_now();
// This throws a conniption if there's a problem
$gm->insert();
$gm->distribute();
return $gm;
}
function distribute()
{
$group = User_group::staticGet('id', $this->to_group);
$member = $group->getMembers();
while ($member->fetch()) {
Group_message_profile::send($this, $member);
}
}
function getGroup()
{
$group = User_group::staticGet('id', $this->to_group);
if (empty($group)) {
throw new ServerException(_('No group for group message'));
}
return $group;
}
function getSender()
{
$sender = Profile::staticGet('id', $this->from_profile);
if (empty($sender)) {
throw new ServerException(_('No sender for group message'));
}
return $sender;
}
static function forGroup($group, $offset, $limit)
{
// XXX: cache
$gm = new Group_message();
$gm->to_group = $group->id;
$gm->orderBy('created DESC');
$gm->limit($offset, $limit);
$gm->find();
return $gm;
}
}

View File

@ -0,0 +1,189 @@
<?php
/**
* Who received a group message
*
* PHP version 5
*
* @category Data
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
/**
* Data class for group direct messages for users
*
* @category GroupPrivateMessage
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* @see DB_DataObject
*/
class Group_message_profile extends Memcached_DataObject
{
public $__table = 'group_message_profile'; // table name
public $to_profile; // int
public $group_message_id; // char(36) primary_key not_null
public $created;
/**
* Get an instance by key
*
* This is a utility method to get a single instance with a given key value.
*
* @param string $k Key to use to lookup (usually 'user_id' for this class)
* @param mixed $v Value to lookup
*
* @return Group_message object found, or null for no hits
*
*/
function staticGet($k, $v=null)
{
return Memcached_DataObject::staticGet('Group_message_profile', $k, $v);
}
/**
* return table definition for DB_DataObject
*
* DB_DataObject needs to know something about the table to manipulate
* instances. This method provides all the DB_DataObject needs to know.
*
* @return array array of column definitions
*/
function table()
{
return array('to_profile' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'group_message_id' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
}
/**
* return key definitions for DB_DataObject
*
* DB_DataObject needs to know about keys that the table has, since it
* won't appear in StatusNet's own keys list. In most cases, this will
* simply reference your keyTypes() function.
*
* @return array list of key field names
*/
function keys()
{
return array_keys($this->keyTypes());
}
/**
* return key definitions for Memcached_DataObject
*
* @return array associative array of key definitions, field name to type:
* 'K' for primary key: for compound keys, add an entry for each component;
* 'U' for unique keys: compound keys are not well supported here.
*/
function keyTypes()
{
return array('to_profile' => 'K', 'group_message_id' => 'K');
}
/**
* No sequence keys in this table.
*/
function sequenceKey()
{
return array(false, false, false);
}
function send($gm, $profile)
{
$gmp = new Group_message_profile();
$gmp->group_message_id = $gm->id;
$gmp->to_profile = $profile->id;
$gmp->created = common_sql_now();
$gmp->insert();
$gmp->notify();
return $gmp;
}
function notify()
{
// XXX: add more here
$this->notifyByMail();
}
function notifyByMail()
{
$to = User::staticGet('id', $this->to_profile);
if (empty($to) || is_null($to->email) || !$to->emailnotifymsg) {
return true;
}
$gm = Group_message::staticGet('id', $this->group_message_id);
$from_profile = Profile::staticGet('id', $gm->from_profile);
$group = $gm->getGroup();
common_switch_locale($to->language);
// TRANS: Subject for direct-message notification email.
// TRANS: %s is the sending user's nickname.
$subject = sprintf(_('New private message from %s to group %s'), $from->nickname, $group->nickname);
$from_profile = $from->getProfile();
// 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 a private message to group %3\$s:\n\n".
"------------------------------------------------------\n".
"%4\$s\n".
"------------------------------------------------------\n\n".
"You can reply to their message here:\n\n".
"%5\$s\n\n".
"Don't reply to this email; it won't get to them.\n\n".
"With kind regards,\n".
"%6\$s\n"),
$from_profile->getBestName(),
$from->nickname,
$group->nickname,
$this->content,
common_local_url('newmessage', array('to' => $from->id)),
common_config('site', 'name'));
$headers = _mail_prepare_headers('message', $to->nickname, $from->nickname);
common_switch_locale();
return mail_to_user($to, $subject, $body, $headers);
}
}

View File

@ -0,0 +1,201 @@
<?php
/**
* Data class for group privacy settings
*
* PHP version 5
*
* @category Data
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Data class for group privacy
*
* Stores admin preferences about the group.
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* @see DB_DataObject
*/
class Group_privacy_settings extends Memcached_DataObject
{
public $__table = 'group_privacy_settings';
/** ID of the group. */
public $group_id;
/** When to allow privacy: always, sometimes, or never. */
public $allow_privacy;
/** Who can send private messages: everyone, member, admin */
public $allow_sender;
/** row creation timestamp */
public $created;
/** Last-modified timestamp */
public $modified;
/** NEVER is */
const SOMETIMES = -1;
const NEVER = 0;
const ALWAYS = 1;
/** These are bit-mappy, as a hedge against the future. */
const EVERYONE = 1;
const MEMBER = 2;
const ADMIN = 4;
/**
* Get an instance by key
*
* This is a utility method to get a single instance with a given key value.
*
* @param string $k Key to use to lookup (usually 'user_id' for this class)
* @param mixed $v Value to lookup
*
* @return User_greeting_count object found, or null for no hits
*/
function staticGet($k, $v=null)
{
return Memcached_DataObject::staticGet('Group_privacy_settings', $k, $v);
}
/**
* return table definition for DB_DataObject
*
* DB_DataObject needs to know something about the table to manipulate
* instances. This method provides all the DB_DataObject needs to know.
*
* @return array array of column definitions
*/
function table()
{
return array('group_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'allow_privacy' => DB_DATAOBJECT_INT,
'allow_sender' => DB_DATAOBJECT_INT,
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
}
/**
* return key definitions for DB_DataObject
*
* DB_DataObject needs to know about keys that the table has, since it
* won't appear in StatusNet's own keys list. In most cases, this will
* simply reference your keyTypes() function.
*
* @return array list of key field names
*/
function keys()
{
return array_keys($this->keyTypes());
}
/**
* return key definitions for Memcached_DataObject
*
* @return array associative array of key definitions, field name to type:
* 'K' for primary key: for compound keys, add an entry for each component;
* 'U' for unique keys: compound keys are not well supported here.
*/
function keyTypes()
{
return array('group_id' => 'K');
}
/**
* Magic formula for non-autoincrementing integer primary keys
*
* @return array magic three-false array that stops auto-incrementing.
*/
function sequenceKey()
{
return array(false, false, false);
}
function forGroup($group)
{
$gps = Group_privacy_settings::staticGet('group_id', $group->id);
if (empty($gps)) {
// make a fake one with defaults
$gps = new Group_privacy_settings();
$gps->allow_privacy = Group_privacy_settings::SOMETIMES;
$gps->allow_sender = Group_privacy_settings::MEMBER;
}
return $gps;
}
function ensurePost($user, $group)
{
$gps = self::forGroup($group);
if ($gps->allow_privacy == Group_privacy_settings::NEVER) {
throw new Exception(sprintf(_('Group %s does not allow private messages.'),
$group->nickname));
}
switch ($gps->allow_sender) {
case Group_privacy_settings::EVERYONE:
$profile = $user->getProfile();
if (Group_block::isBlocked($group, $profile)) {
throw new Exception(sprintf(_('User %s is blocked from group %s.'),
$user->nickname,
$group->nickname));
}
break;
case Group_privacy_settings::MEMBER:
if (!$user->isMember($group)) {
throw new Exception(sprintf(_('User %s is not a member of group %s.'),
$user->nickname,
$group->nickname));
}
break;
case Group_privacy_settings::ADMIN:
if (!$user->isAdmin($group)) {
throw new Exception(sprintf(_('User %s is not an administrator of group %s.'),
$user->nickname,
$group->nickname));
}
break;
default:
throw new Exception(sprintf(_('Unknown privacy settings for group %s.'),
$group->nickname));
}
return true;
}
}

View File

@ -0,0 +1,208 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* List of private messages to this 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 GroupPrivateMessage
* @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);
}
/**
* Show a list of private messages to this group
*
* @category GroupPrivateMessage
* @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 GroupinboxAction extends GroupDesignAction
{
var $gm;
/**
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
$cur = common_current_user();
if (empty($cur)) {
throw new ClientException(_('Only for logged-in users'), 403);
}
$nicknameArg = $this->trimmed('nickname');
$nickname = common_canonical_nickname($nicknameArg);
if ($nickname != $nicknameArg) {
$url = common_local_url('groupinbox', array('nickname' => $nickname));
common_redirect($url);
return false;
}
$localGroup = Local_group::staticGet('nickname', $nickname);
if (empty($localGroup)) {
throw new ClientException(_('No such group'), 404);
}
$this->group = User_group::staticGet('id', $localGroup->group_id);
if (empty($this->group)) {
throw new ClientException(_('No such group'), 404);
}
if (!$cur->isMember($this->group)) {
throw new ClientException(_('Only for members'), 403);
}
$this->page = $this->trimmed('page');
if (!$this->page) {
$this->page = 1;
}
$this->gm = Group_message::forGroup($this->group,
($this->page - 1) * MESSAGES_PER_PAGE,
MESSAGES_PER_PAGE + 1);
return true;
}
function showLocalNav()
{
$nav = new GroupNav($this, $this->group);
$nav->show();
}
function showNoticeForm()
{
$form = new GroupMessageForm($this, $this->group);
$form->show();
}
function showContent()
{
$gml = new GroupMessageList($this, $this->gm);
$cnt = $gml->show();
if ($cnt == 0) {
$this->element('p', 'guide', _m('This group has not received any private messages.'));
}
$this->pagination($this->page > 1,
$cnt > MESSAGES_PER_PAGE,
$this->page,
'groupinbox',
array('nickname' => $this->group->nickname));
}
/**
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{
$this->showPage();
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
/**
* Title of the page
*
* @return string page title, with page number
*/
function title()
{
$base = $this->group->getFancyName();
if ($this->page == 1) {
return sprintf(_('%s group inbox'), $base);
} else {
// TRANS: Page title for any but first group page.
// TRANS: %1$s is a group name, $2$s is a page number.
return sprintf(_('%1$s group inbox, page %2$d'),
$base,
$this->page);
}
}
/**
* Show the page notice
*
* Shows instructions for the page
*
* @return void
*/
function showPageNotice()
{
$instr = $this->getInstructions();
$output = common_markup_to_html($instr);
$this->elementStart('div', 'instructions');
$this->raw($output);
$this->elementEnd('div');
}
/**
* Instructions for using this page
*
* @return string localised instructions for using the page
*/
function getInstructions()
{
// TRANS: Instructions for user inbox page.
return _m('This is the group inbox, which lists all incoming private messages for this group.');
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Command object for messages to groups
*
* 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 Command
* @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);
}
/**
* Command object for messages to groups
*
* @category General
* @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 GroupMessageCommand extends Command
{
/** User sending the message. */
var $user;
/** Nickname of the group they're sending to. */
var $nickname;
/** Text of the message. */
var $text;
/**
* Constructor
*
* @param User $user User sending the message
* @param string $nickname Nickname of the group
* @param string $text Text of message
*/
function __construct($user, $nickname, $text)
{
$this->user = $user;
$this->nickname = $nickname;
$this->text = $text;
}
function handle($channel)
{
// Throws a command exception if group not found
$group = $this->getGroup($this->nickname);
$gm = Group_message::send($this->user, $group, $this->text);
$channel->output($this->user,
sprintf(_('Direct message to group %s sent.'),
$group->nickname));
return true;
}
}

View File

@ -0,0 +1,166 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Form for posting a group message
*
* 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 GroupPrivateMessage
* @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);
}
/**
* Form for posting a group message
*
* @category GroupPrivateMessage
* @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 GroupMessageForm extends Form
{
var $group;
var $content;
/**
* Constructor
*
* @param HTMLOutputter $out Output context
* @param User_group $group Group to post to
*
* @todo add a drop-down list to post to any group
*/
function __construct($out, $group, $content=null)
{
parent::__construct($out);
$this->group = $group;
$this->content = $content;
}
/**
* Action for the form
*/
function action()
{
return common_local_url('newgroupmessage',
array('nickname' => $this->group->nickname));
}
/**
* Legend for the form
*
* @param
*
* @return
*/
function formLegend()
{
$this->out->element('legend',
null,
sprintf(_('Message to %s'), $this->group->nickname));
}
/**
* id for the form
*
* @param
*
* @return
*/
function id()
{
return 'form_notice-group-message';
}
/**
* class for the form
*
* @param
*
* @return
*/
function formClass()
{
return 'form_notice';
}
/**
* Entry data
*
* @param
*
* @return
*/
function formData()
{
$this->out->element('label', array('for' => 'notice_data-text',
'id' => 'notice_data-text-label'),
sprintf(_('Direct message to %s'), $this->group->nickname));
$this->out->element('textarea', array('id' => 'notice_data-text',
'cols' => 35,
'rows' => 4,
'name' => 'content'),
($this->content) ? $this->content : '');
$contentLimit = Message::maxContent();
if ($contentLimit > 0) {
$this->out->elementStart('dl', 'form_note');
$this->out->element('dt', null, _('Available characters'));
$this->out->element('dd', array('id' => 'notice_text-count'),
$contentLimit);
$this->out->elementEnd('dl');
}
}
/**
* Legend for the form
*
* @param
*
* @return
*/
function formActions()
{
$this->out->element('input', array('id' => 'notice_action-submit',
'class' => 'submit',
'name' => 'message_send',
'type' => 'submit',
'value' => _m('Send button for sending notice', 'Send')));
}
}

View File

@ -0,0 +1,90 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Widget for showing list of group messages
*
* 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 GroupPrivateMessage
* @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);
}
/**
* Widget for showing list of group messages
*
* @category GroupPrivateMessage
* @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 GroupMessageList extends Widget
{
var $gm;
/**
* Constructor
*
* @param HTMLOutputter $out output context
* @param Group_message $gm Group message stream
*/
function __construct($out, $gm)
{
parent::__construct($out);
$this->gm = $gm;
}
/**
* Show the list
*
* @return void
*/
function show()
{
$this->out->elementStart('ul', 'notices messages group-messages');
$cnt = 0;
while ($this->gm->fetch() && $cnt <= MESSAGES_PER_PAGE) {
$cnt++;
if ($cnt > MESSAGES_PER_PAGE) {
break;
}
$gmli = new GroupMessageListItem($this->out, $this->gm);
$gmli->show();
}
$this->out->elementEnd('ul');
return $cnt;
}
}

View File

@ -0,0 +1,113 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Widget for showing an individual group message
*
* 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 GroupPrivateMessage
* @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);
}
/**
* Widget for showing a single group message
*
* @category GroupPrivateMessage
* @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 GroupMessageListItem extends Widget
{
var $gm;
/**
* Constructor
*
* @param HTMLOutputter $out output context
* @param Group_message $gm Group message
*/
function __construct($out, $gm)
{
parent::__construct($out);
$this->gm = $gm;
}
/**
* Show the item
*
* @return void
*/
function show()
{
$group = $this->gm->getGroup();
$sender = $this->gm->getSender();
$this->out->elementStart('li', array('class' => 'hentry notice message group-message',
'id' => 'message-' . $this->gm->id));
$this->out->elementStart('div', 'entry-title');
$this->out->elementStart('span', 'vcard author');
$this->out->elementStart('a',
array('href' => $sender->profileurl,
'class' => 'url'));
$avatar = $sender->getAvatar(AVATAR_STREAM_SIZE);
$this->out->element('img', array('src' => ($avatar) ?
$avatar->displayUrl() :
Avatar::defaultImage(AVATAR_STREAM_SIZE),
'width' => AVATAR_STREAM_SIZE,
'height' => AVATAR_STREAM_SIZE,
'class' => 'photo avatar',
'alt' => $sender->getBestName()));
$this->out->element('span',
array('class' => 'nickname fn'),
$sender->nickname);
$this->out->elementEnd('a');
$this->out->elementEnd('span');
$this->out->elementStart('p', array('class' => 'entry-content message-content'));
$this->out->raw($this->gm->rendered);
$this->out->elementEnd('p');
$this->out->elementEnd('div');
$this->out->elementStart('div', 'entry-content');
$this->out->elementStart('a', array('rel' => 'bookmark',
'class' => 'timestamp',
'href' => $this->gm->url));
$dt = common_date_iso8601($this->gm->created);
$this->out->element('abbr', array('class' => 'published',
'title' => $dt),
common_date_string($this->gm->created));
$this->out->elementEnd('a');
$this->out->elementEnd('div');
$this->out->elementEnd('li');
}
}

View File

@ -0,0 +1,161 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Action for adding a new group message
*
* 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);
}
/**
* Action for adding a new group message
*
* @category Action
* @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 NewgroupmessageAction extends Action
{
var $group;
var $user;
var $text;
/**
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
$this->user = common_current_user();
if (empty($this->user)) {
throw new ClientException(_('Must be logged in.'), 403);
}
if (!$this->user->hasRight(Right::NEWMESSAGE)) {
throw new Exception(sprintf(_('User %s not allowed to send private messages.'),
$this->user->nickname));
}
$nicknameArg = $this->trimmed('nickname');
$nickname = common_canonical_nickname($nicknameArg);
if ($nickname != $nicknameArg) {
$url = common_local_url('newgroupmessage', array('nickname' => $nickname));
common_redirect($url, 301);
return false;
}
$localGroup = Local_group::staticGet('nickname', $nickname);
if (empty($localGroup)) {
throw new ClientException(_('No such group'), 404);
}
$this->group = User_group::staticGet('id', $localGroup->group_id);
if (empty($this->group)) {
throw new ClientException(_('No such group'), 404);
}
// This throws an exception on error
Group_privacy_settings::ensurePost($this->user, $this->group);
// If we're posted to, check session token and get text
if ($this->isPost()) {
$this->checkSessionToken();
$this->text = $this->trimmed('content');
}
return true;
}
/**
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{
if ($this->isPost()) {
$this->sendNewMessage();
} else {
$this->showPage();
}
}
function showNoticeForm()
{
$form = new GroupMessageForm($this, $this->group);
$form->show();
}
function sendNewMessage()
{
$gm = Group_message::send($this->user, $this->group, $this->text);
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
$this->element('title', null, _('Message sent'));
$this->elementEnd('head');
$this->elementStart('body');
$this->element('p',
array('id' => 'command_result'),
sprintf(_('Direct message to %s sent.'),
$this->group->nickname));
$this->elementEnd('body');
$this->elementEnd('html');
} else {
common_redirect($gm->url, 303);
}
}
function title()
{
return sprintf(_('New message to group %s'), $this->group->nickname);
}
}

View File

@ -0,0 +1,188 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Show a single group message
*
* 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 GroupPrivateMessage
* @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);
}
/**
* Show a single private group message
*
* @category GroupPrivateMessage
* @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 ShowgroupmessageAction extends Action
{
var $gm;
var $group;
var $sender;
var $user;
/**
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
$this->user = common_current_user();
if (empty($this->user)) {
throw new ClientException(_('Only logged-in users can view private messages.'),
403);
}
$id = $this->trimmed('id');
$this->gm = Group_message::staticGet('id', $id);
if (empty($this->gm)) {
throw new ClientException(_('No such message'), 404);
}
$this->group = User_group::staticGet('id', $this->gm->to_group);
if (empty($this->group)) {
throw new ServerException(_('Group not found.'));
}
if (!$this->user->isMember($this->group)) {
throw new ClientException(_('Cannot read message.'), 403);
}
$this->sender = Profile::staticGet('id', $this->gm->from_profile);
if (empty($this->sender)) {
throw new ServerException(_('No sender found.'));
}
return true;
}
/**
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{
$this->showPage();
}
/**
* Title of the page
*/
function title()
{
return sprintf(_('Message from %1$s to group %2$s on %3$s'),
$this->sender->nickname,
$this->group->nickname,
common_exact_date($this->gm->created));
}
/**
* Show the content area.
*/
function showContent()
{
$this->elementStart('ul', 'notices messages');
$gmli = new GroupMessageListItem($this, $this->gm);
$gmli->show();
$this->elementEnd('ul');
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
/**
* Return last modified, if applicable.
*
* MAY override
*
* @return string last modified http header
*/
function lastModified()
{
return max(strtotime($this->group->modified),
strtotime($this->sender->modified),
strtotime($this->gm->modified));
}
/**
* Return etag, if applicable.
*
* MAY override
*
* @return string etag http header
*/
function etag()
{
$avatar = $this->sender->getAvatar(AVATAR_STREAM_SIZE);
$avtime = ($avatar) ? strtotime($avatar->modified) : 0;
return 'W/"' . implode(':', array($this->arg('action'),
common_user_cache_hash(),
common_language(),
$this->gm->id,
strtotime($this->sender->modified),
strtotime($this->group->modified),
$avtime)) . '"';
}
}

View File

@ -40,8 +40,8 @@ class InfiniteScrollPlugin extends Plugin
function onEndShowScripts($action)
{
$action->script('plugins/InfiniteScroll/jquery.infinitescroll.js');
$action->script('plugins/InfiniteScroll/infinitescroll.js');
$action->script($this->path('jquery.infinitescroll.js'));
$action->script($this->path('infinitescroll.js'));
}
function onPluginVersion(&$versions)

View File

@ -51,7 +51,7 @@ class LinkPreviewPlugin extends Plugin
{
$user = common_current_user();
if ($user && common_config('attachments', 'process_links')) {
$action->script('plugins/LinkPreview/linkpreview.min.js');
$action->script($this->path('linkpreview.min.js'));
$data = json_encode(array(
'api' => common_local_url('oembedproxy'),
'width' => common_config('attachments', 'thumbwidth'),

View File

@ -129,7 +129,7 @@ class MapstractionPlugin extends Plugin
break;
case 'openlayers':
// Use our included stripped & minified OpenLayers.
$action->script(common_path('plugins/Mapstraction/OpenLayers/OpenLayers.js'));
$action->script($this->path('OpenLayers/OpenLayers.js'));
break;
case 'yahoo':
$action->script(sprintf('http://api.maps.yahoo.com/ajaxymap?v=3.8&appid=%s',
@ -145,13 +145,13 @@ class MapstractionPlugin extends Plugin
//
// Note that OpenLayers.js needs to be separate, or it won't
// be able to find its UI images and styles.
$action->script(common_path('plugins/Mapstraction/usermap-mxn-openlayers.min.js'));
$action->script($this->path('usermap-mxn-openlayers.min.js'));
} else {
$action->script(sprintf('%s?(%s)',
common_path('plugins/Mapstraction/js/mxn.js'),
$this->path('js/mxn.js'),
$this->provider));
$action->script(common_path('plugins/Mapstraction/usermap.js'));
$action->script($this->path('usermap.js'));
}
$action->inlineScript(sprintf('var _provider = "%s";', $this->provider));

View File

@ -89,7 +89,7 @@ class MeteorPlugin extends RealtimePlugin
{
$scripts = parent::_getScripts();
$scripts[] = 'http://'.$this->webserver.(($this->webport == 80) ? '':':'.$this->webport).'/meteor.js';
$scripts[] = common_path('plugins/Meteor/meteorupdater.min.js');
$scripts[] = $this->path('meteorupdater.min.js');
return $scripts;
}

View File

@ -241,13 +241,13 @@ class MobileProfilePlugin extends WAP20Plugin
if (file_exists(Theme::file('css/mp-screen.css'))) {
$action->cssLink('css/mp-screen.css', null, 'screen');
} else {
$action->cssLink('plugins/MobileProfile/mp-screen.css',null,'screen');
$action->cssLink($this->path('mp-screen.css'),null,'screen');
}
if (file_exists(Theme::file('css/mp-handheld.css'))) {
$action->cssLink('css/mp-handheld.css', null, 'handheld');
} else {
$action->cssLink('plugins/MobileProfile/mp-handheld.css',null,'handheld');
$action->cssLink($this->path('mp-handheld.css'),null,'handheld');
}
// Allow other plugins to load their styles.

View File

@ -51,13 +51,13 @@ class ModPlusPlugin extends Plugin
{
$user = common_current_user();
if ($user) {
$action->script('plugins/ModPlus/modplus.js');
$action->script($this->path('modplus.js'));
}
return true;
}
function onEndShowStatusNetStyles($action) {
$action->cssLink('plugins/ModPlus/modplus.css');
$action->cssLink($this->path('modplus.css'));
return true;
}

View File

@ -326,12 +326,12 @@ class NewMenuPlugin extends Plugin
function onEndShowStyles($action)
{
if (($this->showCSS ||
if (($this->loadCSS ||
in_array(common_config('site', 'theme'),
array('default', 'identica', 'h4ck3r'))) &&
($action instanceof AccountSettingsAction ||
$action instanceof ConnectSettingsAction)) {
$action->cssLink(common_path('plugins/NewMenu/newmenu.css'));
$action->cssLink($this->path('newmenu.css'));
}
return true;
}

View File

@ -41,7 +41,12 @@ border-radius-topright:7px;
border-radius-topright:0;
-moz-border-radius-topright:0;
-webkit-border-top-right-radius:0;
min-height: 360px;
}
body[id$=settings] #aside_primary {
display:none;
float: right;
width: 17.25%;
margin-right: 10.45%;
margin-top: 6px;
min-height: 0px;
}

View File

@ -419,12 +419,12 @@ class OStatusPlugin extends Plugin
}
function onEndShowStatusNetStyles($action) {
$action->cssLink('plugins/OStatus/theme/base/css/ostatus.css');
$action->cssLink($this->path('theme/base/css/ostatus.css'));
return true;
}
function onEndShowStatusNetScripts($action) {
$action->script('plugins/OStatus/js/ostatus.js');
$action->script($this->path('js/ostatus.js'));
return true;
}

View File

@ -1112,7 +1112,8 @@ class Ostatus_profile extends Memcached_DataObject
return $url;
}
}
return common_path('plugins/OStatus/images/96px-Feed-icon.svg.png');
return Plugin::staticPath('OStatus', 'images/96px-Feed-icon.svg.png');
}
/**
@ -1781,12 +1782,14 @@ class Ostatus_profile extends Memcached_DataObject
$oprofile = Ostatus_profile::ensureWebfinger($rest);
break;
default:
common_log(LOG_WARNING,
"Unrecognized URI protocol for profile: $protocol ($uri)");
throw new ServerException("Unrecognized URI protocol for profile: $protocol ($uri)");
break;
}
} else {
throw new ServerException("No URI protocol for profile: ($uri)");
}
}
return $oprofile;
}

View File

@ -116,8 +116,9 @@ class RealtimePlugin extends Plugin
function onEndShowStatusNetStyles($action)
{
$action->cssLink('plugins/Realtime/realtimeupdate.css',
null, 'screen, projection, tv');
$action->cssLink(Plugin::staticPath('Realtime', 'realtimeupdate.css'),
null,
'screen, projection, tv');
return true;
}
@ -322,7 +323,7 @@ class RealtimePlugin extends Plugin
function _getScripts()
{
return array('plugins/Realtime/realtimeupdate.min.js');
return array(Plugin::staticPath('Realtime', 'realtimeupdate.min.js'));
}
/**

View File

@ -33,7 +33,7 @@ class ShareNoticePlugin extends Plugin
);
function onEndShowStatusNetStyles($action) {
$action->cssLink('plugins/ShareNotice/css/sharenotice.css');
$action->cssLink($this->path('css/sharenotice.css'));
return true;
}

View File

@ -41,7 +41,7 @@ class TabFocusPlugin extends Plugin
function onEndShowScripts($action)
{
$action->script('plugins/TabFocus/tabfocus.js');
$action->script($this->path('tabfocus.js'));
}
function onPluginVersion(&$versions)

View File

@ -39,6 +39,10 @@ if (!defined('STATUSNET')) {
*
* Converts the notice form in browser to a rich-text editor.
*
* FIXME: this plugin DOES NOT load its static files from the configured
* plugin server if one exists. There are cross-server permissions errors
* if you try to do that (something about window.tinymce).
*
* @category WYSIWYG
* @package StatusNet
* @author Evan Prodromou <evan@status.net>

View File

@ -45,7 +45,7 @@ function add_twitter_user($twitter_id, $screen_name)
$fuser = new Foreign_user();
$fuser->nickname = $screen_name;
$fuser->uri = 'http://twitter.com/#!/' . $screen_name;
$fuser->uri = 'http://twitter.com/' . $screen_name;
$fuser->id = $twitter_id;
$fuser->service = TWITTER_SERVICE;
$fuser->created = common_sql_now();
@ -173,18 +173,20 @@ function broadcast_twitter($notice)
// Don't bother with basic auth, since it's no longer allowed
if (!empty($flink) && TwitterOAuthClient::isPackedToken($flink->credentials)) {
if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) {
$retweet = retweet_notice($flink, Notice::staticGet('id', $notice->repeat_of));
if (is_object($retweet)) {
Notice_to_status::saveNew($notice->id, twitter_id($retweet));
return true;
if (is_twitter_bound($notice, $flink)) {
if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) {
$retweet = retweet_notice($flink, Notice::staticGet('id', $notice->repeat_of));
if (is_object($retweet)) {
Notice_to_status::saveNew($notice->id, twitter_id($retweet));
return true;
} else {
// Our error processing will have decided if we need to requeue
// this or can discard safely.
return $retweet;
}
} else {
// Our error processing will have decided if we need to requeue
// this or can discard safely.
return $retweet;
return broadcast_oauth($notice, $flink);
}
} else if (is_twitter_bound($notice, $flink)) {
return broadcast_oauth($notice, $flink);
}
}

View File

@ -264,7 +264,7 @@ class TwitterImport
function ensureProfile($user)
{
// check to see if there's already a profile for this user
$profileurl = 'http://twitter.com/#!/' . $user->screen_name;
$profileurl = 'http://twitter.com/' . $user->screen_name;
$profile = $this->getProfileByUrl($user->screen_name, $profileurl);
if (!empty($profile)) {

View File

@ -83,7 +83,7 @@ class TwitterloginAction extends Action
$this->elementStart('a', array('href' => common_local_url('twitterauthorization',
null,
array('signin' => true))));
$this->element('img', array('src' => common_path('plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png'),
$this->element('img', array('src' => Plugin::staticPath('TwitterBridge', 'Sign-in-with-Twitter-lighter.png'),
'alt' => _m('Sign in with Twitter')));
$this->elementEnd('a');
}

View File

@ -176,12 +176,12 @@ class YammeradminpanelAction extends AdminPanelAction
function showStylesheets()
{
parent::showStylesheets();
$this->cssLink('plugins/YammerImport/css/admin.css', null, 'screen, projection, tv');
$this->cssLink(Plugin::staticPath('YammerImport', 'css/admin.css'), null, 'screen, projection, tv');
}
function showScripts()
{
parent::showScripts();
$this->script('plugins/YammerImport/js/yammer-admin.js');
$this->script(Plugin::staticPath('YammerImport', 'js/yammer-admin.js'));
}
}