diff options
-rw-r--r-- | actions/editgroup.php | 225 | ||||
-rw-r--r-- | actions/groupbyid.php | 108 | ||||
-rw-r--r-- | actions/groupmembers.php | 138 | ||||
-rw-r--r-- | actions/joingroup.php | 149 | ||||
-rw-r--r-- | actions/leavegroup.php | 153 | ||||
-rw-r--r-- | actions/newgroup.php | 204 | ||||
-rw-r--r-- | actions/showgroup.php | 386 | ||||
-rwxr-xr-x | classes/Group_inbox.php | 22 | ||||
-rwxr-xr-x | classes/Group_member.php | 24 | ||||
-rwxr-xr-x | classes/Related_group.php | 22 | ||||
-rw-r--r-- | classes/User.php | 28 | ||||
-rwxr-xr-x | classes/User_group.php | 90 | ||||
-rwxr-xr-x[-rw-r--r--] | classes/laconica.ini | 46 | ||||
-rw-r--r-- | db/laconica.sql | 57 | ||||
-rw-r--r-- | htaccess.sample | 10 | ||||
-rw-r--r-- | js/util.js | 33 | ||||
-rw-r--r-- | lib/groupeditform.php | 160 | ||||
-rw-r--r-- | lib/groupnav.php | 107 | ||||
-rw-r--r-- | lib/joinform.php | 116 | ||||
-rw-r--r-- | lib/leaveform.php | 116 | ||||
-rw-r--r-- | lib/util.php | 30 |
21 files changed, 2213 insertions, 11 deletions
diff --git a/actions/editgroup.php b/actions/editgroup.php new file mode 100644 index 000000000..82b78cc5c --- /dev/null +++ b/actions/editgroup.php @@ -0,0 +1,225 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Edit an existing group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Group + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@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); +} + +/** + * Add a new group + * + * This is the form for adding a new 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 EditgroupAction extends Action +{ + var $msg; + var $group = null; + + function title() + { + return sprintf(_('Edit %s group'), $this->group->nickname); + } + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_config('inboxes','enabled')) { + $this->serverError(_('Inboxes must be enabled for groups to work')); + return false; + } + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to create a group.')); + return false; + } + + $nickname_arg = $this->trimmed('nickname'); + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('editgroup', $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; + } + + $cur = common_current_user(); + + if (!$cur->isAdmin($group)) { + $this->clientError(_('You must be an admin to edit the group'), 403); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On GET, show the form. On POST, try to save the group. + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->trySave(); + } else { + $this->showForm(); + } + } + + function showForm($msg=null) + { + $this->msg = $msg; + $this->showPage(); + } + + function showLocalNav() + { + $nav = new GroupNav($this, $this->group); + $nav->show(); + } + + function showContent() + { + $form = new GroupEditForm($this, $this->group); + $form->show(); + } + + function showPageNotice() + { + if ($this->msg) { + $this->element('p', 'error', $this->msg); + } else { + $this->element('p', 'instructions', + _('Use this form to edit the group.')); + } + } + + function trySave() + { + $nickname = common_canonical_nickname($this->trimmed('nickname')); + $fullname = $this->trimmed('fullname'); + $homepage = $this->trimmed('homepage'); + $description = $this->trimmed('description'); + $location = $this->trimmed('location'); + + if (!Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => NICKNAME_FMT))) { + $this->showForm(_('Nickname must have only lowercase letters '. + 'and numbers and no spaces.')); + return; + } else if ($this->nicknameExists($nickname)) { + $this->showForm(_('Nickname already in use. Try another one.')); + return; + } else if (!User_group::allowedNickname($nickname)) { + $this->showForm(_('Not a valid nickname.')); + return; + } else if (!is_null($homepage) && (strlen($homepage) > 0) && + !Validate::uri($homepage, + array('allowed_schemes' => + array('http', 'https')))) { + $this->showForm(_('Homepage is not a valid URL.')); + return; + } else if (!is_null($fullname) && strlen($fullname) > 255) { + $this->showForm(_('Full name is too long (max 255 chars).')); + return; + } else if (!is_null($description) && strlen($description) > 140) { + $this->showForm(_('description is too long (max 140 chars).')); + return; + } else if (!is_null($location) && strlen($location) > 255) { + $this->showForm(_('Location is too long (max 255 chars).')); + return; + } + + $orig = clone($this->group); + + $this->group->nickname = $nickname; + $this->group->fullname = $fullname; + $this->group->homepage = $homepage; + $this->group->description = $description; + $this->group->location = $location; + $this->group->created = common_sql_now(); + + $result = $this->group->update($orig); + + if (!$result) { + common_log_db_error($this->group, 'UPDATE', __FILE__); + $this->serverError(_('Could not update group.')); + } + + if ($this->group->nickname != $orig->nickname) { + common_redirect(common_local_url('editgroup', + array('nickname' => $nickname)), + 307); + } else { + $this->showForm(_('Options saved.')); + } + } + + function nicknameExists($nickname) + { + $group = User_group::staticGet('nickname', $nickname); + return (!is_null($group) && + $group != false && + $group->id != $this->group->id); + } +}
\ No newline at end of file diff --git a/actions/groupbyid.php b/actions/groupbyid.php new file mode 100644 index 000000000..678119a94 --- /dev/null +++ b/actions/groupbyid.php @@ -0,0 +1,108 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Permalink for group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Group + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@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/noticelist.php'; +require_once INSTALLDIR.'/lib/feedlist.php'; + +/** + * Permalink for a group + * + * The group nickname can change, but not the group ID. So we use + * an URL with the ID in it as the permanent identifier. + * + * @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 GroupbyidAction extends Action +{ + /** group we're viewing. */ + var $group = null; + + /** + * Is this page read-only? + * + * @return boolean true + */ + + function isReadOnly() + { + return true; + } + + function prepare($args) + { + parent::prepare($args); + + if (!common_config('inboxes','enabled')) { + $this->serverError(_('Inboxes must be enabled for groups to work')); + return false; + } + + $id = $this->arg('id'); + + if (!$id) { + $this->clientError(_('No ID')); + return false; + } + + common_debug("Got ID $id"); + + $this->group = User_group::staticGet('id', $id); + + if (!$this->group) { + $this->clientError(_('No such group'), 404); + return false; + } + + return true; + } + + /** + * Handle the request + * + * Shows a profile for the group, some controls, and a list of + * group notices. + * + * @return void + */ + + function handle($args) + { + common_redirect($this->group->homeUrl(), 303); + } +}
\ No newline at end of file diff --git a/actions/groupmembers.php b/actions/groupmembers.php new file mode 100644 index 000000000..53395c418 --- /dev/null +++ b/actions/groupmembers.php @@ -0,0 +1,138 @@ +<?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); +} + +require_once(INSTALLDIR.'/lib/profilelist.php'); +require_once INSTALLDIR.'/lib/publicgroupnav.php'; + +/** + * List of group members + * + * @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 GroupmembersAction extends Action +{ + var $page = null; + + function isReadOnly() + { + 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('groupmembers', $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 group members'), + $this->group->nickname); + } else { + return sprintf(_('%s group members, 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 in 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; + + $members = $this->group->getMembers($offset, $limit); + + if ($members) { + $member_list = new ProfileList($members, null, $this); + $member_list->show(); + } + + $members->free(); + + $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, + $this->page, 'groupmembers', + array('nickname' => $this->group->nickname)); + } +}
\ No newline at end of file diff --git a/actions/joingroup.php b/actions/joingroup.php new file mode 100644 index 000000000..45470f088 --- /dev/null +++ b/actions/joingroup.php @@ -0,0 +1,149 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Join a group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Group + * @package 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); +} + +/** + * Join a group + * + * This is the action for joining a group. It works more or less like the subscribe action + * for users. + * + * @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 JoingroupAction extends Action +{ + var $group = null; + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_config('inboxes','enabled')) { + $this->serverError(_('Inboxes must be enabled for groups to work')); + return false; + } + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to join a group.')); + return false; + } + + $nickname_arg = $this->trimmed('nickname'); + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('editgroup', $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; + } + + $cur = common_current_user(); + + if ($cur->isMember($group)) { + $this->clientError(_('You are already a member of that group'), 403); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On POST, add the current user to the group + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $cur = common_current_user(); + + $member = new Group_member(); + + $member->group_id = $this->group->id; + $member->profile_id = $cur->id; + $member->created = common_sql_now(); + + $result = $member->insert(); + + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + $this->serverError(sprintf(_('Could not join user %s to group %s'), + $cur->nickname, $this->group->nickname)); + } + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, sprintf(_('%s joined group %s'), + $cur->nickname, + $this->group->nickname)); + $this->elementEnd('head'); + $this->elementStart('body'); + $lf = new LeaveForm($this, $this->group); + $lf->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect(common_local_url('groupmembers', array('nickname' => + $this->group->nickname))); + } + } +}
\ No newline at end of file diff --git a/actions/leavegroup.php b/actions/leavegroup.php new file mode 100644 index 000000000..587208b36 --- /dev/null +++ b/actions/leavegroup.php @@ -0,0 +1,153 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Leave a group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Group + * @package 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); +} + +/** + * Leave a group + * + * This is the action for leaving a group. It works more or less like the subscribe action + * for users. + * + * @category Group + * @package 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 LeavegroupAction extends Action +{ + var $group = null; + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_config('inboxes','enabled')) { + $this->serverError(_('Inboxes must be enabled for groups to work')); + return false; + } + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to join a group.')); + return false; + } + + $nickname_arg = $this->trimmed('nickname'); + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('editgroup', $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; + } + + $cur = common_current_user(); + + if (!$cur->isMember($group)) { + $this->clientError(_('You are not a member of that group'), 403); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On POST, add the current user to the group + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $cur = common_current_user(); + + $member = new Group_member(); + + $member->group_id = $this->group->id; + $member->profile_id = $cur->id; + + if (!$member->find(true)) { + $this->serverError(_('Could not find membership record.')); + return; + } + + $result = $member->delete(); + + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + $this->serverError(sprintf(_('Could not remove user %s to group %s'), + $cur->nickname, $this->group->nickname)); + } + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, sprintf(_('%s left group %s'), + $cur->nickname, + $this->group->nickname)); + $this->elementEnd('head'); + $this->elementStart('body'); + $jf = new JoinForm($this, $this->group); + $jf->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect(common_local_url('groupmembers', array('nickname' => + $this->group->nickname))); + } + } +}
\ No newline at end of file diff --git a/actions/newgroup.php b/actions/newgroup.php new file mode 100644 index 000000000..41c095ec0 --- /dev/null +++ b/actions/newgroup.php @@ -0,0 +1,204 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Add a new group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Group + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@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); +} + +/** + * Add a new group + * + * This is the form for adding a new 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 NewgroupAction extends Action +{ + var $msg; + + function title() + { + return _('New group'); + } + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_config('inboxes','enabled')) { + $this->serverError(_('Inboxes must be enabled for groups to work')); + return false; + } + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to create a group.')); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On GET, show the form. On POST, try to save the group. + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->trySave(); + } else { + $this->showForm(); + } + } + + function showForm($msg=null) + { + $this->msg = $msg; + $this->showPage(); + } + + function showContent() + { + $form = new GroupEditForm($this); + $form->show(); + } + + function showPageNotice() + { + if ($this->msg) { + $this->element('p', 'error', $this->msg); + } else { + $this->element('p', 'instructions', + _('Use this form to create a new group.')); + } + } + + function trySave() + { + $nickname = $this->trimmed('nickname'); + $fullname = $this->trimmed('fullname'); + $homepage = $this->trimmed('homepage'); + $description = $this->trimmed('description'); + $location = $this->trimmed('location'); + + if (!Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => NICKNAME_FMT))) { + $this->showForm(_('Nickname must have only lowercase letters '. + 'and numbers and no spaces.')); + return; + } else if ($this->nicknameExists($nickname)) { + $this->showForm(_('Nickname already in use. Try another one.')); + return; + } else if (!User_group::allowedNickname($nickname)) { + $this->showForm(_('Not a valid nickname.')); + return; + } else if (!is_null($homepage) && (strlen($homepage) > 0) && + !Validate::uri($homepage, + array('allowed_schemes' => + array('http', 'https')))) { + $this->showForm(_('Homepage is not a valid URL.')); + return; + } else if (!is_null($fullname) && strlen($fullname) > 255) { + $this->showForm(_('Full name is too long (max 255 chars).')); + return; + } else if (!is_null($description) && strlen($description) > 140) { + $this->showForm(_('description is too long (max 140 chars).')); + return; + } else if (!is_null($location) && strlen($location) > 255) { + $this->showForm(_('Location is too long (max 255 chars).')); + return; + } + + $cur = common_current_user(); + + // Checked in prepare() above + + assert(!is_null($cur)); + + $group = new User_group(); + + $group->query('BEGIN'); + + $group->nickname = $nickname; + $group->fullname = $fullname; + $group->homepage = $homepage; + $group->description = $description; + $group->location = $location; + $group->created = common_sql_now(); + + $result = $group->insert(); + + if (!$result) { + common_log_db_error($group, 'INSERT', __FILE__); + $this->serverError(_('Could not create group.')); + } + + $member = new Group_member(); + + $member->group_id = $group->id; + $member->profile_id = $cur->id; + $member->is_admin = 1; + $member->created = $group->created; + + $result = $member->insert(); + + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + $this->serverError(_('Could not set group membership.')); + } + + $group->query('COMMIT'); + + common_redirect($group->homeUrl(), 307); + } + + function nicknameExists($nickname) + { + $group = User_group::staticGet('nickname', $nickname); + return (!is_null($group) && $group != false); + } +}
\ No newline at end of file diff --git a/actions/showgroup.php b/actions/showgroup.php new file mode 100644 index 000000000..a1e89a3a3 --- /dev/null +++ b/actions/showgroup.php @@ -0,0 +1,386 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Group main page + * + * 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> + * @author Sarven Capadisli <csarven@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/noticelist.php'; +require_once INSTALLDIR.'/lib/feedlist.php'; + +define('MEMBERS_PER_SECTION', 81); + +/** + * Group main page + * + * @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 ShowgroupAction extends Action +{ + /** group we're viewing. */ + var $group = null; + /** page we're viewing. */ + var $page = null; + + /** + * Is this page read-only? + * + * @return boolean true + */ + + function isReadOnly() + { + return true; + } + + /** + * Title of the page + * + * @return string page title, with page number + */ + + function title() + { + if ($this->page == 1) { + return sprintf(_("%s group"), $this->group->nickname); + } else { + return sprintf(_("%s group, page %d"), + $this->group->nickname, + $this->page); + } + } + + /** + * Prepare the action + * + * Reads and validates arguments and instantiates the attributes. + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_config('inboxes','enabled')) { + $this->serverError(_('Inboxes must be enabled for groups to work')); + return false; + } + + $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('showgroup', $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; + } + + /** + * Handle the request + * + * Shows a profile for the group, some controls, and a list of + * group notices. + * + * @return void + */ + + function handle($args) + { + $this->showPage(); + } + + function showLocalNav() + { + $nav = new GroupNav($this, $this->group); + $nav->show(); + } + + /** + * Show the page content + * + * Shows a group profile and a list of group notices + */ + + function showContent() + { + $this->showGroupProfile(); + $this->showGroupNotices(); + } + + /** + * Show the group notices + * + * @return void + */ + + function showGroupNotices() + { + $notice = $this->group->getNotices(($this->page-1)*NOTICES_PER_PAGE, + NOTICES_PER_PAGE + 1); + + $nl = new NoticeList($notice, $this); + $cnt = $nl->show(); + + $this->pagination($this->page > 1, + $cnt > NOTICES_PER_PAGE, + $this->page, + 'showgroup', + array('nickname' => $this->group->nickname)); + } + + /** + * Show the group profile + * + * Information about the group + * + * @return void + */ + + function showGroupProfile() + { + $this->elementStart('div', array('id' => 'group_profile', + 'class' => 'vcard author')); + + $this->element('h2', null, _('Group profile')); + + $this->elementStart('dl', 'group_depiction'); + $this->element('dt', null, _('Photo')); + $this->elementStart('dd'); + + $logo = ($this->group->homepage_logo) ? + $this->group->homepage_logo : User_group::defaultLogo(AVATAR_PROFILE_SIZE); + + $this->element('img', array('src' => $logo, + 'class' => 'photo avatar', + 'width' => AVATAR_PROFILE_SIZE, + 'height' => AVATAR_PROFILE_SIZE, + 'alt' => $this->group->nickname)); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'group_nickname'); + $this->element('dt', null, _('Nickname')); + $this->elementStart('dd'); + $hasFN = ($this->group->fullname) ? 'nickname url uid' : 'fn nickname url uid'; + $this->element('a', array('href' => $this->group->homeUrl(), + 'rel' => 'me', 'class' => $hasFN), + $this->group->nickname); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + + if ($this->group->fullname) { + $this->elementStart('dl', 'group_fn'); + $this->element('dt', null, _('Full name')); + $this->elementStart('dd'); + $this->element('span', 'fn', $this->group->fullname); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + + if ($this->group->location) { + $this->elementStart('dl', 'group_location'); + $this->element('dt', null, _('Location')); + $this->element('dd', 'location', $this->group->location); + $this->elementEnd('dl'); + } + + if ($this->group->homepage) { + $this->elementStart('dl', 'group_url'); + $this->element('dt', null, _('URL')); + $this->elementStart('dd'); + $this->element('a', array('href' => $this->group->homepage, + 'rel' => 'me', 'class' => 'url'), + $this->group->homepage); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + + if ($this->group->description) { + $this->elementStart('dl', 'group_note'); + $this->element('dt', null, _('Note')); + $this->element('dd', 'note', $this->group->description); + $this->elementEnd('dl'); + } + + $this->elementEnd('div'); + + $this->elementStart('div', array('id' => 'group_actions')); + $this->element('h2', null, _('Group actions')); + $this->elementStart('ul'); + $this->elementStart('li', array('id' => 'group_subscribe')); + $cur = common_current_user(); + if ($cur) { + if ($cur->isMember($this->group)) { + $lf = new LeaveForm($this, $this->group); + $lf->show(); + } else { + $jf = new JoinForm($this, $this->group); + $jf->show(); + } + } + + $this->elementEnd('li'); + + $this->elementEnd('ul'); + $this->elementEnd('div'); + } + + /** + * Show a list of links to feeds this page produces + * + * @return void + */ + + function showExportData() + { + $fl = new FeedList($this); + $fl->show(array(0=>array('href'=>common_local_url('grouprss', + array('nickname' => $this->group->nickname)), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'notices'))); + } + + /** + * Show a list of links to feeds this page produces + * + * @return void + */ + + function showFeeds() + { + $url = + common_local_url('grouprss', + array('nickname' => $this->group->nickname)); + + $this->element('link', array('rel' => 'alternate', + 'href' => $url, + 'type' => 'application/rss+xml', + 'title' => sprintf(_('Notice feed for %s group'), + $this->group->nickname))); + } + + /** + * Fill in the sidebar. + * + * @return void + */ + + function showSections() + { + $this->showMembers(); + } + + /** + * Show mini-list of members + * + * @return void + */ + + function showMembers() + { + $member = $this->group->getMembers(0, MEMBERS_PER_SECTION); + + if (!$member) { + return; + } + + $this->elementStart('div', array('id' => 'user_subscriptions', + 'class' => 'section')); + + $this->element('h2', null, _('Members')); + + $this->elementStart('ul', 'users'); + + $cnt = 0; + + while ($member->fetch() && ++$cnt < MEMBERS_PER_SECTION) { + + $cnt++; + + $this->elementStart('li', 'vcard'); + $this->elementStart('a', array('title' => ($member->fullname) ? + $member->fullname : + $member->nickname, + 'href' => $member->profileurl, + 'rel' => 'contact', + 'class' => 'url')); + $avatar = $member->getAvatar(AVATAR_MINI_SIZE); + $this->element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'class' => 'avatar photo', + 'alt' => ($member->fullname) ? + $member->fullname : + $member->nickname)); + $this->element('span', 'fn nickname', $member->nickname); + $this->elementEnd('a'); + $this->elementEnd('li'); + } + + $this->elementEnd('ul'); + + if ($cnt == MEMBERS_PER_SECTION) { + $this->element('a', array('href' => common_local_url('groupmembers', + array('nickname' => $this->group->nickname))), + _('All members')); + } + } +}
\ No newline at end of file diff --git a/classes/Group_inbox.php b/classes/Group_inbox.php new file mode 100755 index 000000000..826889636 --- /dev/null +++ b/classes/Group_inbox.php @@ -0,0 +1,22 @@ +<?php +/** + * Table Definition for group_inbox + */ +require_once 'classes/Memcached_DataObject'; + +class Group_inbox extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'group_inbox'; // table name + public $group_id; // int(4) primary_key not_null + public $notice_id; // int(4) primary_key not_null + public $created; // datetime() not_null + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Group_inbox',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/Group_member.php b/classes/Group_member.php new file mode 100755 index 000000000..32243fe45 --- /dev/null +++ b/classes/Group_member.php @@ -0,0 +1,24 @@ +<?php +/** + * Table Definition for group_member + */ +require_once 'classes/Memcached_DataObject.php'; + +class Group_member extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'group_member'; // table name + public $group_id; // int(4) primary_key not_null + public $profile_id; // int(4) primary_key not_null + public $is_admin; // tinyint(1) + public $created; // datetime() not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Group_member',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/Related_group.php b/classes/Related_group.php new file mode 100755 index 000000000..40e4904c5 --- /dev/null +++ b/classes/Related_group.php @@ -0,0 +1,22 @@ +<?php +/** + * Table Definition for related_group + */ +require_once 'classes/Memcached_DataObject'; + +class Related_group extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'related_group'; // table name + public $group_id; // int(4) primary_key not_null + public $related_group_id; // int(4) primary_key not_null + public $created; // datetime() not_null + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Related_group',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/User.php b/classes/User.php index 92ff8776b..51e23fccf 100644 --- a/classes/User.php +++ b/classes/User.php @@ -492,4 +492,32 @@ class User extends Memcached_DataObject return true; } + function isMember($group) + { + $mem = new Group_member(); + + $mem->group_id = $group->id; + $mem->profile_id = $this->id; + + if ($mem->find()) { + return true; + } else { + return false; + } + } + + function isAdmin($group) + { + $mem = new Group_member(); + + $mem->group_id = $group->id; + $mem->profile_id = $this->id; + $mem->is_admin = 1; + + if ($mem->find()) { + return true; + } else { + return false; + } + } } diff --git a/classes/User_group.php b/classes/User_group.php new file mode 100755 index 000000000..06c031610 --- /dev/null +++ b/classes/User_group.php @@ -0,0 +1,90 @@ +<?php +/** + * Table Definition for user_group + */ +require_once 'classes/Memcached_DataObject.php'; + +class User_group extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'user_group'; // table name + public $id; // int(4) primary_key not_null + public $nickname; // varchar(64) unique_key + public $fullname; // varchar(255) + public $homepage; // varchar(255) + public $description; // varchar(140) + public $location; // varchar(255) + public $original_logo; // varchar(255) + public $homepage_logo; // varchar(255) + public $stream_logo; // varchar(255) + public $mini_logo; // varchar(255) + public $created; // datetime() not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_group',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function defaultLogo($size) + { + static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile', + AVATAR_STREAM_SIZE => 'stream', + AVATAR_MINI_SIZE => 'mini'); + return theme_path('default-avatar-'.$sizenames[$size].'.png'); + } + + function homeUrl() + { + return common_local_url('showgroup', + array('nickname' => $this->nickname)); + } + + function permalink() + { + return common_local_url('groupbyid', + array('id' => $this->id)); + } + + function getNotices($offset, $limit) + { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN group_inbox ON notice.id = group_inbox.notice_id ' . + 'WHERE group_inbox.group_id = %d '; + return Notice::getStream(sprintf($qry, $this->id), + 'group:notices:'.$this->id, + $offset, $limit); + } + + function allowedNickname($nickname) + { + static $blacklist = array('new'); + return !in_array($nickname, $blacklist); + } + + function getMembers($offset=0, $limit=null) + { + $qry = + 'SELECT profile.* ' . + 'FROM profile JOIN group_member '. + 'ON profile.id = group_member.profile_id ' . + 'WHERE group_member.group_id = %d ' . + 'ORDER BY group_member.created DESC '; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $members = new Profile(); + + $cnt = $members->query(sprintf($qry, $this->id)); + + return $members; + } +} diff --git a/classes/laconica.ini b/classes/laconica.ini index db76b2dee..255122a97 100644..100755 --- a/classes/laconica.ini +++ b/classes/laconica.ini @@ -98,6 +98,26 @@ id = K service = K uri = U +[group_inbox] +group_id = 129 +notice_id = 129 +created = 142 + +[group_inbox__keys] +group_id = K +notice_id = K + +[group_member] +group_id = 129 +profile_id = 129 +is_admin = 17 +created = 142 +modified = 384 + +[group_member__keys] +group_id = K +profile_id = K + [invitation] code = 130 user_id = 129 @@ -225,6 +245,15 @@ claimed = 14 notice_id = K transport = K +[related_group] +group_id = 129 +related_group_id = 129 +created = 142 + +[related_group__keys] +group_id = K +related_group_id = K + [remember_me] code = 130 user_id = 129 @@ -332,6 +361,23 @@ jabber = U sms = U uri = U +[user_group] +id = 129 +nickname = 2 +fullname = 2 +homepage = 2 +description = 2 +location = 2 +original_logo = 2 +homepage_logo = 2 +stream_logo = 2 +mini_logo = 2 +created = 142 +modified = 384 + +[user_group__keys] +id = N + [user_openid] canonical = 130 display = 130 diff --git a/db/laconica.sql b/db/laconica.sql index a366a6bcb..012270b51 100644 --- a/db/laconica.sql +++ b/db/laconica.sql @@ -368,3 +368,60 @@ create table profile_block ( constraint primary key (blocker, blocked) ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + +create table user_group ( + + id integer auto_increment primary key comment 'unique identifier', + + nickname varchar(64) unique key comment 'nickname for addressing', + fullname varchar(255) comment 'display name', + homepage varchar(255) comment 'URL, cached so we dont regenerate', + description varchar(140) comment 'descriptive biography', + location varchar(255) comment 'related physical location, if any', + + original_logo varchar(255) comment 'original size logo', + homepage_logo varchar(255) comment 'homepage (profile) size logo', + stream_logo varchar(255) comment 'stream-sized logo', + mini_logo varchar(255) comment 'mini logo', + + created datetime not null comment 'date this record was created', + modified timestamp comment 'date this record was modified', + + index user_group_nickname_idx (nickname) + +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + +create table group_member ( + + group_id integer not null comment 'foreign key to user_group' references user_group (id), + profile_id integer not null comment 'foreign key to profile table' references profile (id), + is_admin boolean default false comment 'is this user an admin?', + + created datetime not null comment 'date this record was created', + modified timestamp comment 'date this record was modified', + + constraint primary key (group_id, profile_id) + +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + +create table related_group ( + + group_id integer not null comment 'foreign key to user_group' references user_group (id), + related_group_id integer not null comment 'foreign key to user_group' references user_group (id), + + created datetime not null comment 'date this record was created', + + constraint primary key (group_id, related_group_id) + +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + +create table group_inbox ( + group_id integer not null comment 'group receiving the message' references user_group (id), + notice_id integer not null comment 'notice received' references notice (id), + created datetime not null comment 'date the notice was created', + + constraint primary key (group_id, notice_id), + index group_inbox_created_idx (created) + +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + diff --git a/htaccess.sample b/htaccess.sample index 8c8b2152a..a0fc74248 100644 --- a/htaccess.sample +++ b/htaccess.sample @@ -85,6 +85,15 @@ RewriteRule ^peopletag/([a-zA-Z0-9]+)$ index.php?action=peopletag&tag=$1 [L,QSA] RewriteRule ^featured/?$ index.php?action=featured [L,QSA] RewriteRule ^favorited/?$ index.php?action=favorited [L,QSA] +RewriteRule ^group/new index.php?action=newgroup [L,QSA] +RewriteRule ^group/([a-zA-Z0-9]+)/edit index.php?action=editgroup&nickname=$1 [L,QSA] +RewriteRule ^group/([a-zA-Z0-9]+)/join index.php?action=joingroup&nickname=$1 [L,QSA] +RewriteRule ^group/([a-zA-Z0-9]+)/leave index.php?action=leavegroup&nickname=$1 [L,QSA] +RewriteRule ^group/([a-zA-Z0-9]+)/members index.php?action=groupmembers&nickname=$1 [L,QSA] +RewriteRule ^group/([0-9]+)/id index.php?action=groupbyid&id=$1 [L,QSA] +RewriteRule ^group/([a-zA-Z0-9]+)/rss index.php?action=grouprss&nickname=$1 [L,QSA] +RewriteRule ^group/([a-zA-Z0-9]+)$ index.php?action=showgroup&nickname=$1 [L,QSA] + # Twitter-compatible API rewrites # XXX: Surely these can be refactored a little -- Zach RewriteRule ^api/statuses/public_timeline(.*)$ index.php?action=api&apiaction=statuses&method=public_timeline$1 [L,QSA] @@ -145,6 +154,7 @@ RewriteRule ^(\w+)/favorites/rss$ index.php?action=favoritesrss&nickname=$1 [L,Q RewriteRule ^(\w+)/inbox$ index.php?action=inbox&nickname=$1 [L,QSA] RewriteRule ^(\w+)/outbox$ index.php?action=outbox&nickname=$1 [L,QSA] RewriteRule ^(\w+)/microsummary$ index.php?action=microsummary&nickname=$1 [L,QSA] +RewriteRule ^(\w+)/groups$ index.php?action=usergroups&nickname=$1 [L,QSA] RewriteRule ^(\w+)$ index.php?action=showstream&nickname=$1 [L,QSA] diff --git a/js/util.js b/js/util.js index d98b63dd7..bb68c2587 100644 --- a/js/util.js +++ b/js/util.js @@ -24,7 +24,7 @@ $(document).ready(function(){ var remaining = maxLength - currentLength; var counter = $("#notice_text-count"); counter.text(remaining); - + if (remaining <= 0) { $("#form_notice").addClass("warning"); } else { @@ -45,10 +45,10 @@ $(document).ready(function(){ if ($("#notice_data-text").length) { $("#notice_data-text").bind("keyup", counter); $("#notice_data-text").bind("keydown", submitonreturn); - + // run once in case there's something in there counter(); - + // set the focus $("#notice_data-text").focus(); } @@ -73,6 +73,24 @@ $(document).ready(function(){ } }; + var joinoptions = { dataType: 'xml', + success: function(xml) { var new_form = document._importNode($('form', xml).get(0), true); + var leave = new_form.id; + var join = leave.replace('leave', 'join'); + $('form#'+join).replaceWith(new_form); + $('form#'+leave).ajaxForm(leaveoptions).each(addAjaxHidden); + } + }; + + var leaveoptions = { dataType: 'xml', + success: function(xml) { var new_form = document._importNode($('form', xml).get(0), true); + var join = new_form.id; + var leave = join.replace('join', 'leave'); + $('form#'+leave).replaceWith(new_form); + $('form#'+join).ajaxForm(joinoptions).each(addAjaxHidden); + } + }; + function addAjaxHidden() { var ajax = document.createElement('input'); ajax.setAttribute('type', 'hidden'); @@ -83,14 +101,18 @@ $(document).ready(function(){ $("form.form_favor").ajaxForm(favoptions); $("form.form_disfavor").ajaxForm(disoptions); + $("form.form_group_join").ajaxForm(joinoptions); + $("form.form_group_leave").ajaxForm(leaveoptions); $("form.form_favor").each(addAjaxHidden); $("form.form_disfavor").each(addAjaxHidden); + $("form.form_group_join").each(addAjaxHidden); + $("form.form_group_leave").each(addAjaxHidden); $("#nudge").ajaxForm ({ dataType: 'xml', beforeSubmit: function(xml) { $("form#nudge input[type=submit]").attr("disabled", "disabled"); $("form#nudge input[type=submit]").addClass("disabled"); }, - success: function(xml) { $("#nudge").replaceWith(document._importNode($("#nudge_response", xml).get(0),true)); + success: function(xml) { $("#nudge").replaceWith(document._importNode($("#nudge_response", xml).get(0),true)); $("#nudge input[type=submit]").removeAttr("disabled"); $("#nudge input[type=submit]").removeClass("disabled"); } @@ -134,7 +156,6 @@ $(document).ready(function(){ $(".form_user_subscribe").each(addAjaxHidden); $(".form_user_unsubscribe").each(addAjaxHidden); - var PostNotice = { dataType: 'xml', beforeSubmit: function(formData, jqForm, options) { if ($("#notice_data-text").get(0).value.length == 0) { $("#form_notice").addClass("warning"); @@ -166,7 +187,7 @@ $(document).ready(function(){ $(".notice").hover( function () { $(this).addClass('hover'); - }, + }, function () { $(this).removeClass('hover'); } diff --git a/lib/groupeditform.php b/lib/groupeditform.php new file mode 100644 index 000000000..f6e326078 --- /dev/null +++ b/lib/groupeditform.php @@ -0,0 +1,160 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Form for editing a group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Form + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 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/form.php'; + +/** + * Form for editing 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 UnsubscribeForm + */ + +class GroupEditForm extends Form +{ + /** + * group for user to join + */ + + var $group = null; + + /** + * Constructor + * + * @param Action $out output channel + * @param User_group $group group to join + */ + + function __construct($out=null, $group=null) + { + parent::__construct($out); + + $this->group = $group; + } + + /** + * ID of the form + * + * @return string ID of the form + */ + + function id() + { + if ($this->group) { + return 'group_edit-' . $this->group->id; + } else { + return 'group_add'; + } + } + + /** + * class of the form + * + * @return string of the form class + */ + + function formClass() + { + return 'form_group_add'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + if ($this->group) { + return common_local_url('editgroup', + array('nickname' => $this->group->nickname)); + } else { + return common_local_url('newgroup'); + } + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart('ul', 'form_data'); + $this->out->elementStart('li'); + $this->out->input('nickname', _('Nickname'), + ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $this->group->nickname, + _('1-64 lowercase letters or numbers, no punctuation or spaces')); + $this->out->elementEnd('li'); + $this->out->elementStart('li'); + $this->out->input('fullname', _('Full name'), + ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $this->group->fullname); + $this->out->elementEnd('li'); + $this->out->elementStart('li'); + $this->out->input('homepage', _('Homepage'), + ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $this->group->homepage, + _('URL of the homepage or blog of the group or topic')); + $this->out->elementEnd('li'); + $this->out->elementStart('li'); + $this->out->textarea('description', _('description'), + ($this->out->arg('description')) ? $this->out->arg('description') : $this->group->description, + _('Describe the group or topic in 140 chars')); + $this->out->elementEnd('li'); + $this->out->elementStart('li'); + $this->out->input('location', _('Location'), + ($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'); + $this->out->elementEnd('ul'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Save')); + } +} diff --git a/lib/groupnav.php b/lib/groupnav.php new file mode 100644 index 000000000..32803fd42 --- /dev/null +++ b/lib/groupnav.php @@ -0,0 +1,107 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Tabset for a particular group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Action + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 2008 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/widget.php'; + +/** + * Tabset for a group + * + * Shows a group of tabs for a particular user group + * + * @category Output + * @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 HTMLOutputter + */ + +class GroupNav extends Widget +{ + var $action = null; + var $group = null; + + /** + * Construction + * + * @param Action $action current action, used for output + */ + + function __construct($action=null, $group=null) + { + parent::__construct($action); + $this->action = $action; + $this->group = $group; + } + + /** + * Show the menu + * + * @return void + */ + + function show() + { + $action_name = $this->action->trimmed('action'); + $nickname = $this->group->nickname; + + $this->out->elementStart('ul', array('class' => 'nav')); + $this->out->menuItem(common_local_url('showgroup', array('nickname' => + $nickname)), + _('Group'), + sprintf(_('%s group'), $nickname), + $action_name == 'showgroup', + 'nav_group_group'); + $this->out->menuItem(common_local_url('groupmembers', array('nickname' => + $nickname)), + _('Members'), + sprintf(_('%s group members'), $nickname), + $action_name == 'groupmembers', + 'nav_group_members'); + + $cur = common_current_user(); + + if ($cur && $cur->isAdmin($this->group)) { + $this->out->menuItem(common_local_url('editgroup', array('nickname' => + $nickname)), + _('Admin'), + sprintf(_('Edit %s group properties'), $nickname), + $action_name == 'editgroup', + 'nav_group_admin'); + } + $this->out->elementEnd('ul'); + } +} diff --git a/lib/joinform.php b/lib/joinform.php new file mode 100644 index 000000000..1edb2f72d --- /dev/null +++ b/lib/joinform.php @@ -0,0 +1,116 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Form for joining a group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Form + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 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/form.php'; + +/** + * Form for joining 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 UnsubscribeForm + */ + +class JoinForm extends Form +{ + /** + * group for user to join + */ + + var $group = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param group $group group to join + */ + + function __construct($out=null, $group=null) + { + parent::__construct($out); + + $this->group = $group; + } + + /** + * ID of the form + * + * @return string ID of the form + */ + + function id() + { + return 'group-join-' . $this->group->id; + } + + /** + * class of the form + * + * @return string of the form class + */ + + function formClass() + { + return 'form_group_join'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('joingroup', + array('nickname' => $this->group->nickname)); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Join')); + } +} diff --git a/lib/leaveform.php b/lib/leaveform.php new file mode 100644 index 000000000..696559a25 --- /dev/null +++ b/lib/leaveform.php @@ -0,0 +1,116 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Form for leaving a group + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Form + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 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/form.php'; + +/** + * Form for leaving 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 UnsubscribeForm + */ + +class LeaveForm extends Form +{ + /** + * group for user to leave + */ + + var $group = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param group $group group to leave + */ + + function __construct($out=null, $group=null) + { + parent::__construct($out); + + $this->group = $group; + } + + /** + * ID of the form + * + * @return string ID of the form + */ + + function id() + { + return 'group-leave-' . $this->group->id; + } + + /** + * class of the form + * + * @return string of the form class + */ + + function formClass() + { + return 'form_group_leave'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('leavegroup', + array('nickname' => $this->group->nickname)); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Leave')); + } +} diff --git a/lib/util.php b/lib/util.php index ed7284183..4d4a3b20f 100644 --- a/lib/util.php +++ b/lib/util.php @@ -915,6 +915,24 @@ function common_fancy_url($action, $args=null) } else { return common_path('main/sup'); } + case 'newgroup': + return common_path('group/new'); + case 'showgroup': + return common_path('group/'.$args['nickname']); + case 'editgroup': + return common_path('group/'.$args['nickname'].'/edit'); + case 'joingroup': + return common_path('group/'.$args['nickname'].'/join'); + case 'leavegroup': + return common_path('group/'.$args['nickname'].'/leave'); + case 'groupbyid': + return common_path('group/'.$args['id'].'/id'); + case 'grouprss': + return common_path('group/'.$args['nickname'].'/rss'); + case 'groupmembers': + return common_path('group/'.$args['nickname'].'/members'); + case 'usergroups': + return common_path($args['nickname'].'/groups'); default: return common_simple_url($action, $args); } @@ -1026,14 +1044,16 @@ function common_redirect($url, $code=307) 302 => "Found", 303 => "See Other", 307 => "Temporary Redirect"); + header("Status: ${code} $status[$code]"); header("Location: $url"); - common_start_xml('a', - '-//W3C//DTD XHTML 1.0 Strict//EN', - 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'); - common_element('a', array('href' => $url), $url); - common_end_xml(); + $xo = new XMLOutputter(); + $xo->startXML('a', + '-//W3C//DTD XHTML 1.0 Strict//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'); + $xo->output('a', array('href' => $url), $url); + $xo->endXML(); exit; } |