summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README8
-rw-r--r--actions/block.php2
-rw-r--r--actions/blockedfromgroup.php315
-rw-r--r--actions/editgroup.php59
-rw-r--r--actions/groupblock.php215
-rw-r--r--actions/groupmembers.php325
-rw-r--r--actions/groupunblock.php149
-rw-r--r--actions/joingroup.php5
-rw-r--r--actions/makeadmin.php166
-rw-r--r--actions/newgroup.php51
-rw-r--r--actions/showgroup.php15
-rw-r--r--actions/showstream.php8
-rw-r--r--actions/subscribers.php32
-rw-r--r--actions/subscriptions.php34
-rw-r--r--actions/unblock.php2
-rw-r--r--classes/Group_alias.php41
-rw-r--r--classes/Group_block.php115
-rw-r--r--classes/Notice.php13
-rw-r--r--classes/Status_network.php25
-rw-r--r--classes/User_group.php102
-rwxr-xr-xclasses/laconica.ini18
-rw-r--r--[-rwxr-xr-x]classes/statusnet.ini5
-rw-r--r--db/laconica.sql75
-rw-r--r--db/site.sql6
-rw-r--r--index.php21
-rw-r--r--lib/common.php2
-rw-r--r--lib/groupeditform.php11
-rw-r--r--lib/grouplist.php2
-rw-r--r--lib/groupnav.php6
-rw-r--r--lib/noticeform.php4
-rw-r--r--lib/noticelist.php14
-rw-r--r--lib/peoplesearchresults.php15
-rw-r--r--lib/profileaction.php4
-rw-r--r--lib/profilelist.php170
-rw-r--r--lib/profileminilist.php24
-rw-r--r--lib/router.php17
-rw-r--r--lib/subscriptionlist.php131
-rw-r--r--lib/theme.php27
-rw-r--r--lib/util.php2
-rwxr-xr-xscripts/maildaemon.php14
-rw-r--r--theme/base/css/display.css22
-rw-r--r--theme/base/css/ie.css9
-rw-r--r--theme/default/css/display.css8
-rw-r--r--theme/default/css/ie.css7
-rw-r--r--theme/identica/css/display.css8
-rw-r--r--theme/identica/css/ie.css7
46 files changed, 2082 insertions, 229 deletions
diff --git a/README b/README
index 2099f94d6..8fb4a941c 100644
--- a/README
+++ b/README
@@ -1196,7 +1196,6 @@ reporturl: URL to post statistics to. Defaults to Laconica developers'
set 'run' to 'never' than to set this value to something
nonsensical.
-
attachments
-----------
@@ -1226,6 +1225,13 @@ user_quota: total size in bytes a user can store on this server. Each user
monthly_quota: total size permitted in the current month. This is the total
size in bytes that a user can upload each month.
+group
+-----
+
+Options for group functionality.
+
+maxaliases: maximum number of aliases a group can have. Default 3. Set
+ to 0 or less to prevent aliases in a group.
Troubleshooting
===============
diff --git a/actions/block.php b/actions/block.php
index 34f991dc6..0efee5932 100644
--- a/actions/block.php
+++ b/actions/block.php
@@ -180,7 +180,7 @@ class BlockAction extends Action
if ($action) {
common_redirect(common_local_url($action, $args), 303);
} else {
- common_redirect(common_local_url('subscriptions',
+ common_redirect(common_local_url('subscribers',
array('nickname' => $cur->nickname)),
303);
}
diff --git a/actions/blockedfromgroup.php b/actions/blockedfromgroup.php
new file mode 100644
index 000000000..541ebcfd9
--- /dev/null
+++ b/actions/blockedfromgroup.php
@@ -0,0 +1,315 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * List of group members
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Group
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * List of profiles blocked from this group
+ *
+ * @category Group
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
+ */
+
+class BlockedfromgroupAction extends Action
+{
+ var $page = null;
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
+
+ $nickname_arg = $this->arg('nickname');
+ $nickname = common_canonical_nickname($nickname_arg);
+
+ // Permanent redirect on non-canonical nickname
+
+ if ($nickname_arg != $nickname) {
+ $args = array('nickname' => $nickname);
+ if ($this->page != 1) {
+ $args['page'] = $this->page;
+ }
+ common_redirect(common_local_url('blockedfromgroup', $args), 301);
+ return false;
+ }
+
+ if (!$nickname) {
+ $this->clientError(_('No nickname'), 404);
+ return false;
+ }
+
+ $this->group = User_group::staticGet('nickname', $nickname);
+
+ if (!$this->group) {
+ $this->clientError(_('No such group'), 404);
+ return false;
+ }
+
+ return true;
+ }
+
+ function title()
+ {
+ if ($this->page == 1) {
+ return sprintf(_('%s blocked profiles'),
+ $this->group->nickname);
+ } else {
+ return sprintf(_('%s blocked profiles, page %d'),
+ $this->group->nickname,
+ $this->page);
+ }
+ }
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $this->showPage();
+ }
+
+ function showPageNotice()
+ {
+ $this->element('p', 'instructions',
+ _('A list of the users blocked from joining this group.'));
+ }
+
+ function showLocalNav()
+ {
+ $nav = new GroupNav($this, $this->group);
+ $nav->show();
+ }
+
+ function showContent()
+ {
+ $offset = ($this->page-1) * PROFILES_PER_PAGE;
+ $limit = PROFILES_PER_PAGE + 1;
+
+ $cnt = 0;
+
+ $blocked = $this->group->getBlocked($offset, $limit);
+
+ if ($blocked) {
+ $blocked_list = new GroupBlockList($blocked, $this->group, $this);
+ $cnt = $blocked_list->show();
+ }
+
+ $blocked->free();
+
+ $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
+ $this->page, 'blockedfromgroup',
+ array('nickname' => $this->group->nickname));
+ }
+}
+
+class GroupBlockList extends ProfileList
+{
+ var $group = null;
+
+ function __construct($profile, $group, $action)
+ {
+ parent::__construct($profile, $action);
+
+ $this->group = $group;
+ }
+
+ function newListItem($profile)
+ {
+ return new GroupBlockListItem($profile, $this->group, $this->action);
+ }
+}
+
+class GroupBlockListItem extends ProfileListItem
+{
+ var $group = null;
+
+ function __construct($profile, $group, $action)
+ {
+ parent::__construct($profile, $action);
+
+ $this->group = $group;
+ }
+
+ function showActions()
+ {
+ $this->startActions();
+ $this->showGroupUnblockForm();
+ $this->endActions();
+ }
+
+ function showGroupUnblockForm()
+ {
+ $user = common_current_user();
+
+ if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group)) {
+ $this->out->elementStart('li', 'entity_block');
+ $bf = new GroupUnblockForm($this->out, $this->profile, $this->group,
+ array('action' => 'blockedfromgroup',
+ 'nickname' => $this->group->nickname));
+ $bf->show();
+ $this->out->elementEnd('li');
+ }
+ }
+}
+
+/**
+ * Form for unblocking a user from a group
+ *
+ * @category Form
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
+ *
+ * @see UnblockForm
+ */
+
+class GroupUnblockForm extends Form
+{
+ /**
+ * Profile of user to block
+ */
+
+ var $profile = null;
+
+ /**
+ * Group to block the user from
+ */
+
+ var $group = null;
+
+ /**
+ * Return-to args
+ */
+
+ var $args = null;
+
+ /**
+ * Constructor
+ *
+ * @param HTMLOutputter $out output channel
+ * @param Profile $profile profile of user to block
+ * @param User_group $group group to block user from
+ * @param array $args return-to args
+ */
+
+ function __construct($out=null, $profile=null, $group=null, $args=null)
+ {
+ parent::__construct($out);
+
+ $this->profile = $profile;
+ $this->group = $group;
+ $this->args = $args;
+ }
+
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ // This should be unique for the page.
+ return 'unblock-' . $this->profile->id;
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
+ return 'form_group_unblock';
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return common_local_url('groupunblock');
+ }
+
+ /**
+ * Legend of the Form
+ *
+ * @return void
+ */
+ function formLegend()
+ {
+ $this->out->element('legend', null, _('Unblock user from group'));
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->out->hidden('unblockto-' . $this->profile->id,
+ $this->profile->id,
+ 'unblockto');
+ $this->out->hidden('unblockgroup-' . $this->group->id,
+ $this->group->id,
+ 'unblockgroup');
+ if ($this->args) {
+ foreach ($this->args as $k => $v) {
+ $this->out->hidden('returnto-' . $k, $v);
+ }
+ }
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', _('Unblock'), 'submit', null, _('Unblock this user'));
+ }
+}
diff --git a/actions/editgroup.php b/actions/editgroup.php
index 39dad0465..29a7bce43 100644
--- a/actions/editgroup.php
+++ b/actions/editgroup.php
@@ -171,6 +171,7 @@ class EditgroupAction extends Action
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
+ $aliasstring = $this->trimmed('aliases');
if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
@@ -201,6 +202,39 @@ class EditgroupAction extends Action
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')) {
+ $this->showForm(sprintf(_('Too many aliases! Maximum %d.'),
+ common_config('group', 'maxaliases')));
+ return;
+ }
+
+ foreach ($aliases as $alias) {
+ if (!Validate::string($alias, array('min_length' => 1,
+ 'max_length' => 64,
+ 'format' => NICKNAME_FMT))) {
+ $this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
+ return;
+ }
+ if ($this->nicknameExists($alias)) {
+ $this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
+ $alias));
+ return;
+ }
+ // XXX assumes alphanum nicknames
+ if (strcmp($alias, $nickname) == 0) {
+ $this->showForm(_('Alias can\'t be the same as nickname.'));
+ return;
+ }
+ }
+
+ $this->group->query('BEGIN');
+
$orig = clone($this->group);
$this->group->nickname = $nickname;
@@ -217,6 +251,14 @@ class EditgroupAction extends Action
$this->serverError(_('Could not update group.'));
}
+ $result = $this->group->setAliases($aliases);
+
+ if (!$result) {
+ $this->serverError(_('Could not create aliases.'));
+ }
+
+ $this->group->query('COMMIT');
+
if ($this->group->nickname != $orig->nickname) {
common_redirect(common_local_url('editgroup',
array('nickname' => $nickname)),
@@ -229,9 +271,20 @@ class EditgroupAction extends Action
function nicknameExists($nickname)
{
$group = User_group::staticGet('nickname', $nickname);
- return (!is_null($group) &&
- $group != false &&
- $group->id != $this->group->id);
+
+ if (!empty($group) &&
+ $group->id != $this->group->id) {
+ return true;
+ }
+
+ $alias = Group_alias::staticGet('alias', $nickname);
+
+ if (!empty($alias) &&
+ $alias->group_id != $this->group->id) {
+ return true;
+ }
+
+ return false;
}
}
diff --git a/actions/groupblock.php b/actions/groupblock.php
new file mode 100644
index 000000000..93662da79
--- /dev/null
+++ b/actions/groupblock.php
@@ -0,0 +1,215 @@
+<?php
+/**
+ * Block a user from a group action class.
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, 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('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Block a user from a group
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ */
+
+class GroupblockAction extends Action
+{
+ var $profile = null;
+ var $group = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ if (!common_logged_in()) {
+ $this->clientError(_('Not logged in.'));
+ return false;
+ }
+ $token = $this->trimmed('token');
+ if (empty($token) || $token != common_session_token()) {
+ $this->clientError(_('There was a problem with your session token. Try again, please.'));
+ return;
+ }
+ $id = $this->trimmed('blockto');
+ if (empty($id)) {
+ $this->clientError(_('No profile specified.'));
+ return false;
+ }
+ $this->profile = Profile::staticGet('id', $id);
+ if (empty($this->profile)) {
+ $this->clientError(_('No profile with that ID.'));
+ return false;
+ }
+ $group_id = $this->trimmed('blockgroup');
+ if (empty($group_id)) {
+ $this->clientError(_('No group specified.'));
+ return false;
+ }
+ $this->group = User_group::staticGet('id', $group_id);
+ if (empty($this->group)) {
+ $this->clientError(_('No such group.'));
+ return false;
+ }
+ $user = common_current_user();
+ if (!$user->isAdmin($this->group)) {
+ $this->clientError(_('Only an admin can block group members.'), 401);
+ return false;
+ }
+ if (Group_block::isBlocked($this->group, $this->profile)) {
+ $this->clientError(_('User is already blocked from group.'));
+ return false;
+ }
+ // XXX: could have proactive blocks, but we don't have UI for it.
+ if (!$this->profile->isMember($this->group)) {
+ $this->clientError(_('User is not a member of group.'));
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Handle request
+ *
+ * Shows a page with list of favorite notices
+ *
+ * @param array $args $_REQUEST args; handled in prepare()
+ *
+ * @return void
+ */
+ function handle($args)
+ {
+ parent::handle($args);
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ if ($this->arg('no')) {
+ common_redirect(common_local_url('groupmembers',
+ array('nickname' => $this->group->nickname)),
+ 303);
+ } elseif ($this->arg('yes')) {
+ $this->blockProfile();
+ } elseif ($this->arg('blockto')) {
+ $this->showPage();
+ }
+ }
+ }
+
+ function showContent() {
+ $this->areYouSureForm();
+ }
+
+ function title() {
+ return _('Block user from group');
+ }
+
+ function showNoticeForm() {
+ // nop
+ }
+
+ /**
+ * Confirm with user.
+ *
+ * Shows a confirmation form.
+ *
+ * @return void
+ */
+
+ function areYouSureForm()
+ {
+ $id = $this->profile->id;
+ $this->element('p', null,
+ sprintf(_('Are you sure you want to block user "%s" from the group "%s"? '.
+ 'They will be removed from the group, unable to post, and '.
+ 'unable to subscribe to the group in the future.'),
+ $this->profile->getBestName(),
+ $this->group->getBestName()));
+ $this->elementStart('form', array('id' => 'block-' . $id,
+ 'method' => 'post',
+ 'class' => 'block',
+ 'action' => common_local_url('groupblock')));
+ $this->hidden('token', common_session_token());
+ $this->hidden('blockto-' . $this->profile->id,
+ $this->profile->id,
+ 'blockto');
+ $this->hidden('blockgroup-' . $this->group->id,
+ $this->group->id,
+ 'blockgroup');
+ foreach ($this->args as $k => $v) {
+ if (substr($k, 0, 9) == 'returnto-') {
+ $this->hidden($k, $v);
+ }
+ }
+ $this->submit('no', _('No'));
+ $this->submit('yes', _('Yes'));
+ $this->elementEnd('form');
+ }
+
+ /**
+ * Actually block a user.
+ *
+ * @return void
+ */
+
+ function blockProfile()
+ {
+ $block = Group_block::blockProfile($this->group, $this->profile,
+ common_current_user());
+
+ if (empty($block)) {
+ $this->serverError(_("Database error blocking user from group."));
+ return false;
+ }
+
+ // Now, gotta figure where we go back to
+ foreach ($this->args as $k => $v) {
+ if ($k == 'returnto-action') {
+ $action = $v;
+ } elseif (substr($k, 0, 9) == 'returnto-') {
+ $args[substr($k, 9)] = $v;
+ }
+ }
+
+ if ($action) {
+ common_redirect(common_local_url($action, $args), 303);
+ } else {
+ common_redirect(common_local_url('groupmembers',
+ array('nickname' => $this->group->nickname)),
+ 303);
+ }
+ }
+}
+
diff --git a/actions/groupmembers.php b/actions/groupmembers.php
index 21e5ebbaa..abfad3f0d 100644
--- a/actions/groupmembers.php
+++ b/actions/groupmembers.php
@@ -127,7 +127,7 @@ class GroupmembersAction extends Action
$members = $this->group->getMembers($offset, $limit);
if ($members) {
- $member_list = new ProfileList($members, null, $this);
+ $member_list = new GroupMemberList($members, $this->group, $this);
$cnt = $member_list->show();
}
@@ -138,3 +138,326 @@ class GroupmembersAction extends Action
array('nickname' => $this->group->nickname));
}
}
+
+class GroupMemberList extends ProfileList
+{
+ var $group = null;
+
+ function __construct($profile, $group, $action)
+ {
+ parent::__construct($profile, $action);
+
+ $this->group = $group;
+ }
+
+ function newListItem($profile)
+ {
+ return new GroupMemberListItem($profile, $this->group, $this->action);
+ }
+}
+
+class GroupMemberListItem extends ProfileListItem
+{
+ var $group = null;
+
+ function __construct($profile, $group, $action)
+ {
+ parent::__construct($profile, $action);
+
+ $this->group = $group;
+ }
+
+ function showActions()
+ {
+ $this->startActions();
+ $this->showSubscribeButton();
+ $this->showMakeAdminForm();
+ $this->showGroupBlockForm();
+ $this->endActions();
+ }
+
+ function showMakeAdminForm()
+ {
+ $user = common_current_user();
+
+ if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group) &&
+ !$this->profile->isAdmin($this->group)) {
+ $this->out->elementStart('li', 'entity_make_admin');
+ $maf = new MakeAdminForm($this->out, $this->profile, $this->group,
+ array('action' => 'groupmembers',
+ 'nickname' => $this->group->nickname));
+ $maf->show();
+ $this->out->elementEnd('li');
+ }
+
+ }
+ function showGroupBlockForm()
+ {
+ $user = common_current_user();
+
+ if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group)) {
+ $this->out->elementStart('li', 'entity_block');
+ $bf = new GroupBlockForm($this->out, $this->profile, $this->group,
+ array('action' => 'groupmembers',
+ 'nickname' => $this->group->nickname));
+ $bf->show();
+ $this->out->elementEnd('li');
+ }
+
+ }
+}
+
+/**
+ * Form for blocking a user from a group
+ *
+ * @category Form
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
+ *
+ * @see BlockForm
+ */
+
+class GroupBlockForm extends Form
+{
+ /**
+ * Profile of user to block
+ */
+
+ var $profile = null;
+
+ /**
+ * Group to block the user from
+ */
+
+ var $group = null;
+
+ /**
+ * Return-to args
+ */
+
+ var $args = null;
+
+ /**
+ * Constructor
+ *
+ * @param HTMLOutputter $out output channel
+ * @param Profile $profile profile of user to block
+ * @param User_group $group group to block user from
+ * @param array $args return-to args
+ */
+
+ function __construct($out=null, $profile=null, $group=null, $args=null)
+ {
+ parent::__construct($out);
+
+ $this->profile = $profile;
+ $this->group = $group;
+ $this->args = $args;
+ }
+
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ // This should be unique for the page.
+ return 'block-' . $this->profile->id;
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
+ return 'form_group_block';
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return common_local_url('groupblock');
+ }
+
+ /**
+ * Legend of the Form
+ *
+ * @return void
+ */
+ function formLegend()
+ {
+ $this->out->element('legend', null, _('Block user from group'));
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->out->hidden('blockto-' . $this->profile->id,
+ $this->profile->id,
+ 'blockto');
+ $this->out->hidden('blockgroup-' . $this->group->id,
+ $this->group->id,
+ 'blockgroup');
+ if ($this->args) {
+ foreach ($this->args as $k => $v) {
+ $this->out->hidden('returnto-' . $k, $v);
+ }
+ }
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', _('Block'), 'submit', null, _('Block this user'));
+ }
+}
+
+/**
+ * Form for making a user an admin for a group
+ *
+ * @category Form
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
+ */
+
+class MakeAdminForm extends Form
+{
+ /**
+ * Profile of user to block
+ */
+
+ var $profile = null;
+
+ /**
+ * Group to block the user from
+ */
+
+ var $group = null;
+
+ /**
+ * Return-to args
+ */
+
+ var $args = null;
+
+ /**
+ * Constructor
+ *
+ * @param HTMLOutputter $out output channel
+ * @param Profile $profile profile of user to block
+ * @param User_group $group group to block user from
+ * @param array $args return-to args
+ */
+
+ function __construct($out=null, $profile=null, $group=null, $args=null)
+ {
+ parent::__construct($out);
+
+ $this->profile = $profile;
+ $this->group = $group;
+ $this->args = $args;
+ }
+
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ // This should be unique for the page.
+ return 'makeadmin-' . $this->profile->id;
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
+ return 'form_make_admin';
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return common_local_url('makeadmin', array('nickname' => $this->group->nickname));
+ }
+
+ /**
+ * Legend of the Form
+ *
+ * @return void
+ */
+
+ function formLegend()
+ {
+ $this->out->element('legend', null, _('Make user an admin of the group'));
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->out->hidden('profileid-' . $this->profile->id,
+ $this->profile->id,
+ 'profileid');
+ $this->out->hidden('groupid-' . $this->group->id,
+ $this->group->id,
+ 'groupid');
+ if ($this->args) {
+ foreach ($this->args as $k => $v) {
+ $this->out->hidden('returnto-' . $k, $v);
+ }
+ }
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', _('Make Admin'), 'submit', null, _('Make this user an admin'));
+ }
+}
diff --git a/actions/groupunblock.php b/actions/groupunblock.php
new file mode 100644
index 000000000..a0bcb01f9
--- /dev/null
+++ b/actions/groupunblock.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Block a user from a group action class.
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, 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('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Unlock a user from a group
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ */
+
+class GroupunblockAction extends Action
+{
+ var $profile = null;
+ var $group = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ if (!common_logged_in()) {
+ $this->clientError(_('Not logged in.'));
+ return false;
+ }
+ $token = $this->trimmed('token');
+ if (empty($token) || $token != common_session_token()) {
+ $this->clientError(_('There was a problem with your session token. Try again, please.'));
+ return;
+ }
+ $id = $this->trimmed('unblockto');
+ if (empty($id)) {
+ $this->clientError(_('No profile specified.'));
+ return false;
+ }
+ $this->profile = Profile::staticGet('id', $id);
+ if (empty($this->profile)) {
+ $this->clientError(_('No profile with that ID.'));
+ return false;
+ }
+ $group_id = $this->trimmed('unblockgroup');
+ if (empty($group_id)) {
+ $this->clientError(_('No group specified.'));
+ return false;
+ }
+ $this->group = User_group::staticGet('id', $group_id);
+ if (empty($this->group)) {
+ $this->clientError(_('No such group.'));
+ return false;
+ }
+ $user = common_current_user();
+ if (!$user->isAdmin($this->group)) {
+ $this->clientError(_('Only an admin can unblock group members.'), 401);
+ return false;
+ }
+ if (!Group_block::isBlocked($this->group, $this->profile)) {
+ $this->clientError(_('User is not blocked from group.'));
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Handle request
+ *
+ * @param array $args $_REQUEST args; handled in prepare()
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ $this->unblockProfile();
+ }
+ }
+
+ /**
+ * Unblock a user.
+ *
+ * @return void
+ */
+
+ function unblockProfile()
+ {
+ $result = Group_block::unblockProfile($this->group, $this->profile);
+
+ if (!$result) {
+ $this->serverError(_('Error removing the block.'));
+ return;
+ }
+
+ foreach ($this->args as $k => $v) {
+ if ($k == 'returnto-action') {
+ $action = $v;
+ } else if (substr($k, 0, 9) == 'returnto-') {
+ $args[substr($k, 9)] = $v;
+ }
+ }
+
+ if ($action) {
+ common_redirect(common_local_url($action, $args), 303);
+ } else {
+ common_redirect(common_local_url('blockedfromgroup',
+ array('nickname' => $this->group->nickname)),
+ 303);
+ }
+ }
+}
+
diff --git a/actions/joingroup.php b/actions/joingroup.php
index a5d82ddc7..0e4f96eaf 100644
--- a/actions/joingroup.php
+++ b/actions/joingroup.php
@@ -96,6 +96,11 @@ class JoingroupAction extends Action
return false;
}
+ if (Group_block::isBlocked($this->group, $cur->getProfile())) {
+ $this->clientError(_('You have been blocked from that group by the admin.'), 403);
+ return false;
+ }
+
return true;
}
diff --git a/actions/makeadmin.php b/actions/makeadmin.php
new file mode 100644
index 000000000..899c23ae4
--- /dev/null
+++ b/actions/makeadmin.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * Make another user an admin of a group
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, 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('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Make another user an admin of a group
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ */
+
+class MakeadminAction extends Action
+{
+ var $profile = null;
+ var $group = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ if (!common_logged_in()) {
+ $this->clientError(_('Not logged in.'));
+ return false;
+ }
+ $token = $this->trimmed('token');
+ if (empty($token) || $token != common_session_token()) {
+ $this->clientError(_('There was a problem with your session token. Try again, please.'));
+ return;
+ }
+ $id = $this->trimmed('profileid');
+ if (empty($id)) {
+ $this->clientError(_('No profile specified.'));
+ return false;
+ }
+ $this->profile = Profile::staticGet('id', $id);
+ if (empty($this->profile)) {
+ $this->clientError(_('No profile with that ID.'));
+ return false;
+ }
+ $group_id = $this->trimmed('groupid');
+ if (empty($group_id)) {
+ $this->clientError(_('No group specified.'));
+ return false;
+ }
+ $this->group = User_group::staticGet('id', $group_id);
+ if (empty($this->group)) {
+ $this->clientError(_('No such group.'));
+ return false;
+ }
+ $user = common_current_user();
+ if (!$user->isAdmin($this->group)) {
+ $this->clientError(_('Only an admin can make another user an admin.'), 401);
+ return false;
+ }
+ if ($this->profile->isAdmin($this->group)) {
+ $this->clientError(sprintf(_('%s is already an admin for group "%s".'),
+ $this->profile->getBestName(),
+ $this->group->getBestName()),
+ 401);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Handle request
+ *
+ * @param array $args $_REQUEST args; handled in prepare()
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ $this->makeAdmin();
+ }
+ }
+
+ /**
+ * Make user an admin
+ *
+ * @return void
+ */
+
+ function makeAdmin()
+ {
+ $member = Group_member::pkeyGet(array('group_id' => $this->group->id,
+ 'profile_id' => $this->profile->id));
+
+ if (empty($member)) {
+ $this->serverError(_('Can\'t get membership record for %s in group %s'),
+ $this->profile->getBestName(),
+ $this->group->getBestName());
+ }
+
+ $orig = clone($member);
+
+ $member->is_admin = 1;
+
+ $result = $member->update($orig);
+
+ if (!$result) {
+ common_log_db_error($member, 'UPDATE', __FILE__);
+ $this->serverError(_('Can\'t make %s an admin for group %s'),
+ $this->profile->getBestName(),
+ $this->group->getBestName());
+ }
+
+ foreach ($this->args as $k => $v) {
+ if ($k == 'returnto-action') {
+ $action = $v;
+ } else if (substr($k, 0, 9) == 'returnto-') {
+ $args[substr($k, 9)] = $v;
+ }
+ }
+
+ if ($action) {
+ common_redirect(common_local_url($action, $args), 303);
+ } else {
+ common_redirect(common_local_url('groupmembers',
+ array('nickname' => $this->group->nickname)),
+ 303);
+ }
+ }
+}
diff --git a/actions/newgroup.php b/actions/newgroup.php
index 67cd6b2f1..0289e77c2 100644
--- a/actions/newgroup.php
+++ b/actions/newgroup.php
@@ -123,6 +123,7 @@ class NewgroupAction extends Action
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
+ $aliasstring = $this->trimmed('aliases');
if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
@@ -153,6 +154,37 @@ class NewgroupAction extends Action
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')) {
+ $this->showForm(sprintf(_('Too many aliases! Maximum %d.'),
+ common_config('group', 'maxaliases')));
+ return;
+ }
+
+ foreach ($aliases as $alias) {
+ if (!Validate::string($alias, array('min_length' => 1,
+ 'max_length' => 64,
+ 'format' => NICKNAME_FMT))) {
+ $this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
+ return;
+ }
+ if ($this->nicknameExists($alias)) {
+ $this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
+ $alias));
+ return;
+ }
+ // XXX assumes alphanum nicknames
+ if (strcmp($alias, $nickname) == 0) {
+ $this->showForm(_('Alias can\'t be the same as nickname.'));
+ return;
+ }
+ }
+
$cur = common_current_user();
// Checked in prepare() above
@@ -177,6 +209,12 @@ class NewgroupAction extends Action
$this->serverError(_('Could not create group.'));
}
+ $result = $group->setAliases($aliases);
+
+ if (!$result) {
+ $this->serverError(_('Could not create aliases.'));
+ }
+
$member = new Group_member();
$member->group_id = $group->id;
@@ -199,7 +237,18 @@ class NewgroupAction extends Action
function nicknameExists($nickname)
{
$group = User_group::staticGet('nickname', $nickname);
- return (!is_null($group) && $group != false);
+
+ if (!empty($group)) {
+ return true;
+ }
+
+ $alias = Group_alias::staticGet('alias', $nickname);
+
+ if (!empty($alias)) {
+ return true;
+ }
+
+ return false;
}
}
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 29b6fa1e6..357f579d8 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -272,6 +272,17 @@ class ShowgroupAction extends Action
$this->elementEnd('dl');
}
+ if (common_config('group', 'maxaliases') > 0) {
+ $aliases = $this->group->getAliases();
+
+ if (!empty($aliases)) {
+ $this->elementStart('dl', 'entity_aliases');
+ $this->element('dt', null, _('Aliases'));
+ $this->element('dd', 'aliases', implode(' ', $aliases));
+ $this->elementEnd('dl');
+ }
+ }
+
$this->elementEnd('div');
$this->elementStart('div', 'entity_actions');
@@ -283,7 +294,7 @@ class ShowgroupAction extends Action
if ($cur->isMember($this->group)) {
$lf = new LeaveForm($this, $this->group);
$lf->show();
- } else {
+ } else if (!Group_block::isBlocked($this->group, $cur->getProfile())) {
$jf = new JoinForm($this, $this->group);
$jf->show();
}
@@ -344,7 +355,7 @@ class ShowgroupAction extends Action
$this->element('h2', null, _('Members'));
- $pml = new ProfileMiniList($member, null, $this);
+ $pml = new ProfileMiniList($member, $this);
$cnt = $pml->show();
if ($cnt == 0) {
$this->element('p', null, _('(None)'));
diff --git a/actions/showstream.php b/actions/showstream.php
index e2f4e24d4..72316b259 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -320,10 +320,14 @@ class ShowstreamAction extends ProfileAction
$blocked = $cur->hasBlocked($this->profile);
$this->elementStart('li', 'entity_block');
if ($blocked) {
- $ubf = new UnblockForm($this, $this->profile);
+ $ubf = new UnblockForm($this, $this->profile,
+ array('action' => 'showstream',
+ 'nickname' => $this->profile->nickname));
$ubf->show();
} else {
- $bf = new BlockForm($this, $this->profile);
+ $bf = new BlockForm($this, $this->profile,
+ array('action' => 'showstream',
+ 'nickname' => $this->profile->nickname));
$bf->show();
}
$this->elementEnd('li');
diff --git a/actions/subscribers.php b/actions/subscribers.php
index 4482de9a7..66ac00fb1 100644
--- a/actions/subscribers.php
+++ b/actions/subscribers.php
@@ -130,18 +130,34 @@ class SubscribersAction extends GalleryAction
}
}
-class SubscribersList extends ProfileList
+class SubscribersList extends SubscriptionList
{
- function showBlockForm()
+ function newListItem($profile)
{
- $bf = new BlockForm($this->out, $this->profile,
- array('action' => 'subscribers',
- 'nickname' => $this->owner->nickname));
- $bf->show();
+ return new SubscribersListItem($profile, $this->owner, $this->action);
}
+}
- function isReadOnly($args)
+class SubscribersListItem extends SubscriptionListItem
+{
+ function showActions()
{
- return true;
+ $this->startActions();
+ $this->showSubscribeButton();
+ // Relevant code!
+ $this->showBlockForm();
+ $this->endActions();
+ }
+
+ function showBlockForm()
+ {
+ $user = common_current_user();
+
+ if (!empty($user) && $this->owner->id == $user->id) {
+ $bf = new BlockForm($this->out, $this->profile,
+ array('action' => 'subscribers',
+ 'nickname' => $this->owner->nickname));
+ $bf->show();
+ }
}
}
diff --git a/actions/subscriptions.php b/actions/subscriptions.php
index 095b18ad8..4124abea4 100644
--- a/actions/subscriptions.php
+++ b/actions/subscriptions.php
@@ -137,22 +137,46 @@ class SubscriptionsAction extends GalleryAction
}
}
-class SubscriptionsList extends ProfileList
+// XXX SubscriptionsList and SubscriptionList are dangerously close
+
+class SubscriptionsList extends SubscriptionList
{
- function showOwnerControls($profile)
+ function newListItem($profile)
+ {
+ return new SubscriptionsListItem($profile, $this->owner, $this->action);
+ }
+}
+
+class SubscriptionsListItem extends SubscriptionListItem
+{
+ function showProfile()
+ {
+ $this->startProfile();
+ $this->showAvatar();
+ $this->showFullName();
+ $this->showLocation();
+ $this->showHomepage();
+ $this->showBio();
+ $this->showTags();
+ // Relevant portion!
+ $this->showOwnerControls();
+ $this->endProfile();
+ }
+
+ function showOwnerControls()
{
$sub = Subscription::pkeyGet(array('subscriber' => $this->owner->id,
- 'subscribed' => $profile->id));
+ 'subscribed' => $this->profile->id));
if (!$sub) {
return;
}
- $this->out->elementStart('form', array('id' => 'subedit-' . $profile->id,
+ $this->out->elementStart('form', array('id' => 'subedit-' . $this->profile->id,
'method' => 'post',
'class' => 'form_subscription_edit',
'action' => common_local_url('subedit')));
$this->out->hidden('token', common_session_token());
- $this->out->hidden('profile', $profile->id);
+ $this->out->hidden('profile', $this->profile->id);
$this->out->checkbox('jabber', _('Jabber'), $sub->jabber);
$this->out->checkbox('sms', _('SMS'), $sub->sms);
$this->out->submit('save', _('Save'));
diff --git a/actions/unblock.php b/actions/unblock.php
index 8573b2a87..6e671c9dd 100644
--- a/actions/unblock.php
+++ b/actions/unblock.php
@@ -118,7 +118,7 @@ class UnblockAction extends Action
if ($action) {
common_redirect(common_local_url($action, $args), 303);
} else {
- common_redirect(common_local_url('subscriptions',
+ common_redirect(common_local_url('subscribers',
array('nickname' => $cur->nickname)),
303);
}
diff --git a/classes/Group_alias.php b/classes/Group_alias.php
new file mode 100644
index 000000000..e801e50e1
--- /dev/null
+++ b/classes/Group_alias.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Table Definition for group_alias
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2009, Control Yourself, 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('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Group_alias extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'group_alias'; // table name
+ public $alias; // varchar(64) primary_key not_null
+ public $group_id; // int(4) not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Group_alias',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/classes/Group_block.php b/classes/Group_block.php
new file mode 100644
index 000000000..4c583d8e2
--- /dev/null
+++ b/classes/Group_block.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Table Definition for group_block
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, 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('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Group_block extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'group_block'; // table name
+ public $group_id; // int(4) primary_key not_null
+ public $blocked; // int(4) primary_key not_null
+ public $blocker; // int(4) not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Group_block',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function &pkeyGet($kv)
+ {
+ return Memcached_DataObject::pkeyGet('Group_block', $kv);
+ }
+
+ static function isBlocked($group, $profile)
+ {
+ $block = Group_block::pkeyGet(array('group_id' => $group->id,
+ 'blocked' => $profile->id));
+ return !empty($block);
+ }
+
+ static function blockProfile($group, $profile, $blocker)
+ {
+ // Insert the block
+
+ $block = new Group_block();
+
+ $block->query('BEGIN');
+
+ $block->group_id = $group->id;
+ $block->blocked = $profile->id;
+ $block->blocker = $blocker->id;
+
+ $result = $block->insert();
+
+ if (!$result) {
+ common_log_db_error($block, 'INSERT', __FILE__);
+ return null;
+ }
+
+ // Delete membership if any
+
+ $member = new Group_member();
+
+ $member->group_id = $group->id;
+ $member->profile_id = $profile->id;
+
+ if ($member->find(true)) {
+ $result = $member->delete();
+ if (!$result) {
+ common_log_db_error($member, 'DELETE', __FILE__);
+ return null;
+ }
+ }
+
+ // Commit, since both have been done
+
+ $block->query('COMMIT');
+
+ return $block;
+ }
+
+ static function unblockProfile($group, $profile)
+ {
+ $block = Group_block::pkeyGet(array('group_id' => $group->id,
+ 'blocked' => $profile->id));
+
+ if (empty($block)) {
+ return null;
+ }
+
+ $result = $block->delete();
+
+ if (!$result) {
+ common_log_db_error($block, 'DELETE', __FILE__);
+ return null;
+ }
+
+ return true;
+ }
+
+}
diff --git a/classes/Notice.php b/classes/Notice.php
index 1c4858149..68602b1f7 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -125,7 +125,12 @@ class Notice extends Memcached_DataObject
$profile = Profile::staticGet($profile_id);
- $final = common_shorten_links($content);
+ $final = common_shorten_links($content);
+
+ if (mb_strlen($final) > 140) {
+ common_log(LOG_INFO, 'Rejecting notice that is too long.');
+ return _('Problem saving notice. Too long.');
+ }
if (!$profile) {
common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
@@ -747,16 +752,16 @@ class Notice extends Memcached_DataObject
foreach (array_unique($match[1]) as $nickname) {
/* XXX: remote groups. */
- $group = User_group::staticGet('nickname', $nickname);
+ $group = User_group::getForNickname($nickname);
- if (!$group) {
+ if (empty($group)) {
continue;
}
// we automatically add a tag for every group name, too
$tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($nickname),
- 'notice_id' => $this->id));
+ 'notice_id' => $this->id));
if (is_null($tag)) {
$this->saveTag($nickname);
diff --git a/classes/Status_network.php b/classes/Status_network.php
index f7747f71d..d2b942bfb 100644
--- a/classes/Status_network.php
+++ b/classes/Status_network.php
@@ -12,11 +12,13 @@ class Status_network extends DB_DataObject
public $nickname; // varchar(64) primary_key not_null
public $hostname; // varchar(255) unique_key
public $pathname; // varchar(255) unique_key
- public $sitename; // varchar(255)
public $dbhost; // varchar(255)
public $dbuser; // varchar(255)
public $dbpass; // varchar(255)
public $dbname; // varchar(255)
+ public $sitename; // varchar(255)
+ public $theme; // varchar(255)
+ public $logo; // varchar(255)
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@@ -37,13 +39,19 @@ class Status_network extends DB_DataObject
return true;
}
- static function setupSite($servername, $pathname)
+ static function setupSite($servername, $pathname, $wildcard)
{
global $config;
- $parts = explode('.', $servername);
+ // XXX I18N, probably not crucial for hostnames
+ // XXX This probably needs a tune up
- $sn = Status_network::staticGet('nickname', $parts[0]);
+ if (0 == strncasecmp(strrev($wildcard), strrev($servername), strlen($wildcard))) {
+ $parts = explode('.', $servername);
+ $sn = Status_network::staticGet('nickname', strtolower($parts[0]));
+ } else {
+ $sn = Status_network::staticGet('hostname', strtolower($servername));
+ }
if (!empty($sn)) {
$dbhost = (empty($sn->dbhost)) ? 'localhost' : $sn->dbhost;
@@ -52,7 +60,16 @@ class Status_network extends DB_DataObject
$dbname = (empty($sn->dbname)) ? $sn->nickname : $sn->dbname;
$config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
+
$config['site']['name'] = $sn->sitename;
+
+ if (!empty($sn->theme)) {
+ $config['site']['theme'] = $sn->theme;
+ }
+ if (!empty($sn->logo)) {
+ $config['site']['logo'] = $sn->logo;
+ }
+
return true;
} else {
return false;
diff --git a/classes/User_group.php b/classes/User_group.php
index a135015ba..1a24124bb 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -125,6 +125,29 @@ class User_group extends Memcached_DataObject
return $members;
}
+ function getBlocked($offset=0, $limit=null)
+ {
+ $qry =
+ 'SELECT profile.* ' .
+ 'FROM profile JOIN group_block '.
+ 'ON profile.id = group_block.blocked ' .
+ 'WHERE group_block.group_id = %d ' .
+ 'ORDER BY group_block.modified DESC ';
+
+ if ($limit != null) {
+ if (common_config('db','type') == 'pgsql') {
+ $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+ } else {
+ $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+ }
+ }
+
+ $blocked = new Profile();
+
+ $blocked->query(sprintf($qry, $this->id));
+ return $blocked;
+ }
+
function setOriginal($filename)
{
$imagefile = new ImageFile($this->id, Avatar::path($filename));
@@ -137,4 +160,83 @@ class User_group extends Memcached_DataObject
common_debug(common_log_objstring($this));
return $this->update($orig);
}
+
+ function getBestName()
+ {
+ return ($this->fullname) ? $this->fullname : $this->nickname;
+ }
+
+ function getAliases()
+ {
+ $aliases = array();
+
+ // XXX: cache this
+
+ $alias = new Group_alias();
+
+ $alias->group_id = $this->id;
+
+ if ($alias->find()) {
+ while ($alias->fetch()) {
+ $aliases[] = $alias->alias;
+ }
+ }
+
+ $alias->free();
+
+ return $aliases;
+ }
+
+ function setAliases($newaliases) {
+
+ $newaliases = array_unique($newaliases);
+
+ $oldaliases = $this->getAliases();
+
+ # Delete stuff that's old that not in new
+
+ $to_delete = array_diff($oldaliases, $newaliases);
+
+ # Insert stuff that's in new and not in old
+
+ $to_insert = array_diff($newaliases, $oldaliases);
+
+ $alias = new Group_alias();
+
+ $alias->group_id = $this->id;
+
+ foreach ($to_delete as $delalias) {
+ $alias->alias = $delalias;
+ $result = $alias->delete();
+ if (!$result) {
+ common_log_db_error($alias, 'DELETE', __FILE__);
+ return false;
+ }
+ }
+
+ foreach ($to_insert as $insalias) {
+ $alias->alias = $insalias;
+ $result = $alias->insert();
+ if (!$result) {
+ common_log_db_error($alias, 'INSERT', __FILE__);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ static function getForNickname($nickname)
+ {
+ $nickname = common_canonical_nickname($nickname);
+ $group = User_group::staticGet('nickname', $nickname);
+ if (!empty($group)) {
+ return $group;
+ }
+ $alias = Group_alias::staticGet('alias', $nickname);
+ if (!empty($alias)) {
+ return User_group::staticGet('id', $alias->group_id);
+ }
+ return null;
+ }
}
diff --git a/classes/laconica.ini b/classes/laconica.ini
index 07aa016fe..c04ae758f 100755
--- a/classes/laconica.ini
+++ b/classes/laconica.ini
@@ -170,6 +170,24 @@ id = K
service = K
uri = U
+[group_alias]
+alias = 130
+group_id = 129
+modified = 384
+
+[group_alias__keys]
+alias = K
+
+[group_block]
+group_id = 129
+blocked = 129
+blocker = 129
+modified = 384
+
+[group_block__keys]
+group_id = K
+blocked = K
+
[group_inbox]
group_id = 129
notice_id = 129
diff --git a/classes/statusnet.ini b/classes/statusnet.ini
index a70cd4122..8123265e4 100755..100644
--- a/classes/statusnet.ini
+++ b/classes/statusnet.ini
@@ -1,13 +1,14 @@
-
[status_network]
nickname = 130
hostname = 2
pathname = 2
-sitename = 2
dbhost = 2
dbuser = 2
dbpass = 2
dbname = 2
+sitename = 2
+theme = 2
+logo = 2
created = 142
modified = 384
diff --git a/db/laconica.sql b/db/laconica.sql
index 2ca084e48..bd95d1ade 100644
--- a/db/laconica.sql
+++ b/db/laconica.sql
@@ -431,49 +431,50 @@ create table group_inbox (
create table file (
id integer primary key auto_increment,
- url varchar(255), mimetype varchar(50),
- size integer,
- title varchar(255),
- date integer(11),
- protected integer(1),
+ url varchar(255) comment 'destination URL after following redirections',
+ mimetype varchar(50) comment 'mime type of resource',
+ size integer comment 'size of resource when available',
+ title varchar(255) comment 'title of resource when available',
+ date integer(11) comment 'date of resource according to http query',
+ protected integer(1) comment 'true when URL is private (needs login)',
unique(url)
) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
create table file_oembed (
id integer primary key auto_increment,
- file_id integer,
- version varchar(20),
- type varchar(20),
- provider varchar(50),
- provider_url varchar(255),
- width integer,
- height integer,
- html text,
- title varchar(255),
- author_name varchar(50),
- author_url varchar(255),
- url varchar(255),
+ file_id integer comment 'oEmbed for that URL/file' references file (id),
+ version varchar(20) comment 'oEmbed spec. version',
+ type varchar(20) comment 'oEmbed type: photo, video, link, rich',
+ provider varchar(50) comment 'name of this oEmbed provider',
+ provider_url varchar(255) comment 'URL of this oEmbed provider',
+ width integer comment 'width of oEmbed resource when available',
+ height integer comment 'height of oEmbed resource when available',
+ html text comment 'html representation of this oEmbed resource when applicable',
+ title varchar(255) comment 'title of oEmbed resource when available',
+ author_name varchar(50) comment 'author name for this oEmbed resource',
+ author_url varchar(255) comment 'author URL for this oEmbed resource',
+ url varchar(255) comment 'URL for this oEmbed resource when applicable (photo, link)',
unique(file_id)
) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
create table file_redirection (
id integer primary key auto_increment,
- url varchar(255),
- file_id integer,
- redirections integer,
- httpcode integer,
+ url varchar(255) comment 'short URL (or any other kind of redirect) for file (id)',
+ file_id integer comment 'short URL for what URL/file' references file (id),
+ redirections integer comment 'redirect count',
+ httpcode integer comment 'HTTP status code (20x, 30x, etc.)',
unique(url)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table file_thumbnail (
id integer primary key auto_increment,
- file_id integer,
- url varchar(255),
- width integer,
- height integer,
+ file_id integer comment 'thumbnail for what URL/file' references file (id),
+ url varchar(255) comment 'URL of thumbnail',
+ width integer comment 'width of thumbnail',
+ height integer comment 'height of thumbnail',
unique(file_id),
unique(url)
@@ -481,8 +482,8 @@ create table file_thumbnail (
create table file_to_post (
id integer primary key auto_increment,
- file_id integer,
- post_id integer,
+ file_id integer comment 'id of URL/file' references file (id),
+ post_id integer comment 'id of the notice it belongs to' references notice (id),
unique(file_id, post_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
@@ -496,3 +497,23 @@ create table design (
linkcolor integer comment 'link color',
backgroundimage varchar(255) comment 'background image, if any'
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_block (
+ group_id integer not null comment 'group profile is blocked from' references user_group (id),
+ blocked integer not null comment 'profile that is blocked' references profile (id),
+ blocker integer not null comment 'user making the block' references user (id),
+ modified timestamp comment 'date of blocking',
+
+ constraint primary key (group_id, blocked)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_alias (
+
+ alias varchar(64) primary key comment 'additional nickname for the group',
+ group_id integer not null comment 'group profile is blocked from' references user_group (id),
+ modified timestamp comment 'date alias was created',
+
+ index group_alias_group_id_idx (group_id)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/db/site.sql b/db/site.sql
index 660ba475b..a9f64e5a5 100644
--- a/db/site.sql
+++ b/db/site.sql
@@ -5,12 +5,16 @@ create table status_network (
nickname varchar(64) primary key comment 'nickname',
hostname varchar(255) unique key comment 'alternate hostname if any',
pathname varchar(255) unique key comment 'alternate pathname if any',
- sitename varchar(255) comment 'display name',
+
dbhost varchar(255) comment 'database host',
dbuser varchar(255) comment 'database username',
dbpass varchar(255) comment 'database password',
dbname varchar(255) comment 'database name',
+ sitename varchar(255) comment 'display name',
+ theme varchar(255) comment 'theme name',
+ logo varchar(255) comment 'site logo',
+
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified'
diff --git a/index.php b/index.php
index 4eff99dff..f5b32ea09 100644
--- a/index.php
+++ b/index.php
@@ -48,13 +48,18 @@ function handleError($error)
$logmsg .= " : ". $error->getDebugInfo();
}
common_log(LOG_ERR, $logmsg);
- $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
- 'so the site won\'t work properly. '.
- 'The site admins probably know about the problem, '.
- 'but you can contact them at %s to make sure. '.
- 'Otherwise, wait a few minutes and try again.'),
- common_config('site', 'name'),
- common_config('site', 'email'));
+ if ($error instanceof DB_DataObject_Error) {
+ $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
+ 'so the site won\'t work properly. '.
+ 'The site admins probably know about the problem, '.
+ 'but you can contact them at %s to make sure. '.
+ 'Otherwise, wait a few minutes and try again.'),
+ common_config('site', 'name'),
+ common_config('site', 'email'));
+ } else {
+ $msg = _('An important error occured, probably related to email setup. '.
+ 'Check logfiles for more info..');
+ }
$dac = new DBErrorAction($msg, 500);
$dac->showPage();
@@ -70,7 +75,7 @@ function main()
global $user, $action, $config;
Snapshot::check();
-
+
if (!_have_config()) {
$msg = sprintf(_("No configuration file found. Try running ".
"the installation program first."));
diff --git a/lib/common.php b/lib/common.php
index a55fb264e..eb3bf4e4d 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -206,6 +206,8 @@ $config =
'user_quota' => 50000000,
'monthly_quota' => 15000000,
),
+ 'group' =>
+ array('maxaliases' => 3),
);
$config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
diff --git a/lib/groupeditform.php b/lib/groupeditform.php
index ca674f3c8..7e8d6eea3 100644
--- a/lib/groupeditform.php
+++ b/lib/groupeditform.php
@@ -111,7 +111,6 @@ class GroupEditForm extends Form
}
}
-
/**
* Name of the form
*
@@ -157,6 +156,16 @@ class GroupEditForm extends Form
($this->out->arg('location')) ? $this->out->arg('location') : $this->group->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(_('Extra nicknames for the group, comma- or space- separated, max %d'),
+ common_config('group', 'maxaliases')));;
+ $this->out->elementEnd('li');
+ }
$this->out->elementEnd('ul');
}
diff --git a/lib/grouplist.php b/lib/grouplist.php
index 1b8547499..1ded5160b 100644
--- a/lib/grouplist.php
+++ b/lib/grouplist.php
@@ -166,7 +166,7 @@ class GroupList extends Widget
if ($user->isMember($this->group)) {
$lf = new LeaveForm($this->out, $this->group);
$lf->show();
- } else {
+ } else if (!Group_block::isBlocked($this->group, $user->getProfile())) {
$jf = new JoinForm($this->out, $this->group);
$jf->show();
}
diff --git a/lib/groupnav.php b/lib/groupnav.php
index 90bdc1014..194247982 100644
--- a/lib/groupnav.php
+++ b/lib/groupnav.php
@@ -95,6 +95,12 @@ class GroupNav extends Widget
$cur = common_current_user();
if ($cur && $cur->isAdmin($this->group)) {
+ $this->out->menuItem(common_local_url('blockedfromgroup', array('nickname' =>
+ $nickname)),
+ _('Blocked'),
+ sprintf(_('%s blocked users'), $nickname),
+ $action_name == 'blockedfromgroup',
+ 'nav_group_blocked');
$this->out->menuItem(common_local_url('editgroup', array('nickname' =>
$nickname)),
_('Admin'),
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 3212f382a..0ad365856 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -148,12 +148,12 @@ class NoticeForm extends Form
$this->out->element('dd', array('id' => 'notice_text-count'),
'140');
$this->out->elementEnd('dl');
- $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
- $this->out->element('label', array('for' => 'notice_data-attach'), _('Attach'));
+ $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
$this->out->element('input', array('id' => 'notice_data-attach',
'type' => 'file',
'name' => 'attach',
'title' => _('Attach a file')));
+ $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
if ($this->action) {
$this->out->hidden('notice_return-to', $this->action, 'returnto');
}
diff --git a/lib/noticelist.php b/lib/noticelist.php
index e44997004..ad792441a 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -205,24 +205,10 @@ class NoticeListItem extends Widget
return 'shownotice' !== $this->out->args['action'];
}
-/*
- function attachmentCount($discriminant = true) {
- $file_oembed = new File_oembed;
- $query = "select count(*) as c from file_oembed join file_to_post on file_oembed.file_id = file_to_post.file_id where post_id=" . $this->notice->id;
- $file_oembed->query($query);
- $file_oembed->fetch();
- return intval($file_oembed->c);
- }
-*/
-
- function showWithAttachment() {
- }
-
function showNoticeInfo()
{
$this->out->elementStart('div', 'entry-content');
$this->showNoticeLink();
-// $this->showWithAttachment();
$this->showNoticeSource();
$this->showContext();
$this->out->elementEnd('div');
diff --git a/lib/peoplesearchresults.php b/lib/peoplesearchresults.php
index d3f840852..9d9d17299 100644
--- a/lib/peoplesearchresults.php
+++ b/lib/peoplesearchresults.php
@@ -56,20 +56,25 @@ class PeopleSearchResults extends ProfileList
function __construct($profile, $terms, $action)
{
- parent::__construct($profile, $terms, $action);
+ parent::__construct($profile, $action);
+
$this->terms = array_map('preg_quote',
array_map('htmlspecialchars', $terms));
+
$this->pattern = '/('.implode('|',$terms).')/i';
}
- function highlight($text)
+ function newProfileItem($profile)
{
- return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
+ return new PeopleSearchResultItem($profile, $this->action);
}
+}
- function isReadOnly($args)
+class PeopleSearchResultItem extends ProfileListItem
+{
+ function highlight($text)
{
- return true;
+ return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
}
}
diff --git a/lib/profileaction.php b/lib/profileaction.php
index a14d3846e..2519922b2 100644
--- a/lib/profileaction.php
+++ b/lib/profileaction.php
@@ -109,7 +109,7 @@ class ProfileAction extends OwnerDesignAction
$this->element('h2', null, _('Subscriptions'));
if ($profile) {
- $pml = new ProfileMiniList($profile, $this->user, $this);
+ $pml = new ProfileMiniList($profile, $this);
$cnt = $pml->show();
if ($cnt == 0) {
$this->element('p', null, _('(None)'));
@@ -138,7 +138,7 @@ class ProfileAction extends OwnerDesignAction
$this->element('h2', null, _('Subscribers'));
if ($profile) {
- $pml = new ProfileMiniList($profile, $this->user, $this);
+ $pml = new ProfileMiniList($profile, $this);
$cnt = $pml->show();
if ($cnt == 0) {
$this->element('p', null, _('(None)'));
diff --git a/lib/profilelist.php b/lib/profilelist.php
index a4cc23555..a604230f8 100644
--- a/lib/profilelist.php
+++ b/lib/profilelist.php
@@ -49,25 +49,37 @@ class ProfileList extends Widget
{
/** Current profile, profile query. */
var $profile = null;
- /** Owner of this list */
- var $owner = null;
/** Action object using us. */
var $action = null;
- function __construct($profile, $owner=null, $action=null)
+ function __construct($profile, $action=null)
{
parent::__construct($action);
$this->profile = $profile;
- $this->owner = $owner;
$this->action = $action;
}
function show()
{
+ $this->startList();
+ $cnt = $this->showProfiles();
+ $this->endList();
+ return $cnt;
+ }
+ function startList()
+ {
$this->out->elementStart('ul', 'profiles');
+ }
+ function endList()
+ {
+ $this->out->elementEnd('ul');
+ }
+
+ function showProfiles()
+ {
$cnt = 0;
while ($this->profile->fetch()) {
@@ -75,24 +87,66 @@ class ProfileList extends Widget
if($cnt > PROFILES_PER_PAGE) {
break;
}
- $this->showProfile();
+ $pli = $this->newListItem($this->profile);
+ $pli->show();
}
- $this->out->elementEnd('ul');
-
return $cnt;
}
- function showProfile()
+ function newListItem($profile)
+ {
+ return new ProfileListItem($this->profile, $this->action);
+ }
+}
+
+class ProfileListItem extends Widget
+{
+ /** Current profile. */
+ var $profile = null;
+ /** Action object using us. */
+ var $action = null;
+
+ function __construct($profile, $action)
+ {
+ parent::__construct($action);
+
+ $this->profile = $profile;
+ $this->action = $action;
+ }
+
+ function show()
+ {
+ $this->startItem();
+ $this->showProfile();
+ $this->showActions();
+ $this->endItem();
+ }
+
+ function startItem()
{
$this->out->elementStart('li', array('class' => 'profile',
'id' => 'profile-' . $this->profile->id));
+ }
- $user = common_current_user();
- $is_own = !is_null($user) && isset($this->owner) && ($user->id === $this->owner->id);
+ function showProfile()
+ {
+ $this->startProfile();
+ $this->showAvatar();
+ $this->showFullName();
+ $this->showLocation();
+ $this->showHomepage();
+ $this->showBio();
+ $this->endProfile();
+ }
+ function startProfile()
+ {
$this->out->elementStart('div', 'entity_profile vcard');
+ }
+ function showAvatar()
+ {
$avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
$this->out->elementStart('a', array('href' => $this->profile->profileurl,
'class' => 'url'));
@@ -108,7 +162,10 @@ class ProfileList extends Widget
$this->out->raw($this->highlight($this->profile->nickname));
$this->out->elementEnd('span');
$this->out->elementEnd('a');
+ }
+ function showFullName()
+ {
if (!empty($this->profile->fullname)) {
$this->out->elementStart('dl', 'entity_fn');
$this->out->element('dt', null, 'Full name');
@@ -119,6 +176,10 @@ class ProfileList extends Widget
$this->out->elementEnd('dd');
$this->out->elementEnd('dl');
}
+ }
+
+ function showLocation()
+ {
if (!empty($this->profile->location)) {
$this->out->elementStart('dl', 'entity_location');
$this->out->element('dt', null, _('Location'));
@@ -127,6 +188,10 @@ class ProfileList extends Widget
$this->out->elementEnd('dd');
$this->out->elementEnd('dl');
}
+ }
+
+ function showHomepage()
+ {
if (!empty($this->profile->homepage)) {
$this->out->elementStart('dl', 'entity_url');
$this->out->element('dt', null, _('URL'));
@@ -138,6 +203,10 @@ class ProfileList extends Widget
$this->out->elementEnd('dd');
$this->out->elementEnd('dl');
}
+ }
+
+ function showBio()
+ {
if (!empty($this->profile->bio)) {
$this->out->elementStart('dl', 'entity_note');
$this->out->element('dt', null, _('Note'));
@@ -146,57 +215,33 @@ class ProfileList extends Widget
$this->out->elementEnd('dd');
$this->out->elementEnd('dl');
}
+ }
- # If we're on a list with an owner (subscriptions or subscribers)...
-
- if ($this->owner) {
- # Get tags
- $tags = Profile_tag::getTags($this->owner->id, $this->profile->id);
-
- $this->out->elementStart('dl', 'entity_tags');
- $this->out->elementStart('dt');
- if ($is_own) {
- $this->out->element('a', array('href' => common_local_url('tagother',
- array('id' => $this->profile->id))),
- _('Tags'));
- } else {
- $this->out->text(_('Tags'));
- }
- $this->out->elementEnd('dt');
- $this->out->elementStart('dd');
- if ($tags) {
- $this->out->elementStart('ul', 'tags xoxo');
- foreach ($tags as $tag) {
- $this->out->elementStart('li');
- $this->out->element('span', 'mark_hash', '#');
- $this->out->element('a', array('rel' => 'tag',
- 'href' => common_local_url($this->action->trimmed('action'),
- array('nickname' => $this->owner->nickname,
- 'tag' => $tag))),
- $tag);
- $this->out->elementEnd('li');
- }
- $this->out->elementEnd('ul');
- } else {
- $this->out->text(_('(none)'));
- }
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
- }
-
- if ($is_own) {
- $this->showOwnerControls($this->profile);
- }
-
+ function endProfile()
+ {
$this->out->elementEnd('div');
+ }
- $this->out->elementStart('div', 'entity_actions');
+ function showActions()
+ {
+ $this->startActions();
+ $this->showSubscribeButton();
+ $this->endActions();
+ }
+ function startActions()
+ {
+ $this->out->elementStart('div', 'entity_actions');
$this->out->elementStart('ul');
+ }
+ function showSubscribeButton()
+ {
// Is this a logged-in user, looking at someone else's
// profile?
+ $user = common_current_user();
+
if (!empty($user) && $this->profile->id != $user->id) {
$this->out->elementStart('li', 'entity_subscribe');
if ($user->isSubscribed($this->profile)) {
@@ -207,33 +252,22 @@ class ProfileList extends Widget
$sf->show();
}
$this->out->elementEnd('li');
- $this->out->elementStart('li', 'entity_block');
- if ($user->id == $this->owner->id) {
- $this->showBlockForm();
- }
- $this->out->elementEnd('li');
}
+ }
+ function endActions()
+ {
$this->out->elementEnd('ul');
-
$this->out->elementEnd('div');
-
- $this->out->elementEnd('li');
}
- /* Override this in subclasses. */
-
- function showOwnerControls($profile)
+ function endItem()
{
- return;
+ $this->out->elementEnd('li');
}
function highlight($text)
{
return htmlspecialchars($text);
}
-
- function showBlockForm()
- {
- }
}
diff --git a/lib/profileminilist.php b/lib/profileminilist.php
index 57496d0e9..09bef6f7c 100644
--- a/lib/profileminilist.php
+++ b/lib/profileminilist.php
@@ -47,26 +47,20 @@ define('PROFILES_PER_MINILIST', 27);
class ProfileMiniList extends ProfileList
{
- function show()
+ function startList()
{
$this->out->elementStart('ul', 'entities users xoxo');
+ }
- $cnt = 0;
-
- while ($this->profile->fetch()) {
- $cnt++;
- if($cnt > PROFILES_PER_MINILIST) {
- break;
- }
- $this->showProfile();
- }
-
- $this->out->elementEnd('ul');
-
- return $cnt;
+ function newListItem($profile)
+ {
+ return new ProfileMiniListItem($profile, $this->action);
}
+}
- function showProfile()
+class ProfileMiniListItem extends ProfileListItem
+{
+ function show()
{
$this->out->elementStart('li', 'vcard');
$this->out->elementStart('a', array('title' => $this->profile->getBestName(),
diff --git a/lib/router.php b/lib/router.php
index 456d1793e..0fbaba9ed 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -101,7 +101,8 @@ class Router
$main = array('login', 'logout', 'register', 'subscribe',
'unsubscribe', 'confirmaddress', 'recoverpassword',
'invite', 'favor', 'disfavor', 'sup',
- 'block', 'subedit');
+ 'block', 'unblock', 'subedit',
+ 'groupblock', 'groupunblock');
foreach ($main as $a) {
$m->connect('main/'.$a, array('action' => $a));
@@ -164,10 +165,10 @@ class Router
array('action' => 'newnotice'),
array('replyto' => '[A-Za-z0-9_-]+'));
- $m->connect('notice/:notice/file',
- array('action' => 'file'),
+ $m->connect('notice/:notice/file',
+ array('action' => 'file'),
array('notice' => '[0-9]+'));
-
+
$m->connect('notice/:notice',
array('action' => 'shownotice'),
array('notice' => '[0-9]+'));
@@ -228,6 +229,14 @@ class Router
array('nickname' => '[a-zA-Z0-9]+'));
}
+ $m->connect('group/:nickname/blocked',
+ array('action' => 'blockedfromgroup'),
+ array('nickname' => '[a-zA-Z0-9]+'));
+
+ $m->connect('group/:nickname/makeadmin',
+ array('action' => 'makeadmin'),
+ array('nickname' => '[a-zA-Z0-9]+'));
+
$m->connect('group/:id/id',
array('action' => 'groupbyid'),
array('id' => '[0-9]+'));
diff --git a/lib/subscriptionlist.php b/lib/subscriptionlist.php
new file mode 100644
index 000000000..23da64cca
--- /dev/null
+++ b/lib/subscriptionlist.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Widget to show a list of profiles
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Public
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/profilelist.php';
+
+/**
+ * Widget to show a list of subscriptions
+ *
+ * @category Public
+ * @package Laconica
+ * @author Zach Copley <zach@controlyourself.ca>
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
+ */
+
+class SubscriptionList extends ProfileList
+{
+ /** Owner of this list */
+ var $owner = null;
+
+ function __construct($profile, $owner=null, $action=null)
+ {
+ parent::__construct($profile, $action);
+
+ $this->owner = $owner;
+ }
+
+ function newListItem($profile)
+ {
+ return new SubscriptionListItem($profile, $this->owner, $this->action);
+ }
+}
+
+class SubscriptionListItem extends ProfileListItem
+{
+ /** Owner of this list */
+ var $owner = null;
+
+ function __construct($profile, $owner, $action)
+ {
+ parent::__construct($profile, $action);
+
+ $this->owner = $owner;
+ }
+
+ function showProfile()
+ {
+ $this->startProfile();
+ $this->showAvatar();
+ $this->showFullName();
+ $this->showLocation();
+ $this->showHomepage();
+ $this->showBio();
+ // Relevant portion!
+ $this->showTags();
+ $this->endProfile();
+ }
+
+ function isOwn()
+ {
+ $user = common_current_user();
+ return (!empty($user) && ($this->owner->id == $user->id));
+ }
+
+ function showTags()
+ {
+ $tags = Profile_tag::getTags($this->owner->id, $this->profile->id);
+
+ $this->out->elementStart('dl', 'entity_tags');
+ $this->out->elementStart('dt');
+ if ($this->isOwn()) {
+ $this->out->element('a', array('href' => common_local_url('tagother',
+ array('id' => $this->profile->id))),
+ _('Tags'));
+ } else {
+ $this->out->text(_('Tags'));
+ }
+ $this->out->elementEnd('dt');
+ $this->out->elementStart('dd');
+ if ($tags) {
+ $this->out->elementStart('ul', 'tags xoxo');
+ foreach ($tags as $tag) {
+ $this->out->elementStart('li');
+ $this->out->element('span', 'mark_hash', '#');
+ $this->out->element('a', array('rel' => 'tag',
+ 'href' => common_local_url($this->action->trimmed('action'),
+ array('nickname' => $this->owner->nickname,
+ 'tag' => $tag))),
+ $tag);
+ $this->out->elementEnd('li');
+ }
+ $this->out->elementEnd('ul');
+ } else {
+ $this->out->text(_('(none)'));
+ }
+ $this->out->elementEnd('dd');
+ $this->out->elementEnd('dl');
+ }
+}
diff --git a/lib/theme.php b/lib/theme.php
index bef660cbf..0d8824822 100644
--- a/lib/theme.php
+++ b/lib/theme.php
@@ -70,30 +70,3 @@ function theme_path($relative, $theme=null)
return common_path('theme/'.$theme.'/'.$relative);
}
}
-
-/**
- * Gets the full URL of a file in a skin dir based on its relative name
- *
- * @param string $relative relative path within the theme, skin directory
- * @param string $theme name of the theme; defaults to current theme
- * @param string $skin name of the skin; defaults to current theme
- *
- * @return string URL of the file
- */
-
-function skin_path($relative, $theme=null, $skin=null)
-{
- if (!$theme) {
- $theme = common_config('site', 'theme');
- }
- if (!$skin) {
- $skin = common_config('site', 'skin');
- }
- $server = common_config('theme', 'server');
- if ($server) {
- return 'http://'.$server.'/'.$theme.'/skin/'.$skin.'/'.$relative;
- } else {
- return common_path('theme/'.$theme.'/skin/'.$skin.'/'.$relative);
- }
-}
-
diff --git a/lib/util.php b/lib/util.php
index b3a94a5a0..49c6ae108 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -591,7 +591,7 @@ function common_at_link($sender_id, $nickname)
function common_group_link($sender_id, $nickname)
{
$sender = Profile::staticGet($sender_id);
- $group = User_group::staticGet('nickname', common_canonical_nickname($nickname));
+ $group = User_group::getForNickname($nickname);
if ($group && $sender->isMember($group)) {
$attrs = array('href' => $group->permalink(),
'class' => 'url');
diff --git a/scripts/maildaemon.php b/scripts/maildaemon.php
index b9facec1a..9dd647bf4 100755
--- a/scripts/maildaemon.php
+++ b/scripts/maildaemon.php
@@ -66,7 +66,13 @@ class MailerDaemon
return true;
}
$msg = $this->cleanup_msg($msg);
- $this->add_notice($user, $msg);
+ $err = $this->add_notice($user, $msg);
+ if (is_string($err)) {
+ $this->error($from, $err);
+ return false;
+ } else {
+ return true;
+ }
}
function error($from, $msg)
@@ -130,17 +136,15 @@ class MailerDaemon
function add_notice($user, $msg)
{
- // should test
- // $msg_shortened = common_shorten_links($msg);
- // if (mb_strlen($msg_shortened) > 140) ERROR and STOP
$notice = Notice::saveNew($user->id, $msg, 'mail');
if (is_string($notice)) {
$this->log(LOG_ERR, $notice);
- return;
+ return $notice;
}
common_broadcast_notice($notice);
$this->log(LOG_INFO,
'Added notice ' . $notice->id . ' from user ' . $user->nickname);
+ return true;
}
function parse_message($fname)
diff --git a/theme/base/css/display.css b/theme/base/css/display.css
index dc275e19f..0cbd0d774 100644
--- a/theme/base/css/display.css
+++ b/theme/base/css/display.css
@@ -445,6 +445,8 @@ width:80.789%;
height:67px;
line-height:1.5;
padding:7px 7px 16px 7px;
+position:relative;
+z-index:2;
}
#form_notice label {
display:block;
@@ -452,23 +454,23 @@ float:left;
font-size:1.3em;
margin-bottom:7px;
}
-#form_notice label[for=notice_data-attach] {
-text-indent:-9999px;
-}
#form_notice label[for=notice_data-attach],
#form_notice #notice_data-attach {
position:absolute;
top:25px;
-right:49px;
+cursor:pointer;
+}
+#form_notice label[for=notice_data-attach] {
+text-indent:-9999px;
+left:394px;
width:16px;
height:16px;
-cursor:pointer;
}
#form_notice #notice_data-attach {
-text-indent:-279px;
-}
-#form_notice #notice_submit label {
-display:none;
+left:183px;
+padding:0;
+
+height:16px;
}
#form_notice .form_note {
position:absolute;
@@ -616,6 +618,8 @@ display:block;
.form_user_block input.submit,
.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
.entity_send-a-message a,
.entity_edit a,
.form_user_nudge input.submit,
diff --git a/theme/base/css/ie.css b/theme/base/css/ie.css
index 8183fee67..d1b0558ec 100644
--- a/theme/base/css/ie.css
+++ b/theme/base/css/ie.css
@@ -8,6 +8,15 @@ top:0;
#form_notice textarea {
width:78%;
}
+#form_notice .form_note + label {
+position:absolute;
+top:25px;
+left:380px;
+text-indent:-9999px;
+height:16px;
+width:16px;
+display:block;
+}
#form_notice #notice_action-submit {
width:17%;
max-width:17%;
diff --git a/theme/default/css/display.css b/theme/default/css/display.css
index 34f6b3b8a..166e62157 100644
--- a/theme/default/css/display.css
+++ b/theme/default/css/display.css
@@ -57,6 +57,8 @@ a,
div.notice-options input,
.form_user_block input.submit,
.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
.entity_send-a-message a,
.form_user_nudge input.submit,
.entity_nudge p,
@@ -148,6 +150,8 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
.form_user_nudge input.submit,
.form_user_block input.submit,
.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
.entity_nudge p {
background-position: 0 40%;
background-repeat: no-repeat;
@@ -177,7 +181,9 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
background-image:url(../../base/images/icons/twotone/green/mail.gif);
}
.form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
background-image:url(../../base/images/icons/twotone/green/shield.gif);
}
diff --git a/theme/default/css/ie.css b/theme/default/css/ie.css
index 2b06768ea..6501f4e48 100644
--- a/theme/default/css/ie.css
+++ b/theme/default/css/ie.css
@@ -3,7 +3,12 @@
.notice-options input.submit {
color:#fff;
}
-
#site_nav_local_views a {
background-color:#ACCCDA;
}
+#form_notice .form_note + label {
+background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+}
+#form_notice #notice_data-attach {
+filter: alpha(opacity=0);
+} \ No newline at end of file
diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css
index 8a03a4d77..cab42f16f 100644
--- a/theme/identica/css/display.css
+++ b/theme/identica/css/display.css
@@ -57,6 +57,8 @@ a,
div.notice-options input,
.form_user_block input.submit,
.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
.entity_send-a-message a,
.form_user_nudge input.submit,
.entity_nudge p,
@@ -148,6 +150,8 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
.form_user_nudge input.submit,
.form_user_block input.submit,
.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
.entity_nudge p {
background-position: 0 40%;
background-repeat: no-repeat;
@@ -177,7 +181,9 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
background-image:url(../../base/images/icons/twotone/green/mail.gif);
}
.form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
background-image:url(../../base/images/icons/twotone/green/shield.gif);
}
diff --git a/theme/identica/css/ie.css b/theme/identica/css/ie.css
index 2f463bb44..69db16aad 100644
--- a/theme/identica/css/ie.css
+++ b/theme/identica/css/ie.css
@@ -3,7 +3,12 @@
.notice-options input.submit {
color:#fff;
}
-
#site_nav_local_views a {
background-color:#D0DFE7;
}
+#form_notice .form_note + label {
+background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+}
+#form_notice #notice_data-attach {
+filter: alpha(opacity=0);
+} \ No newline at end of file