Commit b0927663 authored by Brion Vibber's avatar Brion Vibber

Merge branch 'master' into 0.9.x

parents 308e615a 2bf8a689
......@@ -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
......@@ -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
=======
......
This diff is collapsed.
......@@ -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();
}
}
\ No newline at end of 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 ($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;
}
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');
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));
} 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;
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $nickname) == 0) {
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.
$this->showForm(_('Alias cannot be the same as nickname.'));
// 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;
}
}
$cur = common_current_user();
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
// Checked in prepare() above
assert(!is_null($cur));
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));
$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);
$this->group = $group;
Event::handle('EndGroupSaveForm', array($this));
common_redirect($group->homeUrl(), 303);
}
}
function nicknameExists($nickname)
......
......@@ -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();
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -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,42 +116,18 @@ class ShowmessageAction extends MailboxAction
}
}
function getMessages()
{
$message = new Message();
$message->id = $this->message->id;
$message->find();
return $message;
}
function getMessageProfile()
function showContent()
{
if ($this->user->id == $this->message->from_profile) {
return $this->message->getTo();
} else if ($this->user->id == $this->message->to_profile) {
return $this->message->getFrom();
} else {
// This shouldn't happen
return null;
}
$this->elementStart('ul', 'notices messages');
$ml = new ShowMessageListItem($this, $this->message, $this->user);
$ml->show();
$this->elementEnd('ul');
}
/**
* Don't show local navigation
*
* @return void
*/
function showLocalNavBlock()
{
}
/**
* Don't show page notice
*
* @return void
*/
function showPageNoticeBlock()
function isReadOnly($args)
{
return true;
}
/**
......@@ -164,22 +135,30 @@ class ShowmessageAction extends MailboxAction
*
* @return void
*/
function showAside()
{
function showAside() {
}
}
/**
* Don't show any instructions
*
* @return string
*/
function getInstructions()
class ShowMessageListItem extends MessageListItem
{
var $user;
function __construct($out, $message, $user)
{
return '';
parent::__construct($out, $message);
$this->user = $user;
}
function isReadOnly($args)
function getMessageProfile()
{
return true;
if ($this->user->id == $this->message->from_profile) {
return $this->message->getTo();
} else if ($this->user->id == $this->message->to_profile) {
return $this->message->getFrom();
} else {
// This shouldn't happen
return null;
}
}
}
......@@ -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;
}
......
......@@ -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));
}
......
......@@ -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));
}
}
}
......@@ -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.
......
......@@ -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()) {
......
......@@ -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.'));
}
$result = $group->insert();
if (!isset($uri) || empty($uri)) {
$orig = clone($group);
$group->uri = common_local_url('groupbyid', array('id' => $group->id));
$result = $group->update($orig);